#!/usr/bin/env luajit -- Read the README file for what this is all about. If there is no README or similar, then you can find the link to the source below. local lunamark = require("lunamark") -- https://github.com/jgm/lunamark local opts = { layout='compact', -- This list is copied from the lunamark source code, until I discover a way to discover it. The descriptions are useful to. containers=false, -- Put sections in containers (e.g. div or section tags) slides=false, -- Like containers, but do not nest them startnum=true, -- Start number of an ordered list is significant smart=false, -- Smart typography (quotes, dashes, ellipses) preserve_tabs=true, -- Don't expand tabs to spaces notes=true, -- Footnotes inline_notes=true, -- Inline footnotes definition_lists=true, -- Definition lists citations=true, -- Citations citation_nbsps=true, -- Turn spacing into non-breaking spaces in citations fenced_code_blocks=true, -- Fenced code blocks lua_metadata=true, -- Lua metadata pandoc_title_blocks=true, -- Pandoc style title blocks hash_enumerators=true, -- may be used as ordered list enumerator require_blank_before_blockquote=false, require_blank_before_header=false, require_blank_before_fenced_code_block=false, fancy_lists=true, -- Pandoc style fancy lists task_list=true, -- GitHub-Flavored Markdown task list strikeout=true, -- Strike-through with double tildes mark=true, -- Highlight with double equals subscript=true, -- Subscripted text between tildes superscript=true, -- Superscripted text between circumflexes bracketed_spans=true, -- Spans with attributes fenced_divs=true, -- Divs with attributes raw_attribute=true, -- Raw pass-through on code elements fenced_code_attributes=true, -- Fenced code block attributes link_attributes=true, -- Link attributes pipe_tables=true, -- PHP Markdown Extra pipe table support table_captions=true, -- Table caption syntax extension header_attributes=true, -- Header attributes line_blocks=true, -- Line blocks escaped_line_breaks=true, -- Pandoc-style escaped hard line breaks } local writer = lunamark.writer.html5.new(opts) local template = '' local h = io.open("default.template", 'r') if nil ~= h then template = h:read('*a') h:close() else print('oops! No such file ' .. 'default.template') end -- Can override the various writer functions, there's something for each of the basic HTML elements. -- Override anything that's generic. local parse = lunamark.reader.markdown.new(writer, opts) -- Some global data. local globalData = { ['s'] = ' ', ['_'] = ' ', ['dlr'] = '$', ['devuanCinnabarDark'] = '#310202', ['devuanCinnabarLight'] = '#510505', ['devuanDarkPurpyDark'] = '#33313b', ['devuanDarkPurpyLight'] = '#3c3a45', ['devuanDeepSeaDark'] = '#132f40', ['devuanDeepSeaLight'] = '#1a4562', ['devuanSaphireDark'] = '#004489', ['devuanSaphireLight'] = '#00509f', ['karenPurple'] = '#8800ff', ['onefangPurple'] = '#cc00ff', favicon = 'nYAW_icon.png', logo = 'nYAW.png', header = '', home = '', history = '', footer = 'Powered by notYetAnotherWiki version 0.0. ', --menu = '', } local Files, Subs = {}, {} -- Useful functions, part 0. -- A simple table.subtable = subtable wont work, you end up with a reference so that changes to the later get applied to the former. local derefiTable = function(t, strip) local argh = {} for l, y in ipairs(t) do if (l ~= y.name) and strip then table.insert(argh, y) end end return argh end local derefTable = function(t, strip) local argh = {} for l, y in pairs(t) do argh[l] = y end return argh end -- String together the bits array into a path string. Or the other way around. lol local stringBits = function(l) local bits = {} local last = 1 for j = 1, #l do if '/' == string.sub(l, j, j) then table.insert(bits, string.sub(l, last, j - 1)) last = j + 1 end end return bits, string.sub(l, last) end -- Put a value into the Files or Subs table, creating things if needed. local toFile = function(name, key, value) if nil == Files[name] then local bits, bit = stringBits(name) local path = '' Files[name] = {} Files[name].bits = bits Files[name].bit = bit for i, d in ipairs(bits) do if '' ~= path then path = path .. '/' end path = path .. d end Files[name].path = path -- Files[name].body = '' end if nil ~= key then Files[name][key] = value end for i, v in ipairs{'metadata', 'bits', } do if nil == Files[name][v] then Files[name][v] = {} end end end local toSub = function(name, key, value) if nil == Subs[name] then Subs[name] = {} end if nil ~= key then Subs[name][key] = value end for i, v in ipairs{'metadata', 'bits', 'files', 'subs'} do if nil == Subs[name][v] then Subs[name][v] = {} end end end -- Actually start doing things. -- Create the base of everything.md here, so it gets picked up as usual in the file scan. local body, h = '', io.open('everything.md', 'w') h:write('# All the pages\n') h:close() -- Scan the subdirectories looking for .md files. local directory = arg[1] toSub('') if nil == directory then directory = '.' end if '.' ~= directory then for l in io.popen('find . -name "*.md" -type f,l -printf "%P\n"'):lines() do toFile(string.gsub(l, '%.md$', '')) end end -- Can add in a distant directory to, for putting it's results in the current directory. for l in io.popen('find ' .. directory .. ' -name "*.md" -type f,l -printf "%P\n"'):lines() do local n = string.gsub(l, '%.md$', '') if nil == Files[n] then toFile(n) end end -- Gotta figure out all the files and subs first. File and sub metadata comes along for the ride, coz we need them later. local newMeta = {} for name, file in pairs(Files) do local bitter, path = '', '' local bits, bit = file.bits, file.bit local ln = #bits local body, metadata = '', {} -- Go through our bits, construct Subs with bits. if ln > 0 then bitter = bits[1] end if '' ~= bitter then Subs[''].subs[bitter] = bitter end -- "bitter end" was entirely by accident, I'm keeping it. B-) for i, d in ipairs(bits) do if '' ~= path then path = path .. '/' end path = path .. d toSub(path, 'bits', derefiTable(bits, true)) if i < ln then Subs[path].subs[bits[i + 1]] = bits[i + 1] table.remove(Subs[path].bits, #bits) end end if '.md' == string.sub(name, -3, -1) then -- This is a metadata only file, no content, stash the matadata. for l in io.open(name .. '.md'):lines() do for k, v in string.gmatch(l, "(%w+)%s*=%s*(.+)") do if nil == v then print(name .. ' ' .. k) else metadata[k] = v end end end if '.md' == name then toSub(path, 'metadata', metadata) elseif '/.md' == string.sub(name, -4, -1) then toSub(path, 'metadata', metadata) -- else toFile(string.sub(name, 1, -4), 'metadata', metadata) else newMeta[string.sub(name, 1, -4)] = metadata end Files[name] = nil end end -- FIXTHEM - Lua doesn't like modifying the thing you are pair()ing, like we want to do in the last loop. for name, file in pairs(newMeta) do if nil == Files[name] then toFile(name) end if nil == Files[name].metadata then Files[name].metadata = {} end for k, v in pairs(file) do if nil == Files[name].metadata[k] then Files[name].metadata[k] = v end end end -- Open the files and do the iniital cleanups. for name, file in pairs(Files) do if '.md' ~= string.sub(name, -3, -1) then -- print('Parsing ' .. name .. '.md') h = io.open(name .. '.md', 'r') if nil ~= h then body = h:read('*a') ; h:close() local f0, f1, token -- Deal with my typical double spaced sentence endings. local result = '' local start = 1 repeat f0, f1, token = string.find(body, '([%.%?%!] )', start) if fail ~= f0 then result = result .. string.sub(body, start, f0) if ' ' == string.sub(body, f1 + 1, f1 + 1) then result = result .. '$s$ ' -- ' ' gets turned into hex 0xA0 by parse(). So feed it another metadata token that gets translated to . else result = result .. ' ' end start = f1 + 1 end until fail == f0 result = result .. string.sub(body, start) -- Do the same for fixing the \' \" \| etc mess pandoc left that sed can't fix. body = result result = '' start = 1 repeat f0, f1, token = string.find(body, '(%\\[%"%\'%|%$])', start) if fail ~= f0 then result = result .. string.sub(body, start, f0 - 1) start = f1 end until fail == f0 result = result .. string.sub(body, start) body = result end Files[name].body = body table.insert(Subs[Files[name].path].files, Files[name].bit) end end -- Create an "everything" page, for URL links to every file.HTML. -- TODO - sort the list of pages. local bdy, h = Files['everything'].body, io.open('everything.md', 'a+') bdy = bdy .. '\n\n| page | converted | Foswiki | PmWiki | \n| ---- | --------- | ------- | ------ | ' for name, file in pairs(Files) do local metadata = derefTable(Files[name].metadata, true) if 'everything' ~= name then local ln, fw, pw = 'DUNNO', '', '' if 'PmWiki' == metadata.ogWiki then pw = '[' .. metadata.ogURL .. '](' .. metadata.ogURL .. ')' end if 'Foswiki' == metadata.ogWiki then fw = '[' .. metadata.ogURL .. '](' .. metadata.ogURL .. ')' end if nil ~= file.bit then ln = file.bit end bdy = bdy .. '\n| ' .. name .. ' | [' .. ln .. '](<' .. name .. '.HTML>) | ' .. fw .. ' | ' .. pw .. ' |' end end h:write(bdy) h:close() toFile('everything', 'body', parse(bdy)) -- These functions assume the above file and sub scan has completed. -- Which page in this directory should we show? -- NOTE - only looking for the .md files we scanned for before, any stray HTML, html, HTM, and htm files will get ignored. local whichPage = function(f) local fl = '' if nil ~= Subs[f] then if nil ~= Subs[f].whichPage then return Subs[f].whichPage end if nil ~= Subs[f].files then if 1 == #(Subs[f].files) then fl = Subs[f].files[1] else -- Standard files to search for. for i, v in ipairs{'about', 'readme', 'index', 'homepage', 'mainpage', 'webhome'} do for j, w in ipairs(Subs[f].files) do if v == string.lower(w) then fl = w break end end if '' ~= fl then break end end -- If nothing else, just grab the first one. if ('' == fl) and (nil ~= Subs[f].files[1]) then fl = Subs[f].files[1] end end end end if '' ~= fl then fl = fl .. '.HTML' ; Subs[f].whichPage = fl end return fl end -- Calculate a link from the source directory to the destination directory. local linkFrom = function(source, dest) local depth = 0 local link = '' if source ~= dest then for i, v in ipairs(Subs[source].bits) do if v ~= Subs[dest].bits[i] then depth = i break end end if #(Subs[dest].bits) >= #(Subs[source].bits) then depth = #(Subs[source].bits) for i, v in ipairs(Subs[dest].bits) do if i > depth then if '' ~= link then link = link .. '/' end link = link .. Subs[dest].bits[i] end end if '' ~= link then link = link .. '/' end else depth = #(Subs[source].bits) - depth depth = depth + 1 link = string.rep('../', depth) end end return link end -- More of this actually doing things nonsense. -- Loop through Subs, doing whichPage and inheritance. for name, sub in pairs(Subs) do sub.whichPage = whichPage(name) local metadata = sub.metadata for i, s in pairs(sub.subs) do local nm = i if '' ~= name then nm = name .. '/' .. i end ss = Subs[nm] for k, v in pairs(metadata) do if nil == ss.metadata[k] then if ('favicon' == k) or ('logo' == k) then ss.metadata[k] = linkFrom(nm, name) .. v else ss.metadata[k] = v end end end end end -- Loop through the files we found and actually create their HTML files. for name, file in pairs(Files) do local path, result = '', '' local body, metadata = Files[name].body, derefTable(Files[name].metadata, true) local bits, bit = Files[name].bits, Files[name].bit local ln = #bits path = table.concat(bits, '/', 1, ln) if '' ~= body then -- Inherit stuff from sub and global. local mdata = Subs[path].metadata for k, v in pairs(mdata) do if nil == file.metadata[k] then file.metadata[k] = v end end Files[name].metadata = file.metadata metadata = derefTable(Files[name].metadata, true) for m, x in pairs(globalData) do if nil == metadata[m] then metadata[m] = x end end if nil ~= metadata.ogURL then body = body .. '\n\n---\n\n[Original page](' .. metadata.ogURL .. '), maybe you can edit it.\n' end -- Figure out this pages trail links. metadata.home = linkFrom(path, '') .. Subs[''].whichPage metadata.trail = '' for i, b in ipairs(bits) do local p = table.concat(bits, '/', 1, i) if i < #bits then metadata.trail = metadata.trail .. '' .. b .. ' 👣 ' linkFrom(path, table.concat(bits, '/', 1, i)) else metadata.trail = metadata.trail .. b .. ' ' end end -- Figure out this pages header links. metadata.header = '' subs = {} for i, f in pairs(Subs[path].subs) do table.insert(subs, f) end table.sort(subs, function(a, b) return (string.lower(a) < string.lower(b)) end) for i, f in ipairs(subs) do local pth = path if '' ~= path then pth = path .. '/' end metadata.header = metadata.header .. '' .. f .. ' ' end -- Figure out this pages menu links. metadata.menu = '' if nil == metadata.title then metadata.title = bit end if nil ~= Subs[path].files then table.sort(Subs[path].files, function(a, b) return (string.lower(a) < string.lower(b)) end) end for i, f in ipairs(Subs[path].files) do local title, url = nil, nil if '' == path then title = Files[f].metadata.title url = Files[f].metadata.URL else title = Files[path .. '/' .. f].metadata.title url = Files[path .. '/' .. f].metadata.URL end if nil == title then title = f end if bit == f then metadata.menu = metadata.menu .. '
' .. title .. '
' else if nil ~= url then metadata.menu = metadata.menu .. '' else metadata.menu = metadata.menu .. '' end end end -- Figure out this pages footer links. if nil ~= metadata.pagehistory then metadata.history = 'Page history
' end if nil ~= metadata.sourcecode then metadata.footer = 'source code ' .. metadata.footer end if nil ~= metadata.feedatom then metadata.footer = 'atom feed ' .. metadata.footer end if metadata.footer ~= globalData.footer then metadata.footer = 'Web site ' .. metadata.footer end metadata.footer = '' .. metadata.footer .. '
' -- Do our own metadata replacement, it's simple and works better. local temp = template local start = 1 local f0, f1, token -- Toss the body in first, so the scan can deal with it to. f0, f1, token = string.find(temp, '%$(body)%$') if fail ~= f0 then -- NOTE - this is where we actually parse the markup into HTML. temp = string.sub(temp, 1, f0 - 1) .. parse(body) .. string.sub(temp, f1 + 1) end -- The actual metadata replacement scan. result = '' repeat f0, f1, token = string.find(temp, '%$([%w_]+)%$', start) if fail ~= f0 then if nil ~= metadata[token] then result = result .. string.sub(temp, start, f0 - 1) .. metadata[token] end start = f1 + 1 end until fail == f0 result = result .. string.sub(temp, start) -- Write the file. if '' ~= result then local base = name .. '.HTML' -- print('From ' .. name .. '.md -> ' .. base) local a, e = io.open(base, 'w') if nil == a then print('Could not open ' .. base .. ' - ' .. e) else a:write(result) a:close() end end end end