forked from mio/gemwriter
		
	Change config format
- Change config format and parsing - Fix support for multiple capsules - Include scp as a transfer option - Fix more bugs
This commit is contained in:
		
							parent
							
								
									e1400e1ed8
								
							
						
					
					
						commit
						baead4e5f1
					
				
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,4 +1,2 @@
 | 
				
			|||||||
log
 | 
					gemwriter
 | 
				
			||||||
config.lua
 | 
					gemwriter.luastatic.c
 | 
				
			||||||
config.ini
 | 
					 | 
				
			||||||
template.ini
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +0,0 @@
 | 
				
			|||||||
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
 | 
					 | 
				
			||||||
							
								
								
									
										85
									
								
								env.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								env.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,85 @@
 | 
				
			|||||||
 | 
					local env = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- App information
 | 
				
			||||||
 | 
					env.app = {
 | 
				
			||||||
 | 
					  name = "gemwriter",
 | 
				
			||||||
 | 
					  exec_name = "gemwriter",
 | 
				
			||||||
 | 
					  version = "0.2",
 | 
				
			||||||
 | 
					  last_updated = "2022-08-05",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					env.defaults = {
 | 
				
			||||||
 | 
					  config_dir = "$HOME/.config/" .. env.app.exec_name,
 | 
				
			||||||
 | 
					  config_files = {
 | 
				
			||||||
 | 
					    atom_entry = "atom.entry.xml",
 | 
				
			||||||
 | 
					    atom_header = "atom.header.xml",
 | 
				
			||||||
 | 
					    config = "config.toml",
 | 
				
			||||||
 | 
					    index = "index.gmi",
 | 
				
			||||||
 | 
					    page = "page.gmi",
 | 
				
			||||||
 | 
					    post = "post.gmi",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  post_slug = "untitled",
 | 
				
			||||||
 | 
					  post_slug_date_format = "%Y-%m-%d",
 | 
				
			||||||
 | 
					  -- Atom feed date format
 | 
				
			||||||
 | 
					  post_date_format = "%Y-%m-%dT%H:%M:%SZ",
 | 
				
			||||||
 | 
					  post_ext = ".gmi",
 | 
				
			||||||
 | 
					  post_file_pattern = "[%/](%d%d%d%d%-%d%d%-%d%d)(.*)[%.](%a*)",
 | 
				
			||||||
 | 
					  page_slug = os.date("%Y%m%d-%H%M%S"),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Configurable settings
 | 
				
			||||||
 | 
					env.toml_vars = {
 | 
				
			||||||
 | 
					  general = "general",
 | 
				
			||||||
 | 
					  capsules = "capsules",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					env.general = {
 | 
				
			||||||
 | 
					  app_lang = "en",
 | 
				
			||||||
 | 
					  main_capsule = "main",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					env.capsules = {
 | 
				
			||||||
 | 
					  main = {
 | 
				
			||||||
 | 
					    author = "Gem",
 | 
				
			||||||
 | 
					    title = "Gemlog",
 | 
				
			||||||
 | 
					    subtitle = "A Gemini log",
 | 
				
			||||||
 | 
					    url = "gemini://domain.tld",
 | 
				
			||||||
 | 
					    log_url = "gemini://domain.tld/log",
 | 
				
			||||||
 | 
					    capsule_dir = env.defaults.config_dir .. "/capsule",
 | 
				
			||||||
 | 
					    gemlog_dir = env.defaults.config_dir .. "/capsule/log",
 | 
				
			||||||
 | 
					    gen_index_page = true,
 | 
				
			||||||
 | 
					    index_page = "index.gmi",
 | 
				
			||||||
 | 
					    gen_atom_feed = true,
 | 
				
			||||||
 | 
					    atom_feed = "atom.xml",
 | 
				
			||||||
 | 
					    transfer_mode = "scp",
 | 
				
			||||||
 | 
					    scp_exec = "/usr/bin/scp",
 | 
				
			||||||
 | 
					    scp_target = "/path/to/remote/capsule",
 | 
				
			||||||
 | 
					    rsync_exec = "/usr/bin/rsync",
 | 
				
			||||||
 | 
					    rsync_options = "-avz",
 | 
				
			||||||
 | 
					    rsync_dest = "/path/to/remote/capsule",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					env.defaults_toml = [[
 | 
				
			||||||
 | 
					[]] .. env.toml_vars.general ..  [[]
 | 
				
			||||||
 | 
					app_lang = "]] .. env.general.app_lang .. [["
 | 
				
			||||||
 | 
					main_capsule = "]] .. env.general.main_capsule .. [["
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[]] .. env.toml_vars.capsules .. "." ..  env.general.main_capsule .. [[]
 | 
				
			||||||
 | 
					author = "]] .. env.capsules.main.author .. [["
 | 
				
			||||||
 | 
					title = "]] .. env.capsules.main.title .. [["
 | 
				
			||||||
 | 
					subtitle = "]] .. env.capsules.main.subtitle .. [["
 | 
				
			||||||
 | 
					url = "]] .. env.capsules.main.url .. [["
 | 
				
			||||||
 | 
					log_url = "]] .. env.capsules.main.log_url .. [["
 | 
				
			||||||
 | 
					capsule_dir = "]] ..  env.capsules.main.capsule_dir .. [["
 | 
				
			||||||
 | 
					gemlog_dir = "]] ..  env.capsules.main.gemlog_dir .. [["
 | 
				
			||||||
 | 
					gen_index_page = "]] .. tostring(env.capsules.main.gen_index_page) .. [["
 | 
				
			||||||
 | 
					index_page = "]] .. env.capsules.main.index_page .. [["
 | 
				
			||||||
 | 
					gen_atom_feed = "]] .. tostring(env.capsules.main.gen_atom_feed) .. [["
 | 
				
			||||||
 | 
					atom_feed = "]] .. env.capsules.main.atom_feed .. [["
 | 
				
			||||||
 | 
					transfer_mode = "]] .. env.capsules.main.transfer_mode .. [["
 | 
				
			||||||
 | 
					scp_exec = "]] .. env.capsules.main.scp_exec .. [["
 | 
				
			||||||
 | 
					scp_target = "]] .. env.capsules.main.scp_target .. [["
 | 
				
			||||||
 | 
					rsync_exec = "]] .. env.capsules.main.rsync_exec .. [["
 | 
				
			||||||
 | 
					rsync_options = "]] .. env.capsules.main.rsync_options .. [["
 | 
				
			||||||
 | 
					rsync_dest = "]] .. env.capsules.main.rsync_dest .. [["
 | 
				
			||||||
 | 
					]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return env
 | 
				
			||||||
							
								
								
									
										465
									
								
								gemwriter.lua
									
									
									
									
									
								
							
							
						
						
									
										465
									
								
								gemwriter.lua
									
									
									
									
									
								
							@ -1,245 +1,382 @@
 | 
				
			|||||||
local conf = require("config")
 | 
					local env = require("env")
 | 
				
			||||||
local tpl = require("template")
 | 
					local lang = require("lang.en")
 | 
				
			||||||
local util = require("util")
 | 
					local util = require("util")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local writer = {}
 | 
					local writer = {}
 | 
				
			||||||
 | 
					writer.docs = {}
 | 
				
			||||||
writer.app = {
 | 
					 | 
				
			||||||
  exec_name = "gw",
 | 
					 | 
				
			||||||
  last_updated = "2022-07-24",
 | 
					 | 
				
			||||||
  name = "gemwriter",
 | 
					 | 
				
			||||||
  version = "0.1",
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
writer.conf = {}
 | 
					writer.conf = {}
 | 
				
			||||||
writer.defaults = {
 | 
					writer.posts = {}
 | 
				
			||||||
  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.docs.gen_config = [[
 | 
				
			||||||
 | 
					  Generate a default config directory.
 | 
				
			||||||
writer.msg = {
 | 
					  ]]
 | 
				
			||||||
  add_entry = "Created ",
 | 
					function writer.gen_config()
 | 
				
			||||||
  help = writer.app.exec_name .. [[ [options] [slug]
 | 
					  writer.conf.config_dir = util.replace_shell_vars(env.defaults.config_dir)
 | 
				
			||||||
 | 
					  util.make_dir(writer.conf.config_dir)
 | 
				
			||||||
Options:
 | 
					  for name, file in pairs(env.defaults.config_files) do
 | 
				
			||||||
 | 
					    -- Check each file individually anyway to avoid overwriting existing
 | 
				
			||||||
page [slug]     Add a new page with the given name
 | 
					    -- files
 | 
				
			||||||
post [slug]     Add a new entry with the given name
 | 
					    if util.read_file(writer.conf.config_dir .. "/" .. file) == "" then
 | 
				
			||||||
index           Generate an index page and feed of entries
 | 
					      if file == env.defaults.config_files.config then
 | 
				
			||||||
publish         Index and copy posts to a remote server (requires rsync)
 | 
					        util.write_file(writer.conf.config_dir .. "/" .. file,
 | 
				
			||||||
help            Show this help message
 | 
					          env.defaults_toml)
 | 
				
			||||||
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
 | 
					      else
 | 
				
			||||||
      writer.conf[k] = config[k]
 | 
					        util.write_file(writer.conf.config_dir .. "/" .. file, lang[name])
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  -- Create capsule and gemlog directories
 | 
				
			||||||
 | 
					  util.make_dir(env.capsules.main.capsule_dir)
 | 
				
			||||||
 | 
					  util.make_dir(env.capsules.main.gemlog_dir)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					writer.docs.parse_config = [[
 | 
				
			||||||
 | 
					  Read a config file in TOML format and load the values into a table.
 | 
				
			||||||
 | 
					  ]]
 | 
				
			||||||
 | 
					function writer.parse_config(config_file)
 | 
				
			||||||
 | 
					  local lines = util.split_lines(config_file)
 | 
				
			||||||
 | 
					  local config_group = env.toml_vars.general
 | 
				
			||||||
 | 
					  local line_cap = ""
 | 
				
			||||||
 | 
					  writer.conf.capsules = {}
 | 
				
			||||||
 | 
					  for l = 1, #lines do
 | 
				
			||||||
 | 
					    -- Config group
 | 
				
			||||||
 | 
					    if string.sub(lines[l], 1, 1) == "[" then
 | 
				
			||||||
 | 
					      for tv, label in pairs(env.toml_vars) do
 | 
				
			||||||
 | 
					        if string.find(lines[l], label) ~= nil then config_group = label end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					      if config_group == env.toml_vars.capsules then
 | 
				
			||||||
 | 
					        local _, lb_sep = string.find(lines[l], "%[" .. config_group .. "%.")
 | 
				
			||||||
 | 
					        local rb_sep, _ = string.find(lines[l], "%]")
 | 
				
			||||||
 | 
					        if lb_sep ~= nil then
 | 
				
			||||||
 | 
					          line_cap = string.sub(lines[l], lb_sep + 1, rb_sep - 1)
 | 
				
			||||||
 | 
					          writer.conf.capsules[line_cap] = {}
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- Config variables (ignore comments)
 | 
				
			||||||
 | 
					    elseif (string.sub(lines[l], 1, 1) ~= "#") and
 | 
				
			||||||
 | 
					      (string.find(lines[l], "=") ~= nil) then
 | 
				
			||||||
 | 
					      local eq_sep, _ = string.find(lines[l], " = ")
 | 
				
			||||||
 | 
					      local qt_sep, _ = string.find(lines[l], "\"")
 | 
				
			||||||
 | 
					      local k = string.sub(lines[l], 1, eq_sep - 1)
 | 
				
			||||||
 | 
					      local val = string.sub(lines[l], eq_sep + 4, string.len(lines[l]) - 1)
 | 
				
			||||||
 | 
					      if qt_sep == nil then
 | 
				
			||||||
 | 
					        val = string.sub(lines[l], eq_sep + 3, string.len(lines[l]))
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					      -- Load settings by key and their values to the config table
 | 
				
			||||||
 | 
					      if config_group == env.toml_vars.general then
 | 
				
			||||||
 | 
					        writer.conf[k] = val
 | 
				
			||||||
 | 
					      elseif config_group == env.toml_vars.capsules then
 | 
				
			||||||
 | 
					        -- Convert boolean values
 | 
				
			||||||
 | 
					        if (val == "\"true\"") or (val == "\"false\"") then
 | 
				
			||||||
 | 
					          writer.conf.capsules[line_cap][k] = util.to_bool(val)
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          writer.conf.capsules[line_cap][k] = val
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -- If the main capsule id is not the app default, update the id
 | 
				
			||||||
 | 
					  if (writer.conf.cap_id == env.general.main_capsule) and
 | 
				
			||||||
 | 
					    (writer.conf.main_capsule ~= env.general.main_capsule) then
 | 
				
			||||||
 | 
					    writer.conf.cap_id = writer.conf.main_capsule
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  -- Add an alias to the selected capsule's table
 | 
				
			||||||
 | 
					  writer.conf.cap = writer.conf.capsules[writer.conf.cap_id]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -- Fill in the remaining defaults
 | 
				
			||||||
 | 
					  for k, v in pairs(env.defaults) do
 | 
				
			||||||
 | 
					    if writer.conf[k] == nil then
 | 
				
			||||||
 | 
					      writer.conf[k] = env.defaults[k]
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -- Replace shell variables in paths to make them usable
 | 
				
			||||||
 | 
					  for c_id, _ in pairs(writer.conf.capsules) do
 | 
				
			||||||
 | 
					    writer.conf.capsules[c_id]["capsule_dir"] = util.replace_shell_vars(
 | 
				
			||||||
 | 
					      writer.conf.capsules[c_id]["capsule_dir"])
 | 
				
			||||||
 | 
					    writer.conf.capsules[c_id]["gemlog_dir"] = util.replace_shell_vars(
 | 
				
			||||||
 | 
					      writer.conf.capsules[c_id]["gemlog_dir"])
 | 
				
			||||||
 | 
					    -- Create capsule and gemlog directories
 | 
				
			||||||
 | 
					    util.make_dir(writer.conf.capsules[c_id]["capsule_dir"])
 | 
				
			||||||
 | 
					    util.make_dir(writer.conf.capsules[c_id]["gemlog_dir"])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -- Set lang
 | 
				
			||||||
 | 
					  lang = require("lang." .. writer.conf.app_lang)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					writer.docs.load_config = [[
 | 
				
			||||||
 | 
					  Check whether there is an existing config and load it if found. Exit if a
 | 
				
			||||||
 | 
					  capsule is not found.
 | 
				
			||||||
 | 
					  ]]
 | 
				
			||||||
 | 
					function writer.load_config(cap_id)
 | 
				
			||||||
 | 
					  writer.conf.config_dir = util.replace_shell_vars(env.defaults.config_dir)
 | 
				
			||||||
 | 
					  local config_file = util.read_file(writer.conf.config_dir .. "/" ..
 | 
				
			||||||
 | 
					    env.defaults.config_files.config)
 | 
				
			||||||
 | 
					  local config_new = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -- No config found
 | 
				
			||||||
 | 
					  if (config_file == "") and (not config_new) then
 | 
				
			||||||
 | 
					    writer.gen_config()
 | 
				
			||||||
 | 
					    config_new = true
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  -- Config found, read the configuration and load the values to an
 | 
				
			||||||
 | 
					  -- associative table, writer.conf.
 | 
				
			||||||
 | 
					  if (config_file ~= "") and (not config_new) then
 | 
				
			||||||
 | 
					    -- Check capsule id exists, abort if no valid capsule found
 | 
				
			||||||
 | 
					    if (cap_id == nil) or (string.find(cap_id, " ") ~= nil) then
 | 
				
			||||||
 | 
					      writer.conf.cap_id = env.general.main_capsule
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      writer.conf.cap_id = cap_id
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local cap_label = "%[" .. env.toml_vars.capsules .. "%." ..
 | 
				
			||||||
 | 
					      tostring(cap_id) .. "%]"
 | 
				
			||||||
 | 
					    if (string.find(config_file, cap_label) == nil) and
 | 
				
			||||||
 | 
					      (writer.conf.cap_id ~= env.general.main_capsule) then
 | 
				
			||||||
 | 
					      print(lang.errs.invalid_cap_id)
 | 
				
			||||||
 | 
					      os.exit()
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    writer.parse_config(config_file)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function writer.replace_vars(str, vars, vals)
 | 
					writer.docs.add_gemtext = [[
 | 
				
			||||||
  local text = str
 | 
					  Given a title and mode, create a new gemtext file. Supported modes: page,
 | 
				
			||||||
  for k, v in pairs(vars) do
 | 
					  post.
 | 
				
			||||||
    text = string.gsub(text, v, vals[k])
 | 
					  ]]
 | 
				
			||||||
 | 
					function writer.add_gemtext(title, mode)
 | 
				
			||||||
 | 
					  local file_name = title
 | 
				
			||||||
 | 
					  local post_title = title
 | 
				
			||||||
 | 
					  if ((title == "") or (title == nil)) and (mode == "post") then
 | 
				
			||||||
 | 
					    file_name = os.date(writer.conf.post_slug_date_format) .. "-" ..
 | 
				
			||||||
 | 
					      writer.conf.post_slug .. writer.conf.post_ext
 | 
				
			||||||
 | 
					    post_title = writer.conf.post_slug
 | 
				
			||||||
 | 
					  elseif ((title == "") or (title == nil)) and (mode == "page") then
 | 
				
			||||||
 | 
					    file_name = writer.conf.page_slug .. writer.conf.post_ext
 | 
				
			||||||
 | 
					    post_title = writer.conf.page_slug
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    -- Strip punctuation from the title and convert to file name
 | 
				
			||||||
 | 
					    local delim = "delim-" .. tostring(math.random(90000000000))
 | 
				
			||||||
 | 
					    file_name = string.gsub(file_name, " ", delim)
 | 
				
			||||||
 | 
					    if mode == "post" then
 | 
				
			||||||
 | 
					      file_name = os.date(writer.conf.post_slug_date_format) .. "-" ..
 | 
				
			||||||
 | 
					        string.gsub(file_name, "%p", "") .. writer.conf.post_ext
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      file_name = string.gsub(file_name, "%p", "") .. writer.conf.post_ext
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  return text
 | 
					    file_name = string.lower(string.gsub(file_name, delim, "-"))
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  local text = lang.post
 | 
				
			||||||
 | 
					  local file_path = writer.conf.cap.gemlog_dir .. "/" .. file_name
 | 
				
			||||||
 | 
					  if mode == "page" then
 | 
				
			||||||
 | 
					    text = lang.page
 | 
				
			||||||
 | 
					    file_path = writer.conf.cap.capsule_dir .. "/" .. file_name
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  text = string.gsub(text, lang.tpl_vars.post.date,
 | 
				
			||||||
 | 
					    os.date(writer.conf.post_date_format))
 | 
				
			||||||
 | 
					  text = string.gsub(text, lang.tpl_vars.post.author, writer.conf.cap.author)
 | 
				
			||||||
 | 
					  text = string.gsub(text, lang.tpl_vars.post.title, post_title)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  util.write_file(file_path, text)
 | 
				
			||||||
 | 
					  return file_name
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function writer.add_entry(slug)
 | 
					writer.docs.get_posts = [[
 | 
				
			||||||
  local text = tpl.log_gmi
 | 
					  Get a list of gemlog posts and return the file names and their contents in a
 | 
				
			||||||
  text = string.gsub(text, tpl.vars.entry.date,
 | 
					  table.
 | 
				
			||||||
    os.date(writer.conf.entry_date_format))
 | 
					  ]]
 | 
				
			||||||
  text = string.gsub(text, tpl.vars.entry.author, writer.conf.author)
 | 
					function writer.get_posts()
 | 
				
			||||||
 | 
					  local ls = util.ls_grep(writer.conf.cap.gemlog_dir, writer.conf.post_ext)
 | 
				
			||||||
  local entry_name = slug .. writer.conf.entry_ext
 | 
					  local posts = {}
 | 
				
			||||||
  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 n = 0
 | 
				
			||||||
  local files_list = util.split_str(ls)
 | 
					  local files_list = util.split_str(ls)
 | 
				
			||||||
  for f = 1, #files_list do
 | 
					  for f = 1, #files_list do
 | 
				
			||||||
    if files_list[f] ~= writer.conf.index_file then
 | 
					    if files_list[f] ~= writer.conf.cap.index_page then
 | 
				
			||||||
      n = n + 1
 | 
					      n = n + 1
 | 
				
			||||||
      entries[n] = { files_list[f], util.read_file(writer.conf.output_dir ..
 | 
					      posts[n] = { files_list[f], util.read_file(writer.conf.cap.gemlog_dir ..
 | 
				
			||||||
        "/" .. files_list[f]) }
 | 
					        "/" .. files_list[f]) }
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  return entries
 | 
					  return posts
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function writer.get_entries_meta()
 | 
					writer.docs.get_posts_meta = [[
 | 
				
			||||||
 | 
					  Extract gemlog posts metadata and return them as an associative table.
 | 
				
			||||||
 | 
					  ]]
 | 
				
			||||||
 | 
					function writer.get_posts_meta()
 | 
				
			||||||
  local meta = {}
 | 
					  local meta = {}
 | 
				
			||||||
  local entries = writer.get_entries()
 | 
					  local posts = writer.get_posts()
 | 
				
			||||||
  for e = 1, #entries do
 | 
					  for e = 1, #posts do
 | 
				
			||||||
    meta[e] = {}
 | 
					    meta[e] = {}
 | 
				
			||||||
    if string.find(entries[e][2], "# ") == nil then
 | 
					    if string.find(posts[e][2], "# ") == nil then
 | 
				
			||||||
      meta[e]["title"] = entries[e][1]
 | 
					      meta[e]["title"] = posts[e][1]
 | 
				
			||||||
      meta[e]["content"] = entries[e][2]
 | 
					      meta[e]["content"] = posts[e][2]
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      meta[e]["title"] = util.extract_str(entries[e][2], "# ", "\n")
 | 
					      meta[e]["title"] = util.extract_str(posts[e][2], "# ", "\n")
 | 
				
			||||||
      hi1, hi2 = string.find(entries[e][2], "# ")
 | 
					      hi1, hi2 = string.find(posts[e][2], "# ")
 | 
				
			||||||
      meta[e]["content"] = string.sub(entries[e][2], hi1,
 | 
					      meta[e]["content"] = string.sub(posts[e][2], hi1,
 | 
				
			||||||
        string.len(entries[e][2]))
 | 
					        string.len(posts[e][2]))
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
    if string.find(entries[e][2], "author: ") == nil then
 | 
					    if string.find(posts[e][2], "author: ") == nil then
 | 
				
			||||||
      meta[e]["author"] = writer.conf.author
 | 
					      meta[e]["author"] = writer.conf.cap.author
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      meta[e]["author"] = util.extract_str(entries[e][2], "author: ", "\n")
 | 
					      meta[e]["author"] = util.extract_str(posts[e][2], "author: ", "\n")
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
    if string.find(entries[e][2], "date: ") == nil then
 | 
					    if string.find(posts[e][2], "date: ") == nil then
 | 
				
			||||||
      meta[e]["date"] = util.extract_file_date(writer.conf.output_dir,
 | 
					      meta[e]["date"] = util.extract_file_date(writer.conf.cap.gemlog_dir,
 | 
				
			||||||
        entries[e][1])
 | 
					        posts[e][1])
 | 
				
			||||||
      if (meta[e]["date"] == "") or (meta[e]["date"] == nil) then
 | 
					      if (meta[e]["date"] == "") or (meta[e]["date"] == nil) then
 | 
				
			||||||
        meta[e]["date"] = os.date(writer.conf.entry_date_format)
 | 
					        meta[e]["date"] = os.date(writer.conf.post_date_format)
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      meta[e]["date"] = util.extract_str(entries[e][2], "date: ", "\n")
 | 
					      meta[e]["date"] = util.extract_str(posts[e][2], "date: ", "\n")
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
    if string.find(entries[e][2], "summary: ") == nil then
 | 
					    if string.find(posts[e][2], "summary: ") == nil then
 | 
				
			||||||
      meta[e]["summary"] = meta[e]["title"]
 | 
					      meta[e]["summary"] = meta[e]["title"]
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      meta[e]["summary"] = util.extract_str(entries[e][2], "summary: ", "\n")
 | 
					      meta[e]["summary"] = util.extract_str(posts[e][2], "summary: ", "\n")
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
    if string.find(entries[e][2], "tags: ") == nil then
 | 
					    if string.find(posts[e][2], "tags: ") == nil then
 | 
				
			||||||
      meta[e]["tags"] = ""
 | 
					      meta[e]["tags"] = ""
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      meta[e]["tags"] = util.extract_str(entries[e][2], "tags: ", "\n")
 | 
					      meta[e]["tags"] = util.extract_str(posts[e][2], "tags: ", "\n")
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
    meta[e]["url"] =  writer.conf.url .. "/" .. entries[e][1]
 | 
					    meta[e]["url"] =  writer.conf.cap.log_url .. "/" .. posts[e][1]
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  return meta
 | 
					  return meta
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					writer.docs.gen_index_page = [[
 | 
				
			||||||
 | 
					  Generate a gemlog index page listing all gemlog posts.
 | 
				
			||||||
 | 
					  ]]
 | 
				
			||||||
function writer.gen_index_page()
 | 
					function writer.gen_index_page()
 | 
				
			||||||
  local index_text = writer.replace_vars(tpl.index_gmi, tpl.vars.log,
 | 
					  local index_text = util.replace_vars(lang.index, lang.tpl_vars.log,
 | 
				
			||||||
    writer.conf)
 | 
					    writer.conf.cap)
 | 
				
			||||||
  local entries_text = ""
 | 
					  local posts_text = ""
 | 
				
			||||||
  -- Reverse insert links to log entries, newest first
 | 
					  -- Reverse insert links to log posts, newest first
 | 
				
			||||||
  for e = #writer.entries, 1, -1 do
 | 
					  for e = #writer.posts, 1, -1 do
 | 
				
			||||||
    local entry_date = util.split_str(writer.entries[e]["date"], "(.*)[T]")[1]
 | 
					    local post_date = util.split_str(writer.posts[e]["date"], "(.*)[T]")[1]
 | 
				
			||||||
    -- Get entry filename from the url
 | 
					    -- If post date cannot be extracted, revert to current date
 | 
				
			||||||
    local fi1, fi2 = string.find(writer.entries[e]["url"],
 | 
					    if post_date == nil then
 | 
				
			||||||
      writer.conf.entry_file_pattern)
 | 
					      post_date = os.date(writer.conf.post_date_format)
 | 
				
			||||||
    entries_text = entries_text .. "\n=> " ..
 | 
					 | 
				
			||||||
      string.sub(writer.entries[e]["url"], fi1 + 1, fi2) .. " " ..
 | 
					 | 
				
			||||||
      entry_date .. "    " .. writer.entries[e]["title"]
 | 
					 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  index_text = string.gsub(index_text, tpl.vars.index.entries, entries_text)
 | 
					    -- Get post filename from the url
 | 
				
			||||||
  util.write_file(writer.conf.output_dir .. "/" .. writer.conf.index_file,
 | 
					    local fi1, fi2 = string.find(writer.posts[e]["url"],
 | 
				
			||||||
    index_text)
 | 
					      writer.conf.post_file_pattern)
 | 
				
			||||||
 | 
					    posts_text = posts_text .. "\n=> " ..
 | 
				
			||||||
 | 
					      string.sub(writer.posts[e]["url"], fi1 + 1, fi2) .. " " ..
 | 
				
			||||||
 | 
					      post_date .. "    " .. writer.posts[e]["title"]
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  index_text = string.gsub(index_text, lang.tpl_vars.index.posts,
 | 
				
			||||||
 | 
					    posts_text)
 | 
				
			||||||
 | 
					  util.write_file(writer.conf.cap.gemlog_dir .. "/" ..
 | 
				
			||||||
 | 
					    writer.conf.cap.index_page, index_text)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					writer.docs.gen_atom_feed = [[
 | 
				
			||||||
 | 
					  Generate an Atom feed of gemlog posts.
 | 
				
			||||||
 | 
					  ]]
 | 
				
			||||||
function writer.gen_atom_feed()
 | 
					function writer.gen_atom_feed()
 | 
				
			||||||
  local feed_meta = {
 | 
					  local feed_meta = {
 | 
				
			||||||
    date = os.date(writer.conf.entry_date_format),
 | 
					    date = os.date(writer.conf.post_date_format),
 | 
				
			||||||
    url = writer.conf.url .. "/" .. writer.conf.atom_file,
 | 
					    url = writer.conf.cap.url .. "/" .. writer.conf.cap.atom_feed,
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  local feed_text = writer.replace_vars(tpl.atom_header, tpl.vars.log,
 | 
					  local feed_text = util.replace_vars(lang.atom_header, lang.tpl_vars.log,
 | 
				
			||||||
    writer.conf)
 | 
					    writer.conf.cap)
 | 
				
			||||||
  feed_text = writer.replace_vars(feed_text, tpl.vars.feed, feed_meta)
 | 
					  feed_text = util.replace_vars(feed_text, lang.tpl_vars.feed, feed_meta)
 | 
				
			||||||
  local feed_entry = ""
 | 
					  local feed_post = ""
 | 
				
			||||||
  -- Reverse insert log entries, newest first
 | 
					  -- Reverse insert log posts, newest first
 | 
				
			||||||
  for e = #writer.entries, 1, -1 do
 | 
					  for e = #writer.posts, 1, -1 do
 | 
				
			||||||
    feed_entry = tpl.atom_entry
 | 
					    feed_post = lang.atom_entry
 | 
				
			||||||
    feed_entry = writer.replace_vars(feed_entry, tpl.vars.entry,
 | 
					    -- Escape html entities in post contents. "%" is also a special character
 | 
				
			||||||
      writer.entries[e])
 | 
					    -- for string.gsub() and similar functions, and will cause an error if not
 | 
				
			||||||
    feed_text = feed_text .. feed_entry
 | 
					    -- escaped.
 | 
				
			||||||
 | 
					    writer.posts[e]["content"] =
 | 
				
			||||||
 | 
					      util.replace_html_entities(writer.posts[e]["content"])
 | 
				
			||||||
 | 
					    feed_post = util.replace_vars(feed_post, lang.tpl_vars.post,
 | 
				
			||||||
 | 
					      writer.posts[e])
 | 
				
			||||||
 | 
					    feed_text = feed_text .. feed_post
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
  feed_text = feed_text .. tpl.atom_footer
 | 
					  feed_text = feed_text .. lang.atom_footer
 | 
				
			||||||
  util.write_file(writer.conf.output_dir .. "/" .. writer.conf.atom_file,
 | 
					  util.write_file(writer.conf.cap.gemlog_dir .. "/" ..
 | 
				
			||||||
    feed_text)
 | 
					    writer.conf.cap.atom_feed, feed_text)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					writer.docs.publish = [[
 | 
				
			||||||
 | 
					  Transfer a capsule's contents to a remote location.
 | 
				
			||||||
 | 
					  ]]
 | 
				
			||||||
function writer.publish()
 | 
					function writer.publish()
 | 
				
			||||||
  os.execute(writer.conf.rsync_exec .. " " .. writer.conf.rsync_options ..
 | 
					  if writer.conf.cap.transfer_mode == "rsync" then
 | 
				
			||||||
    " " .. writer.conf.output_dir .. " " .. writer.conf.rsync_remote)
 | 
					    os.execute(writer.conf.cap.rsync_exec .. " " ..
 | 
				
			||||||
 | 
					      writer.conf.cap.rsync_options .. " " .. writer.conf.cap.capsule_dir ..
 | 
				
			||||||
 | 
					      "/ " .. writer.conf.cap.rsync_dest .. "/")
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    os.execute(writer.conf.cap.scp_exec .. " -r " ..
 | 
				
			||||||
 | 
					      writer.conf.cap.capsule_dir .. "/* " .. writer.conf.cap.scp_target)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local cli = {}
 | 
					local cli = {}
 | 
				
			||||||
 | 
					cli.docs = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cli.docs.handle_args = [[
 | 
				
			||||||
 | 
					  Match command-line options to the respective application functions, passing
 | 
				
			||||||
 | 
					  any additional arguments to the functions.
 | 
				
			||||||
 | 
					  ]]
 | 
				
			||||||
function cli.handle_args(args)
 | 
					function cli.handle_args(args)
 | 
				
			||||||
  if (args[1] ~= "help") and (args[1] ~= "version") then
 | 
					  if (args[1] ~= lang.opts.help) and (args[1] ~= lang.opts.version) then
 | 
				
			||||||
    writer.load_config(conf)
 | 
					    writer.load_config(args[2])
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (args[1] == "index") or (args[1] == "publish") then
 | 
					  if (args[1] == lang.opts.index) or (args[1] == lang.opts.publish) then
 | 
				
			||||||
    writer.entries = writer.get_entries_meta()
 | 
					    writer.posts = writer.get_posts_meta()
 | 
				
			||||||
    if writer.conf.gen_index_page then writer.gen_index_page() end
 | 
					    if writer.conf.cap.gen_index_page then writer.gen_index_page() end
 | 
				
			||||||
    if writer.conf.gen_atom_feed then writer.gen_atom_feed() end
 | 
					    if writer.conf.cap.gen_atom_feed then writer.gen_atom_feed() end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if args[1] == "post" then
 | 
					  if args[1] == lang.opts.config then
 | 
				
			||||||
    local slug = args[2]
 | 
					    writer.load_config()
 | 
				
			||||||
    if slug == "" then
 | 
					    print(lang.msgs.load_config .. writer.conf.config_dir)
 | 
				
			||||||
      slug = writer.conf.entry_slug
 | 
					
 | 
				
			||||||
 | 
					  elseif (args[1] == lang.opts.post) or (args[1] == lang.opts.page) then
 | 
				
			||||||
 | 
					    local file_name = ""
 | 
				
			||||||
 | 
					    if args[3] ~= nil then
 | 
				
			||||||
 | 
					      file_name = writer.add_gemtext(args[3], args[1])
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      slug = os.date("%Y-%m-%d") .. "-" .. slug
 | 
					      file_name = writer.add_gemtext(args[2], args[1])
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
    writer.add_entry(slug)
 | 
					    print(lang.msgs.add_gemtext .. file_name)
 | 
				
			||||||
    print(writer.msg.add_entry .. slug .. writer.conf.entry_ext)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  elseif args[1] == "page" then
 | 
					  elseif args[1] == lang.opts.index then
 | 
				
			||||||
    local slug = args[2]
 | 
					    print(lang.msgs.index)
 | 
				
			||||||
    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
 | 
					  elseif args[1] == lang.opts.publish then
 | 
				
			||||||
    print(writer.msg.index)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  elseif args[1] == "publish" then
 | 
					 | 
				
			||||||
    writer.publish()
 | 
					    writer.publish()
 | 
				
			||||||
    print(writer.msg.publish)
 | 
					    print(lang.msgs.publish)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  elseif args[1] == "help" then
 | 
					  elseif args[1] == lang.opts.help then
 | 
				
			||||||
    print(writer.msg.help)
 | 
					    print(env.app.exec_name .. lang.msgs.help)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  elseif args[1] == "version" then
 | 
					  elseif args[1] == lang.opts.version then
 | 
				
			||||||
    print(writer.app.name .. " " .. writer.app.version ..
 | 
					    print(env.app.name .. " " .. env.app.version ..
 | 
				
			||||||
      " (" .. writer.app.last_updated .. ")")
 | 
					      " (" .. env.app.last_updated .. ")")
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										145
									
								
								lang/en.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								lang/en.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,145 @@
 | 
				
			|||||||
 | 
					local en = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Templates
 | 
				
			||||||
 | 
					en.tpl_vars = {
 | 
				
			||||||
 | 
					  post = {
 | 
				
			||||||
 | 
					    author = "{{ post_author }}",
 | 
				
			||||||
 | 
					    content = "{{ post_content }}",
 | 
				
			||||||
 | 
					    date = "{{ post_date }}",
 | 
				
			||||||
 | 
					    summary = "{{ post_summary }}",
 | 
				
			||||||
 | 
					    tags = "{{ post_tags }}",
 | 
				
			||||||
 | 
					    title = "{{ post_title }}",
 | 
				
			||||||
 | 
					    url = "{{ post_url }}",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  feed = {
 | 
				
			||||||
 | 
					    date = "{{ feed_date }}",
 | 
				
			||||||
 | 
					    url = "{{ feed_url }}",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  index = {
 | 
				
			||||||
 | 
					    posts = "{{ posts }}",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  log = {
 | 
				
			||||||
 | 
					    author = "{{ log_author }}",
 | 
				
			||||||
 | 
					    subtitle = "{{ log_subtitle }}",
 | 
				
			||||||
 | 
					    title = "{{ log_title }}",
 | 
				
			||||||
 | 
					    log_url = "{{ log_url }}",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					en.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"/>
 | 
				
			||||||
 | 
					]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					en.atom_entry = [[
 | 
				
			||||||
 | 
					  <entry>
 | 
				
			||||||
 | 
					    <id>{{ post_url }}</id>
 | 
				
			||||||
 | 
					    <title>
 | 
				
			||||||
 | 
					      <![CDATA[{{ post_title }}]] .. "]]>" .. [[
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </title>
 | 
				
			||||||
 | 
					    <updated>{{ post_date }}</updated>
 | 
				
			||||||
 | 
					    <author>
 | 
				
			||||||
 | 
					      <name>{{ post_author }}</name>
 | 
				
			||||||
 | 
					    </author>
 | 
				
			||||||
 | 
					    <link href="{{ post_url }}" rel="alternate"/>
 | 
				
			||||||
 | 
					    <summary>
 | 
				
			||||||
 | 
					      <![CDATA[{{ post_summary }}]] .. "]]>" .. [[
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </summary>
 | 
				
			||||||
 | 
					    <content>
 | 
				
			||||||
 | 
					      <![CDATA[{{ post_content }}]] .. "]]>" .. [[
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </content>
 | 
				
			||||||
 | 
					  </entry>
 | 
				
			||||||
 | 
					]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					en.atom_footer = [[</feed>]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					en.post = [[---
 | 
				
			||||||
 | 
					date: {{ post_date }}
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# {{ post_title }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Links
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					=> gemini:// link
 | 
				
			||||||
 | 
					=> gemini:// link (img)
 | 
				
			||||||
 | 
					=> https:// link (https)]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					en.index = [[
 | 
				
			||||||
 | 
					# {{ log_title }}
 | 
				
			||||||
 | 
					{{ posts }}
 | 
				
			||||||
 | 
					]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					en.page = [[
 | 
				
			||||||
 | 
					# {{ post_title }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Heading 2
 | 
				
			||||||
 | 
					### Heading 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					List:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Preformatted text
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Links
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					=> gemini:// link
 | 
				
			||||||
 | 
					=> gemini:// link (img)
 | 
				
			||||||
 | 
					=> https:// link (https)]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- App command options and messages output
 | 
				
			||||||
 | 
					en.opts = {
 | 
				
			||||||
 | 
					  config = "config",
 | 
				
			||||||
 | 
					  page = "page",
 | 
				
			||||||
 | 
					  post = "post",
 | 
				
			||||||
 | 
					  index = "index",
 | 
				
			||||||
 | 
					  publish = "publish",
 | 
				
			||||||
 | 
					  help = "help",
 | 
				
			||||||
 | 
					  version = "version",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					en.msgs = {
 | 
				
			||||||
 | 
					  add_gemtext = "Created ",
 | 
				
			||||||
 | 
					  help = [[ [options] [capsule] [title]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Options:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config                    Generate a config directory
 | 
				
			||||||
 | 
					page [capsule] [title]    Add a new page with the given title
 | 
				
			||||||
 | 
					post [capsule] [title]    Add a new gemlog post with the given title
 | 
				
			||||||
 | 
					index                     Generate an index page and feed of posts
 | 
				
			||||||
 | 
					publish                   Index and copy posts remotely using scp
 | 
				
			||||||
 | 
					help                      Show this help message
 | 
				
			||||||
 | 
					version                   Print version info]],
 | 
				
			||||||
 | 
					  index = "Created index page and feed.",
 | 
				
			||||||
 | 
					  load_config = [[Created config files. Please edit them with the correct
 | 
				
			||||||
 | 
					details before proceeding. The config files can be found at:
 | 
				
			||||||
 | 
					]],
 | 
				
			||||||
 | 
					  publish = "Published capsule.",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					en.errs = {
 | 
				
			||||||
 | 
					  invalid_cap_id = "Error: unknown capsule id.",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return en
 | 
				
			||||||
							
								
								
									
										63
									
								
								readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								readme.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					# Gemwriter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A little command-line helper for publishing [Gemini] sites or "capsules".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Options:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config                    Generate a config directory
 | 
				
			||||||
 | 
					page [capsule] [title]    Add a new page with the given title
 | 
				
			||||||
 | 
					post [capsule] [title]    Add a new gemlog post with the given title
 | 
				
			||||||
 | 
					index                     Generate an index page and feed of posts
 | 
				
			||||||
 | 
					publish                   Index and copy posts remotely using scp
 | 
				
			||||||
 | 
					help                      Show this help message
 | 
				
			||||||
 | 
					version                   Print version info
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Requirements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Linux or Unix-based OS
 | 
				
			||||||
 | 
					- [Lua] 5.4 (other versions >= 5.1 will probably be fine but are untested)
 | 
				
			||||||
 | 
					- [scp] or [rsync], to transfer files remotely
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Install Lua and [luastatic].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Clone this repository and change into the directory. Run:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ```
 | 
				
			||||||
 | 
					  luastatic gemwriter.lua env.lua util.lua lang/en.lua /usr/lib/liblua.so -I/usr/include -o gemwriter
 | 
				
			||||||
 | 
					  ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  The paths to `liblua.so` and the development headers (i.e.
 | 
				
			||||||
 | 
					  `/usr/include/lua.h`) may need to be adjusted for your distribution.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Move the `gemwriter` executable to a location in your `$PATH`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Quick start
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Generate a new config: `gemwriter config`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. Edit `~/.config/gemwriter/config.toml` with the correct details about your
 | 
				
			||||||
 | 
					   capsule and gemlog.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3. Create a new gemlog post: `gemwriter post "Hello World!"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					4. Publish your capsule: `gemwriter publish`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BSD-3-Clause
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Gemini]: https://gemini.circumlunar.space/
 | 
				
			||||||
 | 
					[Lua]: https://www.lua.org/
 | 
				
			||||||
 | 
					[scp]: https://www.openssh.com/
 | 
				
			||||||
 | 
					[rsync]: https://rsync.samba.org/
 | 
				
			||||||
 | 
					[luastatic]: https://github.com/ers35/luastatic
 | 
				
			||||||
							
								
								
									
										96
									
								
								template.lua
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								template.lua
									
									
									
									
									
								
							@ -1,96 +0,0 @@
 | 
				
			|||||||
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
 | 
					 | 
				
			||||||
							
								
								
									
										120
									
								
								util.lua
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								util.lua
									
									
									
									
									
								
							@ -12,10 +12,114 @@ function util.split_str(str, sep)
 | 
				
			|||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function util.split_lines(str)
 | 
				
			||||||
 | 
					  local tbl = {}
 | 
				
			||||||
 | 
					  local si = 1
 | 
				
			||||||
 | 
					  for i = 1, #str do
 | 
				
			||||||
 | 
					    if string.find(string.sub(str, i, i), "\n") ~= nil then
 | 
				
			||||||
 | 
					      tbl[#tbl + 1] = string.sub(str, si, i - 1)
 | 
				
			||||||
 | 
					      si = i + 1
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  return tbl
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function util.extract_str(full_str, find_str, end_str)
 | 
					function util.extract_str(full_str, find_str, end_str)
 | 
				
			||||||
  fi1, fi2 = string.find(full_str, find_str, 1)
 | 
					  local fi1, fi2 = string.find(full_str, find_str, 1)
 | 
				
			||||||
  ei1, ei2 = string.find(full_str, end_str, fi2)
 | 
					  local ei1, ei2 = string.find(full_str, end_str, fi2)
 | 
				
			||||||
 | 
					  if end_str == "\n" then
 | 
				
			||||||
 | 
					    return string.sub(full_str, fi2 + 1, ei1 - 2)
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
    return string.sub(full_str, fi2 + 1, ei1 - 1)
 | 
					    return string.sub(full_str, fi2 + 1, ei1 - 1)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function util.to_bool(str)
 | 
				
			||||||
 | 
					  local bool = { ["true"] = true, ["false"] = false }
 | 
				
			||||||
 | 
					  return bool(str)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function util.table_keys(tbl)
 | 
				
			||||||
 | 
					  local keys = {}
 | 
				
			||||||
 | 
					  local n = 0
 | 
				
			||||||
 | 
					  for k, v in pairs(tbl) do
 | 
				
			||||||
 | 
					    n = n + 1
 | 
				
			||||||
 | 
					    keys[n] = k
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  return keys
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function util.has_key(tbl, str)
 | 
				
			||||||
 | 
					  local keys = util.table_keys(tbl)
 | 
				
			||||||
 | 
					  for k = 1, #keys do
 | 
				
			||||||
 | 
					    if str == keys[k] then
 | 
				
			||||||
 | 
					      do return true end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  return false
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function util.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 util.replace_html_entities(str)
 | 
				
			||||||
 | 
					  local html_ents = {
 | 
				
			||||||
 | 
					    percent = { "%%", "%" },
 | 
				
			||||||
 | 
					    less_than = { "<", "<" },
 | 
				
			||||||
 | 
					    greater_than = { ">", ">" },
 | 
				
			||||||
 | 
					    left_bracket = { "%[", "[" },
 | 
				
			||||||
 | 
					    right_bracket = { "%]", "]" },
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  local text = str
 | 
				
			||||||
 | 
					  for e, c in pairs(html_ents) do
 | 
				
			||||||
 | 
					    text = string.gsub(text, c[1], c[2])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  return text
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function util.make_dir(dir)
 | 
				
			||||||
 | 
					  os.execute("test -d " .. dir .. " || mkdir -p " .. dir)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function util.ls_grep(dir, str)
 | 
				
			||||||
 | 
					  local ls_cmd = io.popen("ls " .. dir .. " | grep " .. str)
 | 
				
			||||||
 | 
					  return ls_cmd:read("*a")
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function util.get_shell_var(str)
 | 
				
			||||||
 | 
					  local var = os.getenv(str)
 | 
				
			||||||
 | 
					  if var == "" then
 | 
				
			||||||
 | 
					    return nil
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function util.replace_shell_vars(str)
 | 
				
			||||||
 | 
					  -- Replace common shell variable paths
 | 
				
			||||||
 | 
					  if string.find(str, "%$") ~= nil then
 | 
				
			||||||
 | 
					    local shell_vars = { "HOME", "USER" }
 | 
				
			||||||
 | 
					    for v = 1, #shell_vars do
 | 
				
			||||||
 | 
					      str = string.gsub(str, "$" .. shell_vars[v],
 | 
				
			||||||
 | 
					        util.get_shell_var(shell_vars[v]))
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					  return str
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -30,18 +134,18 @@ end
 | 
				
			|||||||
function util.read_file(file)
 | 
					function util.read_file(file)
 | 
				
			||||||
  local fh = io.open(file, "r")
 | 
					  local fh = io.open(file, "r")
 | 
				
			||||||
  local text = ""
 | 
					  local text = ""
 | 
				
			||||||
  io.input(fh)
 | 
					  if fh ~= nil then
 | 
				
			||||||
  text = io.read("*a")
 | 
					    text = fh:read("*a")
 | 
				
			||||||
  io.close(fh)
 | 
					    fh:close()
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
  return text
 | 
					  return text
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function util.write_file(file, str)
 | 
					function util.write_file(file, str)
 | 
				
			||||||
  local fh = io.open(file, "w")
 | 
					  local fh = io.open(file, "w")
 | 
				
			||||||
  io.output(fh)
 | 
					  fh:write(str)
 | 
				
			||||||
  io.write(str)
 | 
					  fh:close()
 | 
				
			||||||
  io.close(fh)
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return util
 | 
					return util
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user