Skip to content

Instantly share code, notes, and snippets.

@wernsey
Last active January 13, 2022 09:02
Show Gist options
  • Save wernsey/eb1525876ca5519822ab48e68207970f to your computer and use it in GitHub Desktop.
Save wernsey/eb1525876ca5519822ab48e68207970f to your computer and use it in GitHub Desktop.
Awk script to generate HTML documentation from markdown text in source code comments.
#! /usr/bin/awk -f
#
# Purpose: Either converts markdown in code comments
# to HTML, or is a poem about Hastur. I forget.
#
# The comments must have the /** */ pattern. Every line in the comment
# must start with a *. Like so:
#
# /**
# * Markdown here...
# */
#
# Configuration options. You can set these in the BEGIN block below, or
# pass them to the script through the `-v` command-line option:
# - `Title="My Document Title"` to set the <title/> in the <head/> section of the HTML
# - `stylesheet = "style.css"` to use a separate file as style sheet.
# - `Theme=n` with n=[1-8] to use one of the predefined color themes.
# Default = Theme 1. Theme 0 disables all stylesheets.
# - `TopLinks=1` to have links to the top of the doc next to headers.
# - `classic_underscore=1` words_with_underscores behave like old markdown where the
# underscores in the word counts as emphasis. The default behaviour is to have
# `words_like_this` not contain any emphasis.
#
# I've tested it with Gawk, Nawk and Mawk.
# Gawk and Nawk worked without issues, but don't use the -W compat
# or -W traditional settings with Gawk.
# Mawk v1.3.4 worked correctly but v1.3.3 choked on it.
#
# Extensions:
# - A link like [link text][heading-name] gets replaced with <a href="#heading-name">link text</a>
# where heading-name corresponds to one of the headings.
# - Insert a Table of Contents by using ![toc]. The Table of Contents is collapsed by default,
# use ![toc+] to insert a ToC that is expanded by default; ![toc-] for a collapsed ToC.
# - Github-style ``` code blocks supported.
# - Github-style ~~strikethrough~~ supported.
# - GitHub-style task lists `- [x]` are supported for documenting bugs and todo lists in code.
# - A couple of ideas from MultiMarkdown:
# - [^footnotes] are supported.
# - *[abbr]: Abbreviations are supported.
# - Space followed by \ before a newline also forces a line break.
# - Default behaviour is to have words_like_this not contain emphasis.
# Limitations:
# - You can't nest <blockquote>s, and they can't contain nested lists
# or pre blocks. You can work around this by using HTML directly.
# - It takes some liberties with how inline (particularly block-level) HTML is processed and not
# all HTML tags supported. HTML forms and <script/> tags are out.
# - Paragraphs in lists differ a bit from other markdowns. Use indented blank lines to get
# to insert <br> tags to emulate paragraphs. Blank lines stop the list by inserting the
# </ul> or </ol> tags.
#
# References:
# - https://tools.ietf.org/html/rfc7764
# - http://daringfireball.net/projects/markdown/syntax
# - https://guides.github.com/features/mastering-markdown/
# - http://fletcher.github.io/MultiMarkdown-4/syntax
# - http://spec.commonmark.org
#
# (c) 2016 Werner Stoop
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved. This file is offered as-is,
# without any warranty.
BEGIN {
# Configuration options
if(Title=="") Title = "Documentation";
if(Theme=="") Theme = 1;
#TopLinks = 1;
#classic_underscore = 1;
if(MaxWidth=="") MaxWidth="1080px";
Mode = "none"; ToC = ""; ToCLevel = 1;
CSS = init_css(Theme);
for(i = 0; i < 128; i++)
_ord[sprintf("%c", i)] = i;
srand();
}
Mode == "none" && /\/\*\*/ {
Mode = "p";
gsub(/\/\*/,"");
}
Mode != "none" && /\*\// {
gsub(/\*\/.*$/,"");
if(match($0, /^[[:space:]]*\*/))
Out = Out filter(substr($0, RSTART+RLENGTH));
if(Mode == "ul" || Mode == "ol") {
while(ListLevel > 1)
Buf = Buf "\n</" Open[ListLevel--] ">";
Out = Out tag(Mode, Buf "\n");
} else {
Buf = trim(scrub(Buf));
if(Buf)
Out = Out tag(Mode, Buf);
}
Mode = "none";
Buf = "";
}
Mode != "none" {
gsub(/\r/, "", $0);
if(match($0,/[[:graph:]]/) && substr($0,RSTART,1)!="*")
next;
gsub(/^[[:space:]]*\*/, "", $0);
}
Mode != "none" && /^[[:space:]]*\[[_ [:alnum:]]+\]:/ {
linkdesc = ""; lastlink = 0;
match($0,/\[.*\]/);
LinkRef = tolower(substr($0, RSTART+1, RLENGTH-2));
st = substr($0, RSTART+RLENGTH+2);
match(st, /[^[:space:]]+/);
url = substr(st, RSTART, RLENGTH);
st = substr(st, RSTART+RLENGTH+1);
if(match(url, /^<.*>/))
url = substr(url, RSTART+1, RLENGTH-2);
if(match(st, /["'(]/)) {
delim = substr(st, RSTART, 1);
edelim = (delim == "(") ? ")" : delim;
if(match(st, delim ".*" edelim))
linkdesc = substr(st, RSTART+1, RLENGTH-2);
}
LinkUrls[LinkRef] = escape(url);
if(!linkdesc) lastlink = 1;
LinkDescs[LinkRef] = escape(linkdesc);
next;
}
Mode != "none" && lastlink && /^[[:space:]]*["'(]/ {
match($0, /["'(]/);
delim = substr($0, RSTART, 1);
edelim = (delim == "(") ? ")" : delim;
st = substr($0, RSTART);
if(match(st, delim ".*" edelim))
LinkDescs[LinkRef] = escape(substr(st,RSTART+1,RLENGTH-2));
lastlink = 0;
next;
}
lastlink { lastlink = 0; }
Mode == "p" && /^[[:space:]]*\[\^[_[:alnum:]]+\]:[[:space:]]*/ {
match($0, /\[\^[[:alnum:]]+\]:/);
name = substr($0, RSTART+2,RLENGTH-4);
def = substr($0, RSTART+RLENGTH+1);
Footnote[tolower(name)] = scrub(def);
next;
}
Mode == "p" && /^[[:space:]]*\*\[[[:alnum:]]+\]:[[:space:]]*/ {
match($0, /\[[[:alnum:]]+\]/);
name = substr($0, RSTART+1,RLENGTH-2);
def = substr($0, RSTART+RLENGTH+2);
Abbrs[toupper(name)] = def;
next;
}
Mode != "none" { Out = Out filter($0); }
END {
print "<!DOCTYPE html>\n<html><head>"
print "<title>" Title "</title>";
if(stylesheet)
print "<link rel=\"stylesheet\" href=\"" stylesheet "\">";
else
print "<style><!--" CSS "\n--></style>";
print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">";
if(ToC && match(Out, /!\[toc[-+]?\]/))
print "<script type=\"text/javascript\"><!--\n" \
"function toggle_toc(n) {\n" \
" var toc=document.getElementById(\"table-of-contents-\" + n);\n" \
" var btn=document.getElementById(\"btn-text\");\n" \
" toc.style.display=(toc.style.display==\"none\")?\"block\":\"none\";\n" \
" btn.textContent=(toc.style.display==\"none\")?\"[+]\":\"[-]\";\n" \
"}\n" \
"//-->\n</script>";
print "</head><body>";
if(Out) {
Out = fix_footnotes(Out);
Out = fix_links(Out);
Out = fix_abbrs(Out);
Out = make_toc(Out);
print trim(Out);
if(footnotes) {
footnotes = fix_links(footnotes);
print "<hr><ol class=\"footnotes\">\n" footnotes "</ol>";
}
}
print "</body></html>"
}
function escape(st) {
gsub(/&/, "\\&amp;", st);
gsub(/</, "\\&lt;", st);
gsub(/>/, "\\&gt;", st);
return st;
}
function strip_tags(st) {
gsub(/<\/?[^>]+>/,"",st);
return st;
}
function trim(st) {
sub(/^[[:space:]]+/, "", st);
sub(/[[:space:]]+$/, "", st);
return st;
}
function filter(st, res,tmp) {
if(Mode == "p") {
if(match(st, /^(( )| *\t)/) || match(st, /^[[:space:]]*```/)) {
preterm = trim(substr(st, RSTART,RLENGTH));
st = substr(st, RSTART+RLENGTH);
if(Buf) res = tag("p", scrub(Buf));
Buf = st;
push("pre");
} else if(!trim(prev) && match(st, /^[[:space:]]*[*-][[:space:]]*[*-][[:space:]]*[*-][-*[:space:]]*$/)) {
if(Buf) res = tag("p", scrub(Buf));
Buf = "";
res = res "<hr>\n";
} else if(match(st, /^[[:space:]]*===+[[:space:]]*$/)) {
Buf = trim(substr(Buf, 1, length(Buf) - length(prev) - 1));
if(Buf) res= tag("p", scrub(Buf));
if(prev) res = res heading(1, scrub(prev));
Buf = "";
} else if(match(st, /^[[:space:]]*---+[[:space:]]*$/)) {
Buf = trim(substr(Buf, 1, length(Buf) - length(prev) - 1));
if(Buf) res = tag("p", scrub(Buf));
if(prev) res = res heading(2, scrub(prev));
Buf = "";
} else if(match(st, /^[[:space:]]*#+/)) {
sub(/#+[[:space:]]*$/, "", st);
match(st, /#+/);
ListLevel = RLENGTH;
tmp = substr(st, RSTART+RLENGTH);
if(Buf) res = tag("p", scrub(Buf));
res = res heading(ListLevel, scrub(trim(tmp)));
Buf = "";
} else if(match(st, /^[[:space:]]*>/)) {
if(Buf) res = tag("p", scrub(Buf));
Buf = scrub(trim(substr(st, RSTART+RLENGTH)));
push("blockquote");
} else if(match(st, /^[[:space:]]*([*+-]|[[:digit:]]+\.)[[:space:]]/)) {
if(Buf) res = tag("p", scrub(Buf));
Buf="";
match(st, /^[[:space:]]*/);
ListLevel = 1;
indent[ListLevel] = RLENGTH;
Open[ListLevel]=match(st, /^[[:space:]]*[*+-][[:space:]]*/)?"ul":"ol";
push(Open[ListLevel]);
res = res filter(st);
} else if(match(st, /^[[:space:]]*$/)) {
if(trim(Buf)) {
res = tag("p", scrub(trim(Buf)));
Buf = "";
}
} else
Buf = Buf st "\n";
} else if(Mode == "blockquote") {
if(match(st, /^[[:space:]]*>[[:space:]]*$/))
Buf = Buf "\n</p><p>";
else if(match(st, /^[[:space:]]*>/))
Buf = Buf "\n" scrub(trim(substr(st, RSTART+RLENGTH)));
else if(match(st, /^[[:space:]]*$/)) {
res = tag("blockquote", tag("p", trim(Buf)));
pop();
res = res filter(st);
} else
Buf = Buf st;
} else if(Mode == "pre") {
if(!preterm && match(st, /^(( )| *\t)/) || preterm && !match(st, /^[[:space:]]*```/))
Buf = Buf ((Buf)?"\n":"") substr(st, RSTART+RLENGTH);
else {
gsub(/\t/," ",Buf);
if(length(trim(Buf)) > 0)
res = tag("pre", tag("code", escape(Buf)));
pop();
if(preterm) sub(/^[[:space:]]*```/,"",st);
res = res filter(st);
}
} else if(Mode == "ul" || Mode == "ol") {
if(ListLevel == 0 || match(st, /^[[:space:]]*$/) && (RLENGTH <= indent[1])) {
while(ListLevel > 1)
Buf = Buf "\n</" Open[ListLevel--] ">";
res = tag(Mode, "\n" Buf "\n");
pop();
} else {
if(match(st, /^[[:space:]]*([*+-]|[[:digit:]]+\.)/)) {
tmp = substr(st, RLENGTH+1);
match(st, /^[[:space:]]*/);
if(RLENGTH > indent[ListLevel]) {
indent[++ListLevel] = RLENGTH;
if(match(st, /^[[:space:]]*[*+-]/))
Open[ListLevel] = "ul";
else
Open[ListLevel] = "ol";
Buf = Buf "\n<" Open[ListLevel] ">";
} else while(RLENGTH < indent[ListLevel])
Buf = Buf "\n</" Open[ListLevel--] ">";
if(match(tmp,/^[[:space:]]*\[[xX[:space:]]?\]/)) {
st = substr(tmp,RLENGTH+1);
tmp = tolower(substr(tmp,RSTART,RLENGTH));
Buf = Buf "<li><input type=\"checkbox\" " (index(tmp,"x")?"checked":"") " disabled>" scrub(st);
} else
Buf = Buf "<li>" scrub(tmp);
} else if(match(st, /^[[:space:]]*$/)){
Buf = Buf "<br>\n";
} else {
sub(/^[[:space:]]+/,"",st);
Buf = Buf "\n" scrub(st);
}
}
}
prev = st;
return res;
}
function scrub(st, mp, ms, me, r, p, tg, a) {
sub(/ $/,"<br>\n",st);
gsub(/( |[[:space:]]+\\)\n/,"<br>\n",st);
while(match(st, /(__?|\*\*?|~~|`+|[&><\\])/)) {
a = substr(st, 1, RSTART-1);
mp = substr(st, RSTART, RLENGTH);
ms = substr(st, RSTART-1,1);
me = substr(st, RSTART+RLENGTH, 1);
p = RSTART+RLENGTH;
if(!classic_underscore && match(mp,/_+/)) {
if(match(ms,/[[:alnum:]]/) && match(me,/[[:alnum:]]/)) {
tg = substr(st, 1, index(st, mp));
r = r tg;
st = substr(st, index(st, mp) + 1);
continue;
}
}
st = substr(st, p);
r = r a;
ms = "";
if(mp == "\\") {
if(match(st, /^!?\[/)) {
r = r "\\" substr(st, RSTART, RLENGTH);
st = substr(st, 2);
} else if(match(st, /^(\*\*|__|~~|`+)/)) {
r = r substr(st, 1, RLENGTH);
st = substr(st, RLENGTH+1);
} else {
r = r substr(st, 1, 1);
st = substr(st, 2);
}
continue;
} else if(mp == "_" || mp == "*") {
if(match(me,/[[:space:]]/)) {
r = r mp;
continue;
}
p = index(st, mp);
while(p && match(substr(st, p-1, 1),/[\\[:space:]]/)) {
ms = ms substr(st, 1, p-1) mp;
st = substr(st, p + length(mp));
p = index(st, mp);
}
if(!p) {
r = r mp ms;
continue;
}
ms = ms substr(st,1,p-1);
r = r itag("em", scrub(ms));
st = substr(st,p+length(mp));
} else if(mp == "__" || mp == "**") {
if(match(me,/[[:space:]]/)) {
r = r mp;
continue;
}
p = index(st, mp);
while(p && match(substr(st, p-1, 1),/[\\[:space:]]/)) {
ms = ms substr(st, 1, p-1) mp;
st = substr(st, p + length(mp));
p = index(st, mp);
}
if(!p) {
r = r mp ms;
continue;
}
ms = ms substr(st,1,p-1);
r = r itag("strong", scrub(ms));
st = substr(st,p+length(mp));
} else if(mp == "~~") {
p = index(st, mp);
if(!p) {
r = r mp;
continue;
}
while(p && substr(st, p-1, 1) == "\\") {
ms = ms substr(st, 1, p-1) mp;
st = substr(st, p + length(mp));
p = index(st, mp);
}
ms = ms substr(st,1,p-1);
r = r itag("del", scrub(ms));
st = substr(st,p+length(mp));
} else if(match(mp, /`+/)) {
p = index(st, mp);
if(!p) {
r = r mp;
continue;
}
ms = substr(st,1,p-1);
r = r itag("code", escape(ms));
st = substr(st,p+length(mp));
} else if(mp == ">") {
r = r "&gt;";
} else if(mp == "<") {
p = index(st, ">");
if(!p) {
r = r "&lt;";
continue;
}
tg = substr(st, 1, p - 1);
if(match(tg,/^[[:alpha:]]+[[:space:]]/)) {
a = substr(tg,RSTART+RLENGTH-1);
tg = substr(tg,1,RLENGTH-1);
} else
a = "";
if(match(tolower(tg), "^/?(a|abbr|div|span|blockquote|pre|img|code|p|em|strong|sup|sub|del|ins|s|u|b|i|br|hr|ul|ol|li|table|thead|tfoot|tbody|tr|th|td|caption|column|col|colgroup|figure|figcaption|dl|dd|dt|mark|cite|q|var|samp|small)$")) {
r = r "<" tg a ">";
} else if(match(tg, "^[[:alpha:]]+://[[:graph:]]+$")) {
if(!a) a = tg;
r = r "<a href=\"" tg "\">" a "</a>";
} else if(match(tg, "^[[:graph:]]+@[[:graph:]]+$")) {
if(!a) a = tg;
r = r "<a href=\"" obfuscate("mailto:" tg) "\">" obfuscate(a) "</a>";
} else {
r = r "&lt;";
continue;
}
st = substr(st, p + 1);
} else if(mp == "&") {
if(match(st, /^[#[:alnum:]]+;/)) {
r = r "&" substr(st, 1, RLENGTH);
st = substr(st, RLENGTH+1);
} else {
r = r "&amp;";
}
}
}
return r st;
}
function push(newmode) {Stack[StackTop++] = Mode; Mode = newmode;}
function pop() {Mode = Stack[--StackTop];Buf = ""; return Mode;}
function heading(level, st, res, href) {
st = trim(st);
if(level > 6) level = 6;
href = tolower(st);
href = strip_tags(href);
gsub(/[^ [:alnum:]]+/, "", href);
gsub(/ +/, "-", href);
LinkUrls[href] = "#" href;
LinkUrls[tolower(st)] = "#" href;
res = tag("h" level, st (TopLinks?"&nbsp;&nbsp;<a class=\"top\" title=\"Return to top\" href=\"#\">&#8593;&nbsp;Top</a>":""), "id=\"" href "\"");
for(;ToCLevel < level; ToCLevel++)
ToC = ToC "<ul class=\"toc-" level "\">";
for(;ToCLevel > level; ToCLevel--)
ToC = ToC "</ul>";
ToC = ToC "<li class=\"toc-" level "\"><a class=\"toc-" level "\" href=\"#" href "\">" st "</a>\n";
ToCLevel = level;
return res;
}
function make_toc(st, r,p,dis,t,n) {
if(!ToC) return st;
for(;ToCLevel > 1;ToCLevel--)
ToC = ToC "</ul>";
p = match(st, /!\[toc[-+]?\]/);
while(p) {
++n;
dis = index(substr(st,RSTART,RLENGTH),"+");
t = "<div>\n<small><a id=\"toc-button\" onclick=\"toggle_toc(" n ")\"><span id=\"btn-text\">" (dis?"[-]":"[+]") "</span>&nbsp;Contents</a></small>\n" \
"<div id=\"table-of-contents-" n "\" style=\"display:" (dis?"block":"none") ";\">\n<ul class=\"toc-1\">" ToC "</ul>\n</div>\n</div>";
r = r substr(st,1,RSTART-1);
if(substr(st,RSTART-1,1) != "\\")
r = r t;
else
r = substr(r,1,length(r)-1) substr(st,RSTART,RLENGTH);
st = substr(st,RSTART+RLENGTH);
p = match(st, /!\[toc[-+]?\]/);
}
return r st;
}
function fix_links(st, lt,ld,lr,url,img,res,rx,pos) {
do {
pos = match(st, /\[[^\]]+\]/);
if(!pos)break;
img=substr(st,RSTART-1,1)=="!";
if(substr(st, RSTART-(img?2:1),1)=="\\") {
res = res substr(st,1,RSTART-(img?3:2));
if(img && substr(st,RSTART,RLENGTH)=="[toc]")res=res "\\";
res = res substr(st,RSTART-(img?1:0),RLENGTH+(img?1:0));
st = substr(st, RSTART + RLENGTH);
continue;
}
res = res substr(st, 1, RSTART-(img?2:1));
rx = substr(st, RSTART, RLENGTH);
st = substr(st, RSTART+RLENGTH);
if(match(st, /^[[:space:]]*\([^)]+\)/)) {
lt = substr(rx, 2, length(rx) - 2);
match(st, /\([^)]+\)/);
url = substr(st, RSTART+1, RLENGTH-2);
st = substr(st, RSTART+RLENGTH);
ld = "";
if(match(url,/[[:space:]]+["']/)) {
ld = url;
url = substr(url, 1, RSTART - 1);
match(ld,/["']/);
delim = substr(ld, RSTART, 1);
if(match(ld,delim ".*" delim))
ld = substr(ld, RSTART+1, RLENGTH-2);
} else ld = "";
if(img)
res = res "<img src=\"" url "\" title=\"" ld "\" alt=\"" lt "\">";
else
res = res "<a href=\"" url "\" title=\"" ld "\">" lt "</a>";
} else if(match(st, /^[[:space:]]*\[[^\]]*\]/)) {
lt = substr(rx, 2, length(rx) - 2);
match(st, /\[[^\]]*\]/);
lr = trim(tolower(substr(st, RSTART+1, RLENGTH-2)));
if(!lr) {
lr = tolower(trim(lt));
if(LinkDescs[lr]) lt = LinkDescs[lr];
}
st = substr(st, RSTART+RLENGTH);
url = LinkUrls[lr];
ld = LinkDescs[lr];
if(img)
res = res "<img src=\"" url "\" title=\"" ld "\" alt=\"" lt "\">";
else
res = res "<a href=\"" url "\" title=\"" ld "\">" lt "</a>";
} else
res = res (img?"!":"") rx;
} while(pos > 0);
return res st;
}
function fix_footnotes(st, r,p,n,i,d,fn,fc) {
p = match(st, /\[\^[^\]]+\]/);
while(p) {
r = r substr(st,1,RSTART-1);
d = substr(st,RSTART+2,RLENGTH-3);
n = tolower(d);
st = substr(st,RSTART+RLENGTH);
if(Footnote[tolower(n)]) {
if(!fn[n]) fn[n] = ++fc;
d = Footnote[n];
} else {
Footnote[n] = scrub(d);
if(!fn[n]) fn[n] = ++fc;
}
footname[fc] = n;
d = strip_tags(d);
if(length(d) > 20) d = substr(d,1,20) "&hellip;";
r = r "<sup title=\"" d "\"><a href=\"#footnote-" fn[n] "\" id=\"footnote-pos-" fn[n] "\" class=\"footnote\">[" fn[n] "]</a></sup>";
p = match(st, /\[\^[^\]]+\]/);
}
for(i=1;i<=fc;i++)
footnotes = footnotes "<li id=\"footnote-" i "\">" Footnote[footname[i]] \
"<a title=\"Return to Document\" class=\"footnote-back\" href=\"#footnote-pos-" i \
"\">&nbsp;&nbsp;&#8630;&nbsp;Back</a></li>\n";
return r st;
}
function fix_abbrs(str, st,k,r,p) {
for(k in Abbrs) {
r = "";
st = str;
t = escape(Abbrs[toupper(k)]);
gsub(/&/,"\\&", t);
p = match(st,"[^[:alnum:]]" k "[^[:alnum:]]");
while(p) {
r = r substr(st, 1, RSTART);
r = r "<abbr title=\"" t "\">" k "</abbr>";
st = substr(st, RSTART+RLENGTH-1);
p = match(st,"[^[:alnum:]]" k "[^[:alnum:]]");
}
str = r st;
}
return str;
}
function tag(t, body, attr) {
if(attr)
attr = " " trim(attr);
# https://www.w3.org/TR/html5/grouping-content.html#the-p-element
if(t == "p" && (match(body, /<\/?(div|table|blockquote|dl|ol|ul|h[[:digit:]]|hr|pre)[>[:space:]]/))|| (match(body,/!\[toc\]/) && substr(body, RSTART-1,1) != "\\"))
return "<" t attr ">" body "\n";
else
return "<" t attr ">" body "</" t ">\n";
}
function itag(t, body) {
return "<" t ">" body "</" t ">";
}
function obfuscate(e, r,i,t,o) {
for(i = 1; i <= length(e); i++) {
t = substr(e,i,1);
r = int(rand() * 100);
if(r > 50)
o = o sprintf("&#x%02X;", _ord[t]);
else if(r > 10)
o = o sprintf("&#%d;", _ord[t]);
else
o = o t;
}
return o;
}
function bright(c,a ,r,g,b) {
sub(/^#/,"",c);
r = 255*a + _hex[toupper(substr(c,1,2))]*(1-a);
g = 255*a + _hex[toupper(substr(c,3,2))]*(1-a);
b = 255*a + _hex[toupper(substr(c,5,2))]*(1-a);
return sprintf("#%02X%02X%02X",r>255?255:r,g>255?255:g,b>255?255:b);
}
function init_css(Theme, css,ss,hr,c1,c2,c3,c4,c5,bg1,bg2,bg3,bg4,ff,i) {
if(Theme == "0") return "";
css["body"] = "color:%color1%;font-family:%font-family%;line-height:1.5em;" \
"padding:1em 2em;width:80%;max-width:%maxwidth%;margin:0 auto;min-height:100%;float:none;";
css["h1"] = "color:%color1%;border-bottom:1px solid %background1%;padding:0.3em 0.1em;";
css["h2"] = "color:%color2%;border-bottom:1px solid %background2%;padding:0.2em 0.1em;";
css["h3"] = "color:%color3%;border-bottom:1px solid %background3%;padding:0.1em 0.1em;";
css["h4,h5,h6"] = "color:%color4%;padding:0.1em 0.1em;";
css["h1,h2,h3,h4,h5,h6"] = "font-weight:normal;line-height:1.2em;";
css["h4"] = "border-bottom:1px solid %background4%";
css["p"] = "margin:0.5em 0.1em;"
css["hr"] = "background:%hr%;height:1px;border:0;"
css["a"] = "color:%color2%;";
css["a:visited"] = "color:%color2%;";
css["a:active"] = "color:%color4%;";
css["a:hover"] = "color:%color4%;";
css["a.top"] = "font-size:x-small;text-decoration:initial;float:right;";
css["strong,b"] = "color:%color1%";
css["code"] = "color:%color2%;";
css["blockquote"] = "margin-left:1em;color:%color2%;background:%color5%;border-left:0.2em solid %color3%;border-radius:3px;padding:0.25em 0.5em;";
css["pre"] = "color:%color2%;background:%color5%;border-radius:5px;line-height:1.25em;margin:0.25em 0.5em;padding:0.75em;";
css["table"] = "border-collapse:collapse;margin:0.5em;";
css["th,td"] = "padding:0.5em 0.75em;border:1px solid %color4%;";
css["th"] = "color:%color2%;border:1px solid %color3%;border-bottom:2px solid %color3%;";
css["tr:nth-child(odd)"] = "background-color:%color5%;";
css["div"] = "padding:0.5em;";
css["caption"] = "padding:0.5em;font-style:italic;";
css["dl"] = "margin:0.5em;";
css["dt"] = "font-weight:bold;";
css["dd"] = "padding:0.3em;";
css["mark"] = "color:%color2%;background-color:%color5%;";
css["del,s"] = "color:%color4%;";
css["a#toc-button"] = "color:%color3%;background:%color5%;cursor:pointer;font-size:small;padding: 0.3em 0.5em 0.5em 0.5em;font-family:monospace;border-radius:3px;";
css["a#toc-button:hover"] = "color:%color5%;background:%color3%;";
css["div#table-of-contents"] = "padding:0;font-size:smaller;";
css["abbr"] = "cursor:help;";
css["ol.footnotes"] = "font-size:small;color:%color4%";
css["a.footnote"] = "font-size:smaller;text-decoration:initial;";
css["a.footnote-back"] = "text-decoration:initial;font-size:x-small;";
css[".fade"] = "color:%color5%;";
css[".highlight"] = "color:%color2%;background-color:%color5%;";
if(Theme==2){
c1="#303F9F";c2="#0449CC";c3="#2162FA";c4="#4B80FB";c5="#EDF2FF";
ff="\"Trebuchet MS\", Helvetica, sans-serif";
} else if(Theme==3){
c1="#430005";c2="#740009";c3="#A6373F";c4="#c55158";c5="#fbf2f2";
ff="Verdana, Geneva, sans-serif";
} else if(Theme==4){
c1="#083900";c2="#0D6300";c3="#3C8D2F";c4="#50be3f";c5="#f2faf1";
ff="Georgia, serif";
} else if(Theme==5){
c1="#453700";c2="#775F00";c3="#AA9339";c4="#c7b057";c5="#fbf9f2";
ff="Tahoma, Geneva, sans-serif";
} else if(Theme==6){
c1="#315067";c2="#4F9E9C";c3="#77AD93";c4="#95CE94";c5="#F6FFEE";
ff="Verdana, sans-serif;";
} else if(Theme==7){
c1="#35305D";c2="#646379";c3="#7A74A5";c4="#646392";c5="#fafafa";
} else if(Theme==8){
c1="#215FC2";c2="#D49095";c3="#AD90D4";c4="#90D4CF";c5="#F7FDEF";
} else {
c1="#092859";c2="#1351b5";c3="#d47034";c4="#DC7435";c5="#F6F8FF";
}
if(!ff) ff = "sans-serif"
for(i = 0; i<=255; i++)_hex[sprintf("%02X",i)]=i;
bg1 = bright(c1,0.5);
bg2 = bright(c2,0.5);
bg3 = bright(c3,0.5);
bg4 = bright(c3,0.75);
hr = bright(c1,0.75);
for(k in css)
ss = ss "\n" k "{" css[k] "}";
gsub(/%maxwidth%/,MaxWidth,ss);
gsub(/%color1%/,c1,ss);
gsub(/%color2%/,c2,ss);
gsub(/%color3%/,c3,ss);
gsub(/%color4%/,c4,ss);
gsub(/%color5%/,c5,ss);
gsub(/%background1%/,bg1,ss);
gsub(/%background2%/,bg2,ss);
gsub(/%background3%/,bg3,ss);
gsub(/%background4%/,bg4,ss);
gsub(/%font-family%/,ff,ss);
gsub(/%hr%/,hr,ss);
return ss;
}
/**
* `comdown.awk` Demonstration
* ===========================
* ![toc]
*
* This file demonstrates how to comment your code with
* the `comdown.awk` script. It also serves as a functional test.
*
* Comments in your code that are to be included in the output must start with
* the sequence `/**`.
* Other comments and source code are ignored by the script.
*
Lines that don't start with *s, like this one, are ignored.
*
* Blank lines in the comment separate paragraphs.
*
* Two spaces at the end of the line
* forces a linebreak. Here is another line<br>break.
*
* Headings can be written like so:
* Heading 1
* =========
* Heading 2
* ---------
*
* Alternatively:
* ```
* # Heading 1
* ## Heading 2
* ### Heading 3
* ```
* # Heading 1
* ## Heading 2
* ### Heading 3
* #### Heading 4
* ##### Heading 5
* ###### Heading 6
* \# symbols after the heading are cleared, so you can also write `## Heading 2 ##`
* if you prefer. There is a '\\#' escape sequence.
*
* Escaping and not escaping ampersands: & &amp; &sect; &#167; &#xA7; and angle brackets: < > &lt; &gt; `&lt;`.
* This is a number of operators that broke escaping "=", "<>" (alternatively "!="), "<", "<=", ">" and ">=".
*
* Horizontal rules:
*
* -----
*
* * * * * * * *
*
* Block Level Formatting
* ----------------------
* These options are available at the block level.
* ### Lists
* #### Ordered List ####
* 1. Item 1
* 1. Item 1.1
* 1981\. This line started with a number.
* 1. Item 1.2;
* This item consists of multiple lines
* with a forced line break (two spaces at the end of the last line).
* 1. Item 2
* * Item 2.1; List styles can be mixed.
* * Item 2.2
*
* The blank line above contains whitespace, hence a new list is not started
* (Paragraphs in lists differ a bit from other markdowns).
* * Item 2.3
* 1. Item 3
* 1. Item 3.1
* 1. Item 3.1.1
* 1. Item 3.2
*
* #### Unordered List
* - Item 1
* + Item 1.1
* + Item 1.2
* - Item 2
* 1. Item 2.1; Again, list styles can be mixed.
* 1. Item 2.2
* - Item 3
* * Item 3.1
* * Item 3.2
*
* GitHub-style task lists are also supported:
* - [ ] Task 1
* - [] Subtask 1a
* - [X] Subtask 1b
* - [x] Task 2 - **completed.**
* - [] Task 3
* 1. [ ] Subtask 3a
* 1. [x] Subtask 3b
* - [x] ~~Task 4~~ - also completed
*
* ### Block Quotes
* > This is a blockquote. It
* may span multiple lines.
* >
* > Blank lines like the one above separates paragraphs within the quote.
* >
* > Unfortunately it can't contain nested quotes lists
* > and code in this implementation.
*
* The empty line above ends the quote.
*
* ### Code Blocks
* Code indented with tabs:
* //Some code, indented with a single tab:
* int main(int argc, char *argv[]) {
* return 0;
* }
*
* Code indented with spaces:
* //Some code, indented with spaces
*
* int main(int argc, char *argv[]) {
* return 0;
* }
* This particular implementation doesn't care about blank lines
* after the code block.
*
* GitHub-style code blocks:
* ```
* //Some code, wrapped in ```
*
* int main(int argc, char *argv[]) {
* return 0;
* }
* ```
* Unfortunately, if you use C/C++ you have to escape your asterisks
* like **int foo(int \*x, int \*y)** or this **int main(int argc, char \*argv[])**
* unless you use backticks: `int main(int argc, char *argv[])`
*
* Regression test: The #es in this sample would've caused problems:
*
* # Compile like so:
* mvn package
*
* # Generate Javadocs
* mvn javadoc:javadoc
*
* This is a diagram:
*
* +-------+ +--------+
* | | | |
* | Foo +-----> Bar |
* | Block | | Block |
* | | | |
* +---^---+ +----+---+
* | |
* | |
* | +----v---+
* | | |
* | | |
* +---------+ |
* | |
* +--------+
*
* Hyperlinks
* ----------
* * Example hyperlink 1: [This link](http://example.com) is inline
* * Example hyperlink 1B: [This link][link1] is not inline; escape charaters in the url.
* * Example hyperlink 2: [This link] [link2] and [this one][LINK2] (case insensitive) has a title attribute.
* You can also do [link2][].
* * Example hyperlink 3: [This link <&>][link3] has a title attribute on separate line and
* escaped characters in the link text.
* * Example hyperlink 4: [funny example][funny] in `<angle brackets>`
* * Example hyperlink 5: [example 5](http://example.com?x=5&y=10 "Example Title <&>") with inline title attribute and
* escaped characters in the link description.
* * Links can be placed inline: <http://www.example.com>.
* * e-mail addresses get obfuscated: <address@example.com>
* * Relative links that refer to specific headings are supported.
* For example [Block Level Formatting][block-level-formatting] - replace spaces with -, remove all
* other non-alphanumerics and everything lowercase.
* Alternatively [Block Level Formatting][Block Level Formatting] or [ Using HTML in Documents ][]
* * [This link](http://example.com/some_random_page) has \_underscores\_ where
* the usual rules about escaping apply, but [this one][underscores] doesn't.
*
* Not links: [foo] and this one \[foo](www.example.com).
*
* [link1]: http://example.com?x=5&y=10
* [link2]: http://example.com/2 (Second Example; Escaped characters: < & >)
* [link3]: http://example.com/3
* (Third Example <&>)
* [funny]: <http://example.com/funny> (Link in angle brackets)
* [underscores]: http://example.com/some_random_page
*
* Images
* ------
*
* Image syntax `\![Image Alt Text](example.png)`
* Escaping images `\![Image Alt Text](example.png)` and links `\[Link Alt Text](example.com)`
*
* Images can be encoded as Data URIs: ![Red Dot][reddot]
* <sub>The red dot comes from [Wikipedia][datauri]</sub>
*
* [dataURI]: https://en.wikipedia.org/wiki/Data_URI_scheme "Data URI scheme"
* [reddot]: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
*
* Line-Level Formatting
* ----------------------
* * \_Emphasized\_ produces _Emphasized_
* * A word *containing_nested_underscores* can be treated in one of two ways depending on
* whether the variable `classic_underscore` is in the script.
* * A word containing*nested*asterisks will be treated as emphasis.
* * \*Emphasized\* produces *Emphasized*
* * \__Strongly Emphasized\__ produces __Strongly Emphasized__
* * \**Strongly Emphasized\** produces **Strongly Emphasized**
* * \`Code Block\` produces `Code Block`
* * \`\`Code Block\`\` produces ``Code Block``
* * \`\`Code Block with embedded backtick: \` \`\` produces ``Code Block with embedded backtick: ` ``
* * \~~Strike through\~~ produces ~~Strike through~~
* * _You **can** mix styles within `other` styles_
* * But the backtick code blocks cause asterisks and underscores to be ignored:
* `void do_something(Widget *foo, int p, int q, Wotzit *bar, int zoop)`
* * Whitespace surrounding the * or _ will cause it to be treated as literal:
* * _ this text would not be emphasized _ and neither would this * mmmm *
* * ** this text would not be emphasized ** and neither would this __ mmmm __
*
* Extensions
* ----------
* The special tag `\![toc]` can be used to insert a table of contents in the document.
* Leave a blank line below it to ensure the paragraphs are formatted correctly.
*
* [MultiMarkdown][]-style footnotes are supported:
* - Here is a reference to the footnote[^footnote][^<http://example.com>].
* - Here is an inline footnote[^An _inline_ footnote. **Text** _formatting_ ``works``
* here as well; Some characters to escape: < & > and <http://example.com>].
* - Here is a reference to the third footnote[^footnote3].
*
* MultiMarkdown's syntax for abbreviations is also supported.
* For example HTML, XML and GUI.
* ```
* *[HTML]: Hypertext Markup Language
* *[XML]: eXstensible Markup Language; Escaped characters: < > &
* *[GUI]: Graphical User Interface
* ```
* But only whole words: HTML5 and SXML doesn't get the `<abbr/>` tag.
*
* [^footnote]: This is a footnote; **Text** _formatting_ ``works``. Some characters to escape: < & >
* [^footnote3]: This is footnote number 3. This one contains a [hyperlink][link1]
* [MultiMarkdown]: http://fletcher.github.io/MultiMarkdown-4/syntax
* *[HTML]: Hypertext Markup Language
* *[XML]: eXstensible Markup Language; Escaped characters: < > &
* *[GUI]: Graphical User Interface
*
* Another idea borrowed from MultiMarkdown is to have a \
* space followed by a \ at the end of a line force a line break. \
* This is useful because I have the habit of trimming trailing spaces.
*
* Using HTML in Documents
* -----------------------
* ### A HTML Table
* <table>
* <tr><th>Column A</th><th>Column B</th><th>Column C</th></tr>
* <tr><td>Item 1a</td><td>Item 1b</td><td>Item 1c</td></tr>
* <tr><td>Item 2a</td><td>Item 2b</td><td>Item 2c</td></tr>
* <tr><td>Item 3a</td><td>Item 3b</td><td>Item 3c</td></tr>
* <tr><td>Item 4a</td><td>Item 4b</td><td>Item 4c</td></tr>
* <tr><td>Item 5a</td><td>Item 5b</td><td>Item 5c</td></tr>
* <tr><td>Item 6a</td><td>Item 6b</td><td>Item 6c</td></tr>
* </table>
*
* ### A Definitions List
* <dl>
* <dt>Item 1</dt>
* <dd>A description of item 1.</dd>
* <dt>Item 2</dt>
* <dd>A description of item 2.</dd>
* <dt>Item 3</dt>
* <dd>A description of item 3.</dd>
* </dl>
*
* ### Some Other Tags
* Some <b>bold text</b>, some <i>italic text</i>, a <q>quote</q>,
* a <var class="xxx">variable</var>, <ins>inserted text</ins>, <del>deleted text</del>,
* a <mark>marked block</mark>, a <cite>[citation]</cite>.
*
* A `<div/>` element:
* <div class="highlight">A `<div/>` element</div>
*
* A `<span/>` element:
* <span class="highlight">A `<span/>` element</span>
*
* Not all HTML tags are supported. For example:
* <script type="text/javascript">$(function(){alert("GOTCHA!");});</script>
*
* This is how you work around the limitations of block quotes:
* <blockquote>
* Block quote.
* <blockquote>
* Block quote within a block quote.
* </blockquote>
* <pre><code>
* &lt;pre/&gt; block within the blockquote.
* </code></pre>
* </blockquote>
*
* Lorem Ipsum
* -----------
*
* Some Lorem Ipsum from [lipsum.com](http://www.lipsum.com/) to see how the styles work
* with large paragraphs:
*
* Lorem **ipsum** dolor sit amet, consectetur **adipiscing elit**. In interdum ut nulla suscipit
* tincidunt. Mauris sollicitudin consectetur elit sit amet iaculis. Aliquam urna neque,
* pretium quis eros non, pellentesque tempus augue. Vestibulum ornare, lacus non sagittis
* elementum, est ante placerat dolor, vitae tincidunt orci felis id felis. Etiam id nisl
* sed turpis pulvinar condimentum. Etiam neque tortor, sollicitudin id metus sed, mollis
* maximus enim. Sed risus ante, suscipit quis ex vitae, consectetur ultricies diam. Nulla
* sollicitudin quis purus ornare tempor. Sed rhoncus sapien volutpat neque pretium, nec
* dapibus nisl iaculis. Praesent ultrices risus eget purus semper pulvinar sed ut ligula.
* Nunc ac nisl neque.
*
* Sed enim enim, fermentum at lectus eu, tincidunt sollicitudin mi. Praesent vel auctor
* elit. Etiam ac vulputate nisl. Etiam egestas urna quis velit varius convallis. Vestibulum
* sed porta mi. Vestibulum ac dolor eu purus mattis bibendum congue sed nunc. Curabitur sed
* venenatis neque. Curabitur et eros ac leo ultrices ultricies vitae ut justo.
*
* Vestibulum viverra venenatis quam, quis faucibus magna commodo hendrerit. Sed at dui et orci
* mattis accumsan. Integer vulputate blandit volutpat. Mauris non sem a velit posuere fringilla.
* Phasellus id arcu euismod, blandit lectus a, tempus justo. Aenean efficitur, velit nec aliquet
* rhoncus, nisi lectus efficitur diam, non dignissim est metus et sem. Sed ornare lacus eget
* convallis semper. Fusce malesuada nunc et mauris facilisis consectetur. Pellentesque
* consectetur suscipit mauris, eu lobortis nisi consequat nec. Sed sagittis ac ligula sit amet
* scelerisque. Curabitur ipsum risus, imperdiet ut pellentesque eu, hendrerit sed erat.
*
* Praesent auctor, lacus quis condimentum interdum, leo orci elementum tellus, nec eleifend
* mauris tellus non ipsum. Aenean sit amet congue ante. Morbi ultricies pharetra tortor, a
* elementum purus congue laoreet. Fusce varius semper enim, non pretium urna ultricies et.
* Aliquam laoreet urna non tristique suscipit. Donec sollicitudin sit amet erat id cursus.
* Aliquam nisl nisi, maximus et molestie id, viverra tempor neque. Duis et interdum nisi.
* Nullam vulputate sed risus et finibus. Etiam eu leo et mi elementum laoreet vestibulum ut
* ipsum. Pellentesque bibendum dictum est, sit amet placerat diam aliquet eu. Phasellus
* dignissim tristique lacus a semper. Phasellus nec sollicitudin lectus.
*
* Duis non lectus purus. Sed ornare nulla felis, id suscipit mi suscipit vel. Duis nec ipsum
* a arcu posuere vulputate et ut ante. Vivamus vitae erat et tortor varius consequat at sit amet
* nulla. In rutrum, lacus et posuere auctor, diam sapien varius diam, id **vehicula enim urna vel
* massa**. Aliquam iaculis volutpat nisi, a ultricies eros tristique eu. Suspendisse ac mattis lectus.
* Nunc facilisis massa non maximus cursus. Etiam consequat, magna nec sollicitudin luctus, nisi leo
* tincidunt ipsum, vitae suscipit arcu arcu id velit. Mauris auctor faucibus scelerisque.
*/
/* This comment is not processed because it doesn't start with the ** */
/** Comments on lines by themselves are treated as separate paragraphs. */
//Some text that should not be converted.
int main(int argc, char *argv[]) {
/** You can put comments anywhere in your code. */
printf("hello, world");
return 0;
}
#! /usr/bin/awk -f
#
# Markdown processor in AWK. It is simplified from comdown.awk
#
# (c) 2016 Werner Stoop
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved. This file is offered as-is,
# without any warranty.
BEGIN {
# Configuration options
if(Title=="") Title = "Documentation";
if(Theme=="") Theme = 1;
#TopLinks = 1;
#classic_underscore = 1;
if(MaxWidth=="") MaxWidth="1080px";
Mode = "p"; ToC = ""; ToCLevel = 1;
CSS = init_css(Theme);
for(i = 0; i < 128; i++)
_ord[sprintf("%c", i)] = i;
srand();
}
/^[[:space:]]*\[[_ [:alnum:]]+\]:/ {
linkdesc = ""; lastlink = 0;
match($0,/\[.*\]/);
LinkRef = tolower(substr($0, RSTART+1, RLENGTH-2));
st = substr($0, RSTART+RLENGTH+2);
match(st, /[^[:space:]]+/);
url = substr(st, RSTART, RLENGTH);
st = substr(st, RSTART+RLENGTH+1);
if(match(url, /^<.*>/))
url = substr(url, RSTART+1, RLENGTH-2);
if(match(st, /["'(]/)) {
delim = substr(st, RSTART, 1);
edelim = (delim == "(") ? ")" : delim;
if(match(st, delim ".*" edelim))
linkdesc = substr(st, RSTART+1, RLENGTH-2);
}
LinkUrls[LinkRef] = escape(url);
if(!linkdesc) lastlink = 1;
LinkDescs[LinkRef] = escape(linkdesc);
next;
}
lastlink && /^[[:space:]]*["'(]/ {
match($0, /["'(]/);
delim = substr($0, RSTART, 1);
edelim = (delim == "(") ? ")" : delim;
st = substr($0, RSTART);
if(match(st, delim ".*" edelim))
LinkDescs[LinkRef] = escape(substr(st,RSTART+1,RLENGTH-2));
lastlink = 0;
next;
}
lastlink { lastlink = 0; }
Mode == "p" && /^[[:space:]]*\[\^[_[:alnum:]]+\]:[[:space:]]*/ {
match($0, /\[\^[[:alnum:]]+\]:/);
name = substr($0, RSTART+2,RLENGTH-4);
def = substr($0, RSTART+RLENGTH+1);
Footnote[tolower(name)] = scrub(def);
next;
}
Mode == "p" && /^[[:space:]]*\*\[[[:alnum:]]+\]:[[:space:]]*/ {
match($0, /\[[[:alnum:]]+\]/);
name = substr($0, RSTART+1,RLENGTH-2);
def = substr($0, RSTART+RLENGTH+2);
Abbrs[toupper(name)] = def;
next;
}
{ Out = Out filter($0); }
END {
if(match($0, /^[[:space:]]*\*/))
Out = Out filter(substr($0, RSTART+RLENGTH));
if(Mode == "ul" || Mode == "ol") {
while(ListLevel > 1)
Buf = Buf "\n</" Open[ListLevel--] ">";
Out = Out tag(Mode, Buf "\n");
} else {
Buf = trim(scrub(Buf));
if(Buf)
Out = Out tag(Mode, Buf);
}
Mode = "none";
Buf = "";
print "<!DOCTYPE html>\n<html><head>"
print "<title>" Title "</title>";
if(stylesheet)
print "<link rel=\"stylesheet\" href=\"" stylesheet "\">";
else
print "<style><!--" CSS "\n--></style>";
print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">";
if(ToC && match(Out, /!\[toc[-+]?\]/))
print "<script type=\"text/javascript\"><!--\n" \
"function toggle_toc(n) {\n" \
" var toc=document.getElementById(\"table-of-contents-\" + n);\n" \
" var btn=document.getElementById(\"btn-text\");\n" \
" toc.style.display=(toc.style.display==\"none\")?\"block\":\"none\";\n" \
" btn.textContent=(toc.style.display==\"none\")?\"[+]\":\"[-]\";\n" \
"}\n" \
"//-->\n</script>";
print "</head><body>";
if(Out) {
Out = fix_footnotes(Out);
Out = fix_links(Out);
Out = fix_abbrs(Out);
Out = make_toc(Out);
print trim(Out);
if(footnotes) {
footnotes = fix_links(footnotes);
print "<hr><ol class=\"footnotes\">\n" footnotes "</ol>";
}
}
print "</body></html>"
}
function escape(st) {
gsub(/&/, "\\&amp;", st);
gsub(/</, "\\&lt;", st);
gsub(/>/, "\\&gt;", st);
return st;
}
function strip_tags(st) {
gsub(/<\/?[^>]+>/,"",st);
return st;
}
function trim(st) {
sub(/^[[:space:]]+/, "", st);
sub(/[[:space:]]+$/, "", st);
return st;
}
function filter(st, res,tmp) {
if(Mode == "p") {
if(match(st, /^(( )| *\t)/) || match(st, /^[[:space:]]*```/)) {
preterm = trim(substr(st, RSTART,RLENGTH));
st = substr(st, RSTART+RLENGTH);
if(Buf) res = tag("p", scrub(Buf));
Buf = st;
push("pre");
} else if(!trim(prev) && match(st, /^[[:space:]]*[*-][[:space:]]*[*-][[:space:]]*[*-][-*[:space:]]*$/)) {
if(Buf) res = tag("p", scrub(Buf));
Buf = "";
res = res "<hr>\n";
} else if(match(st, /^[[:space:]]*===+[[:space:]]*$/)) {
Buf = trim(substr(Buf, 1, length(Buf) - length(prev) - 1));
if(Buf) res= tag("p", scrub(Buf));
if(prev) res = res heading(1, scrub(prev));
Buf = "";
} else if(match(st, /^[[:space:]]*---+[[:space:]]*$/)) {
Buf = trim(substr(Buf, 1, length(Buf) - length(prev) - 1));
if(Buf) res = tag("p", scrub(Buf));
if(prev) res = res heading(2, scrub(prev));
Buf = "";
} else if(match(st, /^[[:space:]]*#+/)) {
sub(/#+[[:space:]]*$/, "", st);
match(st, /#+/);
ListLevel = RLENGTH;
tmp = substr(st, RSTART+RLENGTH);
if(Buf) res = tag("p", scrub(Buf));
res = res heading(ListLevel, scrub(trim(tmp)));
Buf = "";
} else if(match(st, /^[[:space:]]*>/)) {
if(Buf) res = tag("p", scrub(Buf));
Buf = scrub(trim(substr(st, RSTART+RLENGTH)));
push("blockquote");
} else if(match(st, /^[[:space:]]*([*+-]|[[:digit:]]+\.)[[:space:]]/)) {
if(Buf) res = tag("p", scrub(Buf));
Buf="";
match(st, /^[[:space:]]*/);
ListLevel = 1;
indent[ListLevel] = RLENGTH;
Open[ListLevel]=match(st, /^[[:space:]]*[*+-][[:space:]]*/)?"ul":"ol";
push(Open[ListLevel]);
res = res filter(st);
} else if(match(st, /^[[:space:]]*$/)) {
if(trim(Buf)) {
res = tag("p", scrub(trim(Buf)));
Buf = "";
}
} else
Buf = Buf st "\n";
} else if(Mode == "blockquote") {
if(match(st, /^[[:space:]]*>[[:space:]]*$/))
Buf = Buf "\n</p><p>";
else if(match(st, /^[[:space:]]*>/))
Buf = Buf "\n" scrub(trim(substr(st, RSTART+RLENGTH)));
else if(match(st, /^[[:space:]]*$/)) {
res = tag("blockquote", tag("p", trim(Buf)));
pop();
res = res filter(st);
} else
Buf = Buf st;
} else if(Mode == "pre") {
if(!preterm && match(st, /^(( )| *\t)/) || preterm && !match(st, /^[[:space:]]*```/))
Buf = Buf ((Buf)?"\n":"") substr(st, RSTART+RLENGTH);
else {
gsub(/\t/," ",Buf);
if(length(trim(Buf)) > 0)
res = tag("pre", tag("code", escape(Buf)));
pop();
if(preterm) sub(/^[[:space:]]*```/,"",st);
res = res filter(st);
}
} else if(Mode == "ul" || Mode == "ol") {
if(ListLevel == 0 || match(st, /^[[:space:]]*$/) && (RLENGTH < indent[1] || RLENGTH == 0)) {
while(ListLevel > 1)
Buf = Buf "\n</" Open[ListLevel--] ">";
res = tag(Mode, "\n" Buf "\n");
pop();
} else {
if(match(st, /^[[:space:]]*([*+-]|[[:digit:]]+\.)/)) {
tmp = substr(st, RLENGTH+1);
match(st, /^[[:space:]]*/);
if(RLENGTH > indent[ListLevel]) {
indent[++ListLevel] = RLENGTH;
if(match(st, /^[[:space:]]*[*+-]/))
Open[ListLevel] = "ul";
else
Open[ListLevel] = "ol";
Buf = Buf "\n<" Open[ListLevel] ">";
} else while(RLENGTH < indent[ListLevel])
Buf = Buf "\n</" Open[ListLevel--] ">";
if(match(tmp,/^[[:space:]]*\[[xX[:space:]]?\]/)) {
st = substr(tmp,RLENGTH+1);
tmp = tolower(substr(tmp,RSTART,RLENGTH));
Buf = Buf "<li><input type=\"checkbox\" " (index(tmp,"x")?"checked":"") " disabled>" scrub(st);
} else
Buf = Buf "<li>" scrub(tmp);
} else if(match(st, /^[[:space:]]*$/)){
Buf = Buf "<br>\n";
} else {
sub(/^[[:space:]]+/,"",st);
Buf = Buf "\n" scrub(st);
}
}
}
prev = st;
return res;
}
function scrub(st, mp, ms, me, r, p, tg, a) {
sub(/ $/,"<br>\n",st);
gsub(/( |[[:space:]]+\\)\n/,"<br>\n",st);
while(match(st, /(__?|\*\*?|~~|`+|[&><\\])/)) {
a = substr(st, 1, RSTART-1);
mp = substr(st, RSTART, RLENGTH);
ms = substr(st, RSTART-1,1);
me = substr(st, RSTART+RLENGTH, 1);
p = RSTART+RLENGTH;
if(!classic_underscore && match(mp,/_+/)) {
if(match(ms,/[[:alnum:]]/) && match(me,/[[:alnum:]]/)) {
tg = substr(st, 1, index(st, mp));
r = r tg;
st = substr(st, index(st, mp) + 1);
continue;
}
}
st = substr(st, p);
r = r a;
ms = "";
if(mp == "\\") {
if(match(st, /^!?\[/)) {
r = r "\\" substr(st, RSTART, RLENGTH);
st = substr(st, 2);
} else if(match(st, /^(\*\*|__|~~|`+)/)) {
r = r substr(st, 1, RLENGTH);
st = substr(st, RLENGTH+1);
} else {
r = r substr(st, 1, 1);
st = substr(st, 2);
}
continue;
} else if(mp == "_" || mp == "*") {
if(match(me,/[[:space:]]/)) {
r = r mp;
continue;
}
p = index(st, mp);
while(p && match(substr(st, p-1, 1),/[\\[:space:]]/)) {
ms = ms substr(st, 1, p-1) mp;
st = substr(st, p + length(mp));
p = index(st, mp);
}
if(!p) {
r = r mp ms;
continue;
}
ms = ms substr(st,1,p-1);
r = r itag("em", scrub(ms));
st = substr(st,p+length(mp));
} else if(mp == "__" || mp == "**") {
if(match(me,/[[:space:]]/)) {
r = r mp;
continue;
}
p = index(st, mp);
while(p && match(substr(st, p-1, 1),/[\\[:space:]]/)) {
ms = ms substr(st, 1, p-1) mp;
st = substr(st, p + length(mp));
p = index(st, mp);
}
if(!p) {
r = r mp ms;
continue;
}
ms = ms substr(st,1,p-1);
r = r itag("strong", scrub(ms));
st = substr(st,p+length(mp));
} else if(mp == "~~") {
p = index(st, mp);
if(!p) {
r = r mp;
continue;
}
while(p && substr(st, p-1, 1) == "\\") {
ms = ms substr(st, 1, p-1) mp;
st = substr(st, p + length(mp));
p = index(st, mp);
}
ms = ms substr(st,1,p-1);
r = r itag("del", scrub(ms));
st = substr(st,p+length(mp));
} else if(match(mp, /`+/)) {
p = index(st, mp);
if(!p) {
r = r mp;
continue;
}
ms = substr(st,1,p-1);
r = r itag("code", escape(ms));
st = substr(st,p+length(mp));
} else if(mp == ">") {
r = r "&gt;";
} else if(mp == "<") {
p = index(st, ">");
if(!p) {
r = r "&lt;";
continue;
}
tg = substr(st, 1, p - 1);
if(match(tg,/^[[:alpha:]]+[[:space:]]/)) {
a = substr(tg,RSTART+RLENGTH-1);
tg = substr(tg,1,RLENGTH-1);
} else
a = "";
if(match(tolower(tg), "^/?(a|abbr|div|span|blockquote|pre|img|code|p|em|strong|sup|sub|del|ins|s|u|b|i|br|hr|ul|ol|li|table|thead|tfoot|tbody|tr|th|td|caption|column|col|colgroup|figure|figcaption|dl|dd|dt|mark|cite|q|var|samp|small)$")) {
r = r "<" tg a ">";
} else if(match(tg, "^[[:alpha:]]+://[[:graph:]]+$")) {
if(!a) a = tg;
r = r "<a href=\"" tg "\">" a "</a>";
} else if(match(tg, "^[[:graph:]]+@[[:graph:]]+$")) {
if(!a) a = tg;
r = r "<a href=\"" obfuscate("mailto:" tg) "\">" obfuscate(a) "</a>";
} else {
r = r "&lt;";
continue;
}
st = substr(st, p + 1);
} else if(mp == "&") {
if(match(st, /^[#[:alnum:]]+;/)) {
r = r "&" substr(st, 1, RLENGTH);
st = substr(st, RLENGTH+1);
} else {
r = r "&amp;";
}
}
}
return r st;
}
function push(newmode) {Stack[StackTop++] = Mode; Mode = newmode;}
function pop() {Mode = Stack[--StackTop];Buf = ""; return Mode;}
function heading(level, st, res, href) {
st = trim(st);
if(level > 6) level = 6;
href = tolower(st);
href = strip_tags(href);
gsub(/[^ [:alnum:]]+/, "", href);
gsub(/ +/, "-", href);
LinkUrls[href] = "#" href;
LinkUrls[tolower(st)] = "#" href;
res = tag("h" level, st (TopLinks?"&nbsp;&nbsp;<a class=\"top\" title=\"Return to top\" href=\"#\">&#8593;&nbsp;Top</a>":""), "id=\"" href "\"");
for(;ToCLevel < level; ToCLevel++)
ToC = ToC "<ul class=\"toc-" level "\">";
for(;ToCLevel > level; ToCLevel--)
ToC = ToC "</ul>";
ToC = ToC "<li class=\"toc-" level "\"><a class=\"toc-" level "\" href=\"#" href "\">" st "</a>\n";
ToCLevel = level;
return res;
}
function make_toc(st, r,p,dis,t,n) {
if(!ToC) return st;
for(;ToCLevel > 1;ToCLevel--)
ToC = ToC "</ul>";
p = match(st, /!\[toc[-+]?\]/);
while(p) {
++n;
dis = index(substr(st,RSTART,RLENGTH),"+");
t = "<div>\n<small><a id=\"toc-button\" onclick=\"toggle_toc(" n ")\"><span id=\"btn-text\">" (dis?"[-]":"[+]") "</span>&nbsp;Contents</a></small>\n" \
"<div id=\"table-of-contents-" n "\" style=\"display:" (dis?"block":"none") ";\">\n<ul class=\"toc-1\">" ToC "</ul>\n</div>\n</div>";
r = r substr(st,1,RSTART-1);
if(substr(st,RSTART-1,1) != "\\")
r = r t;
else
r = substr(r,1,length(r)-1) substr(st,RSTART,RLENGTH);
st = substr(st,RSTART+RLENGTH);
p = match(st, /!\[toc[-+]?\]/);
}
return r st;
}
function fix_links(st, lt,ld,lr,url,img,res,rx,pos) {
do {
pos = match(st, /\[[^\]]+\]/);
if(!pos)break;
img=substr(st,RSTART-1,1)=="!";
if(substr(st, RSTART-(img?2:1),1)=="\\") {
res = res substr(st,1,RSTART-(img?3:2));
if(img && substr(st,RSTART,RLENGTH)=="[toc]")res=res "\\";
res = res substr(st,RSTART-(img?1:0),RLENGTH+(img?1:0));
st = substr(st, RSTART + RLENGTH);
continue;
}
res = res substr(st, 1, RSTART-(img?2:1));
rx = substr(st, RSTART, RLENGTH);
st = substr(st, RSTART+RLENGTH);
if(match(st, /^[[:space:]]*\([^)]+\)/)) {
lt = substr(rx, 2, length(rx) - 2);
match(st, /\([^)]+\)/);
url = substr(st, RSTART+1, RLENGTH-2);
st = substr(st, RSTART+RLENGTH);
ld = "";
if(match(url,/[[:space:]]+["']/)) {
ld = url;
url = substr(url, 1, RSTART - 1);
match(ld,/["']/);
delim = substr(ld, RSTART, 1);
if(match(ld,delim ".*" delim))
ld = substr(ld, RSTART+1, RLENGTH-2);
} else ld = "";
if(img)
res = res "<img src=\"" url "\" title=\"" ld "\" alt=\"" lt "\">";
else
res = res "<a href=\"" url "\" title=\"" ld "\">" lt "</a>";
} else if(match(st, /^[[:space:]]*\[[^\]]*\]/)) {
lt = substr(rx, 2, length(rx) - 2);
match(st, /\[[^\]]*\]/);
lr = trim(tolower(substr(st, RSTART+1, RLENGTH-2)));
if(!lr) {
lr = tolower(trim(lt));
if(LinkDescs[lr]) lt = LinkDescs[lr];
}
st = substr(st, RSTART+RLENGTH);
url = LinkUrls[lr];
ld = LinkDescs[lr];
if(img)
res = res "<img src=\"" url "\" title=\"" ld "\" alt=\"" lt "\">";
else
res = res "<a href=\"" url "\" title=\"" ld "\">" lt "</a>";
} else
res = res (img?"!":"") rx;
} while(pos > 0);
return res st;
}
function fix_footnotes(st, r,p,n,i,d,fn,fc) {
p = match(st, /\[\^[^\]]+\]/);
while(p) {
r = r substr(st,1,RSTART-1);
d = substr(st,RSTART+2,RLENGTH-3);
n = tolower(d);
st = substr(st,RSTART+RLENGTH);
if(Footnote[tolower(n)]) {
if(!fn[n]) fn[n] = ++fc;
d = Footnote[n];
} else {
Footnote[n] = scrub(d);
if(!fn[n]) fn[n] = ++fc;
}
footname[fc] = n;
d = strip_tags(d);
if(length(d) > 20) d = substr(d,1,20) "&hellip;";
r = r "<sup title=\"" d "\"><a href=\"#footnote-" fn[n] "\" id=\"footnote-pos-" fn[n] "\" class=\"footnote\">[" fn[n] "]</a></sup>";
p = match(st, /\[\^[^\]]+\]/);
}
for(i=1;i<=fc;i++)
footnotes = footnotes "<li id=\"footnote-" i "\">" Footnote[footname[i]] \
"<a title=\"Return to Document\" class=\"footnote-back\" href=\"#footnote-pos-" i \
"\">&nbsp;&nbsp;&#8630;&nbsp;Back</a></li>\n";
return r st;
}
function fix_abbrs(str, st,k,r,p) {
for(k in Abbrs) {
r = "";
st = str;
t = escape(Abbrs[toupper(k)]);
gsub(/&/,"\\&", t);
p = match(st,"[^[:alnum:]]" k "[^[:alnum:]]");
while(p) {
r = r substr(st, 1, RSTART);
r = r "<abbr title=\"" t "\">" k "</abbr>";
st = substr(st, RSTART+RLENGTH-1);
p = match(st,"[^[:alnum:]]" k "[^[:alnum:]]");
}
str = r st;
}
return str;
}
function tag(t, body, attr) {
if(attr)
attr = " " trim(attr);
# https://www.w3.org/TR/html5/grouping-content.html#the-p-element
if(t == "p" && (match(body, /<\/?(div|table|blockquote|dl|ol|ul|h[[:digit:]]|hr|pre)[>[:space:]]/))|| (match(body,/!\[toc\]/) && substr(body, RSTART-1,1) != "\\"))
return "<" t attr ">" body "\n";
else
return "<" t attr ">" body "</" t ">\n";
}
function itag(t, body) {
return "<" t ">" body "</" t ">";
}
function obfuscate(e, r,i,t,o) {
for(i = 1; i <= length(e); i++) {
t = substr(e,i,1);
r = int(rand() * 100);
if(r > 50)
o = o sprintf("&#x%02X;", _ord[t]);
else if(r > 10)
o = o sprintf("&#%d;", _ord[t]);
else
o = o t;
}
return o;
}
function bright(c,a ,r,g,b) {
sub(/^#/,"",c);
r = 255*a + _hex[toupper(substr(c,1,2))]*(1-a);
g = 255*a + _hex[toupper(substr(c,3,2))]*(1-a);
b = 255*a + _hex[toupper(substr(c,5,2))]*(1-a);
return sprintf("#%02X%02X%02X",r>255?255:r,g>255?255:g,b>255?255:b);
}
function init_css(Theme, css,ss,hr,c1,c2,c3,c4,c5,bg1,bg2,bg3,bg4,ff,i) {
if(Theme == "0") return "";
css["body"] = "color:%color1%;font-family:%font-family%;line-height:1.5em;" \
"padding:1em 2em;width:80%;max-width:%maxwidth%;margin:0 auto;min-height:100%;float:none;";
css["h1"] = "color:%color1%;border-bottom:1px solid %background1%;padding:0.3em 0.1em;";
css["h2"] = "color:%color2%;border-bottom:1px solid %background2%;padding:0.2em 0.1em;";
css["h3"] = "color:%color3%;border-bottom:1px solid %background3%;padding:0.1em 0.1em;";
css["h4,h5,h6"] = "color:%color4%;padding:0.1em 0.1em;";
css["h1,h2,h3,h4,h5,h6"] = "font-weight:normal;line-height:1.2em;";
css["h4"] = "border-bottom:1px solid %background4%";
css["p"] = "margin:0.5em 0.1em;"
css["hr"] = "background:%hr%;height:1px;border:0;"
css["a"] = "color:%color2%;";
css["a:visited"] = "color:%color2%;";
css["a:active"] = "color:%color4%;";
css["a:hover"] = "color:%color4%;";
css["a.top"] = "font-size:x-small;text-decoration:initial;float:right;";
css["strong,b"] = "color:%color1%";
css["code"] = "color:%color2%;";
css["blockquote"] = "margin-left:1em;color:%color2%;background:%color5%;border-left:0.2em solid %color3%;border-radius:3px;padding:0.25em 0.5em;";
css["pre"] = "color:%color2%;background:%color5%;border-radius:5px;line-height:1.25em;margin:0.25em 0.5em;padding:0.75em;";
css["table"] = "border-collapse:collapse;margin:0.5em;";
css["th,td"] = "padding:0.5em 0.75em;border:1px solid %color4%;";
css["th"] = "color:%color2%;border:1px solid %color3%;border-bottom:2px solid %color3%;";
css["tr:nth-child(odd)"] = "background-color:%color5%;";
css["div"] = "padding:0.5em;";
css["caption"] = "padding:0.5em;font-style:italic;";
css["dl"] = "margin:0.5em;";
css["dt"] = "font-weight:bold;";
css["dd"] = "padding:0.3em;";
css["mark"] = "color:%color2%;background-color:%color5%;";
css["del,s"] = "color:%color4%;";
css["a#toc-button"] = "color:%color3%;background:%color5%;cursor:pointer;font-size:small;padding: 0.3em 0.5em 0.5em 0.5em;font-family:monospace;border-radius:3px;";
css["a#toc-button:hover"] = "color:%color5%;background:%color3%;";
css["div#table-of-contents"] = "padding:0;font-size:smaller;";
css["abbr"] = "cursor:help;";
css["ol.footnotes"] = "font-size:small;color:%color4%";
css["a.footnote"] = "font-size:smaller;text-decoration:initial;";
css["a.footnote-back"] = "text-decoration:initial;font-size:x-small;";
css[".fade"] = "color:%color5%;";
css[".highlight"] = "color:%color2%;background-color:%color5%;";
if(Theme==2){
c1="#303F9F";c2="#0449CC";c3="#2162FA";c4="#4B80FB";c5="#EDF2FF";
ff="\"Trebuchet MS\", Helvetica, sans-serif";
} else if(Theme==3){
c1="#430005";c2="#740009";c3="#A6373F";c4="#c55158";c5="#fbf2f2";
ff="Verdana, Geneva, sans-serif";
} else if(Theme==4){
c1="#083900";c2="#0D6300";c3="#3C8D2F";c4="#50be3f";c5="#f2faf1";
ff="Georgia, serif";
} else if(Theme==5){
c1="#453700";c2="#775F00";c3="#AA9339";c4="#c7b057";c5="#fbf9f2";
ff="Tahoma, Geneva, sans-serif";
} else if(Theme==6){
c1="#315067";c2="#4F9E9C";c3="#77AD93";c4="#95CE94";c5="#F6FFEE";
ff="Verdana, sans-serif;";
} else if(Theme==7){
c1="#35305D";c2="#646379";c3="#7A74A5";c4="#646392";c5="#fafafa";
} else if(Theme==8){
c1="#215FC2";c2="#D49095";c3="#AD90D4";c4="#90D4CF";c5="#F7FDEF";
} else {
c1="#092859";c2="#1351b5";c3="#d47034";c4="#DC7435";c5="#F6F8FF";
}
if(!ff) ff = "sans-serif"
for(i = 0; i<=255; i++)_hex[sprintf("%02X",i)]=i;
bg1 = bright(c1,0.5);
bg2 = bright(c2,0.5);
bg3 = bright(c3,0.5);
bg4 = bright(c3,0.75);
hr = bright(c1,0.75);
for(k in css)
ss = ss "\n" k "{" css[k] "}";
gsub(/%maxwidth%/,MaxWidth,ss);
gsub(/%color1%/,c1,ss);
gsub(/%color2%/,c2,ss);
gsub(/%color3%/,c3,ss);
gsub(/%color4%/,c4,ss);
gsub(/%color5%/,c5,ss);
gsub(/%background1%/,bg1,ss);
gsub(/%background2%/,bg2,ss);
gsub(/%background3%/,bg3,ss);
gsub(/%background4%/,bg4,ss);
gsub(/%font-family%/,ff,ss);
gsub(/%hr%/,hr,ss);
return ss;
}
# Like the Un*x `fmt` command, but for markdown.
#
# It does its best to preserve markdown headings,
# lists, pre-formatted code blocks and block quotes.
BEGIN { if(!Width) Width = 80; }
# Preserve headings
/^[[:space:]]*(=|-)+/ {
if(Buf)
Out = Out Buf "\n" $0 "\n";
else
Out = Out $0 "\n";
Buf = "";
next;
}
# Preformatted text is sent to the output verbatim
/^( |\t)+/ && match(last,/^[[:space:]]*$/) {
Out = Out $0 "\n";
next;
}
$0 !~ /^[[:space:]]*$/ && match(last,/^[[:space:]]*$/) {
# how much is the current line indented
match(str, /^[[:space:]]+/);
Indent = substr(str, 1, RLENGTH);
}
# Blank lines cause blank lines in the output
/^[[:space:]]*$/ {
# You need to preserve $0 for blank lines in preformatted blocks
if(Buf)
Out = Out Buf "\n" $0 "\n";
else
Out = Out "\n" $0;
Buf = "";
last = $0;
InList = 0;
next;
}
# Handle every other input line
{ last = $0; fmt($0); }
# Write output when done
END {
Out = Out Buf;
print Out;
}
function fmt(str, loc,word,n,indent) {
# Get the current line's indentation level.
match(str, /^[[:space:]]+/);
indent = substr(str, 1, RLENGTH);
# Trim leading whitespace
str = substr(str, RLENGTH+1);
# Lines starting with list item characters
# force a line break in the output
if(match(str,/^([*+-]|[[:digit:]]\.)/)) {
if(Buf) Out = Out Buf "\n";
Buf = "";
# Preserve the indentation in the global Indent
# if it is a list that is going to be split.
Indent = indent;
InList = 1; # remember we're in a list on subsequent calls.
} else if(match(str,/^>[[:space:]]+/)) {
Indent = indent substr(str, 1, RLENGTH);
str = substr(str,RLENGTH+1);
}
# Current indentation level = global Indent
indent = Indent;
# This implements the simple algorithm from the wikipedia
# https://en.wikipedia.org/wiki/Line_wrap_and_word_wrap
# There is a better way. See #Minimum_raggedness on that wiki page.
# The C code example on https://www.rosettacode.org/wiki/Word_wrap#C
# may actually be easy to port to Awk (the Awk version on that page
# implements the greedy algorithm, like I do here).
loc = match(str, /[[:space:]]+/);
while(loc) {
word = substr(str, 1, RSTART-1);
n = RSTART+RLENGTH;
# Handle forced line breaks
if(match(str,/( |[[:space:]]+\\)$/)) {
Out = Out str "\n";
n = RSTART+RLENGTH;
} else {
# If the buffer + the word exceeds the allowed width
# then insert a line break. Otherwise, just append the
# word to the buffer.
# Also, preserve the indentation.
if(length(Buf) + length(word) + 1 >= Width) {
Out = Out Buf "\n";
if(InList) indent = Indent " ";
Buf = indent word;
} else if(length(Buf))
Buf = Buf " " word;
else
Buf = indent word;
}
str = substr(str, n);
loc = match(str, /[[:space:]]+/);
}
# Append the remainder of str to Buf
if(length(str)) {
if(length(Buf) + length(str) + 1 >= Width) {
Out = Out Buf "\n";
if(InList) indent = Indent " ";
Buf = indent str;
} else if(length(Buf))
Buf = Buf " " str;
else
Buf = indent str;
}
}
#! /usr/bin/awk -f
#
# Extracts the /**-denoted comments from a source file.
#
# (c) 2016 Werner Stoop
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved. This file is offered as-is,
# without any warranty.
!in_comment && /\/\*\*/ {
in_comment = 1;
sub(/\/\*\*/,"");
}
in_comment && /\*\// { in_comment = 0; }
in_comment && /^[[:space:]]*\*/ {
sub(/^[[:space:]]*\*/,"");
}
in_comment { print; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment