first commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
out/
|
||||
26
README.md
Normal file
26
README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# _kewt_
|
||||
|
||||
_kewt_ is a minimalist ssg inspired by _[werc](http://werc.cat-v.org/)_ and _[kew](https://github.com/uint23/kew)_
|
||||
|
||||
It's meant to be a static site generator, like _[kew](https://github.com/uint23/kew)_ but use only default (POSIX) tooling, like _[werc](http://werc.cat-v.org/)_ (and definitely unlike _[kew](https://github.com/uint23/kew)_)
|
||||
|
||||
## Features
|
||||
|
||||
- No dependencies
|
||||
- Supports many embed types
|
||||
- Automatic css variable replacement for older browsers
|
||||
- Automatic inlining and embedding of many filetypes with `\![link]` or `\`
|
||||
- Inline html support
|
||||
- MFM `$font` and `\<plain>` tags
|
||||
- Admonition support (that's what the blocks like the warning block below are called)
|
||||
|
||||
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
||||
|
||||
## Credits
|
||||
|
||||
- Markdown to html conversion based on [markdown.bash](https://github.com/chadbraunduin/markdown.bash) by [chadbraunduin](https://github.com/chadbraunduin)
|
||||
- Default css style and html template based on _[kew](https://github.com/uint23/kew)_ by [uint23](https://github.com/uint23)
|
||||
|
||||
# Warning
|
||||
>![WARNING]
|
||||
>Most of this was coded at night, while sleepy and a bit sick, and after walking for about 4 hours around a forest, so...
|
||||
27
awk/collect_dir_info.awk
Normal file
27
awk/collect_dir_info.awk
Normal file
@@ -0,0 +1,27 @@
|
||||
BEGIN {
|
||||
slen = length(src)
|
||||
}
|
||||
|
||||
{
|
||||
if (length($0) <= slen) {
|
||||
next
|
||||
}
|
||||
|
||||
rel = substr($0, slen + 2)
|
||||
parent = rel
|
||||
if (sub(/\/[^\/]+$/, "", parent) == 0) {
|
||||
parent = "."
|
||||
}
|
||||
|
||||
all[parent]++
|
||||
if (is_dir[rel]) {
|
||||
dirs[parent]++
|
||||
}
|
||||
is_dir[parent] = 1
|
||||
}
|
||||
|
||||
END {
|
||||
for (parent in all) {
|
||||
printf "%s|%d|%d\n", parent, all[parent], dirs[parent]
|
||||
}
|
||||
}
|
||||
104
awk/generate_sidebar.awk
Normal file
104
awk/generate_sidebar.awk
Normal file
@@ -0,0 +1,104 @@
|
||||
function title_from_name(name) {
|
||||
gsub(/\.md$/, "", name)
|
||||
gsub(/-/, " ", name)
|
||||
return name
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
n_dlines = split(dinfo, dlines, "\n")
|
||||
for (i = 1; i <= n_dlines; i++) {
|
||||
if (split(dlines[i], dparts, "|") == 3) {
|
||||
d_all[dparts[1]] = dparts[2]
|
||||
d_dirs[dparts[1]] = dparts[3]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
rel = substr($0, length(src) + 2)
|
||||
all_paths[rel] = 1
|
||||
ordered_paths[count++] = rel
|
||||
|
||||
dir = rel
|
||||
if (sub(/\/[^\/]+$/, "", dir) == 0) {
|
||||
dir = "."
|
||||
}
|
||||
|
||||
md_count[dir]++
|
||||
if (rel ~ /index\.md$/) {
|
||||
has_index[dir] = 1
|
||||
}
|
||||
}
|
||||
|
||||
END {
|
||||
print "<ul>"
|
||||
if ("index.md" in all_paths) {
|
||||
print "<li><a href=\"/index.html\">Home</a></li>"
|
||||
}
|
||||
|
||||
depth = 0
|
||||
prev_n = 0
|
||||
|
||||
for (idx = 0; idx < count; idx++) {
|
||||
rel = ordered_paths[idx]
|
||||
if (rel == "index.md") {
|
||||
continue
|
||||
}
|
||||
|
||||
n = split(rel, parts, "/")
|
||||
common = 0
|
||||
for (i = 1; i < n && i < prev_n; i++) {
|
||||
if (parts[i] == prev_parts[i]) {
|
||||
common = i
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
while (depth > 0 && opened_levels[depth] > common) {
|
||||
print "</ul></li>"
|
||||
delete opened_levels[depth]
|
||||
depth--
|
||||
}
|
||||
|
||||
for (i = common + 1; i < n; i++) {
|
||||
dir_path = ""
|
||||
for (j = 1; j <= i; j++) {
|
||||
dir_path = dir_path parts[j] "/"
|
||||
}
|
||||
|
||||
this_d = ""
|
||||
for (j = 1; j <= i; j++) {
|
||||
this_d = (this_d == "" ? parts[j] : this_d "/" parts[j])
|
||||
}
|
||||
|
||||
if (flatten == "true" && d_all[this_d] == 1 && d_dirs[this_d] == 1) {
|
||||
continue
|
||||
}
|
||||
|
||||
printf "<li><a href=\"/%sindex.html\">%s</a><ul>\n", dir_path, title_from_name(parts[i])
|
||||
opened_levels[++depth] = i
|
||||
}
|
||||
|
||||
curr_dir = rel
|
||||
if (sub(/\/[^\/]+$/, "", curr_dir) == 0) {
|
||||
curr_dir = "."
|
||||
}
|
||||
is_single = (single_file_index == "true" && md_count[curr_dir] == 1 && !has_index[curr_dir])
|
||||
|
||||
if (parts[n] != "index.md" && !is_single) {
|
||||
path = "/" rel
|
||||
gsub(/\.md$/, ".html", path)
|
||||
printf "<li><a href=\"%s\">%s</a></li>\n", path, title_from_name(parts[n])
|
||||
}
|
||||
|
||||
prev_n = n
|
||||
split(rel, prev_parts, "/")
|
||||
}
|
||||
|
||||
while (depth > 0) {
|
||||
print "</ul></li>"
|
||||
depth--
|
||||
}
|
||||
print "</ul>"
|
||||
}
|
||||
538
awk/markdown_embed.awk
Normal file
538
awk/markdown_embed.awk
Normal file
@@ -0,0 +1,538 @@
|
||||
function is_global_url(src) {
|
||||
return (src ~ /^https?:\/\//)
|
||||
}
|
||||
|
||||
function split_src(src, base, qpos, hpos, cutpos) {
|
||||
base = src
|
||||
qpos = index(base, "?")
|
||||
hpos = index(base, "#")
|
||||
cutpos = 0
|
||||
if (qpos > 0) cutpos = qpos
|
||||
if (hpos > 0 && (cutpos == 0 || hpos < cutpos)) cutpos = hpos
|
||||
if (cutpos > 0) base = substr(base, 1, cutpos - 1)
|
||||
return base
|
||||
}
|
||||
|
||||
function ext_of(src, base, n, parts) {
|
||||
base = split_src(src)
|
||||
n = split(base, parts, ".")
|
||||
if (n < 2) return ""
|
||||
return tolower(parts[n])
|
||||
}
|
||||
|
||||
function is_image_ext(ext) {
|
||||
return (ext ~ /^(png|jpe?g|gif|bmp|webp|svg|ico)$/)
|
||||
}
|
||||
|
||||
function is_audio_ext(ext) {
|
||||
return (ext ~ /^(mp3|wav|ogg|m4a|aac|flac)$/)
|
||||
}
|
||||
|
||||
function is_video_ext(ext) {
|
||||
return (ext ~ /^(mp4|webm|ogv|mov|m4v)$/)
|
||||
}
|
||||
|
||||
function is_inline_text_ext(ext) {
|
||||
return (ext ~ /^(html|txt|md|css|js|mjs|cjs|json|xml|yml|yaml|toml|ini|conf|c|h|cpp|hpp|rs|go|py|sh|lua|php|java|kt|swift|sql|csv|tsv|log)$/)
|
||||
}
|
||||
|
||||
function dirname_of(path, p) {
|
||||
p = path
|
||||
if (sub(/\/[^\/]*$/, "", p)) return p
|
||||
return "."
|
||||
}
|
||||
|
||||
function resolve_local_path(src, rel, candidate) {
|
||||
rel = split_src(src)
|
||||
if (substr(rel, 1, 1) == "/") {
|
||||
rel = substr(rel, 2)
|
||||
if (site_root != "") {
|
||||
candidate = site_root "/" rel
|
||||
if ((getline _tmp < candidate) >= 0) {
|
||||
close(candidate)
|
||||
return candidate
|
||||
}
|
||||
close(candidate)
|
||||
}
|
||||
candidate = rel
|
||||
if ((getline _tmp < candidate) >= 0) {
|
||||
close(candidate)
|
||||
return candidate
|
||||
}
|
||||
close(candidate)
|
||||
|
||||
if (rel == "styles.css" && fallback_file != "") {
|
||||
candidate = fallback_file
|
||||
if ((getline _tmp < candidate) >= 0) {
|
||||
close(candidate)
|
||||
return candidate
|
||||
}
|
||||
close(candidate)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
candidate = input_dir "/" rel
|
||||
if ((getline _tmp < candidate) >= 0) {
|
||||
close(candidate)
|
||||
return candidate
|
||||
}
|
||||
close(candidate)
|
||||
|
||||
candidate = rel
|
||||
if ((getline _tmp < candidate) >= 0) {
|
||||
close(candidate)
|
||||
return candidate
|
||||
}
|
||||
close(candidate)
|
||||
return ""
|
||||
}
|
||||
|
||||
function read_file(path, out, line, rc) {
|
||||
out = ""
|
||||
while ((rc = getline line < path) > 0) {
|
||||
out = out line "\n"
|
||||
}
|
||||
close(path)
|
||||
return out
|
||||
}
|
||||
|
||||
function escape_html(s, t) {
|
||||
t = s
|
||||
gsub(/&/, "\\&", t)
|
||||
gsub(/</, "\\<", t)
|
||||
gsub(/>/, "\\>", t)
|
||||
return t
|
||||
}
|
||||
|
||||
function css_highlight_line(line, m, prop, val) {
|
||||
if (line ~ /^[[:space:]]*\/\*.*\*\/[[:space:]]*$/) {
|
||||
return "<span class=\"tok-com\">" line "</span>"
|
||||
}
|
||||
|
||||
if (line ~ /^[[:space:]]*[^{}][^{}]*\{[[:space:]]*$/) {
|
||||
sub(/\{[[:space:]]*$/, "", line)
|
||||
return "<span class=\"tok-sel\">" line "</span><span class=\"tok-punc\">{</span>"
|
||||
}
|
||||
|
||||
if (line ~ /^[[:space:]]*\}[[:space:]]*$/) {
|
||||
return "<span class=\"tok-punc\">}</span>"
|
||||
}
|
||||
|
||||
if (match(line, /^([[:space:]]*)(--?[A-Za-z0-9_-]+)([[:space:]]*:[[:space:]]*)([^;]*)(;?[[:space:]]*)$/, m)) {
|
||||
prop = "<span class=\"tok-prop\">" m[2] "</span>"
|
||||
gsub(/var\(--[A-Za-z0-9_-]+\)/, "<span class=\"tok-var\">&</span>", m[4])
|
||||
val = "<span class=\"tok-val\">" m[4] "</span>"
|
||||
return m[1] prop m[3] val m[5]
|
||||
}
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
function highlight_code_block_line(line) {
|
||||
return css_highlight_line(line)
|
||||
}
|
||||
|
||||
function highlight_css_block(text, n, i, lines, out) {
|
||||
n = split(text, lines, "\n")
|
||||
out = ""
|
||||
for (i = 1; i <= n; i++) {
|
||||
out = out css_highlight_line(lines[i])
|
||||
if (i < n) out = out "\n"
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function render_code_include(src, force_inline, ext, local_path, content) {
|
||||
if (is_global_url(src)) return ""
|
||||
|
||||
ext = ext_of(src)
|
||||
if (!force_inline && !is_inline_text_ext(ext)) return ""
|
||||
|
||||
local_path = resolve_local_path(src)
|
||||
if (local_path == "") return ""
|
||||
|
||||
content = read_file(local_path)
|
||||
if (content ~ /\n$/) sub(/\n$/, "", content)
|
||||
content = escape_html(content)
|
||||
if (ext == "css") {
|
||||
content = highlight_css_block(content)
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
function render_embed(src, alt, has_alt, force_inline, ext, local_path, content) {
|
||||
if (force_inline && !is_global_url(src)) {
|
||||
local_path = resolve_local_path(src)
|
||||
if (local_path != "") {
|
||||
content = read_file(local_path)
|
||||
if (content ~ /\n$/) sub(/\n$/, "", content)
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
||||
ext = ext_of(src)
|
||||
|
||||
if (is_global_url(src)) {
|
||||
if (is_image_ext(ext)) {
|
||||
if (has_alt) return "<img alt=\"" alt "\" src=\"" src "\" />"
|
||||
return "<img src=\"" src "\" />"
|
||||
}
|
||||
if (is_audio_ext(ext)) return "<audio controls src=\"" src "\"></audio>"
|
||||
if (is_video_ext(ext)) return "<video controls src=\"" src "\"></video>"
|
||||
return "<iframe src=\"" src "\"></iframe>"
|
||||
}
|
||||
|
||||
if (is_image_ext(ext)) {
|
||||
if (has_alt) return "<img alt=\"" alt "\" src=\"" src "\" />"
|
||||
return "<img src=\"" src "\" />"
|
||||
}
|
||||
if (is_audio_ext(ext)) return "<audio controls src=\"" src "\"></audio>"
|
||||
if (is_video_ext(ext)) return "<video controls src=\"" src "\"></video>"
|
||||
|
||||
if (is_inline_text_ext(ext)) {
|
||||
local_path = resolve_local_path(src)
|
||||
if (local_path != "") {
|
||||
content = read_file(local_path)
|
||||
if (content ~ /\n$/) sub(/\n$/, "", content)
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
||||
return "<iframe src=\"" src "\"></iframe>"
|
||||
}
|
||||
|
||||
function extract_attr(tag, attr, pat, m, token) {
|
||||
pat = attr "=\"[^\"]*\""
|
||||
if (match(tag, pat)) {
|
||||
token = substr(tag, RSTART, RLENGTH)
|
||||
sub(/^[^=]*="/, "", token)
|
||||
sub(/"$/, "", token)
|
||||
return token
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
function trim_ws(s) {
|
||||
sub(/^[[:space:]]+/, "", s)
|
||||
sub(/[[:space:]]+$/, "", s)
|
||||
return s
|
||||
}
|
||||
|
||||
function extract_vertical_align(style, rest, part, pos, key, val) {
|
||||
rest = style
|
||||
while (rest != "") {
|
||||
pos = index(rest, ";")
|
||||
if (pos > 0) {
|
||||
part = substr(rest, 1, pos - 1)
|
||||
rest = substr(rest, pos + 1)
|
||||
} else {
|
||||
part = rest
|
||||
rest = ""
|
||||
}
|
||||
part = trim_ws(part)
|
||||
if (part == "") continue
|
||||
pos = index(part, ":")
|
||||
if (pos == 0) continue
|
||||
key = tolower(trim_ws(substr(part, 1, pos - 1)))
|
||||
val = trim_ws(substr(part, pos + 1))
|
||||
if (key == "vertical-align" && val != "") return val
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
function td_has_vertical_align(td_tag, style_attr) {
|
||||
style_attr = extract_attr(td_tag, "style")
|
||||
if (style_attr == "") return 0
|
||||
return (extract_vertical_align(style_attr) != "")
|
||||
}
|
||||
|
||||
function add_td_vertical_align(td_tag, align, style_attr, repl) {
|
||||
style_attr = extract_attr(td_tag, "style")
|
||||
if (style_attr == "") {
|
||||
sub(/>$/, " style=\"vertical-align: " align ";\">", td_tag)
|
||||
return td_tag
|
||||
}
|
||||
repl = style_attr
|
||||
if (repl !~ /;[[:space:]]*$/) repl = repl ";"
|
||||
repl = repl " vertical-align: " align ";"
|
||||
gsub(/&/, "\\&", repl)
|
||||
gsub(/</, "\\<", repl)
|
||||
gsub(/>/, "\\>", repl)
|
||||
sub("style=\"" style_attr "\"", "style=\"" repl "\"", td_tag)
|
||||
return td_tag
|
||||
}
|
||||
|
||||
function apply_td_vertical_align(line, out, rest, seg, td_tag, img_tag, after_td, after_img, style_attr, align, new_td) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (match(rest, /<td[^>]*>[[:space:]]*<img[^>]*>/)) {
|
||||
out = out substr(rest, 1, RSTART - 1)
|
||||
seg = substr(rest, RSTART, RLENGTH)
|
||||
rest = substr(rest, RSTART + RLENGTH)
|
||||
|
||||
after_td = index(seg, ">")
|
||||
if (after_td == 0) {
|
||||
out = out seg
|
||||
continue
|
||||
}
|
||||
td_tag = substr(seg, 1, after_td)
|
||||
after_img = index(seg, "<img")
|
||||
if (after_img == 0) {
|
||||
out = out seg
|
||||
continue
|
||||
}
|
||||
img_tag = substr(seg, after_img)
|
||||
|
||||
style_attr = extract_attr(img_tag, "style")
|
||||
align = extract_vertical_align(style_attr)
|
||||
if (align != "" && !td_has_vertical_align(td_tag)) {
|
||||
new_td = add_td_vertical_align(td_tag, align)
|
||||
seg = new_td substr(seg, after_td + 1)
|
||||
}
|
||||
out = out seg
|
||||
}
|
||||
return out rest
|
||||
}
|
||||
|
||||
function rewrite_img_tags(line, out, rest, tag, src, alt, force_inline_tag, pre, post, repl) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (match(rest, /<img[^>]*\/?>/)) {
|
||||
pre = substr(rest, 1, RSTART - 1)
|
||||
tag = substr(rest, RSTART, RLENGTH)
|
||||
post = substr(rest, RSTART + RLENGTH)
|
||||
src = extract_attr(tag, "src")
|
||||
alt = extract_attr(tag, "alt")
|
||||
force_inline_tag = extract_attr(tag, "data-force-inline")
|
||||
if (is_image_ext(ext_of(src)) && force_inline_tag == "") {
|
||||
# Preserve hand-written <img> attributes (style/class/etc) for normal images.
|
||||
repl = tag
|
||||
} else {
|
||||
repl = render_embed(src, alt, (alt != ""), (force_inline_tag != ""))
|
||||
}
|
||||
out = out pre repl
|
||||
rest = post
|
||||
}
|
||||
return out rest
|
||||
}
|
||||
|
||||
function rewrite_double_bang_with_parens(line, out, rest, token, inside, src, alt, sep, pre, post, repl) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (match(rest, /!!\[[^]]*\]\([^)]*\)/)) {
|
||||
pre = substr(rest, 1, RSTART - 1)
|
||||
token = substr(rest, RSTART, RLENGTH)
|
||||
post = substr(rest, RSTART + RLENGTH)
|
||||
|
||||
inside = token
|
||||
sub(/^!!\[/, "", inside)
|
||||
sep = index(inside, "](")
|
||||
alt = substr(inside, 1, sep - 1)
|
||||
src = substr(inside, sep + 2)
|
||||
sub(/\)$/, "", src)
|
||||
|
||||
repl = render_embed(src, alt, (alt != ""), 1)
|
||||
out = out pre repl
|
||||
rest = post
|
||||
}
|
||||
return out rest
|
||||
}
|
||||
|
||||
function rewrite_double_bang_bare(line, out, rest, token, src, pre, post, repl) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (match(rest, /!!\[[^]]+\]/)) {
|
||||
pre = substr(rest, 1, RSTART - 1)
|
||||
token = substr(rest, RSTART, RLENGTH)
|
||||
post = substr(rest, RSTART + RLENGTH)
|
||||
src = token
|
||||
sub(/^!!\[/, "", src)
|
||||
sub(/\]$/, "", src)
|
||||
repl = render_embed(src, "", 0, 1)
|
||||
out = out pre repl
|
||||
rest = post
|
||||
}
|
||||
return out rest
|
||||
}
|
||||
|
||||
function rewrite_bare_bang(line, out, rest, token, src, pre, post, repl) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (match(rest, /!\[[^]]+\]/)) {
|
||||
pre = substr(rest, 1, RSTART - 1)
|
||||
token = substr(rest, RSTART, RLENGTH)
|
||||
post = substr(rest, RSTART + RLENGTH)
|
||||
src = token
|
||||
sub(/^!\[/, "", src)
|
||||
sub(/\]$/, "", src)
|
||||
repl = render_embed(src, "", 0, 0)
|
||||
out = out pre repl
|
||||
rest = post
|
||||
}
|
||||
return out rest
|
||||
}
|
||||
|
||||
function rewrite_noncode_line(line, out, rest, pstart, pend, code_seg, noncode) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (1) {
|
||||
pstart = index(rest, "<code>")
|
||||
if (pstart == 0) {
|
||||
noncode = rest
|
||||
noncode = rewrite_img_tags(noncode)
|
||||
noncode = rewrite_double_bang_with_parens(noncode)
|
||||
noncode = rewrite_double_bang_bare(noncode)
|
||||
noncode = rewrite_bare_bang(noncode)
|
||||
out = out noncode
|
||||
break
|
||||
}
|
||||
|
||||
noncode = substr(rest, 1, pstart - 1)
|
||||
noncode = rewrite_img_tags(noncode)
|
||||
noncode = rewrite_double_bang_with_parens(noncode)
|
||||
noncode = rewrite_double_bang_bare(noncode)
|
||||
noncode = rewrite_bare_bang(noncode)
|
||||
out = out noncode
|
||||
|
||||
rest = substr(rest, pstart)
|
||||
pend = index(rest, "</code>")
|
||||
if (pend == 0) {
|
||||
out = out rest
|
||||
break
|
||||
}
|
||||
code_seg = substr(rest, 1, pend + length("</code>") - 1)
|
||||
out = out code_seg
|
||||
rest = substr(rest, pend + length("</code>"))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function rewrite_code_double_bang_with_parens(line, out, rest, token, inside, src, sep, pre, post, repl) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (match(rest, /!!\[[^]]*\]\([^)]*\)/)) {
|
||||
pre = substr(rest, 1, RSTART - 1)
|
||||
token = substr(rest, RSTART, RLENGTH)
|
||||
post = substr(rest, RSTART + RLENGTH)
|
||||
|
||||
inside = token
|
||||
sub(/^!!\[/, "", inside)
|
||||
sep = index(inside, "](")
|
||||
src = substr(inside, sep + 2)
|
||||
sub(/\)$/, "", src)
|
||||
|
||||
repl = render_code_include(src, 1)
|
||||
if (repl == "") repl = token
|
||||
out = out pre repl
|
||||
rest = post
|
||||
}
|
||||
return out rest
|
||||
}
|
||||
|
||||
function rewrite_code_double_bang_bare(line, out, rest, token, src, pre, post, repl) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (match(rest, /!!\[[^]]+\]/)) {
|
||||
pre = substr(rest, 1, RSTART - 1)
|
||||
token = substr(rest, RSTART, RLENGTH)
|
||||
post = substr(rest, RSTART + RLENGTH)
|
||||
src = token
|
||||
sub(/^!!\[/, "", src)
|
||||
sub(/\]$/, "", src)
|
||||
repl = render_code_include(src, 1)
|
||||
if (repl == "") repl = token
|
||||
out = out pre repl
|
||||
rest = post
|
||||
}
|
||||
return out rest
|
||||
}
|
||||
|
||||
function rewrite_code_bang_with_parens(line, out, rest, token, inside, src, sep, pre, post, repl) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (match(rest, /!\[[^]]*\]\([^)]*\)/)) {
|
||||
pre = substr(rest, 1, RSTART - 1)
|
||||
token = substr(rest, RSTART, RLENGTH)
|
||||
post = substr(rest, RSTART + RLENGTH)
|
||||
|
||||
inside = token
|
||||
sub(/^!\[/, "", inside)
|
||||
sep = index(inside, "](")
|
||||
src = substr(inside, sep + 2)
|
||||
sub(/\)$/, "", src)
|
||||
|
||||
repl = render_code_include(src, 0)
|
||||
if (repl == "") repl = token
|
||||
out = out pre repl
|
||||
rest = post
|
||||
}
|
||||
return out rest
|
||||
}
|
||||
|
||||
function rewrite_code_bare_bang(line, out, rest, token, src, pre, post, repl) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (match(rest, /!\[[^]]+\]/)) {
|
||||
pre = substr(rest, 1, RSTART - 1)
|
||||
token = substr(rest, RSTART, RLENGTH)
|
||||
post = substr(rest, RSTART + RLENGTH)
|
||||
src = token
|
||||
sub(/^!\[/, "", src)
|
||||
sub(/\]$/, "", src)
|
||||
repl = render_code_include(src, 0)
|
||||
if (repl == "") repl = token
|
||||
out = out pre repl
|
||||
rest = post
|
||||
}
|
||||
return out rest
|
||||
}
|
||||
|
||||
function restore_plain_markers(line) {
|
||||
gsub(/\034P0\034/, "*", line)
|
||||
gsub(/\034P1\034/, "_", line)
|
||||
gsub(/\034P2\034/, "`", line)
|
||||
gsub(/\034P3\034/, "[", line)
|
||||
gsub(/\034P4\034/, "]", line)
|
||||
gsub(/\034P5\034/, "(", line)
|
||||
gsub(/\034P6\034/, ")", line)
|
||||
gsub(/\034P7\034/, "!", line)
|
||||
gsub(/\034P8\034/, "$", line)
|
||||
gsub(/<mfmplain>/, "<span class=\"mfm-plain\">", line)
|
||||
gsub(/<\/mfmplain>/, "</span>", line)
|
||||
return line
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
input_dir = dirname_of(input_file)
|
||||
in_pre_code = 0
|
||||
}
|
||||
|
||||
{
|
||||
line = $0
|
||||
|
||||
start_pre = (line ~ /<pre><code>/)
|
||||
end_pre = (line ~ /<\/code><\/pre>/)
|
||||
|
||||
if (in_pre_code || start_pre) {
|
||||
gsub(/\\!\[/, "\034ESC_BANG_OPEN\034", line)
|
||||
line = rewrite_code_double_bang_with_parens(line)
|
||||
line = rewrite_code_double_bang_bare(line)
|
||||
line = rewrite_code_bang_with_parens(line)
|
||||
line = rewrite_code_bare_bang(line)
|
||||
gsub(/\034ESC_BANG_OPEN\034/, "![", line)
|
||||
line = highlight_code_block_line(line)
|
||||
} else {
|
||||
line = rewrite_noncode_line(line)
|
||||
}
|
||||
|
||||
line = apply_td_vertical_align(line)
|
||||
line = restore_plain_markers(line)
|
||||
print line
|
||||
|
||||
if (start_pre && !end_pre) {
|
||||
in_pre_code = 1
|
||||
} else if (in_pre_code && end_pre) {
|
||||
in_pre_code = 0
|
||||
}
|
||||
}
|
||||
32
awk/render_template.awk
Normal file
32
awk/render_template.awk
Normal file
@@ -0,0 +1,32 @@
|
||||
function replace_all(text, token, value, pos, token_len) {
|
||||
token_len = length(token)
|
||||
while ((pos = index(text, token)) > 0) {
|
||||
text = substr(text, 1, pos - 1) value substr(text, pos + token_len)
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
{
|
||||
line = $0
|
||||
line = replace_all(line, "{{TITLE}}", title)
|
||||
line = replace_all(line, "{{NAV}}", nav)
|
||||
line = replace_all(line, "{{FOOTER}}", footer)
|
||||
line = replace_all(line, "{{CSS}}", style_path)
|
||||
line = replace_all(line, "{{HEAD_EXTRA}}", head_extra)
|
||||
line = replace_all(line, "{{HEADER_BRAND}}", header_brand)
|
||||
|
||||
pos = index(line, "{{CONTENT}}")
|
||||
if (pos > 0) {
|
||||
printf "%s", substr(line, 1, pos - 1)
|
||||
while ((getline content_line < "-") > 0) {
|
||||
gsub(/\.md\)/, ".html)", content_line)
|
||||
gsub(/\.md"/, ".html\"", content_line)
|
||||
gsub(/\.md\?/, ".html?", content_line)
|
||||
gsub(/\.md#/, ".html#", content_line)
|
||||
print content_line
|
||||
}
|
||||
printf "%s\n", substr(line, pos + 11)
|
||||
} else {
|
||||
print line
|
||||
}
|
||||
}
|
||||
64
awk/replace_variables.awk
Normal file
64
awk/replace_variables.awk
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
raw[++n] = $0
|
||||
lines[n] = $0
|
||||
rest = $0
|
||||
|
||||
while (match(rest, /--[A-Za-z0-9_-]+[[:space:]]*:[[:space:]]*[^;]+;/)) {
|
||||
decl = substr(rest, RSTART, RLENGTH)
|
||||
|
||||
name = decl
|
||||
sub(/:.*/, "", name)
|
||||
gsub(/[[:space:]]/, "", name)
|
||||
|
||||
value = decl
|
||||
sub(/^[^:]*:[[:space:]]*/, "", value)
|
||||
sub(/;[[:space:]]*$/, "", value)
|
||||
|
||||
vars[name] = value
|
||||
rest = substr(rest, RSTART + RLENGTH)
|
||||
}
|
||||
}
|
||||
|
||||
END {
|
||||
in_root = 0
|
||||
for (i = 1; i <= n; i++) {
|
||||
line = raw[i]
|
||||
if (!in_root && line ~ /:root[[:space:]]*\{/) {
|
||||
in_root = 1
|
||||
lines[i] = ""
|
||||
if (line ~ /\}/) {
|
||||
in_root = 0
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (in_root) {
|
||||
lines[i] = ""
|
||||
if (line ~ /\}/) {
|
||||
in_root = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 1; i <= n; i++) {
|
||||
line = lines[i]
|
||||
if (line == "") {
|
||||
continue
|
||||
}
|
||||
changed = 1
|
||||
iter = 0
|
||||
|
||||
while (changed && iter < 10) {
|
||||
changed = 0
|
||||
iter++
|
||||
for (name in vars) {
|
||||
token = "var(" name ")"
|
||||
while ((pos = index(line, token)) > 0) {
|
||||
line = substr(line, 1, pos - 1) vars[name] substr(line, pos + length(token))
|
||||
changed = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print line
|
||||
}
|
||||
}
|
||||
358
kewt.sh
Executable file
358
kewt.sh
Executable file
@@ -0,0 +1,358 @@
|
||||
#!/bin/sh
|
||||
|
||||
die() {
|
||||
echo "Error: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [--from <src>] [--to <out>]
|
||||
$0 [src] [out]
|
||||
$0 --new [title]
|
||||
$0 --help
|
||||
|
||||
Options:
|
||||
--help Show this help message.
|
||||
--new [title] Create a new site directory (default: site)
|
||||
--from <src> Source directory (default: site)
|
||||
--to <out> Output directory (default: out)
|
||||
EOF
|
||||
}
|
||||
|
||||
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
awk_dir="$script_dir/awk"
|
||||
|
||||
ensure_root_defaults() {
|
||||
if [ ! -f "./site.conf" ]; then
|
||||
cat > "./site.conf" <<'EOF'
|
||||
title = "kewt"
|
||||
style = "kewt"
|
||||
dir_indexes = true
|
||||
single_file_index = true
|
||||
flatten = false
|
||||
footer = "made with <a href="https://git.krzak.org/N0VA/kewt">kewt</a>"
|
||||
logo = ""
|
||||
display_logo = false
|
||||
display_title = true
|
||||
logo_as_favicon = true
|
||||
favicon = ""
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ ! -f "./template.html" ]; then
|
||||
cat > "./template.html" <<'EOF'
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>{{TITLE}}</title>
|
||||
|
||||
<link rel="stylesheet" href="{{CSS}}" type="text/css" />
|
||||
{{HEAD_EXTRA}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{HEADER_BRAND}}</h1>
|
||||
</header>
|
||||
|
||||
<nav id="side-bar">{{NAV}}</nav>
|
||||
|
||||
<article>{{CONTENT}}</article>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
create_new_site() {
|
||||
new_title="$1"
|
||||
new_dir="site"
|
||||
[ -n "$new_title" ] && new_dir="$new_title"
|
||||
|
||||
[ -e "$new_dir" ] && die "Target '$new_dir' already exists."
|
||||
|
||||
ensure_root_defaults
|
||||
|
||||
mkdir -p "$new_dir"
|
||||
cp "./site.conf" "$new_dir/site.conf"
|
||||
printf "# _kewt_ website\n" > "$new_dir/index.md"
|
||||
|
||||
if [ -n "$new_title" ]; then
|
||||
awk -v new_title="$new_title" '
|
||||
BEGIN { done = 0 }
|
||||
/^title[[:space:]]*=/ {
|
||||
print "title = \"" new_title "\""
|
||||
done = 1
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
END {
|
||||
if (!done) print "title = \"" new_title "\""
|
||||
}
|
||||
' "$new_dir/site.conf" > "$new_dir/site.conf.tmp" && mv "$new_dir/site.conf.tmp" "$new_dir/site.conf"
|
||||
fi
|
||||
|
||||
echo "Created new site at '$new_dir'."
|
||||
exit 0
|
||||
}
|
||||
|
||||
generate_nav() {
|
||||
dinfo=$(find "$1" -not -path '*/.*' | sort -r | awk -v src="$1" -f "$awk_dir/collect_dir_info.awk")
|
||||
find "$1" -name "*.md" | sort | awk -v src="$1" -v single_file_index="$single_file_index" -v flatten="$flatten" -v dinfo="$dinfo" -f "$awk_dir/generate_sidebar.awk"
|
||||
}
|
||||
|
||||
src=""
|
||||
out=""
|
||||
new_mode="false"
|
||||
new_title=""
|
||||
positional_count=0
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--new)
|
||||
new_mode="true"
|
||||
if [ $# -gt 1 ] && [ "${2#-}" = "$2" ]; then
|
||||
new_title="$2"
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
--from)
|
||||
[ $# -lt 2 ] && die "--from requires a value."
|
||||
src="$2"
|
||||
shift
|
||||
;;
|
||||
--to)
|
||||
[ $# -lt 2 ] && die "--to requires a value."
|
||||
out="$2"
|
||||
shift
|
||||
;;
|
||||
--*)
|
||||
die "Unknown option: $1"
|
||||
;;
|
||||
*)
|
||||
positional_count=$((positional_count + 1))
|
||||
if [ "$positional_count" -eq 1 ]; then
|
||||
[ -z "$src" ] && src="$1" || die "Source already set (use either positional or --from)."
|
||||
elif [ "$positional_count" -eq 2 ]; then
|
||||
[ -z "$out" ] && out="$1" || die "Output already set (use either positional or --to)."
|
||||
else
|
||||
die "Too many positional arguments."
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ "$new_mode" = "true" ] && create_new_site "$new_title"
|
||||
|
||||
ensure_root_defaults
|
||||
|
||||
[ -z "$src" ] && src="site"
|
||||
[ -z "$out" ] && out="out"
|
||||
|
||||
[ -d "$src" ] || die "Source directory '$src' does not exist."
|
||||
|
||||
title="kewt"
|
||||
style="kewt"
|
||||
footer="made with <a href=\"https://git.krzak.org/N0VA/kewt\">kewt</a>"
|
||||
dir_indexes="true"
|
||||
single_file_index="true"
|
||||
flatten="false"
|
||||
logo=""
|
||||
display_logo="false"
|
||||
display_title="true"
|
||||
logo_as_favicon="true"
|
||||
favicon=""
|
||||
|
||||
load_config() {
|
||||
[ -f "$1" ] || return
|
||||
while IFS='= ' read -r key val; do
|
||||
val=$(echo "$val" | tr -d '" ' | tr -d "'")
|
||||
case "$key" in
|
||||
title) ;;
|
||||
style) style="$val" ;;
|
||||
dir_indexes) dir_indexes="$val" ;;
|
||||
single_file_index) single_file_index="$val" ;;
|
||||
flatten) flatten="$val" ;;
|
||||
footer) footer="$val" ;;
|
||||
logo) logo="$val" ;;
|
||||
display_logo) display_logo="$val" ;;
|
||||
display_title) display_title="$val" ;;
|
||||
logo_as_favicon) logo_as_favicon="$val" ;;
|
||||
favicon) favicon="$val" ;;
|
||||
esac
|
||||
done < "$1"
|
||||
|
||||
t=$(grep "^title" "$1" | cut -d= -f2- | sed 's/^[ "]*//;s/[ "]*$//')
|
||||
[ -n "$t" ] && title="$t"
|
||||
}
|
||||
|
||||
load_config "./site.conf"
|
||||
load_config "$src/site.conf"
|
||||
|
||||
template="$src/template.html"
|
||||
[ -f "$template" ] || template="./template.html"
|
||||
[ -f "$template" ] || die "Template '$template' not found."
|
||||
|
||||
[ -d "$out" ] && rm -rf "$out"
|
||||
mkdir -p "$out"
|
||||
|
||||
nav=$(generate_nav "$src")
|
||||
|
||||
find_closest() {
|
||||
target="$1"
|
||||
start_dir="$2"
|
||||
curr="$start_dir"
|
||||
while [ "$curr" != "$src" ] && [ "$curr" != "." ] && [ "$curr" != "/" ]; do
|
||||
if [ -f "$curr/$target" ]; then
|
||||
echo "$curr/$target"
|
||||
return
|
||||
fi
|
||||
curr=$(dirname "$curr")
|
||||
done
|
||||
if [ -f "$src/$target" ]; then
|
||||
echo "$src/$target"
|
||||
fi
|
||||
}
|
||||
|
||||
copy_style_with_resolved_vars() {
|
||||
src_style="$1"
|
||||
out_style="$2"
|
||||
awk -f "$awk_dir/replace_variables.awk" "$src_style" > "$out_style"
|
||||
}
|
||||
|
||||
render_markdown() {
|
||||
file="$1"
|
||||
local_template=$(find_closest "template.html" "$(dirname "$file")")
|
||||
[ -z "$local_template" ] && local_template="$template"
|
||||
|
||||
closest_style_src=$(find_closest "styles.css" "$(dirname "$file")")
|
||||
[ -z "$closest_style_src" ] && closest_style_src=$(find_closest "style.css" "$(dirname "$file")")
|
||||
if [ -n "$closest_style_src" ]; then
|
||||
style_rel_to_src="${closest_style_src#$src/}"
|
||||
case "$closest_style_src" in
|
||||
"$src/styles.css") style_rel_to_src="styles.css" ;;
|
||||
"$src/style.css") style_rel_to_src="style.css" ;;
|
||||
esac
|
||||
style_path="/${style_rel_to_src%styles.css}"
|
||||
style_path="${style_path%style.css}styles.css"
|
||||
else
|
||||
style_path="/styles.css"
|
||||
fi
|
||||
|
||||
logo_html=""
|
||||
if [ "$display_logo" = "true" ] && [ -n "$logo" ]; then
|
||||
logo_html="<img class=\"site-logo\" src=\"$logo\" alt=\"$title\" />"
|
||||
fi
|
||||
|
||||
brand_text=""
|
||||
if [ "$display_title" = "true" ]; then
|
||||
brand_text="$title"
|
||||
fi
|
||||
|
||||
if [ -n "$logo_html" ] && [ -n "$brand_text" ]; then
|
||||
header_brand="<a href=\"/index.html\">$logo_html $brand_text</a>"
|
||||
elif [ -n "$logo_html" ]; then
|
||||
header_brand="<a href=\"/index.html\">$logo_html</a>"
|
||||
elif [ -n "$brand_text" ]; then
|
||||
header_brand="<a href=\"/index.html\">$brand_text</a>"
|
||||
else
|
||||
header_brand="<a href=\"/index.html\">$title</a>"
|
||||
fi
|
||||
|
||||
favicon_src=""
|
||||
if [ "$logo_as_favicon" = "true" ] && [ -n "$logo" ]; then
|
||||
favicon_src="$logo"
|
||||
elif [ -n "$favicon" ]; then
|
||||
favicon_src="$favicon"
|
||||
fi
|
||||
head_extra=""
|
||||
if [ -n "$favicon_src" ]; then
|
||||
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
||||
fi
|
||||
|
||||
MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="styles/$style.css" sh "$script_dir/markdown.sh" "$file" | awk -v title="$title" -v nav="$nav" -v footer="$footer" -v style_path="$style_path" -v header_brand="$header_brand" -v head_extra="$head_extra" -f "$awk_dir/render_template.awk" "$local_template"
|
||||
}
|
||||
|
||||
echo "Building site from '$src' to '$out'..."
|
||||
|
||||
find "$src" -type d | sort | while read -r dir; do
|
||||
rel_dir="${dir#$src/}"
|
||||
[ "$dir" = "$src" ] && rel_dir="."
|
||||
out_dir="$out/$rel_dir"
|
||||
mkdir -p "$out_dir"
|
||||
|
||||
if [ -f "$dir/styles.css" ]; then
|
||||
copy_style_with_resolved_vars "$dir/styles.css" "$out_dir/styles.css"
|
||||
elif [ -f "$dir/style.css" ]; then
|
||||
copy_style_with_resolved_vars "$dir/style.css" "$out_dir/styles.css"
|
||||
fi
|
||||
|
||||
[ "$dir_indexes" != "true" ] && continue
|
||||
|
||||
if [ ! -f "$dir/index.md" ]; then
|
||||
if [ "$single_file_index" = "true" ]; then
|
||||
md_count=$(find "$dir" -maxdepth 1 -name "*.md" | wc -l)
|
||||
if [ "$md_count" -eq 1 ]; then
|
||||
md_file=$(find "$dir" -maxdepth 1 -name "*.md")
|
||||
render_markdown "$md_file" > "$out_dir/index.html"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
temp_index="/tmp/kewt_index_$$.md"
|
||||
display_dir="${rel_dir#.}"
|
||||
[ -z "$display_dir" ] && display_dir="/"
|
||||
echo "# Index of $display_dir" > "$temp_index"
|
||||
echo "" >> "$temp_index"
|
||||
find "$dir" -maxdepth 1 -not -path '*/.*' -not -path "$dir" | sort | while read -r entry; do
|
||||
name="${entry##*/}"
|
||||
case "$name" in
|
||||
template.html|site.conf|style.css|index.md) continue ;;
|
||||
esac
|
||||
if [ -d "$entry" ]; then
|
||||
echo "- [${name}/](${name}/index.html)" >> "$temp_index"
|
||||
elif [ "${entry%.md}" != "$entry" ]; then
|
||||
echo "- [${name%.md}](${name%.md}.html)" >> "$temp_index"
|
||||
else
|
||||
echo "- [$name]($name)" >> "$temp_index"
|
||||
fi
|
||||
done
|
||||
render_markdown "$temp_index" > "$out_dir/index.html"
|
||||
rm "$temp_index"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -f "$out/styles.css" ] && [ -f "styles/$style.css" ]; then
|
||||
copy_style_with_resolved_vars "styles/$style.css" "$out/styles.css"
|
||||
fi
|
||||
|
||||
find "$src" -type f | sort | while IFS= read -r file; do
|
||||
rel_path="${file#$src/}"
|
||||
dir_rel=$(dirname "$rel_path")
|
||||
out_dir="$out/$dir_rel"
|
||||
|
||||
case "${file##*/}" in
|
||||
template.html|site.conf|style.css|styles.css) continue ;;
|
||||
esac
|
||||
|
||||
if [ "$single_file_index" = "true" ] && [ "${file%.md}" != "$file" ] && [ ! -f "$(dirname "$file")/index.md" ]; then
|
||||
md_count=$(find "$(dirname "$file")" -maxdepth 1 -name "*.md" | wc -l)
|
||||
[ "$md_count" -eq 1 ] && continue
|
||||
fi
|
||||
|
||||
if [ "${file%.md}" != "$file" ]; then
|
||||
out_file="$out/${rel_path%.md}.html"
|
||||
render_markdown "$file" > "$out_file"
|
||||
else
|
||||
cp "$file" "$out/$rel_path"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Build complete."
|
||||
672
markdown.sh
Executable file
672
markdown.sh
Executable file
@@ -0,0 +1,672 @@
|
||||
#!/bin/sh
|
||||
|
||||
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
awk_dir="$script_dir/awk"
|
||||
|
||||
sed_ere() {
|
||||
if sed -E '' </dev/null >/dev/null 2>&1; then
|
||||
sed -E "$@"
|
||||
else
|
||||
sed -r "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
sed_ere_inplace() {
|
||||
script="$1"
|
||||
file="$2"
|
||||
tmp="${file}.tmp.$$"
|
||||
sed_ere "$script" "$file" > "$tmp" && mv "$tmp" "$file" || {
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
sed_ere_inplace_n() {
|
||||
script="$1"
|
||||
file="$2"
|
||||
tmp="${file}.tmp.$$"
|
||||
sed_ere -n "$script" "$file" > "$tmp" && mv "$tmp" "$file" || {
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
sed_inplace() {
|
||||
script="$1"
|
||||
file="$2"
|
||||
tmp="${file}.tmp.$$"
|
||||
sed "$script" "$file" > "$tmp" && mv "$tmp" "$file" || {
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
temp_file="/tmp/markdown.$$"
|
||||
cat "$@" > "$temp_file"
|
||||
|
||||
awk '
|
||||
function find_unescaped_tag(s, tag, p, off, pos) {
|
||||
p = 1
|
||||
while (1) {
|
||||
off = index(substr(s, p), tag)
|
||||
if (off == 0) return 0
|
||||
pos = p + off - 1
|
||||
if (pos == 1 || substr(s, pos - 1, 1) != "\\") return pos
|
||||
p = pos + 1
|
||||
}
|
||||
}
|
||||
|
||||
function mask_plain(s, t) {
|
||||
t = s
|
||||
gsub(/\*/, "\034P0\034", t)
|
||||
gsub(/_/, "\034P1\034", t)
|
||||
gsub(/`/, "\034P2\034", t)
|
||||
gsub(/\[/, "\034P3\034", t)
|
||||
gsub(/\]/, "\034P4\034", t)
|
||||
gsub(/\(/, "\034P5\034", t)
|
||||
gsub(/\)/, "\034P6\034", t)
|
||||
gsub(/!/, "\034P7\034", t)
|
||||
gsub(/\$/, "\034P8\034", t)
|
||||
return t
|
||||
}
|
||||
BEGIN { in_plain = 0 }
|
||||
{
|
||||
line = $0
|
||||
out = ""
|
||||
while (1) {
|
||||
if (!in_plain) {
|
||||
pos = find_unescaped_tag(line, "<plain>")
|
||||
if (pos == 0) {
|
||||
out = out line
|
||||
break
|
||||
}
|
||||
out = out substr(line, 1, pos - 1) "<mfmplain>"
|
||||
line = substr(line, pos + 7)
|
||||
in_plain = 1
|
||||
} else {
|
||||
pos = find_unescaped_tag(line, "</plain>")
|
||||
if (pos == 0) {
|
||||
out = out mask_plain(line)
|
||||
line = ""
|
||||
break
|
||||
}
|
||||
out = out mask_plain(substr(line, 1, pos - 1)) "</mfmplain>"
|
||||
line = substr(line, pos + 8)
|
||||
in_plain = 0
|
||||
}
|
||||
}
|
||||
print out
|
||||
}
|
||||
' "$temp_file" > "$temp_file.plain.$$" && mv "$temp_file.plain.$$" "$temp_file"
|
||||
|
||||
IFS='
|
||||
'
|
||||
refs=$(sed_ere -n "/^\[.+\]: +/p" "$@")
|
||||
for ref in $refs
|
||||
do
|
||||
ref_id=$(printf %s "$ref" | sed_ere -n "s/^\[(.+)\]: .*/\1/p" | tr -d '\n')
|
||||
ref_url=$(printf %s "$ref" | sed_ere -n "s/^\[.+\]: (.+)/\1/p" | cut -d' ' -f1 | tr -d '\n')
|
||||
ref_title=$(printf %s "$ref" | sed_ere -n "s/^\[.+\]: (.+) \"(.+)\"/\2/p" | sed 's@|@!@g' | tr -d '\n')
|
||||
|
||||
# reference-style image using the label
|
||||
sed_ere_inplace "s|!\[([^]]+)\]\[($ref_id)\]|<img src=\"$ref_url\" title=\"$ref_title\" alt=\"\1\" />|g" "$temp_file"
|
||||
# reference-style link using the label
|
||||
sed_ere_inplace "s|\[([^]]+)\]\[($ref_id)\]|<a href=\"$ref_url\" title=\"$ref_title\">\1</a>|g" "$temp_file"
|
||||
|
||||
# implicit reference-style
|
||||
sed_ere_inplace "s|!\[($ref_id)\]\[\]|<img src=\"$ref_url\" title=\"$ref_title\" alt=\"\1\" />|g" "$temp_file"
|
||||
# implicit reference-style
|
||||
sed_ere_inplace "s|\[($ref_id)\]\[\]|<a href=\"$ref_url\" title=\"$ref_title\">\1</a>|g" "$temp_file"
|
||||
done
|
||||
|
||||
# delete the reference lines
|
||||
sed_ere_inplace "/^\[.+\]: +/d" "$temp_file"
|
||||
|
||||
# normalize GitHub admonition shorthand in blockquotes
|
||||
sed_ere_inplace '
|
||||
/^>!\[/s/^>!\[/> [!/
|
||||
/^>\[!/s/^>\[!/> [!/
|
||||
s/^>([^[:space:]>])/> \1/
|
||||
' "$temp_file"
|
||||
|
||||
# blockquotes
|
||||
# use grep to find all the nested blockquotes
|
||||
while grep '^> ' "$temp_file" >/dev/null
|
||||
do
|
||||
sed_ere_inplace_n '
|
||||
/^$/b blockquote
|
||||
|
||||
H
|
||||
$ b blockquote
|
||||
b
|
||||
|
||||
:blockquote
|
||||
x
|
||||
s/(\n+)(> .*)/\1<blockquote>\n\2\n<\/blockquote>/ # wrap the tags in a blockquote
|
||||
p
|
||||
' "$temp_file"
|
||||
|
||||
sed_inplace '1 d' "$temp_file" # cleanup superfluous first line
|
||||
|
||||
# cleanup blank lines and remove subsequent blockquote characters
|
||||
sed_ere_inplace '
|
||||
/^> /s/^> (.*)/\1/
|
||||
' "$temp_file"
|
||||
done
|
||||
|
||||
# convert [!TYPE] blockquotes into admonition blocks
|
||||
awk '
|
||||
function cap(s) { return toupper(substr(s, 1, 1)) tolower(substr(s, 2)) }
|
||||
BEGIN { count = 0 }
|
||||
{ lines[++count] = $0 }
|
||||
END {
|
||||
i = 1
|
||||
while (i <= count) {
|
||||
if (lines[i] == "<blockquote>") {
|
||||
j = i + 1
|
||||
while (j <= count && lines[j] != "</blockquote>") j++
|
||||
if (j <= count) {
|
||||
first = ""
|
||||
first_idx = 0
|
||||
for (k = i + 1; k < j; k++) {
|
||||
if (lines[k] != "") {
|
||||
first = lines[k]
|
||||
first_idx = k
|
||||
break
|
||||
}
|
||||
}
|
||||
if (first ~ /^\[![A-Za-z]+\]$/) {
|
||||
kind = first
|
||||
sub(/^\[!/, "", kind)
|
||||
sub(/\]$/, "", kind)
|
||||
lkind = tolower(kind)
|
||||
if (lkind == "note" || lkind == "tip" || lkind == "important" || lkind == "warning" || lkind == "caution") {
|
||||
print "<div class=\"admonition admonition-" lkind "\">"
|
||||
print "<p class=\"admonition-title\">" cap(lkind) "</p>"
|
||||
has_body = 0
|
||||
for (k = first_idx + 1; k < j; k++) {
|
||||
if (lines[k] != "") {
|
||||
print "<p>" lines[k] "</p>"
|
||||
has_body = 1
|
||||
}
|
||||
}
|
||||
if (!has_body) print "<p></p>"
|
||||
print "</div>"
|
||||
i = j + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
print lines[i]
|
||||
i++
|
||||
}
|
||||
}
|
||||
' "$temp_file" > "$temp_file.admon.$$" && mv "$temp_file.admon.$$" "$temp_file"
|
||||
|
||||
# Setext-style headers
|
||||
sed_ere_inplace_n '
|
||||
# Setext-style headers need to be wrapped around newlines
|
||||
/^$/ b print
|
||||
|
||||
# else, append to holding area
|
||||
H
|
||||
$ b print
|
||||
b
|
||||
|
||||
:print
|
||||
x
|
||||
/=+$/{
|
||||
s/\n(.*)\n=+$/\n<h1>\1<\/h1>/
|
||||
p
|
||||
b
|
||||
}
|
||||
/\-+$/{
|
||||
s/\n(.*)\n\-+$/\n<h2>\1<\/h2>/
|
||||
p
|
||||
b
|
||||
}
|
||||
p
|
||||
' "$temp_file"
|
||||
|
||||
sed_inplace '1 d' "$temp_file" # cleanup superfluous first line
|
||||
|
||||
# atx-style headers and other block styles
|
||||
sed_ere_inplace '
|
||||
/^#+ /s/ #+$// # kill all ending header characters
|
||||
/^# /s/# ([A-Za-z0-9 ]*)(.*)/<h1 id="\1">\1\2<\/h1>/g # H1
|
||||
/^#{2} /s/#{2} ([A-Za-z0-9 ]*)(.*)/<h2 id="\1">\1\2<\/h2>/g # H2
|
||||
/^#{3} /s/#{3} ([A-Za-z0-9 ]*)(.*)/<h3 id="\1">\1\2<\/h3>/g # H3
|
||||
/^#{4} /s/#{4} ([A-Za-z0-9 ]*)(.*)/<h4 id="\1">\1\2<\/h4>/g # H4
|
||||
/^#{5} /s/#{5} ([A-Za-z0-9 ]*)(.*)/<h5 id="\1">\1\2<\/h5>/g # H5
|
||||
/^#{6} /s/#{6} ([A-Za-z0-9 ]*)(.*)/<h6 id="\1">\1\2<\/h6>/g # H6
|
||||
|
||||
/^\*\*\*+$/s/\*\*\*+/<hr \/>/ # hr with *
|
||||
/^---+$/s/---+/<hr \/>/ # hr with -
|
||||
/^___+$/s/___+/<hr \/>/ # hr with _
|
||||
|
||||
' "$temp_file"
|
||||
|
||||
# unordered lists
|
||||
# use grep to find all the nested lists
|
||||
while grep '^[\*\+\-] ' "$temp_file" >/dev/null
|
||||
do
|
||||
sed_ere_inplace_n '
|
||||
# wrap the list
|
||||
/^$/b list
|
||||
|
||||
# wrap the li tags then add to the hold buffer
|
||||
# use uli instead of li to avoid collisions when processing nested lists
|
||||
/^[\*\+\-] /s/[\*\+\-] (.*)/<\/uli>\n<uli>\n\1/
|
||||
|
||||
H
|
||||
$ b list # if at end of file, check for the end of a list
|
||||
b # else, branch to the end of the script
|
||||
|
||||
# this is where a list is checked for the pattern
|
||||
:list
|
||||
# exchange the hold space into the pattern space
|
||||
x
|
||||
# look for the list items, if there wrap the ul tags
|
||||
/<uli>/{
|
||||
s/(.*)/\n<ul>\1\n<\/uli>\n<\/ul>/ # close the ul tags
|
||||
s/\n<\/uli>// # kill the first superfluous closing tag
|
||||
p
|
||||
b
|
||||
}
|
||||
p
|
||||
' "$temp_file"
|
||||
|
||||
sed_inplace '1 d' "$temp_file" # cleanup superfluous first line
|
||||
|
||||
# convert to the proper li to avoid collisions with nested lists
|
||||
sed_inplace 's/uli>/li>/g' "$temp_file"
|
||||
|
||||
# prepare any nested lists
|
||||
sed_ere_inplace '/^[\*\+\-] /s/(.*)/\n\1\n/' "$temp_file"
|
||||
done
|
||||
|
||||
# ordered lists
|
||||
# use grep to find all the nested lists
|
||||
while grep -E '^[1-9]+\. ' "$temp_file" >/dev/null
|
||||
do
|
||||
sed_ere_inplace_n '
|
||||
# wrap the list
|
||||
/^$/b list
|
||||
|
||||
# wrap the li tags then add to the hold buffer
|
||||
# use oli instead of li to avoid collisions when processing nested lists
|
||||
/^[1-9]+\. /s/[1-9]+\. (.*)/<\/oli>\n<oli>\n\1/
|
||||
|
||||
H
|
||||
$ b list # if at end of file, check for the end of a list
|
||||
b # else, branch to the end of the script
|
||||
|
||||
:list
|
||||
# exchange the hold space into the pattern space
|
||||
x
|
||||
# look for the list items, if there wrap the ol tags
|
||||
/<oli>/{
|
||||
s/(.*)/\n<ol>\1\n<\/oli>\n<\/ol>/ # close the ol tags
|
||||
s/\n<\/oli>// # kill the first superfluous closing tag
|
||||
p
|
||||
b
|
||||
}
|
||||
p
|
||||
' "$temp_file"
|
||||
|
||||
sed_inplace '1 d' "$temp_file" # cleanup superfluous first line
|
||||
|
||||
# convert list items into proper list items to avoid collisions with nested lists
|
||||
sed_inplace 's/oli>/li>/g' "$temp_file"
|
||||
|
||||
# prepare any nested lists
|
||||
sed_ere_inplace '/^[1-9]+\. /s/(.*)/\n\1\n/' "$temp_file"
|
||||
done
|
||||
|
||||
# make escaped periods literal
|
||||
sed_ere_inplace '/^[1-9]+\\. /s/([1-9]+)\\. /\1\. /' "$temp_file"
|
||||
|
||||
# fenced code blocks (triple backticks)
|
||||
awk '
|
||||
BEGIN { in_fence = 0 }
|
||||
{
|
||||
if (!in_fence && $0 ~ /^```/) {
|
||||
print "<pre><code>"
|
||||
in_fence = 1
|
||||
next
|
||||
}
|
||||
if (in_fence && $0 ~ /^```[[:space:]]*$/) {
|
||||
print "</code></pre>"
|
||||
in_fence = 0
|
||||
next
|
||||
}
|
||||
print
|
||||
}
|
||||
END {
|
||||
if (in_fence) print "</code></pre>"
|
||||
}
|
||||
' "$temp_file" > "$temp_file.fence.$$" && mv "$temp_file.fence.$$" "$temp_file"
|
||||
|
||||
|
||||
# code blocks
|
||||
sed_ere_inplace_n '
|
||||
# if at end of file, append the current line to the hold buffer and print it
|
||||
${
|
||||
H
|
||||
b code
|
||||
}
|
||||
|
||||
# wrap the code block on any non code block lines
|
||||
/^\t| {4}/!b code
|
||||
|
||||
# else, append to the holding buffer and do nothing
|
||||
H
|
||||
b # else, branch to the end of the script
|
||||
|
||||
:code
|
||||
# exchange the hold space with the pattern space
|
||||
x
|
||||
# look for the code items, if there wrap the pre-code tags
|
||||
/\t| {4}/{
|
||||
s/(\t| {4})(.*)/<pre><code>\n\1\2\n<\/code><\/pre>/ # wrap the ending tags
|
||||
p
|
||||
b
|
||||
}
|
||||
p
|
||||
' "$temp_file"
|
||||
|
||||
sed_inplace '1 d' "$temp_file" # cleanup superfluous first line
|
||||
|
||||
# convert html characters inside pre-code tags into printable representations
|
||||
sed_ere_inplace '
|
||||
# get inside pre-code tags
|
||||
/^<pre><code>/{
|
||||
:inside
|
||||
n
|
||||
# if you found the end tags, branch out
|
||||
/^<\/code><\/pre>/!{
|
||||
s/&/\&/g # ampersand
|
||||
s/</\</g # less than
|
||||
s/>/\>/g # greater than
|
||||
b inside
|
||||
}
|
||||
}
|
||||
' "$temp_file"
|
||||
|
||||
# remove the first tab (or 4 spaces) from the code lines
|
||||
sed_ere_inplace 's/^\t| {4}(.*)/\1/' "$temp_file"
|
||||
|
||||
# markdown pipe tables
|
||||
awk '
|
||||
function trim(s) {
|
||||
sub(/^[[:space:]]+/, "", s)
|
||||
sub(/[[:space:]]+$/, "", s)
|
||||
return s
|
||||
}
|
||||
|
||||
function is_table_row(line, t) {
|
||||
t = line
|
||||
return (t ~ /^[[:space:]]*\|/ && t ~ /\|[[:space:]]*$/)
|
||||
}
|
||||
|
||||
function is_table_sep(line, t) {
|
||||
if (!is_table_row(line)) return 0
|
||||
t = line
|
||||
gsub(/[|:\-[:space:]]/, "", t)
|
||||
return (t == "" && line ~ /-/)
|
||||
}
|
||||
|
||||
function split_row(line, out, n, i, raw) {
|
||||
raw = line
|
||||
sub(/^[[:space:]]*\|/, "", raw)
|
||||
sub(/\|[[:space:]]*$/, "", raw)
|
||||
n = split(raw, out, /\|/)
|
||||
for (i = 1; i <= n; i++) out[i] = trim(out[i])
|
||||
return n
|
||||
}
|
||||
|
||||
function align_for(sep, t) {
|
||||
t = trim(sep)
|
||||
if (t ~ /^:-+:$/) return "center"
|
||||
if (t ~ /^:-+$/) return "left"
|
||||
if (t ~ /^-+:$/) return "right"
|
||||
return ""
|
||||
}
|
||||
|
||||
function render_cell(cell, inner) {
|
||||
inner = trim(cell)
|
||||
if (inner ~ /^```.*```$/) {
|
||||
sub(/^```[[:space:]]*/, "", inner)
|
||||
sub(/[[:space:]]*```$/, "", inner)
|
||||
return "<pre><code>" inner "</code></pre>"
|
||||
}
|
||||
return inner
|
||||
}
|
||||
|
||||
BEGIN { count = 0 }
|
||||
{ lines[++count] = $0 }
|
||||
|
||||
END {
|
||||
in_pre = 0
|
||||
i = 1
|
||||
while (i <= count) {
|
||||
if (lines[i] ~ /^<pre><code>/) {
|
||||
in_pre = 1
|
||||
print lines[i]
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (in_pre) {
|
||||
print lines[i]
|
||||
if (lines[i] ~ /^<\/code><\/pre>/) in_pre = 0
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if (i < count && is_table_row(lines[i]) && is_table_sep(lines[i + 1])) {
|
||||
n_header = split_row(lines[i], header)
|
||||
n_sep = split_row(lines[i + 1], sep)
|
||||
n_cols = (n_header > n_sep ? n_header : n_sep)
|
||||
|
||||
print "<table>"
|
||||
print "<thead>"
|
||||
print "<tr>"
|
||||
for (c = 1; c <= n_cols; c++) {
|
||||
cell = (c <= n_header ? render_cell(header[c]) : "")
|
||||
a = (c <= n_sep ? align_for(sep[c]) : "")
|
||||
if (a != "") print "<th style=\"text-align: " a ";\">" cell "</th>"
|
||||
else print "<th>" cell "</th>"
|
||||
}
|
||||
print "</tr>"
|
||||
print "</thead>"
|
||||
|
||||
j = i + 2
|
||||
print "<tbody>"
|
||||
while (j <= count && is_table_row(lines[j])) {
|
||||
n_body = split_row(lines[j], body)
|
||||
print "<tr>"
|
||||
for (c = 1; c <= n_cols; c++) {
|
||||
cell = (c <= n_body ? render_cell(body[c]) : "")
|
||||
a = (c <= n_sep ? align_for(sep[c]) : "")
|
||||
if (a != "") print "<td style=\"text-align: " a ";\">" cell "</td>"
|
||||
else print "<td>" cell "</td>"
|
||||
}
|
||||
print "</tr>"
|
||||
j++
|
||||
}
|
||||
print "</tbody>"
|
||||
print "</table>"
|
||||
|
||||
i = j
|
||||
continue
|
||||
}
|
||||
|
||||
if (is_table_sep(lines[i]) && i < count && is_table_row(lines[i + 1])) {
|
||||
n_sep = split_row(lines[i], sep)
|
||||
n_cols = n_sep
|
||||
|
||||
print "<table>"
|
||||
print "<thead>"
|
||||
print "<tr>"
|
||||
for (c = 1; c <= n_cols; c++) {
|
||||
a = align_for(sep[c])
|
||||
if (a != "") print "<th style=\"text-align: " a ";\"></th>"
|
||||
else print "<th></th>"
|
||||
}
|
||||
print "</tr>"
|
||||
print "</thead>"
|
||||
|
||||
j = i + 1
|
||||
print "<tbody>"
|
||||
while (j <= count && is_table_row(lines[j])) {
|
||||
n_body = split_row(lines[j], body)
|
||||
print "<tr>"
|
||||
for (c = 1; c <= n_cols; c++) {
|
||||
cell = (c <= n_body ? render_cell(body[c]) : "")
|
||||
a = align_for(sep[c])
|
||||
if (a != "") print "<td style=\"text-align: " a ";\">" cell "</td>"
|
||||
else print "<td>" cell "</td>"
|
||||
}
|
||||
print "</tr>"
|
||||
j++
|
||||
}
|
||||
print "</tbody>"
|
||||
print "</table>"
|
||||
|
||||
i = j
|
||||
continue
|
||||
}
|
||||
|
||||
print lines[i]
|
||||
i++
|
||||
}
|
||||
}
|
||||
' "$temp_file" > "$temp_file.table.$$" && mv "$temp_file.table.$$" "$temp_file"
|
||||
|
||||
# br tags
|
||||
sed_ere_inplace '
|
||||
# if an empty line, append it to the next line, then check on whether there is two in a row
|
||||
/^$/ {
|
||||
N
|
||||
N
|
||||
/^\n{2}/s/(.*)/\n<br \/>\1/
|
||||
}
|
||||
' "$temp_file"
|
||||
|
||||
# emphasis and strong emphasis and strikethrough
|
||||
sed_ere_inplace_n '
|
||||
# batch up the entire stream of text until a line break in the action
|
||||
/^$/b emphasis
|
||||
|
||||
H
|
||||
$ b emphasis
|
||||
b
|
||||
|
||||
:emphasis
|
||||
x
|
||||
s/\*\*(.+)\*\*/<strong>\1<\/strong>/g
|
||||
s/__([^_]+)__/<strong>\1<\/strong>/g
|
||||
s/\*([^\*]+)\*/<em>\1<\/em>/g
|
||||
s/([^\\])_([^_]+)_/\1<em>\2<\/em>/g
|
||||
s/\~\~(.+)\~\~/<strike>\1<\/strike>/g
|
||||
p
|
||||
' "$temp_file"
|
||||
|
||||
sed_inplace '1 d' "$temp_file" # cleanup superfluous first line
|
||||
|
||||
# paragraphs
|
||||
sed_ere_inplace_n '
|
||||
# if an empty line, check the paragraph
|
||||
/^$/ b para
|
||||
# else append it to the hold buffer
|
||||
H
|
||||
# at end of file, check paragraph
|
||||
$ b para
|
||||
# now branch to end of script
|
||||
b
|
||||
# this is where a paragraph is checked for the pattern
|
||||
:para
|
||||
# return the entire paragraph into the pattern space
|
||||
x
|
||||
# look for non block-level elements, if there - print the p tags
|
||||
/\n<(div|table|pre|p|[ou]l|h[1-6]|[bh]r|blockquote|li)/!{
|
||||
s/(\n+)(.*)/\1<p>\n\2\n<\/p>/
|
||||
p
|
||||
b
|
||||
}
|
||||
p
|
||||
' "$temp_file"
|
||||
|
||||
sed_inplace '1 d' "$temp_file" # cleanup superfluous first line
|
||||
|
||||
# cleanup area where P tags have broken nesting
|
||||
sed_ere_inplace_n '
|
||||
# if the line looks like like an end tag
|
||||
/^<\/(div|table|pre|p|[ou]l|h[1-6]|[bh]r|blockquote)>/{
|
||||
h
|
||||
# if EOF, print the line
|
||||
$ {
|
||||
x
|
||||
b done
|
||||
}
|
||||
# fetch the next line and check on whether or not it is a P tag
|
||||
n
|
||||
/^<\/p>/{
|
||||
G
|
||||
b done
|
||||
}
|
||||
# else, append the line to the previous line and print them both
|
||||
H
|
||||
x
|
||||
}
|
||||
:done
|
||||
p
|
||||
' "$temp_file"
|
||||
|
||||
# inline styles and special characters
|
||||
sed_ere_inplace '
|
||||
/^<pre><code>/,/^<\/code><\/pre>/b
|
||||
|
||||
s/<(http[s]?:\/\/.*)>/<a href=\"\1\">\1<\/a>/g # automatic links
|
||||
s/<(.*@.*\..*)>/<a href=\"mailto:\1\">\1<\/a>/g # automatic email address links
|
||||
|
||||
# inline code
|
||||
s/([^\\])``+ *([^ ]*) *``+/\1<code>\2<\/code>/g
|
||||
s/([^\\])`([^`]*)`/\1<code>\2<\/code>/g
|
||||
|
||||
# force-inline image syntax (double bang)
|
||||
s/!!\[([^]]*)\]\(([^)]*) \"([^\"]*)\"\)/<img data-force-inline=\"1\" alt=\"\1\" src=\"\2\" title=\"\3\" \/>/g
|
||||
s/!!\[([^]]*)\]\(([^)]*)\)/<img data-force-inline=\"1\" alt=\"\1\" src=\"\2\" \/>/g
|
||||
|
||||
s/(^|[^\\])!\[([^]]*)\]\(([^)]*) \"([^\"]*)\"\)/\1<img alt=\"\2\" src=\"\3\" title=\"\4\" \/>/g # inline image with title
|
||||
s/(^|[^\\])!\[([^]]*)\]\(([^)]*)\)/\1<img alt=\"\2\" src=\"\3\" \/>/g # inline image without title
|
||||
|
||||
s/(^|[^\\!])\[([^]]*)\]\(([^)]*) \"([^\"]*)\"\)/\1<a href=\"\3\" title=\"\4\">\2<\/a>/g # inline link with title
|
||||
s/(^|[^\\!])\[([^]]*)\]\(([^)]*)\)/\1<a href=\"\3\">\2<\/a>/g # inline link
|
||||
|
||||
# MFM font syntax
|
||||
s/\$\[font\.serif ([^]]+)\]/<span style=\"font-family: serif;\">\1<\/span>/g
|
||||
s/\$\[font\.monospace ([^]]+)\]/<span style=\"font-family: monospace;\">\1<\/span>/g
|
||||
s/\$\[font\.sans ([^]]+)\]/<span style=\"font-family: sans-serif;\">\1<\/span>/g
|
||||
|
||||
# special characters
|
||||
/&.+;/!s/&/\&/g # ampersand
|
||||
/<[\/a-zA-Z]/!s/</\</g# less than bracket
|
||||
|
||||
# backslash escapes for literal characters
|
||||
s/\\\*/\*/g # asterisk
|
||||
s/\\_/_/g # underscore
|
||||
s/\\`/`/g # underscore
|
||||
s/\\!/!/g # exclamation
|
||||
s/\\#/#/g # pound or hash
|
||||
s/\\\+/\+/g # plus
|
||||
s/\\\-/\-/g # minus
|
||||
s/\\</\</g # less than bracket
|
||||
s/\\>/\>/g # greater than bracket
|
||||
s/\\\\/\\/g # backslash
|
||||
' "$temp_file"
|
||||
|
||||
# display and cleanup
|
||||
awk -v input_file="$1" -v site_root="$MARKDOWN_SITE_ROOT" -v fallback_file="$MARKDOWN_FALLBACK_FILE" -f "$awk_dir/markdown_embed.awk" "$temp_file"
|
||||
rm "$temp_file"
|
||||
11
site.conf
Normal file
11
site.conf
Normal file
@@ -0,0 +1,11 @@
|
||||
title = "kewt"
|
||||
style = "kewt"
|
||||
dir_indexes = true
|
||||
single_file_index = true
|
||||
flatten = false
|
||||
footer = "made with <a href="https://git.krzak.org/N0VA/kewt">kewt</a>"
|
||||
logo = ""
|
||||
display_logo = false
|
||||
display_title = true
|
||||
logo_as_favicon = true
|
||||
favicon = ""
|
||||
BIN
site/Heaven/catgirl.jpg
Normal file
BIN
site/Heaven/catgirl.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 149 KiB |
4
site/Heaven/index.md
Normal file
4
site/Heaven/index.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Heaven
|
||||
|
||||
| --- | --- |
|
||||
| ```![/styles.css]``` | <img style="vertical-align: top;" src="catgirl.jpg"> |
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,17 @@
|
||||
# Hello!
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus malesuada lacus eu ligula semper pharetra. Cras viverra volutpat massa nec sagittis. Aliquam fringilla quam ut tincidunt ultricies. Aliquam erat volutpat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed id sagittis nisi. Aenean vitae urna justo. Vivamus dictum eros ac mi convallis, blandit hendrerit nunc sagittis. Mauris feugiat neque quis justo molestie, vel pulvinar diam faucibus. Ut tempus magna sit amet ex pharetra mollis. Ut elementum metus metus, id consectetur est tempor eget. Proin sed nibh tincidunt, porttitor elit non, blandit ligula. Ut condimentum accumsan lobortis. Nullam nec tempus leo, sit amet iaculis erat. Donec rutrum, orci in elementum varius, nisl elit rutrum nunc, in lacinia lorem enim non enim.
|
||||
|
||||
In vulputate porta pretium. Nam et facilisis dolor. Etiam augue lectus, blandit in congue non, euismod eget felis. Sed gravida bibendum magna. Mauris eu maximus dolor, ac suscipit erat. Nam rutrum ac orci ut sollicitudin. Vivamus imperdiet convallis sem, eu ultrices eros aliquam vel.
|
||||
|
||||
Sed dictum tortor at interdum dignissim. Nunc hendrerit sollicitudin elementum. Donec dictum pulvinar scelerisque. Cras ac dui at enim commodo pulvinar. Mauris non finibus odio, eu blandit lectus. In hac habitasse platea dictumst. Mauris imperdiet purus nec risus feugiat fringilla. Quisque quis nisl non libero mattis lobortis. Phasellus lobortis volutpat elit, ac tempor risus tempus eu. Nunc quam ipsum, ornare sit amet pellentesque et, tempor quis mi. Maecenas accumsan, est in vestibulum pulvinar, lacus eros pharetra dolor, eget gravida lorem urna id magna. In eget dictum nisl, eu viverra metus. Donec tristique at velit vel tincidunt. Maecenas rhoncus fermentum tellus ut sagittis. Proin sit amet laoreet velit. Proin eget tellus porttitor lorem mattis sollicitudin.
|
||||
|
||||
**Never gonna give you up, never gonna let you down, never gonna run around and desert you.**
|
||||
|
||||
Cras vitae sapien egestas, blandit libero et, volutpat augue. Ut augue quam, sollicitudin quis libero laoreet, bibendum imperdiet massa. Duis sed venenatis risus. Praesent a est mollis, viverra erat quis, faucibus elit. Donec at sagittis est, non posuere nisi. Integer posuere pharetra dui in aliquam. Morbi vehicula eros in hendrerit aliquam. Duis in turpis vel mauris mattis convallis in id tortor. Cras et aliquam augue.
|
||||
|
||||
Cras quis consectetur dolor, a sodales tortor. Vestibulum aliquam lacinia metus, sed viverra erat egestas in. Morbi interdum sapien sed bibendum maximus. Aenean accumsan pharetra libero dapibus aliquam. Etiam sodales purus posuere gravida ullamcorper. Vestibulum tincidunt, nibh a pulvinar aliquet, leo tortor pulvinar diam, ut viverra nunc elit bibendum nulla. Praesent vel pulvinar erat, eu efficitur magna. Mauris at consequat purus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce in dui quis nisi elementum aliquam. Proin eget justo sed est commodo accumsan. Suspendisse a feugiat tellus, eget gravida tellus.
|
||||
Binary file not shown.
26
site/index.md
Normal file
26
site/index.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# _kewt_
|
||||
|
||||
_kewt_ is a minimalist ssg inspired by _[werc](http://werc.cat-v.org/)_ and _[kew](https://github.com/uint23/kew)_
|
||||
|
||||
It's meant to be a static site generator, like _[kew](https://github.com/uint23/kew)_ but use only default (POSIX) tooling, like _[werc](http://werc.cat-v.org/)_ (and definitely unlike _[kew](https://github.com/uint23/kew)_)
|
||||
|
||||
## Features
|
||||
|
||||
- No dependencies
|
||||
- Supports many embed types
|
||||
- Automatic css variable replacement for older browsers
|
||||
- Automatic inlining and embedding of many filetypes with `\![link]` or `\`
|
||||
- Inline html support
|
||||
- MFM `$font` and `\<plain>` tags
|
||||
- Admonition support (that's what the blocks like the warning block below are called)
|
||||
|
||||
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
||||
|
||||
## Credits
|
||||
|
||||
- Markdown to html conversion based on [markdown.bash](https://github.com/chadbraunduin/markdown.bash) by [chadbraunduin](https://github.com/chadbraunduin)
|
||||
- Default css style and html template based on _[kew](https://github.com/uint23/kew)_ by [uint23](https://github.com/uint23)
|
||||
|
||||
# Warning
|
||||
>![WARNING]
|
||||
>Most of this was coded at night, while sleepy and a bit sick, and after walking for about 4 hours around a forest, so...
|
||||
171
styles/cat-v.css
Normal file
171
styles/cat-v.css
Normal file
@@ -0,0 +1,171 @@
|
||||
:root {
|
||||
--color-white: white;
|
||||
--color-black: black;
|
||||
--color-border: #ddd;
|
||||
--color-brand: rgb(100, 135, 220);
|
||||
--color-accent: #ff6d06;
|
||||
--color-link: rgb(0, 102, 204);
|
||||
--color-table-border: rgba(128, 128, 128, 0.5);
|
||||
--color-row-alt: rgba(128, 128, 128, 0.1);
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 84%;
|
||||
font-family:
|
||||
Helvetica, Verdana, Arial, "Liberation Sans", FreeSans, sans-serif;
|
||||
}
|
||||
|
||||
header {
|
||||
width: 100%;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
article {
|
||||
margin-left: 16em;
|
||||
padding: 0.5ex 0 5vh 1vw;
|
||||
}
|
||||
|
||||
footer {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
color: var(--color-white);
|
||||
background-color: var(--color-brand);
|
||||
}
|
||||
|
||||
footer div {
|
||||
padding: 1em;
|
||||
float: left;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
header nav {
|
||||
overflow: hidden;
|
||||
background-color: var(--color-brand);
|
||||
color: var(--color-white);
|
||||
padding: 0.3em;
|
||||
border-bottom: 2px solid var(--color-black);
|
||||
font-size: 91%;
|
||||
}
|
||||
|
||||
header nav a,
|
||||
header a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
background-color: var(--color-accent);
|
||||
color: var(--color-black);
|
||||
margin: 0;
|
||||
border-bottom: 2px solid var(--color-black);
|
||||
font-weight: normal;
|
||||
padding: 0.25ex;
|
||||
font-size: 233%;
|
||||
}
|
||||
|
||||
header h1 span {
|
||||
margin-left: 1em;
|
||||
font-size: 50%;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
header a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body > nav {
|
||||
float: left;
|
||||
width: 16em;
|
||||
padding: 0;
|
||||
border-right: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
nav li ul {
|
||||
padding-left: 0.6em;
|
||||
}
|
||||
|
||||
body > nav > div {
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
body > nav > div a {
|
||||
color: var(--color-link);
|
||||
display: block;
|
||||
text-transform: capitalize;
|
||||
font-weight: bold;
|
||||
padding: 0.25em 1ex 0.25em 2mm;
|
||||
font-size: 102%;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body > nav > div a:hover {
|
||||
color: var(--color-white);
|
||||
background-color: var(--color-brand);
|
||||
border-left: var(--color-black) solid 0.2em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body > nav > div p {
|
||||
font-weight: bold;
|
||||
margin: 0 0 0.5em 2mm;
|
||||
padding: 1em 0 0 0;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--color-link);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.thisPage {
|
||||
color: var(--color-black);
|
||||
}
|
||||
|
||||
article h1,
|
||||
article h2 {
|
||||
color: var(--color-link);
|
||||
font-weight: bold;
|
||||
margin: 2em 0 0 0;
|
||||
border-bottom: 2px solid var(--color-link);
|
||||
}
|
||||
|
||||
article h3,
|
||||
article h4,
|
||||
article h5 {
|
||||
color: var(--color-link);
|
||||
font-weight: bold;
|
||||
margin: 2em 0 0 0;
|
||||
}
|
||||
|
||||
article pre {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid var(--color-table-border);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
th {
|
||||
color: var(--color-white);
|
||||
background-color: var(--color-brand);
|
||||
}
|
||||
|
||||
tr:nth-child(odd) {
|
||||
background-color: var(--color-row-alt);
|
||||
}
|
||||
90
styles/kew.css
Normal file
90
styles/kew.css
Normal file
@@ -0,0 +1,90 @@
|
||||
:root {
|
||||
--bg: #646c7f;
|
||||
--fg: #fffde0;
|
||||
--fg-link: #fff18f;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
margin: 0;
|
||||
font-size: 35px;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
header a {
|
||||
color: var(--fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#side-bar {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.side-title {
|
||||
font-size: 25px;
|
||||
margin: 20px 0 8px 0;
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
#side-bar ul {
|
||||
margin: 0 0 0 20px;
|
||||
padding: 0px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#side-bar li {
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--fg-link);
|
||||
text-decoration: none;
|
||||
padding: 1px 2px;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background: var(--fg);
|
||||
color: var(--bg);
|
||||
}
|
||||
|
||||
article {
|
||||
margin: 80px 0 0 0;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 30px;
|
||||
font-size: 25px;
|
||||
color: var(--fg);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding-top: 80px;
|
||||
font-style: italic;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
article,
|
||||
footer {
|
||||
margin-left: 240px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
186
styles/kewt.css
Normal file
186
styles/kewt.css
Normal file
@@ -0,0 +1,186 @@
|
||||
:root {
|
||||
--bg: #646c7f;
|
||||
--fg: #fffde0;
|
||||
--fg-link: #fff18f;
|
||||
--code-bg: #32394a;
|
||||
--code-border: #8f95a4;
|
||||
--code-fg: #fffde0;
|
||||
--code-sel: #fff18f;
|
||||
--code-prop: #ffd27f;
|
||||
--code-val: #cde7ff;
|
||||
--code-var: #b9ffbe;
|
||||
--code-com: #d0d0d0;
|
||||
--adm-note-bg: #3f5666;
|
||||
--adm-note-border: #a8d8ff;
|
||||
--adm-tip-bg: #3f664c;
|
||||
--adm-tip-border: #b9ffbe;
|
||||
--adm-important-bg: #5a4a6c;
|
||||
--adm-important-border: #e4c7ff;
|
||||
--adm-warning-bg: #6b5539;
|
||||
--adm-warning-border: #ffe0a8;
|
||||
--adm-caution-bg: #6f3f3f;
|
||||
--adm-caution-border: #ffb4b4;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
font-family: serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
margin: 0;
|
||||
font-size: 35px;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
header a {
|
||||
color: var(--fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#side-bar {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.side-title {
|
||||
font-size: 25px;
|
||||
margin: 20px 0 8px 0;
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
#side-bar ul {
|
||||
margin: 0 0 0 20px;
|
||||
padding: 0px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#side-bar li {
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--fg-link);
|
||||
text-decoration: none;
|
||||
padding: 1px 2px;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background: var(--fg);
|
||||
color: var(--bg);
|
||||
}
|
||||
|
||||
article {
|
||||
margin: 80px 0 0 0;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 30px;
|
||||
font-size: 25px;
|
||||
color: var(--fg);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: var(--code-bg);
|
||||
color: var(--code-fg);
|
||||
border: 1px solid var(--code-border);
|
||||
margin: 20px 0;
|
||||
padding: 12px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.tok-sel {
|
||||
color: var(--code-sel);
|
||||
}
|
||||
|
||||
.tok-prop {
|
||||
color: var(--code-prop);
|
||||
}
|
||||
|
||||
.tok-val {
|
||||
color: var(--code-val);
|
||||
}
|
||||
|
||||
.tok-var {
|
||||
color: var(--code-var);
|
||||
}
|
||||
|
||||
.tok-com {
|
||||
color: var(--code-com);
|
||||
}
|
||||
|
||||
.tok-punc {
|
||||
color: var(--code-fg);
|
||||
}
|
||||
|
||||
.admonition {
|
||||
margin: 18px 0;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--fg);
|
||||
border-left-width: 5px;
|
||||
}
|
||||
|
||||
.admonition-title {
|
||||
margin: 0 0 8px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.admonition p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.admonition-note {
|
||||
background: var(--adm-note-bg);
|
||||
border-color: var(--adm-note-border);
|
||||
}
|
||||
|
||||
.admonition-tip {
|
||||
background: var(--adm-tip-bg);
|
||||
border-color: var(--adm-tip-border);
|
||||
}
|
||||
|
||||
.admonition-important {
|
||||
background: var(--adm-important-bg);
|
||||
border-color: var(--adm-important-border);
|
||||
}
|
||||
|
||||
.admonition-warning {
|
||||
background: var(--adm-warning-bg);
|
||||
border-color: var(--adm-warning-border);
|
||||
}
|
||||
|
||||
.admonition-caution {
|
||||
background: var(--adm-caution-bg);
|
||||
border-color: var(--adm-caution-border);
|
||||
}
|
||||
|
||||
footer {
|
||||
padding-top: 80px;
|
||||
font-style: italic;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
article,
|
||||
footer {
|
||||
margin-left: 240px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
56
styles/werc.css
Normal file
56
styles/werc.css
Normal file
@@ -0,0 +1,56 @@
|
||||
body {
|
||||
font-family: sans;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header {
|
||||
width: 100%;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
article {
|
||||
margin-left: 18em;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
footer {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
header nav {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
nav a,
|
||||
header a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
header h1 span {
|
||||
margin-left: 1em;
|
||||
font-size: 50%;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
body > nav {
|
||||
float: left;
|
||||
width: 16em;
|
||||
padding-right: 1vw;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
nav li ul {
|
||||
padding-left: 0.6em;
|
||||
}
|
||||
|
||||
footer {
|
||||
overflow: hidden;
|
||||
}
|
||||
20
template.html
Normal file
20
template.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>{{TITLE}}</title>
|
||||
|
||||
<link rel="stylesheet" href="{{CSS}}" type="text/css" />
|
||||
{{HEAD_EXTRA}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{HEADER_BRAND}}</h1>
|
||||
</header>
|
||||
|
||||
<nav id="side-bar">{{NAV}}</nav>
|
||||
|
||||
<article>{{CONTENT}}</article>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user