-- ***********************************************************************
--
-- Module to display the King James Bible
-- Copyright 2021 by Sean Conner.
--
-- This program is free software: you can redistribute it and/or modify it
-- under the terms of the GNU General Public License as published by the
-- Free Software Foundation, either version 3 of the License, or (at your
-- option) any later version.
--
-- This program is distributed in the hope that it will be useful, but
-- WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-- Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program. If not, see .
--
-- Comments, questions and criticisms can be sent to: sean@conman.org
-- ***********************************************************************
-- luacheck: globals CONF tumbler init bookend read_entry entries
-- luacheck: globals filename day_titles
-- luacheck: ignore 611
local fsys = require "org.conman.fsys"
local date = require "org.conman.date"
local ENTITIES = require "org.conman.const.entity"
local lpeg = require "lpeg"
local io = require "io"
local os = require "os"
local utf8 = require "utf8"
local string = require "string"
local math = require "math"
local table = require "table"
local loadfile = loadfile
local require = require
local setmetatable = setmetatable
local type = type
local _ENV = {}
tumbler = require "org.conman.app.mod_blog.tumbler"
CONF =
{
require = setmetatable(
{},
{
__index = function(s) return s end,
__call = function(s) return s end,
}
),
}
-- ***********************************************************************
local function date_cmp(d1,d2)
local rc
rc = d1.year - d2.year if rc ~= 0 then return rc end
rc = d1.month - d2.month if rc ~= 0 then return rc end
rc = d1.day - d2.day if rc ~= 0 then return rc end
return d1.part - d2.part
end
-- ***********************************************************************
local function swap_endpoints(range)
local start = {}
local stop = {}
start.year = range.stop.year
start.month = range.stop.month
stop.year = range.start.year
stop.month = range.start.month
if range.ustart == 'month' then
stop.day = 31
stop.part = 23
elseif range.ustart == 'day' then
stop.day = range.start.day
stop.part = 23
elseif range.ustart == 'part' then
stop.day = range.start.day
stop.part = range.start.part
elseif range.ustart == 'year' then
assert(false)
end
if range.ustop == 'month' then
start.day = 1
start.part = 1
elseif range.ustop == 'day' then
start.day = range.stop.day
start.part = 1
elseif range.ustop == 'part' then
start.day = range.stop.day
start.part = range.stop.part
elseif range.ustop == 'year' then
assert(false)
end
range.start = stop
range.stop = start
end
-- ***********************************************************************
local function when_to_filename(w)
return string.format("%s/%4d/%02d/%02d/%d",CONF.basedir,w.year,w.month,w.day,w.part)
end
-- ***********************************************************************
local function when_to_meta(w,meta)
return string.format("%s/%4d/%02d/%02d/%s",CONF.basedir,w.year,w.month,w.day,meta)
end
-- ***********************************************************************
local parse_date = lpeg.Ct(
lpeg.Cg(lpeg.R"09"^1 / math.tointeger,"year") * lpeg.P"/"
* lpeg.Cg(lpeg.R"09"^1 / math.tointeger,"month") * lpeg.P"/"
* lpeg.Cg(lpeg.R"09"^1 / math.tointeger,"day") * lpeg.P"."
* lpeg.Cg(lpeg.R"09"^1 / math.tointeger,"part")
)
-- ***********************************************************************
local deent do
local char = lpeg.P"" * lpeg.C(lpeg.R"09"^1) * lpeg.P";" / utf8.char
+ lpeg.P"&" * lpeg.C(lpeg.R("az","AZ","09")^1) * lpeg.P";" / ENTITIES
+ lpeg.S" \t\r\n"^1 / " "
+ lpeg.P(1)
deent = lpeg.Cs(char^0)
end
-- ***********************************************************************
function init(config)
config = config or os.getenv("BLOG_CONFIG")
if not config then return end
local f = loadfile(config,"t",CONF)
if not f then return end
f()
return CONF
end
-- ***********************************************************************
function bookend(which)
local file = io.open(CONF.basedir .. "/." .. which)
local line = file:read("l")
local when = parse_date:match(line)
file:close()
return when
end
-- ***********************************************************************
function read_entry(when)
local function get_meta(meta)
local f = io.open(when_to_meta(when,meta),"r")
local l = 0
local line
if not f then
return ""
end
repeat
line = f:read("l")
l = l + 1
until l == when.part
f:close()
return line and deent:match(line) or ""
end
local filename = when_to_filename(when)
if fsys.access(filename,"r") then
local entry = {}
entry.when = { year = when.year , month = when.month , day = when.day , part = when.part }
entry.title = get_meta("titles")
entry.class = get_meta("class")
entry.author = get_meta("authors")
entry.status = get_meta("status")
entry.adtag = get_meta("adtag")
entry._filename = filename
local f = io.open(filename,"r")
entry.body = f:read("a")
f:close()
return entry
end
end
-- ***********************************************************************
function entries(range)
if type(range) == 'string' then
range = tumbler.new(range,bookend "first", bookend "last")
end
if date_cmp(range.start,range.stop) <= 0 then
local function nup(state)
if date_cmp(state.start,state.stop) > 0 then
return
end
local entry = read_entry(state.start)
if entry then
state.start.part = state.start.part + 1
return entry
end
state.start.part = 1
state.start.day = state.start.day + 1
if state.start.day > date.daysinmonth(state.start) then
state.start.day = 1
state.start.month = state.start.month + 1
if state.start.month == 13 then
state.start.month = 1
state.start.year = state.start.year + 1
end
end
return nup(state)
end
return nup,range
else
swap_endpoints(range)
range.start.part = range.start.part + 1
local function ndown(state)
state.start.part = state.start.part - 1
if state.start.part == 0 then
state.start.part = 23
state.start.day = state.start.day - 1
if state.start.day == 0 then
state.start.day = 31
state.start.month = state.start.month - 1
if state.start.month == 0 then
state.start.month = 12
state.start.year = state.start.year - 1
end
end
end
if date_cmp(state.start,state.stop) < 0 then
return
end
local entry = read_entry(state.start)
if entry then
return entry
end
return ndown(state)
end
return ndown,range
end
end
-- ***********************************************************************
function filename(tuple)
return string.format(
"%s/%04d/%02d/%02d/%s",
CONF.basedir,
tuple.start.year,
tuple.start.month,
tuple.start.day,
tuple.filename
)
end
-- ***********************************************************************
function day_titles(when)
local res = {}
local filename = when_to_meta(when,"titles")
if filename and fsys.access(filename,"r") then
for title in io.lines(filename) do
table.insert(res,deent:match(title))
end
end
return res
end
-- ***********************************************************************
return _ENV