2
0
mirror of https://github.com/koreader/koreader synced 2024-11-13 19:11:25 +00:00
koreader/frontend/luadata.lua
poire-z 04d9a557aa Use fsync() for more robust setting files saving
Bump base for util.fsyncOpenedFile() and util.fsyncDirectory().

Use these to force flush to storage device when
saving luasetting, docsetting, and history.lua.
Also dont rotate them to .old until they are at least
60 seconds old.
Also make auto_save_paging_count count right.

Also bump crengine: open (as text) small or empty files
2019-12-10 23:57:32 +01:00

172 lines
4.8 KiB
Lua

--[[--
Handles append-mostly data such as KOReader's bookmarks and dictionary search history.
]]
local LuaSettings = require("luasettings")
local dbg = require("dbg")
local dump = require("dump")
local logger = require("logger")
local util = require("util")
local LuaData = LuaSettings:new{
name = "",
max_backups = 9,
}
--- Creates a new LuaData instance.
function LuaData:open(file_path, o) -- luacheck: ignore 312
if o and type(o) ~= "table" then
if dbg.is_on then
error("LuaData: got "..type(o)..", table expected")
else
o = {}
end
end
-- always initiate a new instance
-- careful, `o` is already a table so we use parentheses
self = LuaData:new(o)
local new = {file=file_path, data={}}
-- some magic to allow for self-describing function names
local _local = {}
_local.__index = _local
setmetatable(_G, _local)
_local[self.name.."Entry"] = function(table)
if table.index then
-- we've got a deleted setting, overwrite with nil
if not table.data then new.data[table.index] = nil end
new.data[table.index] = new.data[table.index] or {}
local size = util.tableSize(table.data)
if size == 1 then
for key, value in pairs(table.data) do
new.data[table.index][key] = value
end
else
new.data[table.index] = table.data
end
-- we've got it all at once
else
new.data = table
end
end
local ok = false
if lfs.attributes(new.file, "mode") == "file" then
ok = pcall(dofile, new.file)
if ok then
logger.dbg("data is read from ", new.file)
else
logger.dbg(new.file, " is invalid, remove.")
os.remove(new.file)
end
end
if not ok then
for i=1, self.max_backups, 1 do
local backup_file = new.file..".old."..i
if lfs.attributes(backup_file, "mode") == "file" then
if pcall(dofile, backup_file) then
logger.dbg("data is read from ", backup_file)
break
else
logger.dbg(backup_file, " is invalid, remove.")
os.remove(backup_file)
end
end
end
end
return setmetatable(new, {__index = self})
end
--- Saves a setting.
function LuaData:saveSetting(key, value)
self.data[key] = value
self:append{
index = key,
data = value,
}
return self
end
--- Deletes a setting.
function LuaData:delSetting(key)
self.data[key] = nil
self:append{
index = key,
}
return self
end
--- Adds item to table.
function LuaData:addTableItem(table_name, value)
local settings_table = self:has(table_name) and self:readSetting(table_name) or {}
table.insert(settings_table, value)
self.data[table_name] = settings_table
self:append{
index = table_name,
data = {[#settings_table] = value},
}
end
local _orig_removeTableItem = LuaSettings.removeTableItem
--- Removes index from table.
function LuaData:removeTableItem(key, index)
_orig_removeTableItem(self, key, index)
self:flush()
return self
end
--- Appends settings to disk.
function LuaData:append(data)
if not self.file then return end
local f_out = io.open(self.file, "a")
if f_out ~= nil then
os.setlocale('C', 'numeric')
f_out:write(self.name.."Entry")
f_out:write(dump(data))
f_out:write("\n")
f_out:close()
end
return self
end
--- Replaces existing settings with table.
function LuaData:reset(table)
self.data = table
self:flush()
return self
end
--- Writes all settings to disk (does not append).
function LuaData:flush()
if not self.file then return end
if lfs.attributes(self.file, "mode") == "file" then
for i=1, self.max_backups, 1 do
if lfs.attributes(self.file..".old."..i, "mode") == "file" then
logger.dbg("LuaData: Rename ", self.file .. ".old." .. i, " to ", self.file .. ".old." .. i+1)
os.rename(self.file, self.file .. ".old." .. i+1)
else
break
end
end
logger.dbg("LuaData: Rename ", self.file, " to ", self.file .. ".old.1")
os.rename(self.file, self.file .. ".old.1")
end
logger.dbg("LuaData: Write to ", self.file)
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!\n")
f_out:write(self.name.."Entry")
f_out:write(dump(self.data))
f_out:write("\n")
f_out:close()
end
return self
end
return LuaData