" html-footer))
(let [f (io.open (.. html-outdir "/log/index.html") :w+)]
(f:write html-index)
(f:close)))
; generate-log generates the log pages and feeds from the posts index
(fn generate-logs []
(table.sort posts (fn [a b]
(> (. a :date) (. b :date))))
(generate-gemini-log)
(generate-html-log)
(generate-ass-feeds)
(generate-rss-feeds))
; index-post adds a log post to the post index
(fn index-post [f]
(io.input (.. content-dir f)) ; open file
(let [post {}]
(tset post :path f)
(let [line (io.read) ; read first line, match title
title (line:match "^#%s*([^\n]+)%s*$")]
(tset post :title title))
(let [line (io.read) ; read second line, match date
date (line:match "^%s*(.+)%s*")]
(tset post :date date))
(table.insert posts post))
(io.close))
; HTML-escapes and trims a string
(fn html-clean [l]
(let [s (l:match "%s*(.+)%s*")]
(-> s
(string.gsub "&" "&")
(string.gsub "<" "<")
(string.gsub ">" ">")
(string.gsub "\"" """)
(string.gsub "''" "'"))))
; converts a gemtext line to an html line. This could probably be done far better. TODO am I overusing string matching here? Maybe could write an lpeg or something similar?
(var state :normal)
; either :normal or :pre depending on the current position in the file.
(var listing false)
; true if we are in a list, false otherwise
(var blockquoting false)
(fn to-html [line]
(var prefix "")
(if (and listing (not (line:match "^%*%s")))
(do
(set listing false)
(set prefix "")))
(if (and blockquoting (not (line:match "^>")))
(do
(set blockquoting false)
(set prefix "")))
(if (= state :pre)
(if (line:match "^```") (do
(set state :normal)
"") line)
(= state :normal)
(if (line:match "^```")
(do
(set state :pre)
(.. prefix "
")) ; open pre
(line:match "^#[^#]")
(.. prefix "
" (html-clean (line:match "^#([^#].+)")) "
")
(line:match "^##[^#]")
(.. prefix "
" (html-clean (line:match "^##([^#].+)")) "
")
(line:match "^###[^#]")
(.. prefix "
" (html-clean (line:match "^###([^#].+)")) "
")
(line:match "^=>")
(.. prefix (html-link line)) ; link
(line:match "^%s*$")
prefix ; blank line
(line:match "^%*%s")
(do
(if (not listing)
(do
(set prefix "
")
(set listing true)))
(.. prefix "
" (html-clean (line:match "^%*(.+)")) "
"))
(line:match "^>%s")
(do
(if (not blockquoting)
(do
(set prefix "
"))))
; processes, converts, and writes out an input gemini page in html
(fn process-html-page [f]
(let [infile (.. content-dir f)
outfile (io.open (.. html-outdir (string.gsub f "%.gmi" ".html")) :w+)]
(outfile:write html-header)
(set state :normal)
(each [line (io.lines infile)]
(let [h (to-html line)]
(outfile:write h "\n")))
(outfile:write html-footer)
(outfile:close)))
; processes and writes out an input gemini page into an output one
(fn process-gemini-page [f]
(let [infile (.. content-dir f)
outfile (io.open (.. gemini-outdir f) :w+)]
(each [l (io.lines infile)]
(if (string.match l "^=>.+") ; if the line is a link
(let [(url name) (string.match l "=> ([^%s]+) (.+)")] ; get the url and name
(if (= (string.sub url 1 1) "/")
; if it's an absolute link, add the base url
(outfile:write (string.format "=> %s%s %s\n" gemini-baseurl url
name))
(outfile:write (string.format "=> %s %s\n" url name))))
(outfile:write l "\n")))
(outfile:write gemini-footer)
(outfile:close)))
; process-file runs for every input file to process t
(fn process-file [f]
(if (string.match f ".+log/.+%.gmi")
(do
(index-post f)
(process-gemini-page f)
(process-html-page f)) ; add log posts to index
(string.match f ".+%.gmi")
(do
(process-gemini-page f)
(process-html-page f))
(do
(os.execute (.. "cp " content-dir f " " gemini-outdir f))
(os.execute (.. "cp " content-dir f " " html-outdir f)))))
; else copy file
; process-dir is run for every input directory to copy the tree into output directories
(fn process-dir [f]
(lfs.mkdir (.. gemini-outdir f))
(lfs.mkdir (.. html-outdir f)))
; walk the input directory tree recursively, processing every file and directory using the process-file and process-dir functions. "base" is the directory that is being walked. "position" is the current position in the directory tree.
(fn walk [base position] ; for each file in the current directory
(each [file (lfs.dir (.. base position))]
(let [fullpath (.. base position "/" file)]
(let [{:mode filemode} (lfs.attributes fullpath)] ; get the mode (file or directory)
(if (and (not= file "..") (not= file ".")) ; skip . and ..
(if (= filemode :directory)
(do
; recurse
(walk base (.. position "/" file))
(table.insert dirs (.. position "/" file)))
(= filemode :file) ; process the file
(table.insert files (.. position "/" file))))))))
; do everything
(os.execute (.. "rm -rf " gemini-outdir))
(os.execute (.. "rm -rf " html-outdir))
(lfs.mkdir gemini-outdir)
(lfs.mkdir html-outdir)
(print "getting files")
(walk content-dir "")
(print "processing directory structure")
(each [_ d (ipairs dirs)]
(process-dir d))
(print "processing files")
(each [_ f (ipairs files)]
(process-file f))
(print "generating feeds")
(generate-logs)
(print "done!")