mirror of
https://github.com/koreader/koreader
synced 2024-10-31 21:20:20 +00:00
4e944dc918
Use texteditor if available Auto disable if patches folder doesnt exist And show a restart message after file edit Sort menu entries in execution order Warning sign for failing patches
107 lines
4.3 KiB
Lua
107 lines
4.3 KiB
Lua
--[[--
|
|
Allows applying developer patches while running KOReader.
|
|
|
|
The contents in `koreader/patches/` are applied on calling `userpatch.applyPatches(priority)`.
|
|
--]]--
|
|
|
|
local isAndroid, android = pcall(require, "android")
|
|
|
|
local userpatch = {
|
|
-- priorities for user patches,
|
|
early_once = "0", -- to be started early on startup (once after an update)
|
|
early = "1", -- to be started early on startup (always, but after an `early_once`)
|
|
late = "2", -- to be started after UIManager is ready (always)
|
|
-- 3-7 are reserved for later use
|
|
before_exit = "8", -- to be started a bit before exit before settings are saved (always)
|
|
on_exit = "9", -- to be started right before exit (always)
|
|
|
|
-- hash table for patch execution status
|
|
-- key: name of the patch
|
|
-- value: true (success), false (failure), nil (not executed)
|
|
execution_status = {},
|
|
|
|
-- the patch function itself
|
|
applyPatches = function(priority) end, -- to be overwritten, if the device allows it.
|
|
}
|
|
|
|
if isAndroid and android.prop.flavor == "fdroid" then
|
|
return userpatch -- allows to use applyPatches as a no-op on F-Droid flavor
|
|
end
|
|
|
|
local lfs = require("libs/libkoreader-lfs")
|
|
local logger = require("logger")
|
|
local sort = require("sort")
|
|
local DataStorage = require("datastorage")
|
|
|
|
-- the directory KOReader is installed in (and runs from)
|
|
local package_dir = lfs.currentdir()
|
|
-- the directory where KOReader stores user data
|
|
local data_dir = DataStorage:getDataDir()
|
|
|
|
--- Run lua patches
|
|
-- Execution order order is alphanum-sort for humans version 4: `1-patch.lua` is executed before `10-patch.lua`
|
|
-- (see http://notebook.kulchenko.com/algorithms/alphanumeric-natural-sorting-for-humans-in-lua)
|
|
-- string directory ... to scan through (flat no recursion)
|
|
-- string priority ... only files starting with `priority` followed by digits and a '-' will be processed.
|
|
-- return true if a patch was executed
|
|
local function runUserPatchTasks(dir, priority)
|
|
if lfs.attributes(dir, "mode") ~= "directory" then
|
|
return
|
|
end
|
|
|
|
local patches = {}
|
|
for entry in lfs.dir(dir) do
|
|
local mode = lfs.attributes(dir .. "/" .. entry, "mode")
|
|
if entry and mode == "file" and entry:match("^" .. priority .. "%d*%-") then
|
|
table.insert(patches, entry)
|
|
end
|
|
end
|
|
|
|
if #patches == 0 then
|
|
return -- nothing to do
|
|
end
|
|
|
|
table.sort(patches, sort.natsort_cmp())
|
|
|
|
for i, entry in ipairs(patches) do
|
|
local fullpath = dir .. "/" .. entry
|
|
if lfs.attributes(fullpath, "mode") == "file" then
|
|
if fullpath:match("%.lua$") then -- execute patch-files first
|
|
logger.info("Applying patch:", fullpath)
|
|
local ok, err = pcall(dofile, fullpath)
|
|
userpatch.execution_status[entry] = ok
|
|
if not ok then
|
|
logger.warn("Patching failed:", err)
|
|
-- Only show InfoMessage, when UIManager is working
|
|
if priority >= userpatch.late and priority < userpatch.before_exit then
|
|
-- Only developers (advanced users) will use this mechanism.
|
|
-- A warning on a patch failure after an OTA update will simplify troubleshooting.
|
|
local UIManager = require("ui/uimanager")
|
|
local InfoMessage = require("ui/widget/infomessage")
|
|
UIManager:show(InfoMessage:new{text = "Error applying patch:\n" .. fullpath}) -- no translate
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
--- This function applies lua patches from `/koreader/patches`
|
|
---- @string priority ... one of the defined priorities in the userpatch hashtable
|
|
function userpatch.applyPatches(priority)
|
|
local patch_dir = data_dir .. "/patches"
|
|
local update_once_marker = package_dir .. "/update_once.marker"
|
|
local update_once_pending = lfs.attributes(update_once_marker, "mode") == "file"
|
|
|
|
if priority >= userpatch.early or update_once_pending then
|
|
local executed_something = runUserPatchTasks(patch_dir, priority)
|
|
if executed_something and update_once_pending then
|
|
-- Only delete update once marker if `early_once` updates have been applied.
|
|
os.remove(update_once_marker) -- Prevent another execution on a further starts.
|
|
end
|
|
end
|
|
end
|
|
|
|
return userpatch
|