\n"
+ flags.p = false
+ elseif flags.gallery then
+ close = "\n"
+ flags.gallery = false
+ end
+
+ return close
+end
+
+function insertIncoming( pagename, incomingname )
+ if incomingname == "pages" then return false end
+
+ local incoming = pages[pagename].incoming
+ for i = 1, #incoming do
+ if incoming[i] == incomingname then
+ return false
+ end
+ end
+
+ table.insert( incoming, incomingname)
+ return true
+end
+
+function firstPass()
+ -- load templates
+ for _,t in pairs(templates) do
+ -- read templates
+ for k,subtemp in pairs( t ) do
+ local f = assert(io.open(string.format("templates/%s",subtemp.path),"r"))
+ subtemp.template = f:read("a")
+ f:close()
+ end
+ end
+
+ -- for each page:
+ -- convert gmo to gmi and html
+ -- and calculate incoming links
+ for name,page in pairs(pages) do
+ local pagemeta = pages[name]
+
+ local gmopath = string.format("src/%s.gmo", pagemeta.slug)
+
+ -- open file
+ local f = assert( io.open(gmopath, "r") )
+
+ -- initialize flags
+ flags = { list = false, pre = false , p = false, gallery = false }
+
+ -- table to store the lines to write to file
+ local lines = { web = {}, gem = {} }
+
+ -- convert one line at a time
+ local count = 1
+ for line in f:lines() do
+ -- the output line:
+ local out = { gem = line, web = nil}
+
+ if count == 1 then -- extract title
+ pagemeta.ptitle = string.match(line, "^#+%s*(.-)$")
+ elseif count == 2 then -- language
+ if line == "" then
+ pagemeta.lang = "en,es-MX" -- default
+ else
+ pagemeta.lang, pagemeta.trlang, pagemeta.trname = string.match("^lang=(.-)%s(.-)->(.-)$")
+ end
+ elseif count == 3 then -- obtain page description
+ pagemeta.pdescription,n = string.gsub(line,"[{}]","")
+ end
+
+ if count <=2 then goto nextline end -- skip normal processing for the first lines
+
+ -- CONVERT LINES
+ if string.match( line, "^```") then -- preformated
+ if flags.pre then
+ out.web = ""
+ else
+ out.web = "
"
+ end
+ flags.pre = not flags.pre
+ goto insert -- skip more checks
+ end
+
+ if flags.pre then
+ out.web = sanitize( line )
+ goto insert
+ end
+
+ if string.match( line, "^%+") then -- + append html
+ out.gem = nil
+ out.web = string.sub(line,3) -- remove "+ "
+
+ elseif string.match( line, "^&") then -- & append gemtext
+ out.gem = string.sub(line,3) -- remove "& "
+
+ elseif string.match( line, "^$") then -- empty line
+ out.web = closeTags()
+
+ elseif string.match( line, "^#+") then -- HEADERS
+ -- TODO create nav
+ local title, level = getHeader( line )
+
+ local close = closeTags()
+ out.web = string.format("%s%s",close,level,title,level)
+
+ elseif string.match( line, "^>") then -- BLOCKQUOTE
+ local close = closeTags()
+ out.web = string.format("%s
%s
", close, string.sub(line,3))
+
+ elseif string.match( line, "^%*") then -- LIST ITEMS
+ local li = string.format("
%s
", string.sub(line,3)) -- remove "* "
+ if not flags.list then -- start list
+ out.web = string.format("
\n%s", li)
+ flags.list = true
+ else -- append list element
+ out.web = li
+ end
+
+ elseif string.match( line, "^=>") then -- LINKS
+ local dest, text = getLink( line )
+ if string.match( dest, "^%./") then --local links
+ local t = string.match( text, "^{(.-)}$" )
+ if t ~= nil then -- wikilink
+ local close = closeTags()
+ out.web = string.format("%s
",close,dest,t)
+
+ elseif isImagePath( dest ) then -- images
+ local img = string.format('',dest,text)
+ if not flags.gallery then
+ local close = closeTags()
+ out.web = string.format("%s\n%s", close, img)
+ flags.gallery = true
+ else
+ out.web = img
+ end
+
+ else -- other files
+ local close = closeTags()
+ out.web = string.format("%s
', dest, text)
+ end
+
+ else -- PARAGRAPHS
+ -- web
+ -- search and replace wikilinks
+ local webline = string.gsub( line, "{(.-)}", function (wikiname)
+ return string.format("%s", pages[wikiname].slug, wikiname)
+ end)
+ if not flags.p then
+ out.web = string.format("
%s", webline)
+ flags.p = true
+ else
+ out.web = string.format(" \n%s",webline)
+ end
+
+ -- gem
+ local gemlinks = {}
+ local gemline = string.gsub( line, "{(.-)}", function (wikiname)
+ table.insert(gemlinks, string.format("=> ./%s.gmi %s",pages[wikiname].slug, wikiname))
+ return wikiname
+ end)
+
+ out.gem = gemline
+ -- append links if there were any
+ if #gemlinks > 0 then
+ out.gem = out.gem.."\n"..table.concat(gemlinks,"\n")
+ end
+ end -- paragraphs
+
+ ::insert:: -- insert line in table
+ for k,l in pairs(out) do
+ if l ~= nil then
+ table.insert( lines[k], l )
+ end
+ end
+
+
+ -- skip incoming links calculation if
mode
+ if flags.pre then goto nextline end
+
+ -- calculate incoming links from outgoing
+ for outname in string.gmatch( line, "{([^ ]?.-[^ ]?)}" ) do
+ insertIncoming( outname, name )
+ end
+
+ ::nextline::
+ count = count + 1
+ end -- end for line in f:lines()
+
+ -- finalize html
+ local close = closeTags()
+ if isPre then
+ table.insert( lines.web, "
" )
+ elseif close~="" then
+ table.insert( lines.web, close )
+ end
+
+ -- set templates
+ pagemeta.outs.web.templates = templates.webEn
+ pagemeta.outs.gem.templates = templates.gemEn
+
+ -- fill templates and write results
+ for key, out in pairs(pagemeta.outs) do
+ local fo = assert( io.open( out.path, "w" ) )
+ fo:write( fillTemplate(out.templates.header.template, pagemeta) )
+ fo:write( table.concat( lines[key],"\n" )) -- content
+ fo:write( "\n" )
+ fo:close()
+ end
+
+ -- close source file
+ f:close()
+ end
+end
+
+function secondPass() -- write incoming links and footer
+
+ for name,page in pairs(pages) do
+ local pagemeta = page
+
+ local doIncoming = true
+ if #pagemeta.incoming > 0 then
+ local gemlines = {}
+ local weblines = { "
" }
+
+ -- format list of incoming links
+ for i = 1, #pagemeta.incoming do
+ name = pagemeta.incoming[i]
+ slug = pages[name].slug
+ table.insert( gemlines, string.format("=> ./%s.gmi %s",slug,name) )
+ table.insert( weblines, string.format("%s ",slug,name) )
+ end
+
+ table.insert(weblines,"
")
+
+ page.gemincoming = table.concat( gemlines, "\n" )
+ page.webincoming = table.concat( weblines )
+
+ else
+ doIncoming = false
+ print( string.format("{%s} is an orphan!", name) )
+ end
+
+ -- fill incoming and footer templates and write results
+ for key, out in pairs(pagemeta.outs) do
+ local fo = assert( io.open( out.path, "a" ) )
+ if doIncoming then
+ fo:write( fillTemplate(out.templates.incoming.template, pagemeta) )
+ end
+ fo:write( fillTemplate(out.templates.footer.template, pagemeta) )
+ fo:close()
+ end
+ end
+end
+
+function initPageMetadata( name ) -- return a table with metadata
+ local meta = {
+ incoming = {},
+ slug = slugify(name), name = name,
+ ptitle = "", pdescription = "",
+ updatedate = "",
+ navcontent = "",
+ gemincoming = "", webincoming = "",
+ }
+ meta.outs = {
+ web = { path = string.format("%s/%s.html", webdir, meta.slug) },
+ gem = { path = string.format("%s/%s.gmi", gemdir, meta.slug) }
+ }
+ -- get update date for file
+ local stat = io.popen(string.format("stat -c %%y src/%s.gmo", meta.slug),"r")
+ local dat = stat:read("l")
+ stat:close()
+ meta.updatedate = string.sub(dat, 1, 10) -- YYYY-MM-DD
+ return meta
+end
+
+function genIndex()
+ local indexpath = "src/pages.gmo"
+
+ -- initialize and open index file
+ os.execute(string.format("cp templates/index.gmi %s",indexpath))
+ local index = assert( io.open(indexpath,"a" ) )
+
+ -- get gmo files in chronological order
+ local gmofiles = io.popen("ls -t src/*gmo")
+ for filename in gmofiles:lines() do
+ local basename = string.sub(filename, 5, -5) -- remove src/ and .gmo
+ local name = spacify(basename)
+ -- create an entry for each file
+ local entry = string.format("=> ./%s.gmi {%s}\n", basename, name)
+ index:write(entry)
+
+ -- also initialize each name in the pages table
+ -- initialize page metadata
+ pages[ name ] = initPageMetadata( name )
+
+ end
+ gmofiles:close()
+ index:close()
+end
+
+function genLog()
+-- generate the following log files:
+-- * twtxt gemini and web
+-- * atom feed gemini and web
+-- * gmo log
+-- * gmisub
+
+ local logsrc = assert( io.open( "src/log.txt", "r" ) )
+
+ local logs = {
+ gemtw = { path = string.format("%s/tw.txt",gemdir), head="templates/twheader.txt" },
+ webtw = { path = string.format("%s/tw.txt",webdir), head="templates/twheader.txt" },
+ gmolog = { path = "src/log.gmo", head="templates/logheader.gmi" },
+ gematom = { path = string.format("%s/atom.xml",gemdir), head="templates/gematomheader.txt" },
+ webatom = { path = string.format("%s/atom.xml",webdir), head="templates/webatomheader.txt" },
+ gemfeed = { path = string.format("%s/feed.gmi",gemdir), head="templates/feedheader.gmi" }
+ }
+
+ -- create and open logs
+ for _,log in pairs(logs) do
+ os.execute(string.format("cp %s %s",log.head,log.path))
+ log.f = assert( io.open( log.path, "a" ) )
+ end
+
+ logs.gematom.f:write(string.format("%sT12:00:00Z\n",fecha))
+ logs.webatom.f:write(string.format("%sT12:00:00Z\n",fecha))
+
+
+ -- process each line of log.txt
+ for line in logsrc:lines() do
+ local pattern = "^(.-)%s(.-)%s{(.-)}$"
+ local timestamp, text, wikilink = string.match(line,pattern)
+ local slug = slugify(wikilink)
+ local date = string.sub(timestamp,1,10) -- YYYY-MM-DD
+
+ -- gmo log
+ local gmoformat = "## %s\n%s\n=> ./%s.gmi {%s}\n\n"
+ logs.gmolog.f:write(string.format(gmoformat, date, text, slug, wikilink))
+
+ -- twtxt
+ local weburl = string.format("https://%s/%s.html",domain,slug)
+ local gemurl = string.format("gemini://%s/%s.gmi",domain,slug)
+ local twtxtformat = "%s\t%s | %s\n"
+ logs.webtw.f:write(string.format(twtxtformat, timestamp, text, weburl))
+ logs.gemtw.f:write(string.format(twtxtformat, timestamp, text, gemurl))
+
+ -- atom
+ local weburldate = string.format("https://%s/%s.html#%s",domain,slug,date)
+ local atomformat = [[
+
+
+%s
+%s
+
+%s
+
+]]
+ logs.webatom.f:write(string.format(atomformat, text, timestamp, weburldate, weburldate))
+ logs.gematom.f:write(string.format(atomformat, text, timestamp, gemurl, gemurl))
+ end
+
+
+ --close logs
+ logs.webatom.f:write("\n")
+ logs.gematom.f:write("\n")
+ for _,log in pairs(logs) do
+ log.f:close()
+ end
+
+ logsrc:close()
+end
+
+
+-- THE SCRIPT
+
+-- create directories
+os.execute(string.format("mkdir -p %s/static %s/img %s/img tmp",webdir,webdir,gemdir))
+
+print(meta.fecha)
+
+-- clean up
+print("limpiando archivos previos...")
+os.execute(string.format("find %s -iname *.html -exec rm {} \\;", webdir))
+os.execute(string.format("find %s -iname *.gmi -exec rm {} \\;", gemdir))
+os.execute("find tmp -iname *.gmo -exec rm {} \\;")
+
+-- copy images
+print("copiando imágenes y otros archivos...")
+local imgs = io.popen("ls src/img/")
+for img in imgs:lines() do
+ os.execute(string.format("cp -vu src/img/%s %s/img/", img, webdir))
+ os.execute(string.format("cp -vu src/img/%s %s/img/", img, gemdir))
+end
+imgs:close()
+
+-- copy stylesheet and misc files
+os.execute(string.format("cp -vu templates/estilo.css %s/static/",webdir))
+
+local miscfiles = { "src/llave_sejo.asc" }
+for k = 1, #miscfiles do
+ os.execute(string.format("cp -vu %s %s/", miscfiles[k], webdir))
+ os.execute(string.format("cp -vu %s %s/", miscfiles[k], gemdir))
+end
+
+
+-- generate log files
+print("generando log...")
+genLog()
+
+-- generate index
+print("generando índice de páginas...")
+genIndex()
+
+-- start first pass
+-- converting from gmo to gmi and html and calculating incoming links
+print("convirtiendo páginas...")
+firstPass()
+-- second pass, write incoming links
+print("escribiendo incoming links...")
+secondPass()
+
+print("listx!")
diff --git a/templates/estilo.css b/templates/estilo.css
new file mode 100644
index 0000000..c1f54bb
--- /dev/null
+++ b/templates/estilo.css
@@ -0,0 +1,100 @@
+/* links externos */
+a[rel^=external]:after{ content: "↗"; }
+a{
+ color: #857;
+}
+a:visited{
+ color: #404;
+}
+a.icon{
+ background-color: #fff;
+}
+/* gemtext-like format */
+/* h1:before{ content: "# "; }
+h2:before{ content: "## "; }
+h3:before{ content: "### "; }*/
+a.arrow:before{ content: "⇒ "; }
+body{
+ color: #100;
+ max-width:36em;
+ margin:0 auto;
+ font-size:18px;
+ padding: 1em;
+ font-family:sans-serif;
+}
+
+footer{
+ font-size: x-small;
+ padding-top:2em;
+}
+
+footer img{
+ margin:0;
+}
+
+pre{
+ background-color: #edd;
+ color: #433;
+ padding:1em;
+ overflow-x: auto;
+}
+
+blockquote{
+ font-style: italic;
+}
+
+main h1{
+ margin-top: 1.5em;
+ font-size: xx-large;
+}
+main h2{
+ font-size: x-large;
+ margin-left:0.3em;
+}
+main h3{
+ font-size: larger;
+ margin-left:1em;
+}
+
+gallery{
+ display:flex;
+ flex-direction:row;
+ flex-wrap:wrap;
+ justify-content:space-evenly;
+ align-content:center;
+}
+img{
+ margin:10px;
+ border-radius:5px;
+ max-width: 320px;
+}
+
+nav{ font-size:smaller; }
+nav li{ padding:0.2em; }
+main li{ padding:0.5em; }
+
+table {
+ font-size: smaller;
+ max-width: 90%;
+ border-collapse: collapse;
+ margin: 1em auto;
+ text-align:left;
+ }
+table th {
+ border-bottom: 1px solid black;
+ padding: 0.1em 1em;
+ }
+table td {
+ padding: 0.1em 1em;
+ }
+td.num{
+ text-align:right;
+}
+
+/* desktop */
+@media screen and (min-width: 600px) {
+ nav li{
+ display:inline;
+ }
+ img{ max-width: 100%; }
+}
diff --git a/templates/feedheader.gmi b/templates/feedheader.gmi
new file mode 100644
index 0000000..b462c56
--- /dev/null
+++ b/templates/feedheader.gmi
@@ -0,0 +1,9 @@
+# compudanzas
+
+our gemsub feed
+
+see also:
+=> ./log.gmi log
+
+# entries
+
diff --git a/templates/gematomheader.txt b/templates/gematomheader.txt
new file mode 100644
index 0000000..0c075ae
--- /dev/null
+++ b/templates/gematomheader.txt
@@ -0,0 +1,9 @@
+
+
+
+gemini://compudanzas.net/
+compudanzas
+sejo
+genlog
+
+
diff --git a/templates/index.gmi b/templates/index.gmi
new file mode 100644
index 0000000..ad3789f
--- /dev/null
+++ b/templates/index.gmi
@@ -0,0 +1,4 @@
+# index of pages
+
+most recently modified, above:
+
diff --git a/templates/logheader.gmi b/templates/logheader.gmi
new file mode 100644
index 0000000..4343de7
--- /dev/null
+++ b/templates/logheader.gmi
@@ -0,0 +1,9 @@
+# compudanzas log
+
+updates of the project
+
+=> ./tw.txt [twtxt]
+=> ./atom.xml [atom feed]
+& => ./feed.gmi [gemsub feed]
+
+# entries
diff --git a/templates/pagefooter.gmi b/templates/pagefooter.gmi
new file mode 100644
index 0000000..d1c6697
--- /dev/null
+++ b/templates/pagefooter.gmi
@@ -0,0 +1,5 @@
+
+# meta
+=> ./home.gmi {title}
+=> ./contact.gmi contact
+=> https://wiki.p2pfoundation.net/Peer_Production_License text, images, and code are shared with the peer production license
diff --git a/templates/pagefooter.html b/templates/pagefooter.html
new file mode 100644
index 0000000..3ef6b5a
--- /dev/null
+++ b/templates/pagefooter.html
@@ -0,0 +1,15 @@
+
+
+