Initial commit

main
mio 2022-07-25 18:49:12 +00:00
commit e1400e1ed8
5 changed files with 404 additions and 0 deletions

4
.gitignore vendored 100644
View File

@ -0,0 +1,4 @@
log
config.lua
config.ini
template.ini

10
config.sample.lua 100644
View File

@ -0,0 +1,10 @@
local config = {
author = "Gem",
title = "gemlog",
subtitle = "a Gemini log",
url = "gemini://",
output_dir = "/path/to/local/log",
rsync_remote = "/path/to/remote/log",
}
return config

247
gemwriter.lua 100644
View File

@ -0,0 +1,247 @@
local conf = require("config")
local tpl = require("template")
local util = require("util")
local writer = {}
writer.app = {
exec_name = "gw",
last_updated = "2022-07-24",
name = "gemwriter",
version = "0.1",
}
writer.conf = {}
writer.defaults = {
atom_file = "atom.xml",
author = "Gem",
-- Atom feed date format
entry_date_format = "%Y-%m-%dT%X+00:00",
entry_ext = ".gmi",
entry_slug = os.date("%Y-%m-%d-%H%M%S"),
entry_file_pattern = "[%/](%d%d%d%d%-%d%d%-%d%d)(.*)[%.](%a*)",
gen_atom_feed = true,
gen_index_page = true,
index_file = "index.gmi",
page_slug = "untitled-" .. os.date("%Y%m%d%H%M%S"),
title = "gemlog",
subtitle = "a Gemini log",
url = "",
output_dir = "log",
rsync_exec = "/usr/bin/rsync",
rsync_options = "-avz",
rsync_remote = "",
}
writer.entries = {}
writer.msg = {
add_entry = "Created ",
help = writer.app.exec_name .. [[ [options] [slug]
Options:
page [slug] Add a new page with the given name
post [slug] Add a new entry with the given name
index Generate an index page and feed of entries
publish Index and copy posts to a remote server (requires rsync)
help Show this help message
version Print version info
]],
index = "Created index page and feed.",
publish = "Published log.",
}
function writer.load_config(config)
for k, v in pairs(writer.defaults) do
if config[k] == nil then
writer.conf[k] = writer.defaults[k]
else
writer.conf[k] = config[k]
end
end
end
function writer.replace_vars(str, vars, vals)
local text = str
for k, v in pairs(vars) do
text = string.gsub(text, v, vals[k])
end
return text
end
function writer.add_entry(slug)
local text = tpl.log_gmi
text = string.gsub(text, tpl.vars.entry.date,
os.date(writer.conf.entry_date_format))
text = string.gsub(text, tpl.vars.entry.author, writer.conf.author)
local entry_name = slug .. writer.conf.entry_ext
os.execute("test -d " .. writer.conf.output_dir .. " || mkdir -p " ..
writer.conf.output_dir)
util.write_file(writer.conf.output_dir .. "/" .. entry_name, text)
end
function writer.get_entries()
local ls_cmd = io.popen("ls " .. writer.conf.output_dir .. " | grep " ..
writer.conf.entry_ext)
local ls = ls_cmd:read("*a")
local entries = {}
local n = 0
local files_list = util.split_str(ls)
for f = 1, #files_list do
if files_list[f] ~= writer.conf.index_file then
n = n + 1
entries[n] = { files_list[f], util.read_file(writer.conf.output_dir ..
"/" .. files_list[f]) }
end
end
return entries
end
function writer.get_entries_meta()
local meta = {}
local entries = writer.get_entries()
for e = 1, #entries do
meta[e] = {}
if string.find(entries[e][2], "# ") == nil then
meta[e]["title"] = entries[e][1]
meta[e]["content"] = entries[e][2]
else
meta[e]["title"] = util.extract_str(entries[e][2], "# ", "\n")
hi1, hi2 = string.find(entries[e][2], "# ")
meta[e]["content"] = string.sub(entries[e][2], hi1,
string.len(entries[e][2]))
end
if string.find(entries[e][2], "author: ") == nil then
meta[e]["author"] = writer.conf.author
else
meta[e]["author"] = util.extract_str(entries[e][2], "author: ", "\n")
end
if string.find(entries[e][2], "date: ") == nil then
meta[e]["date"] = util.extract_file_date(writer.conf.output_dir,
entries[e][1])
if (meta[e]["date"] == "") or (meta[e]["date"] == nil) then
meta[e]["date"] = os.date(writer.conf.entry_date_format)
end
else
meta[e]["date"] = util.extract_str(entries[e][2], "date: ", "\n")
end
if string.find(entries[e][2], "summary: ") == nil then
meta[e]["summary"] = meta[e]["title"]
else
meta[e]["summary"] = util.extract_str(entries[e][2], "summary: ", "\n")
end
if string.find(entries[e][2], "tags: ") == nil then
meta[e]["tags"] = ""
else
meta[e]["tags"] = util.extract_str(entries[e][2], "tags: ", "\n")
end
meta[e]["url"] = writer.conf.url .. "/" .. entries[e][1]
end
return meta
end
function writer.gen_index_page()
local index_text = writer.replace_vars(tpl.index_gmi, tpl.vars.log,
writer.conf)
local entries_text = ""
-- Reverse insert links to log entries, newest first
for e = #writer.entries, 1, -1 do
local entry_date = util.split_str(writer.entries[e]["date"], "(.*)[T]")[1]
-- Get entry filename from the url
local fi1, fi2 = string.find(writer.entries[e]["url"],
writer.conf.entry_file_pattern)
entries_text = entries_text .. "\n=> " ..
string.sub(writer.entries[e]["url"], fi1 + 1, fi2) .. " " ..
entry_date .. " " .. writer.entries[e]["title"]
end
index_text = string.gsub(index_text, tpl.vars.index.entries, entries_text)
util.write_file(writer.conf.output_dir .. "/" .. writer.conf.index_file,
index_text)
end
function writer.gen_atom_feed()
local feed_meta = {
date = os.date(writer.conf.entry_date_format),
url = writer.conf.url .. "/" .. writer.conf.atom_file,
}
local feed_text = writer.replace_vars(tpl.atom_header, tpl.vars.log,
writer.conf)
feed_text = writer.replace_vars(feed_text, tpl.vars.feed, feed_meta)
local feed_entry = ""
-- Reverse insert log entries, newest first
for e = #writer.entries, 1, -1 do
feed_entry = tpl.atom_entry
feed_entry = writer.replace_vars(feed_entry, tpl.vars.entry,
writer.entries[e])
feed_text = feed_text .. feed_entry
end
feed_text = feed_text .. tpl.atom_footer
util.write_file(writer.conf.output_dir .. "/" .. writer.conf.atom_file,
feed_text)
end
function writer.publish()
os.execute(writer.conf.rsync_exec .. " " .. writer.conf.rsync_options ..
" " .. writer.conf.output_dir .. " " .. writer.conf.rsync_remote)
end
local cli = {}
function cli.handle_args(args)
if (args[1] ~= "help") and (args[1] ~= "version") then
writer.load_config(conf)
end
if (args[1] == "index") or (args[1] == "publish") then
writer.entries = writer.get_entries_meta()
if writer.conf.gen_index_page then writer.gen_index_page() end
if writer.conf.gen_atom_feed then writer.gen_atom_feed() end
end
if args[1] == "post" then
local slug = args[2]
if slug == "" then
slug = writer.conf.entry_slug
else
slug = os.date("%Y-%m-%d") .. "-" .. slug
end
writer.add_entry(slug)
print(writer.msg.add_entry .. slug .. writer.conf.entry_ext)
elseif args[1] == "page" then
local slug = args[2]
if slug == "" then slug = writer.conf.page_slug end
writer.add_entry(slug)
print(writer.msg.add_entry .. slug .. writer.conf.entry_ext)
elseif args[1] == "index" then
print(writer.msg.index)
elseif args[1] == "publish" then
writer.publish()
print(writer.msg.publish)
elseif args[1] == "help" then
print(writer.msg.help)
elseif args[1] == "version" then
print(writer.app.name .. " " .. writer.app.version ..
" (" .. writer.app.last_updated .. ")")
end
end
cli.handle_args(arg)

