Initial commit
commit
e1400e1ed8
|
@ -0,0 +1,4 @@
|
|||
log
|
||||
config.lua
|
||||
config.ini
|
||||
template.ini
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
Reference in New Issue