utility added to make sure file handles can't be left open

This commit is contained in:
Tangent / Rose / Nebula Rosa 2024-11-08 18:16:58 -07:00
parent 599d5f0612
commit 98a603e647
2 changed files with 69 additions and 66 deletions

View File

@ -162,21 +162,19 @@ local function download_pages(config)
local temporary_html_file_name = utility.tmp_file_name() local temporary_html_file_name = utility.tmp_file_name()
os.execute("curl " .. download_url:enquote() .. " > " .. temporary_html_file_name) os.execute("curl " .. download_url:enquote() .. " > " .. temporary_html_file_name)
local html_file, err = io.open(temporary_html_file_name, "r") utility.open(temporary_html_file_name, "r", "Could not download " .. download_url:enquote())(function(html_file)
if not html_file then error("Could not download " .. download_url:enquote()) end local raw_html = html_file:read("*all")
local raw_html = html_file:read("*a")
html_file:close() local parser = htmlparser.parse(raw_html)
local content_tag = parser:select(".article > div > div") -- TODO add ability to set selector in config!
local text = content_tag[1]:getcontent()
utility.open(section_dir .. page .. ".html", "w")(function(page_file)
page_file:write(text .. "\n")
end)
end)
os.execute("rm " .. temporary_html_file_name) os.execute("rm " .. temporary_html_file_name)
local parser = htmlparser.parse(raw_html)
local content_tag = parser:select(".article > div > div") -- TODO add ability to set selector in config!
local text = content_tag[1]:getcontent()
local page_file, err = io.open(section_dir .. page .. ".html", "w")
if not page_file then error(err) end
page_file:write(text .. "\n")
page_file:close()
os.execute("sleep " .. tostring(math.random(5))) -- avoid rate limiting os.execute("sleep " .. tostring(math.random(5))) -- avoid rate limiting
end end
end end
@ -201,66 +199,61 @@ local function concatenate_pages(config)
for section = config.sections.start, config.sections.finish do for section = config.sections.start, config.sections.finish do
local section_dir = working_dir .. path_separator .. tostring(section) .. path_separator local section_dir = working_dir .. path_separator .. tostring(section) .. path_separator
local section_file, err = io.open(working_dir .. path_separator .. tostring(section) .. ".md", "w") utility.open(working_dir .. path_separator .. tostring(section) .. ".md", "w")(function(section_file)
if not section_file then error(err) end for page = 1, config.page_counts[section - (config.sections.start - 1)] do
utility.open(section_dir .. page .. ".md", "r")(function(page_file)
for page = 1, config.page_counts[section - (config.sections.start - 1)] do if config.sections.automatic_naming then
local page_file, err = io.open(section_dir .. page .. ".md", "r") local naming_patterns = {
if not page_file then error(err) end "^Prologue$",
if config.sections.automatic_naming then "^Chapter %d+$",
local naming_patterns = { "^%*%*CHAPTER ",
"^Prologue$", }
"^Chapter %d+$", local line = page_file:read("*line")
"^%*%*CHAPTER ", while line do
} for _, pattern in ipairs(naming_patterns) do
local line = page_file:read("*line") if line:find(pattern) then
while line do line = "# " .. line
for _, pattern in ipairs(naming_patterns) do end
if line:find(pattern) then end
line = "# " .. line section_file:write(line .. "\n")
line = page_file:read("*line")
end end
else
section_file:write(page_file:read("*all"))
end end
section_file:write(line .. "\n") section_file:write("\n") -- guarantees no accidental line collisions
line = page_file:read("*line") end)
end
else
section_file:write(page_file:read("*a"))
end end
section_file:write("\n") -- guarantees no accidental line collisions end)
page_file:close()
end
section_file:close()
end end
end end
local function write_markdown_file(config) local function write_markdown_file(config)
local working_dir = get_base_file_name(config) local working_dir = get_base_file_name(config)
local markdown_file, err = io.open(get_base_file_name(config) .. ".md", "w") utility.open(get_base_file_name(config) .. ".md", "w")(function(markdown_file)
if not markdown_file then error(err) end markdown_file:write(format_metadata(config))
markdown_file:write(format_metadata(config)) markdown_file:write(copyright_warning)
markdown_file:write(copyright_warning)
for section = config.sections.start, config.sections.finish do for section = config.sections.start, config.sections.finish do
if config.sections.naming then if config.sections.naming then
markdown_file:write("\n\n# " .. config.sections.naming .. " " .. tostring(section)) markdown_file:write("\n\n# " .. config.sections.naming .. " " .. tostring(section))
elseif config.section_titles then elseif config.section_titles then
markdown_file:write("\n\n# " .. config.section_titles[section]) markdown_file:write("\n\n# " .. config.section_titles[section])
end
markdown_file:write("\n\n")
local section_file_name = working_dir .. path_separator .. tostring(section)
utility.open(section_file_name .. ".md", "r")(function(section_file)
markdown_file:write(section_file:read("*all"))
end)
end end
markdown_file:write("\n\n")
local section_file_name = working_dir .. path_separator .. tostring(section) markdown_file:write("\n\n# Ebook Creation Metadata\n\n")
local section_file, err = io.open(section_file_name .. ".md", "r") markdown_file:write(copyright_warning)
if not section_file then error(err) end markdown_file:write("This ebook was created using the following config:\n\n")
markdown_file:write(section_file:read("*a")) markdown_file:write("```json\n" .. config.config_file_text .. "\n```\n")
section_file:close() end)
end
markdown_file:write("\n\n# Ebook Creation Metadata\n\n")
markdown_file:write(copyright_warning)
markdown_file:write("This ebook was created using the following config:\n\n")
markdown_file:write("```json\n" .. config.config_file_text .. "\n```\n")
markdown_file:close()
end end
local function make_epub(config) local function make_epub(config)
@ -305,10 +298,9 @@ local function argparse(arguments, positional_arguments)
end end
local function main(arguments) local function main(arguments)
local config_file, err = io.open(arguments.json_file_name, "r") local config = utility.open(arguments.json_file_name, "r")(function(config_file)
if not config_file then error(err) end return load_config(config_file:read("*all"))
local config = load_config(config_file:read("*all")) end)
config_file:close()
local actions = { local actions = {
download = download_pages, download = download_pages,

View File

@ -100,6 +100,17 @@ utility.tmp_file_name = function()
return "." .. utility.uuid() .. ".tmp" return "." .. utility.uuid() .. ".tmp"
end end
-- io.open, but errors are immediately thrown, and the file is closed for you
utility.open = function(file_name, mode, custom_error_message)
local file, err = io.open(file_name, mode)
if not file then error(custom_error_message or err) end
return function(fn)
local result = fn(file)
file:close()
return result
end
end
utility.escape_quotes = function(input) utility.escape_quotes = function(input)
-- the order of these commands is important and must be preserved -- the order of these commands is important and must be preserved
input = input:gsub("\\", "\\\\") input = input:gsub("\\", "\\\\")