96
template.lua 100644
View File

@ -0,0 +1,96 @@
local tpl = {}
tpl.vars = {
entry = {
author = "{{ entry_author }}",
content = "{{ entry_content }}",
date = "{{ entry_date }}",
summary = "{{ entry_summary }}",
tags = "{{ entry_tags }}",
title = "{{ entry_title }}",
url = "{{ entry_url }}",
},
feed = {
date = "{{ feed_date }}",
url = "{{ feed_url }}",
},
index = {
entries = "{{ entries }}",
},
log = {
author = "{{ log_author }}",
subtitle = "{{ log_subtitle }}",
title = "{{ log_title }}",
url = "{{ log_url }}",
},
}
tpl.atom_header = [[<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>{{ log_url }}</id>
<title>{{ log_title }}</title>
<subtitle>{{ log_subtitle }}</subtitle>
<updated>{{ feed_date }}</updated>
<author>
<name>{{ log_author }}</name>
</author>
<link href="{{ log_url }}" rel="alternate"/>
<link href="{{ feed_url }}" rel="self" type="application/atom+xml"/>]]
tpl.atom_entry = [[ <entry>
<id>{{ entry_url }}</id>
<title>
<![CDATA[{{ entry_title }}]] .. "]]>" .. [[
</title>
<updated>{{ entry_date }}</updated>
<author>
<name>{{ entry_author }}</name>
</author>
<link href="{{ entry_url }}" rel="alternate"/>
<summary>
<![CDATA[{{ entry_summary }}]] .. "]]>" .. [[
</summary>
<content>
<![CDATA[{{ entry_content }}]] .. "]]>" .. [[
</content>
</entry>
]]
tpl.atom_footer = [[</feed>]]
tpl.log_gmi = [[---
date: {{ entry_date }}
---
# {{ entry_title }}
# Heading 1
## Heading 2
### Heading 3
List:
*
*
*
```
Preformatted text
```
## Links
=> gemini:// link
=> gemini:// link (img)
=> https:// link (https)]]
tpl.index_gmi = [[
# {{ log_title }}
{{ entries }}
]]
return tpl

47
util.lua 100644
View File

@ -0,0 +1,47 @@
local util = {}
function util.split_str(str, sep)
local tbl = {}
local n = 0
if sep == nil then sep = "%S+" end
for sub in string.gmatch(str, sep) do
n = n + 1
tbl[n] = sub
end
return tbl
end
function util.extract_str(full_str, find_str, end_str)
fi1, fi2 = string.find(full_str, find_str, 1)
ei1, ei2 = string.find(full_str, end_str, fi2)
return string.sub(full_str, fi2 + 1, ei1 - 1)
end
function util.extract_file_date(dir, file)
-- Atom feed date format: 2022-01-15T00:00:00+00:00
local ls_cmd = io.popen("ls -l --time-style=\"+%Y-%m-%dT%H:%M:%S%z\" " ..
dir .. " | grep " .. file .. " | awk '{print $6}'")
return ls_cmd:read("*a")
end
function util.read_file(file)
local fh = io.open(file, "r")
local text = ""
io.input(fh)
text = io.read("*a")
io.close(fh)
return text
end
function util.write_file(file, str)
local fh = io.open(file, "w")
io.output(fh)
io.write(str)
io.close(fh)
end
return util