2017-04-15 12:45:56 +00:00
|
|
|
--[[--
|
|
|
|
This module handles generic settings as well as KOReader's global settings system.
|
|
|
|
]]
|
|
|
|
|
2016-06-12 18:28:44 +00:00
|
|
|
local dump = require("dump")
|
2019-12-10 22:00:08 +00:00
|
|
|
local ffiutil = require("ffi/util")
|
2018-01-21 17:44:15 +00:00
|
|
|
local lfs = require("libs/libkoreader-lfs")
|
|
|
|
local logger = require("logger")
|
2016-06-12 18:28:44 +00:00
|
|
|
|
|
|
|
local LuaSettings = {}
|
|
|
|
|
2017-10-07 20:13:46 +00:00
|
|
|
function LuaSettings:new(o)
|
|
|
|
o = o or {}
|
|
|
|
setmetatable(o, self)
|
|
|
|
self.__index = self
|
|
|
|
return o
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Opens a settings file.
|
2016-06-12 18:28:44 +00:00
|
|
|
function LuaSettings:open(file_path)
|
|
|
|
local new = {file=file_path}
|
|
|
|
local ok, stored
|
|
|
|
|
2018-01-21 17:44:15 +00:00
|
|
|
-- File being absent and returning an empty table is a use case,
|
|
|
|
-- so logger.warn() only if there was an existing file
|
|
|
|
local existing = lfs.attributes(new.file, "mode") == "file"
|
|
|
|
|
2016-06-12 18:28:44 +00:00
|
|
|
ok, stored = pcall(dofile, new.file)
|
|
|
|
if ok and stored then
|
|
|
|
new.data = stored
|
|
|
|
else
|
2018-01-21 17:44:15 +00:00
|
|
|
if existing then logger.warn("Failed reading", new.file, "(probably corrupted).") end
|
|
|
|
-- Fallback to .old if it exists
|
|
|
|
ok, stored = pcall(dofile, new.file..".old")
|
|
|
|
if ok and stored then
|
|
|
|
if existing then logger.warn("read from backup file", new.file..".old") end
|
|
|
|
new.data = stored
|
|
|
|
else
|
|
|
|
if existing then logger.warn("no usable backup file for", new.file, "to read from") end
|
|
|
|
new.data = {}
|
|
|
|
end
|
2016-06-12 18:28:44 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return setmetatable(new, {__index = LuaSettings})
|
|
|
|
end
|
|
|
|
|
2019-08-23 17:53:53 +00:00
|
|
|
--- @todo DocSettings can return a LuaSettings to use following awesome features.
|
2017-03-28 22:10:36 +00:00
|
|
|
function LuaSettings:wrap(data)
|
|
|
|
local new = {data = type(data) == "table" and data or {}}
|
|
|
|
return setmetatable(new, {__index = LuaSettings})
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--[[--Reads child settings.
|
|
|
|
|
|
|
|
@usage
|
|
|
|
|
|
|
|
Settings:saveSetting("key", {
|
|
|
|
a = "b",
|
|
|
|
c = "true",
|
|
|
|
d = false,
|
|
|
|
})
|
|
|
|
|
|
|
|
local child = Settings:child("key")
|
|
|
|
|
|
|
|
child:readSetting("a")
|
|
|
|
-- result "b"
|
|
|
|
]]
|
2017-03-28 22:10:36 +00:00
|
|
|
function LuaSettings:child(key)
|
|
|
|
return LuaSettings:wrap(self:readSetting(key))
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Reads a setting.
|
2016-06-12 18:28:44 +00:00
|
|
|
function LuaSettings:readSetting(key)
|
|
|
|
return self.data[key]
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Saves a setting.
|
2016-06-12 18:28:44 +00:00
|
|
|
function LuaSettings:saveSetting(key, value)
|
|
|
|
self.data[key] = value
|
2017-06-14 17:32:16 +00:00
|
|
|
return self
|
2016-06-12 18:28:44 +00:00
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Deletes a setting.
|
2016-06-12 18:28:44 +00:00
|
|
|
function LuaSettings:delSetting(key)
|
|
|
|
self.data[key] = nil
|
2017-06-14 17:32:16 +00:00
|
|
|
return self
|
2016-06-12 18:28:44 +00:00
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Checks if setting exists.
|
2016-09-01 07:05:40 +00:00
|
|
|
function LuaSettings:has(key)
|
|
|
|
return self:readSetting(key) ~= nil
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Checks if setting does not exist.
|
2016-09-01 07:05:40 +00:00
|
|
|
function LuaSettings:hasNot(key)
|
|
|
|
return self:readSetting(key) == nil
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Checks if setting is `true`.
|
2016-09-01 07:05:40 +00:00
|
|
|
function LuaSettings:isTrue(key)
|
|
|
|
return string.lower(tostring(self:readSetting(key))) == "true"
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Checks if setting is `false`.
|
2017-03-28 22:10:36 +00:00
|
|
|
function LuaSettings:isFalse(key)
|
|
|
|
return string.lower(tostring(self:readSetting(key))) == "false"
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Checks if setting is `nil` or `true`.
|
2016-09-01 07:05:40 +00:00
|
|
|
function LuaSettings:nilOrTrue(key)
|
|
|
|
return self:hasNot(key) or self:isTrue(key)
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Checks if setting is `nil` or `false`.
|
2017-03-28 22:10:36 +00:00
|
|
|
function LuaSettings:nilOrFalse(key)
|
|
|
|
return self:hasNot(key) or self:isFalse(key)
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Flips `nil` or `true` to `false`.
|
2016-09-01 07:05:40 +00:00
|
|
|
function LuaSettings:flipNilOrTrue(key)
|
|
|
|
if self:nilOrTrue(key) then
|
|
|
|
self:saveSetting(key, false)
|
|
|
|
else
|
|
|
|
self:delSetting(key)
|
|
|
|
end
|
2017-06-14 17:32:16 +00:00
|
|
|
return self
|
2016-09-01 07:05:40 +00:00
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Flips `nil` or `false` to `true`.
|
2017-03-28 22:10:36 +00:00
|
|
|
function LuaSettings:flipNilOrFalse(key)
|
|
|
|
if self:nilOrFalse(key) then
|
|
|
|
self:saveSetting(key, true)
|
|
|
|
else
|
|
|
|
self:delSetting(key)
|
|
|
|
end
|
2017-06-14 17:32:16 +00:00
|
|
|
return self
|
2017-03-28 22:10:36 +00:00
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Flips setting to `true`.
|
2016-09-01 07:05:40 +00:00
|
|
|
function LuaSettings:flipTrue(key)
|
|
|
|
if self:isTrue(key) then
|
|
|
|
self:delSetting(key)
|
|
|
|
else
|
|
|
|
self:saveSetting(key, true)
|
|
|
|
end
|
2017-06-14 17:32:16 +00:00
|
|
|
return self
|
2016-09-01 07:05:40 +00:00
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Flips setting to `false`.
|
2017-03-28 22:10:36 +00:00
|
|
|
function LuaSettings:flipFalse(key)
|
|
|
|
if self:isFalse(key) then
|
|
|
|
self:delSetting(key)
|
|
|
|
else
|
2019-03-14 19:58:45 +00:00
|
|
|
self:saveSetting(key, false)
|
2017-03-28 22:10:36 +00:00
|
|
|
end
|
2017-06-14 17:32:16 +00:00
|
|
|
return self
|
2017-03-28 22:10:36 +00:00
|
|
|
end
|
|
|
|
|
2020-12-24 08:07:27 +00:00
|
|
|
-- Initializes settings per extension with default values
|
|
|
|
function LuaSettings:initializeExtSettings(key, defaults, force_init)
|
|
|
|
local curr = self:readSetting(key)
|
|
|
|
if not curr or force_init then
|
|
|
|
self:saveSetting(key, defaults)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Returns saved setting for given extension
|
|
|
|
function LuaSettings:getSettingForExt(key, ext)
|
|
|
|
local saved_settings = self:readSetting(key) or {}
|
|
|
|
return saved_settings[ext]
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Sets setting for given extension
|
|
|
|
function LuaSettings:saveSettingForExt(key, value, ext)
|
|
|
|
local saved_settings = self:readSetting(key) or {}
|
|
|
|
saved_settings[ext] = value
|
|
|
|
self:saveSetting(key, saved_settings)
|
|
|
|
end
|
|
|
|
|
2017-10-07 20:13:46 +00:00
|
|
|
--- Adds item to table.
|
|
|
|
function LuaSettings:addTableItem(key, value)
|
|
|
|
local settings_table = self:has(key) and self:readSetting(key) or {}
|
|
|
|
table.insert(settings_table, value)
|
|
|
|
self:saveSetting(key, settings_table)
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Removes index from table.
|
|
|
|
function LuaSettings:removeTableItem(key, index)
|
|
|
|
local settings_table = self:has(key) and self:readSetting(key) or {}
|
|
|
|
table.remove(settings_table, index)
|
|
|
|
self:saveSetting(key, settings_table)
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Replaces existing settings with table.
|
2017-03-24 06:36:15 +00:00
|
|
|
function LuaSettings:reset(table)
|
|
|
|
self.data = table
|
2017-06-14 17:32:16 +00:00
|
|
|
return self
|
2017-03-24 06:36:15 +00:00
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Writes settings to disk.
|
2016-06-12 18:28:44 +00:00
|
|
|
function LuaSettings:flush()
|
2017-03-28 22:10:36 +00:00
|
|
|
if not self.file then return end
|
2019-12-10 22:00:08 +00:00
|
|
|
local directory_updated = false
|
2018-01-21 17:44:15 +00:00
|
|
|
if lfs.attributes(self.file, "mode") == "file" then
|
2019-12-10 22:00:08 +00:00
|
|
|
-- As an additional safety measure (to the ffiutil.fsync* calls
|
|
|
|
-- used below), we only backup the file to .old when it has
|
|
|
|
-- not been modified in the last 60 seconds. This should ensure
|
|
|
|
-- in the case the fsync calls are not supported that the OS
|
|
|
|
-- may have itself sync'ed that file content in the meantime.
|
|
|
|
local mtime = lfs.attributes(self.file, "modification")
|
|
|
|
if mtime < os.time() - 60 then
|
|
|
|
os.rename(self.file, self.file .. ".old")
|
|
|
|
directory_updated = true -- fsync directory content too below
|
|
|
|
end
|
2018-01-21 17:44:15 +00:00
|
|
|
end
|
2016-06-12 18:28:44 +00:00
|
|
|
local f_out = io.open(self.file, "w")
|
|
|
|
if f_out ~= nil then
|
|
|
|
os.setlocale('C', 'numeric')
|
|
|
|
f_out:write("-- we can read Lua syntax here!\nreturn ")
|
2020-11-12 19:38:11 +00:00
|
|
|
f_out:write(dump(self.data, nil, true))
|
2016-06-12 18:28:44 +00:00
|
|
|
f_out:write("\n")
|
2019-12-10 22:00:08 +00:00
|
|
|
ffiutil.fsyncOpenedFile(f_out) -- force flush to the storage device
|
2016-06-12 18:28:44 +00:00
|
|
|
f_out:close()
|
|
|
|
end
|
2019-12-10 22:00:08 +00:00
|
|
|
if directory_updated then
|
|
|
|
-- Ensure the file renaming is flushed to storage device
|
|
|
|
ffiutil.fsyncDirectory(self.file)
|
|
|
|
end
|
2017-06-14 17:32:16 +00:00
|
|
|
return self
|
2016-06-12 18:28:44 +00:00
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Closes settings file.
|
2016-06-12 18:28:44 +00:00
|
|
|
function LuaSettings:close()
|
|
|
|
self:flush()
|
|
|
|
end
|
|
|
|
|
2017-04-15 12:45:56 +00:00
|
|
|
--- Purges settings file.
|
2016-06-12 18:28:44 +00:00
|
|
|
function LuaSettings:purge()
|
|
|
|
if self.file then
|
|
|
|
os.remove(self.file)
|
|
|
|
end
|
2017-06-14 17:32:16 +00:00
|
|
|
return self
|
2016-06-12 18:28:44 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return LuaSettings
|