diff options
Diffstat (limited to 'notYetAnotherWiki.lua')
-rwxr-xr-x | notYetAnotherWiki.lua | 1018 |
1 files changed, 841 insertions, 177 deletions
diff --git a/notYetAnotherWiki.lua b/notYetAnotherWiki.lua index da88a52..f53691b 100755 --- a/notYetAnotherWiki.lua +++ b/notYetAnotherWiki.lua | |||
@@ -1,37 +1,74 @@ | |||
1 | #!/usr/bin/env luajit | 1 | #!/usr/bin/env luajit |
2 | 2 | ||
3 | -- 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. | 3 | --[[ Read the README file for what this is all about. |
4 | If there is no README or similar, then you can find the link to the source below. | ||
4 | 5 | ||
5 | local lcmark = require("lcmark") -- https://github.com/jgm/lcmark | 6 | Normally I define functions and globals at the top, but here I'm interleaving them. |
7 | ]] | ||
6 | 8 | ||
9 | local Lunamark = require("lunamark") -- https://github.com/jgm/lunamark | ||
10 | local Lpeg = require("lpeg") -- https://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html Lunamark uses this, so we can to. | ||
11 | local RE = require("re") -- Part of lpeg. https://www.inf.puc-rio.br/~roberto/lpeg/re.html | ||
7 | 12 | ||
8 | 13 | ||
14 | --------------------------------------------------------------------------------- | ||
9 | -- Some global data. | 15 | -- Some global data. |
10 | local globalData = { | 16 | |
11 | ['_'] = ' ', ['dlr'] = '$', | 17 | local GlobalMetaData = { |
12 | ['devuanCinnabarDark'] = '#310202', ['devuanCinnabarLight'] = '#510505', | 18 | dlr = '$', perc = '%', dot = '.', |
13 | ['devuanDarkPurpyDark'] = '#33313b', ['devuanDarkPurpyLight'] = '#3c3a45', | 19 | devuanCinnabarDark = '#310202', devuanCinnabarLight = '#510505', |
14 | ['devuanDeepSeaDark'] = '#132f40', ['devuanDeepSeaLight'] = '#1a4562', | 20 | devuanDarkPurpyDark = '#33313b', devuanDarkPurpyLight = '#3c3a45', |
15 | ['devuanSaphireDark'] = '#004489', ['devuanSaphireLight'] = '#00509f', | 21 | devuanDeepSeaDark = '#132f40', devuanDeepSeaLight = '#1a4562', |
16 | ['karenPurple'] = '#8800ff', ['onefangPurple'] = '#cc00ff', | 22 | devuanSaphireDark = '#004489', devuanSaphireLight = '#00509f', |
17 | favicon = 'nYAW_icon.png', logo = 'nYAW.png', header = '', --menu = '', | 23 | devuanDevuanDark = '#000000', devuanDevuanLight = '#ffffff', |
18 | history = '', footer = 'Powered by <a href="https://sledjhamr.org/cgit/notYetAnotherWiki/about/">notYetAnotherWiki</a> version 0.0. ', | 24 | -- HTML link colours. Naturally HTML5 deprecated the simple version, replacing it with less simple CSS. |
25 | -- <body> has alink, link, vlink; CSS has active, link, visited, and hover. | ||
26 | devuanDevuanalink = '#03a4ff', devuanDevuanlink = '#0076b6', devuanDevuanvlink = '#6aa4db', devuanDevuanhlink = '#03a4ff', | ||
27 | devuanSDevuanalink = '#98c3db', devuanSDevuanlink = '#ffffff', devuanSDevuanvlink = '#ffffff', devuanSDevuanhlink = '#98c3db', | ||
28 | karenPurple = '#8800ff', onefangPurple = '#cc00ff', onefangGreen = '#42ff00', | ||
29 | PinkFloyd = '#AA00AA', DeepPurple = '#220022', -- From an ancient site of mine, which went from PinkFloyd to DeepPurple as a background gradient. | ||
30 | favicon = 'nYAW_icon.png', logo = 'nYAW.png', | ||
19 | } | 31 | } |
20 | local Sites, Files, Subs = {}, {}, {} | ||
21 | 32 | ||
33 | local Files, Subs, xLinks = {}, {}, {} | ||
34 | local Context = {} -- Coz can't otherwise pass context through to the deeper Lunamark functions I'm overriding. | ||
35 | |||
36 | local Template = '' | ||
37 | local h = io.open("default.template", 'r') | ||
38 | if nil ~= h then | ||
39 | Template = h:read('*a') | ||
40 | h:close() | ||
41 | else | ||
42 | print('oops! No such file ' .. 'default.template') | ||
43 | end | ||
22 | 44 | ||
23 | 45 | ||
46 | --------------------------------------------------------------------------------- | ||
24 | -- Useful functions, part 0. | 47 | -- Useful functions, part 0. |
25 | 48 | ||
26 | -- A simple table.subtable = subtable wont work, you end up with a reference so that changes to the later get applaid to the former. | 49 | -- A simple table.subtable = subtable wont work, you end up with a reference so that changes to the later get applied to the former. |
27 | local copyTable = function(t, strip) | 50 | local derefiTable = function(t, strip) |
28 | local argh = {} | 51 | local argh = {} |
29 | for l, y in ipairs(t) do if (l ~= y.name) and strip then table.insert(argh, y) end end | 52 | for l, y in ipairs(t) do if (l ~= y.name) and strip then table.insert(argh, y) end end |
30 | return argh | 53 | return argh |
31 | end | 54 | end |
55 | local derefTable = function(t, strip) | ||
56 | local argh = {} | ||
57 | for l, y in pairs(t) do argh[l] = y end | ||
58 | return argh | ||
59 | end | ||
32 | 60 | ||
33 | 61 | ||
34 | -- String together the bits array into a path string. | 62 | local writeString = function(base, body) |
63 | local a, e = io.open(base, 'w') | ||
64 | if nil == a then print('Could not open ' .. base .. ' - ' .. e) else | ||
65 | a:write(body) | ||
66 | a:close() | ||
67 | end | ||
68 | end | ||
69 | |||
70 | |||
71 | -- String together the bits array into a path string. Or the other way around. lol | ||
35 | local stringBits = function(l) | 72 | local stringBits = function(l) |
36 | local bits = {} | 73 | local bits = {} |
37 | local last = 1 | 74 | local last = 1 |
@@ -45,258 +82,885 @@ local stringBits = function(l) | |||
45 | end | 82 | end |
46 | 83 | ||
47 | 84 | ||
85 | -- Put a value into the Files or Subs table, creating things if needed. | ||
86 | local toFile = function(name, key, value) | ||
87 | if nil == Files[name] then | ||
88 | local bits, bit = stringBits(name) | ||
89 | local path = '' | ||
90 | Files[name] = {} | ||
91 | Files[name].headers = {} | ||
92 | Files[name].bits = bits | ||
93 | Files[name].bit = bit | ||
94 | for i, d in ipairs(bits) do | ||
95 | if '' ~= path then path = path .. '/' end | ||
96 | path = path .. d | ||
97 | end | ||
98 | Files[name].path = path | ||
99 | -- Files[name].body = '' | ||
100 | if ("Foswiki" == bits[1]) or ("PmWiki" == bits[1]) then Files[name].ogWiki = bits[1] end | ||
101 | end | ||
102 | if nil ~= key then Files[name][key] = value end | ||
103 | for i, v in ipairs{'metadata', 'bits', } do | ||
104 | if nil == Files[name][v] then Files[name][v] = {} end | ||
105 | end | ||
106 | -- Open the files and do the initial cleanups. | ||
107 | local body = '' | ||
108 | if '.md' ~= string.sub(name, -3, -1) then | ||
109 | h = io.open(name .. '.md', 'r') | ||
110 | if nil ~= h then | ||
111 | -- print('Parsing ' .. name .. '.md') | ||
112 | Context = Files[name] | ||
113 | body = h:read('*a') ; h:close() | ||
114 | -- Deal with my typical double spaced sentence endings, and other things. | ||
115 | local result = RE.compile( [=[{~ | ||
116 | ( | ||
117 | {[.?!]{" "}} -> '%1 ' / -- ' ' gets turned into hex 0xA0 by parse(). So feed it another metadata token that gets translated to . Seems we now skip this issue. | ||
118 | {[\\]{['"|$]}} -> '%2' / -- Do the same for fixing the \' \" \| etc mess pandoc left. | ||
119 | {"[ ]{.foswikiGrayFG}" } -> '' / -- Coz Foswiki sucks. No idea why the next thing didn't catch it. | ||
120 | {"[" {([^]])+} "]{.foswiki" {([^FG}])+} "FG}" } -> "<span style='color: %3;'>%2</span>" / | ||
121 | {"::: {."[A-Za-z_. ]+"}"} -> '' / | ||
122 | {":::"} -> '' / | ||
123 | {"-noComment-"} -> ' -- ' / | ||
124 | {"| bgcolor=#" {([^ ])+} " " } -> "| <span style='background: #%2;'> bgcolor=#%2 </span> " / -- Deal with debdog's color table. | ||
125 | {"[" {([^]])+} ']{style="color: ' {([^}])+} "}" } -> "<span style='color: %3;'>%2</span>" / | ||
126 | {"[" {([^]])+} "]{style='color: " {([^}])+} "}" } -> "<span style='color: %3;'>%2</span>" / | ||
127 | {"{#"[A-Za-z_]+"}"} -> '' / | ||
128 | {"### [[edit](/bin/edit/Main/" {([^%nl])+} } -> '' / | ||
129 | {"#"+ " " {([^%nl])+} } -> header / | ||
130 | . | ||
131 | )* ~}]=], { header = function(a) table.insert(Context.headers, a); return a end } ):match(body) | ||
132 | body = result | ||
133 | -- {"<!--".*"-->"} -> '' / | ||
134 | -- {"[$]"} -> '$dlr$' / -- Replace $, coz otherwise it confuses things later. | ||
135 | body = RE.gsub(body, '{[$]}', '$dlr$') | ||
136 | -- {"[%]"} -> '$perc$' / -- Gotta be done after the %1's above. otherwise screws things up when included above. | ||
137 | -- body = RE.gsub(body, '{[%]}', '$perc$') -- Coz otherwise stray % trip up the capture part. | ||
138 | end | ||
139 | Files[name].body = body | ||
140 | end | ||
141 | end | ||
142 | local toSub = function(name, key, value) | ||
143 | if nil == Subs[name] then | ||
144 | local bits, bit = stringBits(name) | ||
145 | local path = '' | ||
146 | Subs[name] = {} | ||
147 | table.insert(bits, bit) | ||
148 | Subs[name].bits = bits | ||
149 | Subs[name].bit = bit | ||
150 | for i, d in ipairs(bits) do | ||
151 | if '' ~= path then path = path .. '/' end | ||
152 | path = path .. d | ||
153 | end | ||
154 | Subs[name].path = path | ||
155 | end | ||
156 | if nil ~= key then Subs[name][key] = value end | ||
157 | for i, v in ipairs{'metadata', 'bits', 'files', 'subs'} do | ||
158 | if nil == Subs[name][v] then Subs[name][v] = {} end | ||
159 | end | ||
160 | end | ||
161 | |||
162 | |||
48 | 163 | ||
164 | local readMdMd = function(name, metadata) | ||
165 | local h1 = io.open(name .. '.md') | ||
166 | if nil == h1 then | ||
167 | -- print('Could not open ' .. name .. '.md') | ||
168 | return {} | ||
169 | else | ||
170 | for l in h1:lines() do | ||
171 | for k, v in string.gmatch(l, "(%w+)%s*=%s*(.+)") do | ||
172 | if nil == v then | ||
173 | print(name .. ' ' .. k) | ||
174 | else | ||
175 | metadata[k] = v | ||
176 | end | ||
177 | end | ||
178 | end | ||
179 | end | ||
180 | return metadata | ||
181 | end | ||
182 | |||
183 | |||
184 | |||
185 | local commonLinky = function(l, body, u, url, beg, en, beg0, en0, bump) | ||
186 | if nil == url then | ||
187 | -- print('OOPS! unknown linky - @' .. l .. '\t\t\t' .. string.sub(body, beg - 9, en) .. ' ' .. string.sub(body, en + 1, en0)) | ||
188 | else | ||
189 | local md = readMdMd(url, {}) | ||
190 | -- if nil ~= md then | ||
191 | if nil ~= md.realURL then url = md.realURL end | ||
192 | -- end | ||
193 | body = string.sub(body, 1, beg - bump) .. url .. string.sub(body, en0 + 1) | ||
194 | here = here + string.len(url) | ||
195 | end | ||
196 | if 1 == bump then | ||
197 | here = here + 1 | ||
198 | beg, en = RE.find(body, [['https://fos.wiki.devuan.org/']], here) | ||
199 | else | ||
200 | beg, en = RE.find(body, [["'https://wiki.devuan.org/"]], here) | ||
201 | end | ||
202 | return beg, en, body, here | ||
203 | end | ||
204 | |||
205 | |||
206 | |||
207 | --------------------------------------------------------------------------------- | ||
49 | -- Actually start doing things. | 208 | -- Actually start doing things. |
50 | 209 | ||
51 | -- Scan the subdirectories looking for .md files. | 210 | -- Create the base of everything.md here, so it gets picked up as usual in the file scan. |
52 | local directory = arg[1] | 211 | h = io.open('everything.md', 'w') |
53 | if nil == directory then directory = '.' end | 212 | if nil ~= h then |
54 | if '.' ~= directory then | 213 | h:close() |
55 | for l in io.popen('find . -name "*.md" -type f,l -printf "%P\n"'):lines() do | 214 | else |
56 | Files[string.gsub(l, '%.md$', '')] = {} | 215 | print("Can't open everything.md for writing.") |
216 | end | ||
217 | |||
218 | -- Scan the sub folders looking for our files. | ||
219 | local Folder = arg[1] | ||
220 | toSub('') | ||
221 | if nil == Folder then Folder = '.' end | ||
222 | --GlobalMetaData.root = Folder | ||
223 | |||
224 | --[[ Sort out realURL for symlinked .md.md files. | ||
225 | realURL is the generic URL part for this page. By policy it points to whatever is the latest copy / symlink of the original download .md.md. | ||
226 | realURL starts out being the path to the downloaded file and friends. | ||
227 | If we make a symlink during SuckIt, then that gets updated to point to the place the symlink is in. | ||
228 | below we compare timestamps and select the latest version if there's more than one symlink. | ||
229 | For the "page symlinked" problem, this should work if realURL is kept updated. | ||
230 | For the "page copied" problem, this should work if realURL is kept updated, same as symlinked really, coz that's just another copy. | ||
231 | For the "page moved" problem, that'll be the most recent symlink, the old one still has it's symlink pointing to the download .md.md, which gets updated with the current realURL. | ||
232 | So when some external old URL points to someplace a page used to be, it's old symlink points to the up to date download .md.md, and we know where to go to find the page now. | ||
233 | A left over .md.md file should have a redirect .HTML page created for it. | ||
234 | ]] | ||
235 | for l in io.popen('find -L ' .. Folder .. ' -name unsorted -prune -o -name "*.md.md" -xtype l -printf "%P\n"'):lines() do | ||
236 | local metadata = readMdMd(string.sub(l, 1, -4), {}) | ||
237 | if nil == metadata.realURL then | ||
238 | metadata.realURL = string.sub(l, 1, -7) | ||
239 | else | ||
240 | if metadata.realURL ~= string.sub(l, 1, -7) then | ||
241 | metadata.realURL = string.sub(l, 1, -7) | ||
242 | -- If this already exists, compare the timestamps, most recent wins. | ||
243 | local time0 = io.popen('ls -l --time-style=+%s "' .. metadata.realURL .. '.md.md" | cut -d \' \' -f 6'):read('l') | ||
244 | local time1 = io.popen('ls -l --time-style=+%s "' .. l .. '" | cut -d \' \' -f 6'):read('l') | ||
245 | if time0 > time1 then metadata = nil end | ||
246 | else metadata = nil end | ||
247 | end | ||
248 | |||
249 | if nil ~= metadata then | ||
250 | -- DUNNO if this writes to the original file, or overwrites the symlink. | ||
251 | local a, e = io.open(l, 'w') | ||
252 | if nil == a then print('Could not open ' .. l .. ' - ' .. e) else | ||
253 | for k, v in pairs(metadata) do | ||
254 | a:write(k .. '=' .. v .. '\n') | ||
255 | end | ||
256 | a:close() | ||
257 | end | ||
57 | end | 258 | end |
58 | end | 259 | end |
59 | -- Can add in a distant directory to, for putting it's results in the current directory. | 260 | |
60 | for l in io.popen('find ' .. directory .. ' -name "*.md" -type f,l -printf "%P\n"'):lines() do | 261 | -- Clean up unsorted. |
262 | for l in io.popen('find -L ' .. Folder .. ' -name unsorted -prune -o -name "*.md" -a -not -name "*.md.md" -xtype l -printf "%P\n"'):lines() do | ||
263 | local tp = '_fos' | ||
264 | local metadata = readMdMd(l, {}) | ||
265 | if nil ~= metadata then | ||
266 | if "PmWiki" == metadata.ogWiki then tp = '_pm' end | ||
267 | if nil ~= metadata.ogFile then | ||
268 | local unsort = 'unsorted/' .. metadata.ogFile | ||
269 | local a, e = io.open(unsort .. tp .. '.md' , 'r') | ||
270 | if nil ~= a then | ||
271 | a:close() | ||
272 | -- Keep the .md.md symlink, delete the rest. | ||
273 | os.execute('rm ' .. unsort .. tp .. '.HTML') | ||
274 | os.execute('rm ' .. unsort .. tp .. '.md') | ||
275 | a, e = io.open(unsort .. tp .. '.HTML', 'w') | ||
276 | if nil == a then print('Could not open ' .. unsort .. tp .. '.HTML' .. ' - ' .. e) else | ||
277 | local dst = string.sub(l, 1, -4) .. '.HTML' | ||
278 | local cnt = 1 | ||
279 | for j = 1, #metadata.ogFile do | ||
280 | if '/' == string.sub(metadata.ogFile, j, j) then cnt = cnt + 1 end | ||
281 | end | ||
282 | dst = string.rep('../', cnt) .. dst | ||
283 | a:write( | ||
284 | [=[<!DOCTYPE html> | ||
285 | <html> | ||
286 | <head><meta http-equiv="refresh" content="0; url=]=] .. dst .. '"' .. [=[/></head> | ||
287 | <body><p>Click this if you don't get redirected to the real page - <a href="]=] .. dst .. '"' .. [=[>Redirect</a></p></body> | ||
288 | </html> | ||
289 | ]=]) | ||
290 | a:close() | ||
291 | print('REDIRECT ' .. unsort .. tp .. '.HTML \t-> ' .. dst) | ||
292 | end | ||
293 | |||
294 | end | ||
295 | end | ||
296 | end | ||
297 | end | ||
298 | |||
299 | -- Look for copied pages from the other wikis. | ||
300 | for l in io.popen('find -L ' .. Folder .. ' -name "*.HTM" -type f,l -printf "%P\n"'):lines() do | ||
301 | -- Only do this if .HTM is newer than .md, or .md doesn't exist. | ||
302 | local htime = io.popen("date -ur " .. l .. " +%s"):read('l') | ||
303 | local mtime = io.popen("date -ur " .. string.sub(l, 1, -4) .. "md +%s 2>/dev/null"):read('l') | ||
304 | if (nil == mtime) or (htime > mtime) then | ||
305 | print('pandoc converting ' .. l .. ' -> ' .. string.sub(l, 1, -4) .. 'md') | ||
306 | os.execute('cp ' .. l .. ' ' .. l .. '_ORIGINAL0') | ||
307 | -- Open the HTM files and do the initial cleanups, then pandoc them. | ||
308 | h = io.open(l, 'r') | ||
309 | if nil ~= h then | ||
310 | local body = h:read('*a') ; h:close() | ||
311 | writeString(l .. '_ORIGINAL1', body) | ||
312 | if 'Foswiki' == string.sub(l, 1, 7) then | ||
313 | -- Strip out the actual content. | ||
314 | local beg, en = RE.find(body, [['<div id="patternMainContents">']]) if nil ~= beg then body = string.sub(body, en + 1) end | ||
315 | beg, en = RE.find(body, [['<div class="patternContent">']]) if nil ~= beg then body = string.sub(body, en + 1) end | ||
316 | beg, en = RE.find(body, [['<div class="foswikiTopic">']]) if nil ~= beg then | ||
317 | if ' -- ' == string.sub(body, en + 1, en + 4) then | ||
318 | beg, en = RE.find(body, '[%nl]', en + 4) | ||
319 | body = string.sub(body, en + 1) | ||
320 | end | ||
321 | end | ||
322 | beg, en = RE.find(body, [['<div class="patternInfo">']]) if nil ~= beg then body = string.sub(body, 1, beg - 1) end | ||
323 | -- beg, en = RE.find(body, [['<div class="foswikiForm foswikiFormStep">']]) if nil ~= beg then body = string.sub(body, 1, en + 1) end | ||
324 | beg, en = RE.find(body, [['<div class="foswikiAttachments foswikiFormStep" style="overflow:auto">']]) if nil ~= beg then body = string.sub(body, 1, beg - 1) end | ||
325 | beg, en = RE.find(body, [['<div class="foswikiSearchResultsPager">']]) if nil ~= beg then body = string.sub(body, 1, beg - 1) end | ||
326 | -- Some clean ups. | ||
327 | local result = RE.compile( [[{~ | ||
328 | ( | ||
329 | {'class="foswikiCurrentTopicLink"'} -> blank / | ||
330 | {'class="foswikiNewLink"'} -> blank / | ||
331 | {"<span class='foswikiSmall'><a href='/bin/edit/Main/" ([^;action=form])* ";action=form'>edit</a></span>"} -> blank / | ||
332 | {" -- "} -> '-noComment-' / | ||
333 | -- {"-- " ([^%nl])* } -> blank / | ||
334 | {'<div class="foswikiTopic">'} -> blank / | ||
335 | {'rel="nofollow"'} -> blank / {"rel='nofollow'"} -> blank / | ||
336 | {"target='_blank'"} -> blank / | ||
337 | {"</div>" ([%nl])* } -> blank / | ||
338 | -- {'style="' ([^"])+ '"'} -> blank / {"style='" ([^'])+ "'"} -> blank / | ||
339 | . | ||
340 | )* ~}]], { blank = function(a) return '' end } ):match(body) | ||
341 | body = result | ||
342 | -- body = RE.gsub(body, [=[{"<!-- ".*"-->"}]=], '') -- FIXME | ||
343 | local here = 1 | ||
344 | beg, en = RE.find(body, [['https://fos.wiki.devuan.org/']], here) | ||
345 | while nil ~= beg do | ||
346 | here = beg + 1 | ||
347 | local beg0, en0 | ||
348 | local url = nil | ||
349 | if '"' == string.sub(body, beg - 1, beg - 1) then | ||
350 | beg0, en0 = RE.find(body, [['"']], en) | ||
351 | url = string.sub(body, en + 1, en0 - 1) | ||
352 | end | ||
353 | if "'" == string.sub(body, beg - 1, beg - 1) then | ||
354 | beg0, en0 = RE.find(body, [["'"]], en) | ||
355 | url = string.sub(body, en + 1, en0) | ||
356 | end | ||
357 | |||
358 | if nil ~= url then | ||
359 | if ('pub/' == string.sub(url, 1, 4)) then | ||
360 | -- FIXME? - evil hack? | ||
361 | url = 'Foswiki/' .. url | ||
362 | --print('FOSWIKI HTM ' .. url) | ||
363 | else | ||
364 | url = nil | ||
365 | end | ||
366 | end | ||
367 | --print('HTM0 ' .. string.sub(body, beg, en + 84) .. ' \t\t') | ||
368 | beg, en, body, here = commonLinky(l, body, 'https://fos.wiki.devuan.org/', url, beg, en, beg0, en0, 1) | ||
369 | --if nil ~= en then print('HTM1 ' .. string.sub(body, beg, en + 84) .. ' \t\t') end | ||
370 | --[=[ | ||
371 | if nil == url then | ||
372 | print('OOPS! unknown linky - @' .. l .. '\t\t\t' .. string.sub(body, beg - 9, en) .. ' ' .. string.sub(body, en + 1, en0)) | ||
373 | else | ||
374 | -- print(' linky - @' .. l .. '\t\t\t' .. string.sub(body, beg - 9, en) .. ' ' .. string.sub(body, en + 1, en0) .. ' -> ' .. url) | ||
375 | local md = readMdMd(url, {}) | ||
376 | -- if nil ~= md then | ||
377 | if nil ~= md.realURL then url = md.realURL end | ||
378 | -- end | ||
379 | body = string.sub(body, 1, beg - 1) .. url .. string.sub(body, en0 + 1) | ||
380 | here = here + #url | ||
381 | end | ||
382 | beg, en = RE.find(body, [['https://fos.wiki.devuan.org/']], here) | ||
383 | ]=] | ||
384 | end | ||
385 | |||
386 | writeString(l .. '_NEW', body) | ||
387 | elseif 'PmWiki' == string.sub(l, 1, 6) then | ||
388 | local beg, en = RE.find(body, [['<!--PageText-->']]) if nil ~= beg then body = string.sub(body, en + 2) end | ||
389 | beg, en = RE.find(body, [["div id='wikitext'>"]]) if nil ~= beg then body = string.sub(body, en + 2) end | ||
390 | beg, en = RE.find(body, [["<div id='printfoot'>"]]) if nil ~= beg then body = string.sub(body, 1, beg - (2 + 9)) end -- There's a </div> to get rid of to. | ||
391 | beg, en = RE.find(body, [['<!--HTMLFooter-->']]) if nil ~= beg then body = string.sub(body, 1, beg - 2) end | ||
392 | local result = RE.compile( [[{~ | ||
393 | ( | ||
394 | {"class='categorylink'"} -> blank / | ||
395 | {"class='createlink'"} -> blank / | ||
396 | {"class='createlinktext'"} -> blank / | ||
397 | {"class='escaped'"} -> blank / | ||
398 | {"class='diffmarkup'"} -> blank / | ||
399 | {"class='selflink'"} -> blank / | ||
400 | {"class='urllink'"} -> blank / | ||
401 | {"<div class='vspace'></div><hr /> <a class='wikilink' href='https://wiki.devuan.org?n=Profiles." .* ([%nl])* } -> blank / | ||
402 | {"<a class='selflink' href='https://wiki.devuan.org?n=Profiles." .* ([%nl])* } -> blank / | ||
403 | {"<div class='vspace'></div><hr />"} -> blank / | ||
404 | {"<div class='vspace'></div>"} -> blank / | ||
405 | {"class='wikilink'"} -> blank / | ||
406 | {'rel="nofollow"'} -> blank / {"rel='nofollow'"} -> blank / | ||
407 | {"target='_blank'"} -> blank / | ||
408 | -- {'style="' ([^"])+ '"'} -> blank / {"style='" ([^'])+ "'"} -> blank / | ||
409 | {"<span class='hlt " {([a-z])+} "'></span><pre" } -> "<pre class='%2'" / | ||
410 | . | ||
411 | )* ~}]], { blank = function(a) return '' end } ):match(body) | ||
412 | body = result | ||
413 | -- body = RE.gsub(body, [=["<a " {([^ >])+} " >"]=], "<a %1>") | ||
414 | -- DONE? - <span class='hlt html'></span><pre style='background-color: #cc00ff;' class='escaped'> ... lines of HTML code ... </pre> | ||
415 | -- most of the time I'll see <pre class='escaped'> | ||
416 | -- My own looking glass has several. | ||
417 | -- Foswiki <pre class='bash'> | ||
418 | -- CommonMark->HTML ---lua <pre><code class="language-lua"> .............................. </code></pre> | ||
419 | -- Seems to be the spec way of doing it. | ||
420 | -- most of the time I'll see <pre><code> | ||
421 | |||
422 | here = 1 | ||
423 | beg, en = RE.find(body, [["'https://wiki.devuan.org/"]], here) | ||
424 | while nil ~= beg do | ||
425 | here = beg + 1 | ||
426 | local beg0, en0 = RE.find(body, [["'"]], en) | ||
427 | -- FIXME? - This might be working around a bug elsewhere. | ||
428 | if "'" == string.sub(body, en0, en0) then en0 = en0 - 1 end | ||
429 | local url = string.sub(body, en + 1, en0) | ||
430 | if '?n=' == string.sub(url, 1, 3) then | ||
431 | url = string.sub(url, 4):gsub('[%a]+%.([%a-]+)', '%1_pm.HTML') | ||
432 | elseif ("'" == url) or ('uploads/' == string.sub(url, 1, 8)) then | ||
433 | -- FIXME - evil hack? Yep, evil hack, need to know the depth of the source, which isn't here. | ||
434 | url = 'PmWiki/' .. url | ||
435 | else | ||
436 | url = nil | ||
437 | end | ||
438 | --print('HTM0 ' .. string.sub(body, beg, en + 84) .. ' \t\t') | ||
439 | beg, en, body, here = commonLinky(l, body, "'https://wiki.devuan.org/", url, beg, en, beg0, en0, 0) | ||
440 | --if nil ~= en then print('HTM1 ' .. string.sub(body, beg, en + 84) .. ' \t\t') end | ||
441 | end | ||
442 | |||
443 | writeString(l .. '_NEW', body) | ||
444 | end | ||
445 | end | ||
446 | |||
447 | ok, rslt, status = os.execute('pandoc --wrap=preserve -f html -t commonmark_x --self-contained ' .. l .. '_NEW' .. ' >' .. string.sub(l, 1, -4) .. 'md') | ||
448 | end | ||
449 | end | ||
450 | |||
451 | if '.' ~= Folder then | ||
452 | for l in io.popen('find -L . -name "*.md" -type f,l -printf "%P\n"'):lines() do | ||
453 | toFile(string.gsub(l, '%.md$', '')) | ||
454 | end | ||
455 | end | ||
456 | |||
457 | -- Can add in a distant folder to, for putting it's results in the current folder. | ||
458 | for l in io.popen('find -L ' .. Folder .. ' -name "*.md" -type f,l -printf "%P\n"'):lines() do | ||
61 | local n = string.gsub(l, '%.md$', '') | 459 | local n = string.gsub(l, '%.md$', '') |
62 | if nil == Files[n] then Files[n] = {} end | 460 | if nil == Files[n] then toFile(n) end |
63 | end | 461 | end |
64 | 462 | ||
65 | -- Gotta figure out all the files and subs first. File and sub metadata comes along for the ride. | 463 | |
66 | Subs[''] = {files = {}, subs = {}, bits = {}} | 464 | -- Gotta figure out all the files and subs first. File and sub metadata comes along for the ride, coz we need them later. |
465 | local NewMeta = {} | ||
67 | for name, file in pairs(Files) do | 466 | for name, file in pairs(Files) do |
68 | local bitter, path = '', '' | 467 | local bitter, path = '', '' |
69 | local bits, bit = stringBits(name) | 468 | local bits, bit = file.bits, file.bit |
70 | local ln = #bits | 469 | local ln = #bits |
470 | local body, metadata = '', {} | ||
71 | 471 | ||
72 | -- Go through our bits, construct Subs with bits. | 472 | -- Go through our bits, construct Subs with bits. |
73 | Files[name].bits = bits | ||
74 | Files[name].bit = bit | ||
75 | if ln > 0 then bitter = bits[1] end | 473 | if ln > 0 then bitter = bits[1] end |
76 | if '' ~= bitter then Subs[''].subs[bitter] = bitter end -- "bitter end" was entirely by accident, I'm keeping it. B-) | 474 | if '' ~= bitter then Subs[''].subs[bitter] = bitter end -- "bitter end" was entirely by accident, I'm keeping it. B-) |
77 | for i, d in ipairs(bits) do | 475 | for i, d in ipairs(bits) do |
78 | if '' ~= path then path = path .. '/' end | 476 | if '' ~= path then path = path .. '/' end |
79 | path = path .. d | 477 | path = path .. d |
80 | if nil == Subs[path] then Subs[path] = {files = {}, subs = {}} end | 478 | toSub(path) |
81 | if i < ln then Subs[path].subs[bits[i + 1]] = bits[i + 1] end | 479 | if i < ln then |
82 | Subs[path].bits = copyTable(bits, true) | 480 | Subs[path].subs[bits[i + 1]] = bits[i + 1] |
83 | if i < ln then table.remove(Subs[path].bits, #bits) end | 481 | table.remove(Subs[path].bits, #bits) |
482 | end | ||
84 | end | 483 | end |
85 | 484 | ||
86 | -- Start the file parsing here, coz we need it's metadata. | 485 | if '.md' == string.sub(name, -3, -1) then |
87 | print('Parsing ' .. name .. '.md') | 486 | -- This is a metadata only file, no content, stash the matadata. |
88 | local h = io.open(name .. '.md', 'r') | 487 | |
89 | -- TODO - should bail here on error? | 488 | metadata = readMdMd(name, metadata) |
90 | if nil ~= h then file.cm = h:read('*a') ; h:close() else print('oops! No such name ' .. name) end | 489 | if '.md' == name then toSub(path, 'metadata', metadata) |
91 | -- Convert the CommonMark to HTML, including the metadata. | 490 | elseif '/.md' == string.sub(name, -4, -1) then toSub(path, 'metadata', metadata) |
92 | local body, metadata, err = lcmark.convert(file.cm, "html", {smart = true, yaml_metadata = true, columns = 0}) | 491 | -- else toFile(string.sub(name, 1, -4), 'metadata', metadata) |
93 | if nil == body then print('oops! ' .. err) | 492 | else NewMeta[string.sub(name, 1, -4)] = metadata -- Coz we can't add to Files here. |
94 | elseif '' == body then | 493 | end |
95 | -- This is a metadata only file, no content, stash the matadata in it's directory. | ||
96 | Subs[path].metadata = metadata | ||
97 | Files[name] = nil | 494 | Files[name] = nil |
98 | else | ||
99 | -- Ordinary md file, stash it's metadata and parsed body. | ||
100 | file.metadata = metadata | ||
101 | file.body = body | ||
102 | table.insert(Subs[path].files, bit) | ||
103 | end | 495 | end |
104 | end | 496 | end |
105 | 497 | ||
498 | -- FIXED - Lua doesn't like modifying the thing you are pair()ing, like we want to do in the last loop. | ||
499 | for name, file in pairs(NewMeta) do | ||
500 | if nil == Files[name] then toFile(name) end | ||
501 | for k, v in pairs(file) do | ||
502 | if nil == Files[name].metadata[k] then Files[name].metadata[k] = v end | ||
503 | end | ||
504 | end | ||
505 | |||
506 | -- Fix up subs now we have all the file bits. | ||
507 | for name, file in pairs(Files) do | ||
508 | if '.md' ~= string.sub(name, -3, -1) then | ||
509 | table.insert(Subs[file.path].files, file.bit) | ||
510 | end | ||
511 | end | ||
512 | |||
513 | -- Find empty subs. | ||
514 | for name, sub in pairs(Subs) do | ||
515 | if 0 == #sub.files then | ||
516 | print("EMPTY " .. name) | ||
517 | h = io.open(name .. '/index.md', 'w') | ||
518 | if nil ~= h then | ||
519 | h:write('This folder has no files.') | ||
520 | h:close() | ||
521 | else | ||
522 | print("Can't open " .. name .. '/index.md for writing.') | ||
523 | end | ||
524 | end | ||
525 | end | ||
106 | 526 | ||
107 | 527 | ||
528 | --------------------------------------------------------------------------------- | ||
108 | -- These functions assume the above file and sub scan has completed. | 529 | -- These functions assume the above file and sub scan has completed. |
109 | 530 | ||
110 | -- Which page in this directory should we show? | 531 | -- Which page in this folder should we show? |
111 | -- NOTE - only looking for the .md files we scanned for before, any stray HTML, html, HTM, atd htm files will get ignored. | 532 | -- NOTE - only looking for the .md files we scanned for before, any stray HTML, html, HTM, and htm files will get ignored. |
112 | local whichPage = function(f) | 533 | local whichPage = function(f) |
113 | local fl = '' | 534 | local fl = '' |
114 | if (nil ~= Subs[f]) and (nil ~= Subs[f].files) then | 535 | if nil ~= Subs[f] then |
115 | if 1 == #(Subs[f].files) then fl = Subs[f].files[1] .. '.HTML' else | 536 | if nil ~= Subs[f].whichPage then return Subs[f].whichPage end |
116 | -- Standard files to search for. | 537 | if nil ~= Subs[f].files then |
117 | for i, v in ipairs{'about', 'readme', 'index', 'homepage', 'mainpage', 'webhome'} do | 538 | if 1 == #(Subs[f].files) then fl = Subs[f].files[1] else |
118 | for j, w in ipairs(Subs[f].files) do | 539 | -- Standard files to search for. |
119 | if v == string.lower(w) then | 540 | for i, v in ipairs{'about', 'readme', 'index', 'homepage', 'mainpage', 'webhome'} do |
120 | fl = v .. '.HTML' | 541 | for j, w in ipairs(Subs[f].files) do |
121 | break | 542 | if v == string.lower(w) then |
543 | fl = w | ||
544 | break | ||
545 | end | ||
122 | end | 546 | end |
547 | if '' ~= fl then break end | ||
123 | end | 548 | end |
124 | if '' ~= fl then break end | 549 | -- If nothing else, just grab the first one. |
550 | if ('' == fl) and (nil ~= Subs[f].files[1]) then fl = Subs[f].files[1] end | ||
125 | end | 551 | end |
126 | -- If nothing else, just grab the first one. | ||
127 | if ('' == fl) and (nil ~= Subs[f].files[1]) then fl = Subs[f].files[1] end | ||
128 | end | 552 | end |
129 | end | 553 | end |
554 | if '' ~= fl then fl = fl .. '.HTML' ; Subs[f].whichPage = fl end | ||
130 | return fl | 555 | return fl |
131 | end | 556 | end |
132 | 557 | ||
133 | 558 | ||
134 | -- Calculate a link from the source directory to the destination directory. | 559 | -- Figure out the original title and link for the original wiki. |
560 | local whichWiki = function(metadata) | ||
561 | local title, link = '', '' | ||
562 | if 'PmWiki' == metadata.ogWiki then | ||
563 | title = metadata.ogBase .. '.' .. metadata.ogFile | ||
564 | link = metadata.ogURL .. '/?n=' .. metadata.ogBase .. '.' .. metadata.ogFile | ||
565 | end | ||
566 | if 'Foswiki' == metadata.ogWiki then | ||
567 | title = metadata.ogBase .. '/' .. metadata.ogFile | ||
568 | link = metadata.ogURL .. '/' .. metadata.ogBase .. '/' .. metadata.ogFile | ||
569 | end | ||
570 | return title, link | ||
571 | end | ||
572 | |||
573 | |||
574 | -- Calculate a link from the source folder to the destination folder. | ||
135 | local linkFrom = function(source, dest) | 575 | local linkFrom = function(source, dest) |
576 | -- Evil hacks! | ||
577 | if 'Profiles' == dest then dest = 'PmWiki/Profiles' end | ||
578 | if 'Onefang' == dest then dest = 'PmWiki/Onefang' end | ||
579 | if 'Tiki' == dest then dest = 'PmWiki/Tiki' end | ||
580 | |||
581 | if source == dest then return '' end | ||
136 | local depth = 0 | 582 | local depth = 0 |
137 | local link = '' | 583 | local lnk = '' |
138 | if source ~= dest then | 584 | if source ~= dest then |
139 | for i, v in ipairs(Subs[source].bits) do | 585 | if nil == Subs[source] then |
140 | if v ~= Subs[dest].bits[i] then | 586 | -- print('!!!! No idea where to find source ' .. source) |
141 | depth = i | 587 | return 'DUNNO' |
142 | break | 588 | end |
589 | if nil == Subs[dest] then | ||
590 | if dest == Subs[source].bit then | ||
591 | return '' | ||
592 | else | ||
593 | -- print('!!!! No idea where to find dest ' .. dest .. ' from ' .. Subs[source].path .. ' / ' .. Subs[source].bit) | ||
594 | return 'DUNNO' | ||
143 | end | 595 | end |
144 | end | 596 | end |
145 | depth = #(Subs[source].bits) - depth | 597 | local s = Subs[source].bits |
146 | depth = depth + 1 | 598 | local d = Subs[dest].bits |
147 | link = string.rep('../', depth) | 599 | local sl = #s |
148 | if (0 == depth) or (depth > #(Subs[dest].bits)) then | 600 | local dl = #d |
149 | for i, v in ipairs(Subs[dest].bits) do | 601 | |
150 | if i >= depth then | 602 | if 0 == dl then |
151 | if '' ~= link then link = link .. '/' end | 603 | depth = sl |
152 | link = link .. Subs[dest].bits[i] | 604 | else |
605 | for i, v in ipairs(s) do | ||
606 | if (nil == d[i]) or (v ~= d[i]) then | ||
607 | depth = i | ||
608 | break | ||
153 | end | 609 | end |
154 | end | 610 | end |
155 | end | 611 | end |
612 | -- depth is where they DON'T match. | ||
613 | local m = depth - 1 | ||
614 | if 0 > m then m = 0 end | ||
615 | lnk = string.rep('../', sl - m) | ||
616 | if 0 ~= (m + 1) then lnk = lnk .. table.concat(d, '/', m + 1, dl) end | ||
156 | end | 617 | end |
157 | return link | 618 | return lnk |
158 | end | 619 | end |
159 | 620 | ||
160 | 621 | ||
161 | 622 | ||
623 | --------------------------------------------------------------------------------- | ||
162 | -- More of this actually doing things nonsense. | 624 | -- More of this actually doing things nonsense. |
163 | 625 | ||
164 | -- Loop through the files we found and actually create their HTML files. | 626 | -- Create an "everything" page, for URL links to every file.HTML. |
627 | local Bdy = '# All the pages\n\n| page | original page | last edited UTC | \n| --------- | ------- | --------------- | ' | ||
628 | Pages = {} | ||
165 | for name, file in pairs(Files) do | 629 | for name, file in pairs(Files) do |
166 | local path, result = '', '' | 630 | local metadata = derefTable(Files[name].metadata, true) |
167 | local body, metadata = Files[name].body, Files[name].metadata | 631 | if ('everything' ~= name) then |
168 | local bits, bit = Files[name].bits, Files[name].bit | 632 | local ln, fw, pw, ts = 'DUNNO', '', '', '' |
169 | local ln = #bits | 633 | local title, link = whichWiki(metadata) |
170 | 634 | link = string.gsub(link, 'https://', 'HTTPS://') -- Prevent this one from being converted. | |
171 | path = table.concat(bits, '/', 1, ln) | 635 | if 'PmWiki' == metadata.ogWiki then pw = 'PmWiki [' .. title .. '](' .. link .. ')' end |
636 | if 'Foswiki' == metadata.ogWiki then fw = 'Foswiki [' .. title .. '](' .. link .. ')' end | ||
637 | if nil ~= metadata.timestamp then ts = metadata.timestamp end | ||
638 | if nil ~= file.bit then ln = file.bit | ||
639 | end | ||
640 | table.insert(Pages, '\n| [' .. name .. '](<' .. name .. '.HTML>) | ' .. fw .. ' ' .. pw .. ' | ' .. ts .. ' |') | ||
172 | 641 | ||
173 | if '' ~= body then | 642 | -- Track our external links. |
174 | -- Continue the parsing and conversion. Start by turning our parsed body into something the lcmark template system can grock. | 643 | if (nil ~= metadata.ogBase) and (nil ~= metadata.ogFile) then |
175 | local bod, err = lcmark.compile_template(body) | 644 | local n = metadata.ogBase |
176 | if nil == bod then print('oops! ' .. err) else | 645 | if 'PmWiki' == metadata.ogWiki then n = n .. '.' else n = n .. '/' end |
177 | local templateFile = metadata.template | 646 | xLinks[n .. metadata.ogFile] = file.path |
178 | if nil == templateFile then templateFile = 'default' end | ||
179 | file.template = templateFile .. '.template' | ||
180 | end | 647 | end |
648 | end | ||
649 | end | ||
650 | table.sort(Pages, function(a, b) return (string.lower(a) < string.lower(b)) end) | ||
651 | for i, f in ipairs(Pages) do | ||
652 | Bdy = Bdy .. f | ||
653 | end | ||
654 | h = io.open('everything.md', 'a+') | ||
655 | if nil ~= h then | ||
656 | h:write(Bdy) | ||
657 | h:close() | ||
658 | else | ||
659 | print("Can't open everything.md for writing.") | ||
660 | end | ||
661 | toFile('everything', 'body', Bdy) | ||
181 | 662 | ||
182 | -- Copy any metadata found in parent directories. | 663 | |
183 | local pth = '' | 664 | -- Loop through Subs, doing whichPage and inheritance. |
184 | for i, d in ipairs(bits) do | 665 | -- It gets to testing/even/deeper BEFORE it gets to testing/even sometimes. So sort them. |
185 | if '' ~= pth then pth = pth .. '/' end | 666 | SUBS = {} |
186 | pth = pth .. d | 667 | for name, sub in pairs(Subs) do |
187 | if nil ~= Subs[pth] then | 668 | table.insert(SUBS, sub) |
188 | if nil ~= Subs[pth].metadata then | 669 | end |
189 | for m, x in pairs(Subs[pth].metadata) do | 670 | table.sort(SUBS, function(a, b) return (string.lower(a.path) < string.lower(b.path)) end) |
190 | if nil == metadata[m] then | 671 | for n, sub in pairs(SUBS) do |
191 | metadata[m] = x | 672 | local name = sub.path |
192 | end | 673 | sub.whichPage = whichPage(name) |
674 | local metadata = sub.metadata | ||
675 | for i, s in pairs(sub.subs) do | ||
676 | local nm = i | ||
677 | if '' ~= name then nm = name .. '/' .. i end | ||
678 | for k, v in pairs(metadata) do | ||
679 | if nil == Subs[nm].metadata[k] then | ||
680 | if ('favicon' == k) or ('logo' == k) then | ||
681 | Subs[nm].metadata[k] = linkFrom(nm, name) .. v | ||
682 | else | ||
683 | if 'hidden' ~= k then -- Don't inherit hidden. | ||
684 | Subs[nm].metadata[k] = v | ||
193 | end | 685 | end |
194 | end | 686 | end |
195 | end | 687 | end |
196 | end | 688 | end |
197 | -- Root directory needs to be handled separately, for now. | 689 | end |
198 | if nil ~= Subs[''].metadata then | 690 | end |
199 | for m, x in pairs(Subs[''].metadata) do if nil == metadata[m] then metadata[m] = x end end | 691 | |
692 | -- Files inheritance. | ||
693 | for name, file in pairs(Files) do | ||
694 | if '' ~= file.body then | ||
695 | local mdata = Subs[file.path].metadata | ||
696 | for k, v in pairs(mdata) do | ||
697 | if nil == file.metadata[k] then | ||
698 | Files[name].metadata[k] = v | ||
699 | end | ||
200 | end | 700 | end |
701 | end | ||
702 | end | ||
201 | 703 | ||
202 | for m, x in pairs(globalData) do if nil == metadata[m] then metadata[m] = x end end | ||
203 | 704 | ||
204 | -- Inherit these images from most recent parent directory that defines them. | 705 | --------------------------------------------------------------------------------- |
205 | for n, y in ipairs{'favicon', 'logo'} do | 706 | -- Setup the lunarmark stuff. |
206 | local pith = '' | 707 | local LunamarkOpts = { |
207 | if nil ~= metadata[y] then | 708 | layout='compact', |
208 | local pth = '' | 709 | -- This list is copied from the lunamark source code, until I discover a way to discover it. The descriptions are useful to. |
209 | for m, x in ipairs(bits) do | 710 | containers=false, -- Put sections in containers (e.g. div or section tags) |
210 | if '' ~= pth then pth = pth .. '/' end | 711 | slides=false, -- Like containers, but do not nest them |
211 | pth = pth .. x | 712 | startnum=true, -- Start number of an ordered list is significant |
212 | if (nil ~= Subs[pth].metadata) and (nil ~= Subs[pth].metadata[y]) then pith = pth end | 713 | smart=false, -- Smart typography (quotes, dashes, ellipses) |
714 | preserve_tabs=true, -- Don't expand tabs to spaces | ||
715 | notes=true, -- Footnotes | ||
716 | inline_notes=true, -- Inline footnotes | ||
717 | definition_lists=true, -- Definition lists | ||
718 | citations=true, -- Citations | ||
719 | citation_nbsps=true, -- Turn spacing into non-breaking spaces in citations | ||
720 | fenced_code_blocks=true, -- Fenced code blocks | ||
721 | lua_metadata=true, -- Lua metadata | ||
722 | pandoc_title_blocks=true, -- Pandoc style title blocks | ||
723 | hash_enumerators=true, -- may be used as ordered list enumerator | ||
724 | require_blank_before_blockquote=false, | ||
725 | require_blank_before_header=false, | ||
726 | require_blank_before_fenced_code_block=false, | ||
727 | fancy_lists=true, -- Pandoc style fancy lists | ||
728 | task_list=true, -- GitHub-Flavored Markdown task list | ||
729 | strikeout=true, -- Strike-through with double tildes | ||
730 | mark=true, -- Highlight with double equals | ||
731 | subscript=true, -- Subscripted text between tildes | ||
732 | superscript=true, -- Superscripted text between circumflexes | ||
733 | bracketed_spans=true, -- Spans with attributes | ||
734 | fenced_divs=true, -- Divs with attributes | ||
735 | raw_attribute=true, -- Raw pass-through on code elements | ||
736 | fenced_code_attributes=true, -- Fenced code block attributes | ||
737 | link_attributes=true, -- Link attributes | ||
738 | pipe_tables=true, -- PHP Markdown Extra pipe table support | ||
739 | table_captions=true, -- Table caption syntax extension | ||
740 | header_attributes=true, -- Header attributes | ||
741 | line_blocks=true, -- Line blocks | ||
742 | escaped_line_breaks=true, -- Pandoc-style escaped hard line breaks | ||
743 | } | ||
744 | local Writer = Lunamark.writer.html5.new(LunamarkOpts) | ||
745 | -- Can override the various writer functions, there's something for each of the basic HTML elements. | ||
746 | local lunaLinky = function(url) -- Fix up the links. | ||
747 | if ('https://wiki.devuan.org/' ~= url) and ('https://fos.wiki.devuan.org/' ~= url) then | ||
748 | -- TODO - This might be covering up a bug elsewhere. | ||
749 | if '/Main/' == string.sub(url, 1, 6) then | ||
750 | local link = linkFrom(Context.path, 'Foswiki/Main') | ||
751 | if '' == link then | ||
752 | url = string.sub(url, 7) .. '.HTML' | ||
753 | else | ||
754 | url = link .. '/' .. string.sub(url, 7) .. '.HTML' | ||
755 | end | ||
756 | end | ||
757 | if '/System/' == string.sub(url, 1, 8) then | ||
758 | url = 'https://fos.wiki.devuan.org' .. url | ||
759 | end | ||
760 | for i, p in ipairs{'https://wiki.devuan.org/?n=', 'https://wiki.devuan.org?n=', 'PmWiki/uploads/', 'Foswiki/pub/', 'https://fos.wiki.devuan.org/'} do | ||
761 | if p == string.sub(url, 1, #p) then | ||
762 | local ur = string.sub(url, #p + 1) | ||
763 | -- TODO - could probably replace some of this mess with RE.gsub() and friends. Meh, it works. | ||
764 | local f4, f5, tk1 = string.find(ur, '?', 1, true) | ||
765 | if fail ~= f4 then | ||
766 | local u = string.sub(ur, 1, f4 - 1) | ||
767 | ur = u | ||
213 | end | 768 | end |
214 | if ('' == pith) and (nil ~= Subs[''].metadata) and (nil ~= Subs[''].metadata[y]) then pith = pth end | 769 | local md |
215 | if '' == pith then metadata[y] = linkFrom(path, pith) .. globalData[y] | 770 | if ('fos' == string.sub(p, 9, 11)) or ('Fos' == string.sub(p, 1, 3)) then |
216 | else metadata[y] = linkFrom(path, pith) .. metadata[y] | 771 | md = readMdMd('Foswiki/' .. ur .. '.md', {}) |
772 | else | ||
773 | md = readMdMd('PmWiki/' .. string.gsub(ur, '%.', '/', 1) .. '.md', {}) | ||
774 | end | ||
775 | if (nil ~= md) and (nil ~= md.realURL) then url = md.realURL end | ||
776 | |||
777 | if ('https://fos.wiki.devuan.org/bin/' ~= string.sub(url, 1, 32)) and ('https://fos.wiki.devuan.org/System/' ~= string.sub(url, 1, 35)) then | ||
778 | local xlnk = xLinks[string.gsub(ur, '%..*', '', 1)] | ||
779 | if nil == Context.path then | ||
780 | url = string.gsub(ur, '%.', '/', 1) | ||
781 | else | ||
782 | if nil == xlnk then xlnk = string.gsub(string.gsub(ur, '%..*', '', 1), '/.*', '', 1) end | ||
783 | if '' ~= Context.path then xlnk = linkFrom(Context.path, xlnk) end | ||
784 | if '/' == string.sub(xlnk, 1, 1) then xlnk = string.sub(xlnk, 2) end | ||
785 | if ('' ~= xlnk) and ('/' ~= string.sub(xlnk, -1)) then xlnk = xlnk .. '/' end | ||
786 | if 'DUNNO/' == xlnk then print('OOPS! page not found - @' .. Context.path .. ' / ' .. Context.bit .. '\t' .. url .. ' -> ' .. xlnk .. ' ' .. string.gsub(ur, '.*%.', '', 1) .. '.HTML') end | ||
787 | end | ||
788 | -- if (nil ~= md) and (nil ~= md.realURL) then url = md.realURL | ||
789 | -- else | ||
790 | url = xlnk .. string.gsub(ur, '.*%.', '', 1) | ||
791 | -- end | ||
792 | if 'PmWiki/uploads/' == p then | ||
793 | url = '../../' .. p .. string.gsub(ur, '%.', '.', 1) | ||
794 | elseif 'Foswiki/pub/' == p then | ||
795 | url = '../../' .. p .. ur | ||
796 | else | ||
797 | url = url .. '.HTML' | ||
798 | end | ||
217 | end | 799 | end |
218 | end | 800 | end |
219 | end | 801 | end |
802 | end | ||
803 | return url | ||
804 | end | ||
220 | 805 | ||
221 | -- Figure out this pages header links. | 806 | function Writer.header(s, level) |
222 | metadata.header = '' | 807 | local text = Lunamark.util.rope_to_string(s) |
223 | for i, f in pairs(Subs[path].subs) do | 808 | -- FIXME - Work around a bug in Lunamark? |
224 | local pth = path | 809 | text = RE.gsub(text, "{[\\]}", "") |
225 | if '' ~= path then pth = path .. '/' end | 810 | return '<h' .. level .. ' id="' .. RE.gsub(text, '{[ ]}', '_') .. '">' .. text .. ' <a style="font-size: 0.42em;" href="#top">🔼</a></h' .. level .. '>' |
226 | local fl = whichPage(pth .. f) | 811 | end |
227 | -- if '' == fl then | 812 | local OgWriterLink = Writer.link -- So we can call the original from within mine, we are just changing the URL. |
228 | -- metadata.header = metadata.header .. f .. ' ' | 813 | function Writer.link(lab, url, tit) |
229 | -- else | 814 | return OgWriterLink(lab, lunaLinky(url), tit) |
230 | metadata.header = metadata.header .. '<a href="' .. f .. '/' .. fl .. '">' .. f .. '</a> ' | 815 | end |
231 | -- end | 816 | local OgWriterImage = Writer.image |
232 | end | 817 | function Writer.image(lab, url, tit) |
818 | return OgWriterImage(lab, lunaLinky(url), tit) | ||
819 | end | ||
820 | |||
821 | local Parse = Lunamark.reader.markdown.new(Writer, LunamarkOpts) | ||
233 | 822 | ||
234 | -- Figure out this pages menu links. | ||
235 | metadata.menu = '' | ||
236 | if nil ~= Subs[path].files then table.sort(Subs[path].files) end | ||
237 | for i, f in ipairs(Subs[path].files) do | ||
238 | if name == f then | ||
239 | metadata.menu = metadata.menu .. '<p>' .. f .. '</p>' | ||
240 | else | ||
241 | metadata.menu = metadata.menu .. '<p><a href="' .. f .. '.HTML">' .. f .. '</a></p>' | ||
242 | end | ||
243 | end | ||
244 | 823 | ||
824 | --------------------------------------------------------------------------------- | ||
825 | -- Loop through the files we found and actually create their HTML files. | ||
826 | for name, file in pairs(Files) do | ||
827 | local body, metadata = Files[name].body, derefTable(Files[name].metadata, true) | ||
828 | local bits, bit = Files[name].bits, Files[name].bit | ||
829 | local ln = #bits | ||
830 | local result = '' | ||
831 | |||
832 | if '' ~= body then | ||
245 | -- Figure out this pages trail links. | 833 | -- Figure out this pages trail links. |
834 | metadata.home = linkFrom(file.path, '') .. Subs[''].whichPage | ||
246 | metadata.trail = '' | 835 | metadata.trail = '' |
247 | for i, b in ipairs(bits) do | 836 | for i, b in ipairs(bits) do |
837 | local p = table.concat(bits, '/', 1, i) | ||
248 | if i < #bits then | 838 | if i < #bits then |
249 | metadata.trail = metadata.trail .. '<a href="' .. linkFrom(path, table.concat(bits, '/', 1, i)) .. whichPage(b) .. '">' .. b .. '</a> ' | 839 | metadata.trail = metadata.trail .. '<a href="' .. linkFrom(file.path, p) .. Subs[p].whichPage .. '">' .. b .. '</a> 👣 ' |
250 | linkFrom(path, table.concat(bits, '/', 1, i)) | 840 | linkFrom(file.path, table.concat(bits, '/', 1, i)) |
251 | else | 841 | else |
252 | metadata.trail = metadata.trail .. b .. ' ' | 842 | metadata.trail = metadata.trail .. ' ' .. b |
253 | end | 843 | end |
254 | end | 844 | end |
255 | -- if '' == metadata.trail then metadata.trail = '<a href="' .. string.rep('../', ln) .. '/' .. whichPage('') .. '">home</a> ' end | ||
256 | |||
257 | -- Figure out this pages footer links. | ||
258 | if nil ~= metadata.pagehistory then metadata.history = '<p>Page <a href="' .. metadata.pagehistory .. '">history</a></p>' end | ||
259 | if nil ~= metadata.sourcecode then metadata.footer = '<a href="' .. metadata.sourcecode .. '">source code</a> ' .. metadata.footer end | ||
260 | if nil ~= metadata.feedatom then metadata.footer = '<a href="' .. metadata.feedatom .. '">atom feed</a> ' .. metadata.footer end | ||
261 | if metadata.footer ~= globalData.footer then metadata.footer = 'Web site ' .. metadata.footer end | ||
262 | metadata.footer = '<p>' .. metadata.footer .. '</p>' | ||
263 | 845 | ||
264 | -- Apply the template to the body. | 846 | -- Figure out this pages header links. |
265 | metadata.body = lcmark.apply_template(bod, metadata) | 847 | metadata.header = '' |
848 | subs = {} | ||
849 | for i, f in pairs(Subs[file.path].subs) do | ||
850 | table.insert(subs, f) | ||
851 | end | ||
852 | table.sort(subs, function(a, b) return (string.lower(a) < string.lower(b)) end) | ||
853 | for i, f in ipairs(subs) do | ||
854 | local pth = file.path | ||
855 | if '' ~= file.path then pth = file.path .. '/' end | ||
856 | if 'true' ~= Subs[pth .. f].metadata.hidden then | ||
857 | metadata.header = metadata.header .. '<a href="' .. f .. '/' .. whichPage(pth .. f) .. '">' .. f .. '</a> 📂 ' | ||
858 | end | ||
859 | end | ||
266 | 860 | ||
267 | -- Put it all in the template. | 861 | -- Figure out this pages menu links. |
268 | local tm = '' | 862 | metadata.menu = '' |
269 | if nil ~= file.template then | 863 | if nil == metadata.title then metadata.title = bit end |
270 | local h = io.open(file.template, 'r') | 864 | if nil ~= Subs[file.path].files then table.sort(Subs[file.path].files, function(a, b) return (string.lower(a) < string.lower(b)) end) end |
271 | if nil ~= h then | 865 | for i, f in ipairs(Subs[file.path].files) do |
272 | tm = tm .. h:read('*a') | 866 | local title, url = nil, nil |
273 | h:close() | 867 | if '' == file.path then |
868 | title = Files[f].metadata.title | ||
869 | url = Files[f].metadata.URL | ||
274 | else | 870 | else |
275 | print('oops! No such file ' .. file.template) | 871 | title = Files[file.path .. '/' .. f].metadata.title |
872 | url = Files[file.path .. '/' .. f].metadata.URL | ||
276 | end | 873 | end |
277 | 874 | if nil == title then title = f end | |
278 | -- TODO - Um not sure why this is here AND at the top of the loop. Here makes more sense. | 875 | if bit == f then |
279 | local template, err = lcmark.compile_template(tm) | 876 | metadata.menu = metadata.menu .. '<p>' .. title .. '</p>' |
280 | if nil == template then print('oops! ' .. err) else | 877 | for j, g in ipairs(file.headers) do |
281 | result = lcmark.apply_template(template, metadata) | 878 | local beg, en = RE.find(g, [['{']]) |
879 | if nil ~= beg then | ||
880 | g = string.sub(g, 1, beg - 2) | ||
881 | end | ||
882 | local h = string.sub(RE.gsub(g, '{[#]}', ''), 2) | ||
883 | local l = string.len(g) - string.len(h) | ||
884 | g = h | ||
885 | h = RE.gsub(h, '{[ ]}', ' ') | ||
886 | -- FIXME - Work around a bug in Lunamark? | ||
887 | g = RE.gsub(g, '{[\\]}', '') | ||
888 | h = RE.gsub(h, '{[\\]}', '') | ||
889 | -- FIXME - if it's a linky, strip off the URL part. The Wiki audit has such things. | ||
890 | metadata.menu = metadata.menu .. '<p>' .. string.rep(' ', l) .. '<a style="font-size: 0.80em;" href="#' .. RE.gsub(g, '{[ ]}', '_') .. '">' .. h .. '</a></p>' | ||
891 | end | ||
892 | else | ||
893 | if nil ~= url then metadata.menu = metadata.menu .. '<p><a href="' .. url .. '">' .. title .. ' ☝</a></p>' | ||
894 | else | ||
895 | local pth = file.path | ||
896 | if '' ~= pth then pth = pth .. '/' end | ||
897 | -- Don't include any left over .md.md files, so don't do this if f.md doesn't exist. | ||
898 | local a, e = io.open(pth .. f .. '.md' , 'r') | ||
899 | if nil ~= a then | ||
900 | a:close() | ||
901 | metadata.menu = metadata.menu .. '<p><a href="' .. f .. '.HTML">' .. title .. '</a></p>' | ||
902 | end | ||
903 | end | ||
282 | end | 904 | end |
905 | end | ||
906 | |||
907 | -- Figure out this pages footer links. | ||
908 | local temp = '' | ||
909 | metadata.footer = '' | ||
910 | if nil == metadata.pagehistory then | ||
911 | if 'Foswiki' == metadata.ogWiki then metadata.pagehistory = metadata.ogURL .. '/bin/oops/' .. metadata.ogBase .. '/' .. metadata.ogFile .. '?template=oopshistory' end | ||
912 | if 'PmWiki' == metadata.ogWiki then metadata.pagehistory = metadata.ogURL .. '/?n=' .. metadata.ogBase .. '.' .. metadata.ogFile .. '?action=diff' end | ||
283 | else | 913 | else |
284 | result = body | 914 | temp = '/' .. name .. '.md' |
285 | end | 915 | end |
916 | if nil ~= metadata.pagehistory then metadata.history = '<p>Page <a href="' .. metadata.pagehistory .. temp .. '">history</a></p>' else | ||
917 | metadata.history = '' | ||
918 | end | ||
919 | if nil ~= metadata.sourcecode then metadata.footer = '<a href="' .. metadata.sourcecode .. '">source code</a>' end | ||
920 | if nil ~= metadata.feedatom then metadata.footer = '<a href="' .. metadata.feedatom .. '">atom feed<img src="feed-icon-14x14.png"></img></a> ' .. metadata.footer end | ||
921 | if metadata.footer ~= '' then metadata.footer = 'Web site ' .. metadata.footer end | ||
922 | -- Add a link to the original page. | ||
923 | if nil ~= metadata.ogURL then | ||
924 | local title, link = whichWiki(metadata) | ||
925 | link = string.gsub(link, 'https://', 'HTTPS://') -- Prevent this one from being converted. | ||
926 | -- TODO - wrap "edit" in a link to actually edit it. PmWiki "?action=edit" Foswiki "https://fos.wiki.devuan.org/bit/edit/" .. page .. "?t=" .. 10 digit random number? | ||
927 | local edit = 'T' | ||
928 | if 'PmWiki' == metadata.ogWiki then edit = 'Maybe you can <a href="' .. link .. '?action=edit">edit</a> t' end | ||
929 | -- if 'Foswiki' == metadata.ogWiki then edit = '' end | ||
930 | metadata.footer = edit .. 'he <a href="' .. link .. '">original page</a>. ' .. metadata.footer | ||
931 | end | ||
932 | metadata.footer = '<p>' .. metadata.footer .. '</p>' | ||
933 | |||
934 | -- Do our own metadata replacement, it's simple and works better. | ||
935 | local temp = Template | ||
936 | -- Toss the body in first, so the scan can deal with it to. | ||
937 | -- NOTE - this is where we actually parse the markup into HTML. | ||
938 | Context = file | ||
939 | body = RE.gsub(body, '{[%nl]^1[%a]+}[.]^1 ', '%1$dot$ ') -- Coz otherwise stray . trip up the list detection. | ||
940 | local bd, md = Parse(body) -- The md is a table of extracted metadata, not likely to be any, and we wont do anything with it. | ||
941 | bd = RE.gsub(bd, '{[%]}', '$perc$') -- Coz otherwise stray % trip up the capture part. | ||
942 | temp = RE.gsub(temp, '"$body$"', bd) | ||
943 | -- The actual metadata replacement. | ||
944 | result = RE.compile ('{~ ({[$][A-Za-z_]+[$]} -> meta / .)* ~}', | ||
945 | { | ||
946 | meta = function(a) | ||
947 | a = string.sub(a, 2, -2) | ||
948 | local md = metadata[a] | ||
949 | if nil == md then | ||
950 | md = GlobalMetaData[a] | ||
951 | if nil == md then | ||
952 | md = a | ||
953 | end | ||
954 | end | ||
955 | return md | ||
956 | end | ||
957 | } ):match(temp) | ||
286 | 958 | ||
287 | -- Write the file. | 959 | -- Write the file. |
288 | if '' ~= result then | 960 | if '' ~= result then |
289 | local base = name .. '.HTML' | 961 | -- print('From ' .. name .. '.md -> ' .. base) |
290 | print('From ' .. name .. '.md -> ' .. base) | 962 | writeString(name .. '.HTML', result) |
291 | local a, e = io.open(base, 'w') | ||
292 | if nil == a then print('Could not open ' .. base .. ' - ' .. e) else | ||
293 | a:write(result) | ||
294 | a:close() | ||
295 | end | ||
296 | end | 963 | end |
297 | |||
298 | else | ||
299 | print('') | ||
300 | end | 964 | end |
301 | end | 965 | end |
302 | 966 | ||