X-Git-Url: http://git.plutz.net/?a=blobdiff_plain;f=markdown.awk;h=ae1cb934132d9d54c66755a850707d77db9a5217;hb=0d64190b6d906c7510c6e5825b26fd7f525d151a;hp=785ece87fbad731d7d4de1343752a2a63ccc9632;hpb=cd49a5ce71b79c56477dc351607484b0718fa594;p=cgilite
diff --git a/markdown.awk b/markdown.awk
index 785ece8..ae1cb93 100755
--- a/markdown.awk
+++ b/markdown.awk
@@ -4,10 +4,6 @@
# EXPERIMENTAL Markdown processor with minimal dependencies.
# Meant to support all features of John Grubers basic Markdown
# + a number of common extensions, mostly inspired by Pandoc Markdown
-#
-# ToDo:
-# - HTML processing / escaping (according to environment flag)
-# - em-dashes and arrows
# Supported Features / TODO:
# ==========================
@@ -42,20 +38,27 @@
#
# Extensions - Block elements:
# ----------------------------
+# - [x] Automatic -wrapping (custom)
# - ? Heading identifiers (php md, pandoc)
+# - [x] Heading attributes (custom)
+# - [x] Automatic heading identifiers (custom)
# - [x] Fenced code blocks (php md, pandoc)
-# - [-] Fenced code attributes
-# - [ ] Tables
+# - [x] Fenced code attributes
+# - [x] Images (as block elements, -wrapped) (custom)
+# - [x] reference style block images
+# - [/] Tables
# - ? Simple table (pandoc)
# - ? Multiline table (pandoc)
-# - ? Grid table (pandoc)
-# - ? Pipe table (php md pandoc)
+# - [x] Grid table (pandoc)
+# - [x] Headerless
+# - [x] Pipe table (php md, pandoc)
# - [x] Line blocks (pandoc)
-# - [ ] Task lists (pandoc)
-# - [ ] Definition lists (php md, pandoc)
+# - [x] Task lists (pandoc, custom)
+# - [x] Definition lists (php md, pandoc)
# - [-] Numbered example lists (pandoc)
# - [-] Metadata blocks (pandoc)
-# - [-] Fenced Divs (pandoc)
+# - [x] Metadata blocks (custom)
+# - [x] Fenced Divs (pandoc)
#
# Extensions - Inline elements:
# ----------------------------
@@ -64,6 +67,8 @@
# - [x] ^Superscript^ ~Subscript~ (pandoc)
# - [-] Bracketed spans (pandoc)
# - [-] Inline attributes (pandoc)
+# - [x] Image attributes (custom, pandoc inspired, not for reference style)
+# - [x] Wiki style links [[PageName]] / [[PageName|Link Text]]
# - [-] TEX-Math (pandoc)
# - ? Footnotes (php md)
# - ? Abbreviations (php md)
@@ -72,7 +77,7 @@
# - ? ... three-dot ellipsis (smartypants)
# - [-] en-dash (smartypants)
# - [ ] Automatic em-dash / en-dash
-# - [ ] Automatic -> Arrows <-
+# - [x] Automatic -> Arrows <- (custom)
function debug(text) { printf "\n---\n%s\n---\n", text > "/dev/stderr"; }
@@ -86,6 +91,21 @@ function HTML ( text ) {
return text;
}
+function URL ( text, sharp ) {
+ gsub( /&/, "%26", text );
+ gsub( /"/, "%22", text );
+ gsub( /'/, "%27", text );
+ gsub( /`/, "%60", text );
+ gsub( /\?/, "%3F", text );
+ if (sharp) gsub( /#/, "%23", text );
+ gsub( /\[/, "%5B", text );
+ gsub( /\]/, "%5D", text );
+ gsub( / /, "%20", text );
+ gsub( / /, "%09", text );
+ gsub( /\\/, "%5C", text );
+ return text;
+}
+
function inline( line, LOCAL, len, code, href, guard ) {
nu = "(\\\\\\\\|\\\\[^\\\\]|[^\\\\_]|_[[:alnum:]])*" # not underline (except when escaped)
na = "(\\\\\\\\|\\\\[^\\\\]|[^\\\\\\*])*" # not asterisk (except when escaped)
@@ -97,13 +117,13 @@ function inline( line, LOCAL, len, code, href, guard ) {
if ( line ~ /^$/ ) { # Recursion End
return "";
- # omit processing of escaped characters
- } else if ( line ~ /^\\[]\\`\*_\{\}\(\)#\+-\.![]/) {
- return substr(line, 2, 1) inline( substr(line, 3) );
+ # omit processing of escaped characters
+ } else if ( line ~ /^\\./) {
+ return HTML(substr(line, 2, 1)) inline( substr(line, 3) );
# hard brakes
} else if ( match(line, /^ \n/) ) {
- return " \n" inline( substr(line, RLENGTH + 1) );
+ return " \n" inline( substr(line, RLENGTH + 1) );
# ``code spans``
} else if ( match( line, /^`+/) ) {
@@ -113,68 +133,97 @@ function inline( line, LOCAL, len, code, href, guard ) {
code = substr( line, len + 1, match( substr(line, len + 1), guard ) - 1)
len = 2 * length(guard) + length(code)
# strip single surrounding white spaces
- code = gensub( / (.*) /, "\\1", "1" , code)
+ code = gensub( /^ | $/, "", "g" , code)
# escape HTML within code span
gsub( /&/, "\\&", code ); gsub( /, "\\<", code ); gsub( />/, "\\>", code );
return "" code "
" inline( substr( line, len + 1 ) )
}
+ # Wiki style links
+ } else if ( match( line, /^\[\[([^]|]+)(\|[^]]+)?\]\]/) ) {
+ len = RLENGTH;
+ href = gensub(/^\[\[([^]|]+)(\|([^]]+))?\]\]/, "\\1", 1, substr(line, 1, len) );
+ text = gensub(/^\[\[([^]|]+)(\|([^]]+))?\]\]/, "\\3", 1, substr(line, 1, len) );
+ if ( ! text ) text = href;
+ return "" HTML(text) " " inline( substr( line, len + 1) );
+
# quick links ("automatic links" in md doc)
} else if ( match( line, /^<[a-zA-Z]+:\/\/([-\.[:alnum:]]+)(:[0-9]*)?(\/[^>]*)?>/ ) ) {
len = RLENGTH;
- href = HTML( substr( line, 2, len - 2) );
+ href = URL( substr( line, 2, len - 2) );
return "" href " " inline( substr( line, len + 1) );
+ # quick link email
+ } else if ( match( line, /^<[a-zA-Z0-9.!#$%&'\''*+\/=?^_`{|}~-]+@[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*>/ ) ) {
+ len = RLENGTH;
+ href = URL( substr( line, 2, len - 2) );
+ return "" href " " inline( substr( line, len + 1) );
+
# inline links
- } else if ( match(line, /^\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/) ) {
+ # ,_______________________Image____________________________,
+ } else if ( match(line, /^\[([^]]+|!\[[^]]*\]\([^"\)]+([ \t]+"[^"]+")?\)(\{[a-zA-Z \t-]*\})?)\]\(([^"\)]+)([[:space:]]+"([^"]+)")?\)/) ) {
len = RLENGTH;
- text = gensub(/^\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\1", "g", line);
- href = gensub(/^\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\2", "g", line);
- title = gensub(/^\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\4", "g", line);
+ text = gensub(/^\[([^]]+|!\[[^]]*\]\([^"\)]+([ \t]+"[^"]+")?\)(\{[a-zA-Z \t-]*\})?)\]\(([^"\)]+)([[:space:]]+"([^"]+)")?\)/, \
+ "\\1", 1, substr(line, 1, len) );
+ href = gensub(/^\[([^]]+|!\[[^]]*\]\([^"\)]+([ \t]+"[^"]+")?\)(\{[a-zA-Z \t-]*\})?)\]\(([^"\)]+)([[:space:]]+"([^"]+)")?\)/, \
+ "\\4", 1, substr(line, 1, len) );
+ title = gensub(/^\[([^]]+|!\[[^]]*\]\([^"\)]+([ \t]+"[^"]+")?\)(\{[a-zA-Z \t-]*\})?)\]\(([^"\)]+)([[:space:]]+"([^"]+)")?\)/, \
+ "\\6", 1, substr(line, 1, len) );
if ( title ) {
- return "" inline( text ) " " inline( substr( line, len + 1) );
+ return "" inline( text ) " " inline( substr( line, len + 1) );
} else {
- return "" inline( text ) " " inline( substr( line, len + 1) );
+ return "" inline( text ) " " inline( substr( line, len + 1) );
}
# reference style links
} else if ( match(line, /^\[([^]]+)\] ?\[([^]]*)\]/ ) ) {
len = RLENGTH;
- text = gensub(/^\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\1", 1, line);
- id = gensub(/^\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\2", 1, line);
+ text = gensub(/^\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\1", 1, substr(line, 1, len) );
+ id = gensub(/^\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\2", 1, substr(line, 1, len) );
if ( ! id ) id = text;
if ( rl_href[id] && rl_title[id] ) {
- return "" inline(text) " " inline( substr( line, len + 1) );
+ return "" inline(text) " " inline( substr( line, len + 1) );
} else if ( rl_href[id] ) {
- return "" inline(text) " " inline( substr( line, len + 1) );
+ return "" inline(text) " " inline( substr( line, len + 1) );
} else {
- return "" substr(line, 1, len) inline( substr(line, len + 1) );
+ return "" HTML(substr(line, 1, len)) inline( substr(line, len + 1) );
}
# inline images
- } else if ( match(line, /^!\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/) ) {
+ } else if ( match(line, /^!\[([^]]*)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)(\{([a-zA-Z \t-]*)\})?/) ) {
len = RLENGTH;
- text = gensub(/^!\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\1", "g", line);
- href = gensub(/^!\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\2", "g", line);
- title = gensub(/^!\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\4", "g", line);
- if ( title ) {
- return " " inline( substr( line, len + 1) );
+ text = gensub(/^!\[([^]]*)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)(\{([a-zA-Z \t-]*)\})?/, "\\1", "g", substr(line, 1, len) );
+ href = gensub(/^!\[([^]]*)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)(\{([a-zA-Z \t-]*)\})?/, "\\2", "g", substr(line, 1, len) );
+ title = gensub(/^!\[([^]]*)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)(\{([a-zA-Z \t-]*)\})?/, "\\4", "g", substr(line, 1, len) );
+ attrib = gensub(/^!\[([^]]*)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)(\{([a-zA-Z \t-]*)\})?/, "\\6", "g", substr(line, 1, len) );
+ if ( title && attrib ) {
+ return " " \
+ inline( substr( line, len + 1) );
+ } else if ( title ) {
+ return " " \
+ inline( substr( line, len + 1) );
+ } else if ( attrib ) {
+ return " " \
+ inline( substr( line, len + 1) );
} else {
- return " " inline( substr( line, len + 1) );
+ return " " \
+ inline( substr( line, len + 1) );
}
# reference style images
- } else if ( match(line, /^!\[([^]]+)\] ?\[([^]]*)\]/ ) ) {
+ } else if ( match(line, /^!\[([^]]*)\] ?\[([^]]*)\]/ ) ) {
len = RLENGTH;
- text = gensub(/^!\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\1", 1, line);
- id = gensub(/^!\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\2", 1, line);
+ text = gensub(/^!\[([^\n]*)\] ?\[([^\n]*)\].*/, "\\1", 1, substr(line, 1, len) );
+ id = gensub(/^!\[([^\n]*)\] ?\[([^\n]*)\].*/, "\\2", 1, substr(line, 1, len) );
if ( ! id ) id = text;
if ( rl_href[id] && rl_title[id] ) {
- return " " inline( substr( line, len + 1) );
+ return " " \
+ inline( substr( line, len + 1) );
} else if ( rl_href[id] ) {
- return " " inline( substr( line, len + 1) );
+ return " " \
+ inline( substr( line, len + 1) );
} else {
- return "" substr(line, 1, len) inline( substr(line, len + 1) );
+ return "" HTML(substr(line, 1, len)) inline( substr(line, len + 1) );
}
# ~~strikeout~~ (pandoc)
@@ -194,7 +243,7 @@ function inline( line, LOCAL, len, code, href, guard ) {
# ignore embedded underscores (pandoc, php md)
} else if ( match(line, "^[[:alnum:]](__|_)") ) {
- return substr( line, 1, RLENGTH) inline( substr(line, RLENGTH + 1) );
+ return HTML(substr( line, 1, RLENGTH)) inline( substr(line, RLENGTH + 1) );
# __strong__$
} else if ( match(line, "^__(([^_[:space:]]|" ieu ")|([^_[:space:]]|" ieu ")(" nu "|" ieu ")*([^_[:space:]]|" ieu "))__$") ) {
@@ -226,6 +275,11 @@ function inline( line, LOCAL, len, code, href, guard ) {
len = RLENGTH;
return "" inline( substr( line, 2, len - 2 ) ) " " inline( substr( line, len + 1 ) );
+ # Macros
+ } else if ( AllowMacros && match( line, /^<<([^>]|>[^>])+>>/) ) {
+ len = RLENGTH;
+ return macro( substr( line, 3, len - 4 ) ) inline(substr(line, len + 1));
+
# Verbatim inline HTML
} else if ( AllowHTML && match( line, /^(|<\?([^\?]|\?[^>])*\?>|]*>|])*\]\]>|<\/[A-Za-z][A-Za-z0-9-]*[[:space:]]*>|<[A-Za-z][A-Za-z0-9-]*([[:space:]]+[A-Za-z_:][A-Za-z0-9_\.:-]*([[:space:]]*=[[:space:]]*([[:space:]"'=<>`]+|"[^"]*"|'[^']*'))?)*[[:space:]]*\/?>)/) ) {
len = RLENGTH;
@@ -236,6 +290,14 @@ function inline( line, LOCAL, len, code, href, guard ) {
len = RLENGTH;
return substr( line, 1, len ) inline(substr(line, len + 1));
+ # Arrows
+ } else if ( line ~ /^-->( |$)/) { # ignore multidash-arrow
+ return "-->" inline( substr(line, 4) );
+ } else if ( line ~ /^<-( |$)/) {
+ return "←" inline( substr(line, 3) );
+ } else if ( line ~ /^->( |$)/) {
+ return "→" inline( substr(line, 3) );
+
# Escape lone HTML character
} else if ( match( line, /^[&<>"']/) ) {
return HTML(substr(line, 1, 1)) inline(substr(line, 2));
@@ -246,7 +308,41 @@ function inline( line, LOCAL, len, code, href, guard ) {
}
}
-function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent ) {
+function headline( hlvl, htxt, attrib, LOCAL, sec, n, HL) {
+ split( gensub( /^(.* )?([0-9]+( [0-9]+){5})$/, "\\2" ,"1", hstack), HL);
+
+ for ( n = hlvl; n <= 6; n++ ) { sec = sec (HL[n]?" ":""); }
+ HL[hlvl]++; for ( n = hlvl + 1; n <= 6; n++) { HL[n] = 0;}
+ hid = ""; for ( n = 2; n <= blvl; n++) { hid = hid BL[n] "/"; }
+ hid = hid HL[1]; for ( n = 2; n <= hlvl; n++) { hid = hid "." HL[n] ; }
+ hid = hid ":" URL(htxt, 1);
+
+ hstack = gensub( /^(.* )?([0-9]+( [0-9]+){5})$/, "\\1" ,"1", hstack) \
+ HL[1] " " HL[2] " " HL[3] " " HL[4] " " HL[5] " " HL[6];
+
+ return sec "" \
+ "" inline( htxt ) \
+ " " \
+ " \n";
+}
+
+# Nested Block, resets heading counters
+function _nblock( block, LOCAL, hlsav, sec, n ) {
+ hlsav = hstack;
+ hstack = hstack " 0 0 0 0 0 0";
+
+ # Block Level
+ blvl++; BL[blvl]++;
+ for ( n = blvl + 1; n in BL; n++) { delete BL[n]; }
+
+ block = _block( block );
+ split( gensub( /^(.* )?([0-9]+( [0-9]+){5})$/, "\\2" ,"1", hstack), HL);
+ sec = ""; for ( n = 1; n <= 6; n++ ) { sec = sec (HL[n]?" ":""); }
+ hstack = hlsav; blvl--;
+ return block sec;
+}
+
+function _block( block, LOCAL, st, len, text, title, attrib, href, guard, code, indent ) {
gsub( /^\n+|\n+$/, "", block );
if ( block == "" ) {
@@ -274,30 +370,159 @@ function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent ) {
len = RLENGTH; st = RSTART;
return substr(block, st, len) _block(substr(block, st + len));
- # Horizontal rule
- } else if ( match( block, /(^|\n) ? ? ?((\* *){3,}|(- *){3,}|(_ *){3,})($|\n)/) ) {
+ # Metadata (custom, block starting with %something)
+ # Metadata is ignored but can be interpreted externally
+ } else if ( match(block, /^%[a-zA-Z]+([[:space:]][^\n]*)?(\n|$)(%[a-zA-Z]+([[:space:]][^\n]*)?(\n|$)|%([[:space:]][^\n]*)?(\n|$)|[ \t]+[^\n[:space:]][^\n]*(\n|$))*/) ) {
len = RLENGTH; st = RSTART;
- return _block(substr(block, 1, st - 1)) "
\n" _block(substr(block, st + len));
+ return _block( substr( block, len + 1) );
# Blockquote (leading >)
} else if ( match( block, /^> /) ) {
match( block, /(^|\n)[[:space:]]*(\n|$)/ ) || match(block, /$/);
len = RLENGTH; st = RSTART;
- return "\n" _block( gensub( /(^|\n)> /, "\n", "g", substr(block, 1, st - 1) ) ) " \n\n" \
+ return "" gensub( /^\n|\n$/, "", "g", _nblock( gensub( /(^|\n)> /, "\n", "g", substr(block, 1, st - 1) ) ) ) " \n\n" \
_block( substr(block, st + len) );
+ # Pipe Tables (pandoc / php md / gfm )
+ } else if ( match(block, "^((\\|)?([^\n]+\\|)+[^\n]+(\\|)?)\n" \
+ "((\\|)?:?(-+:?[\\|+])+:?-+:?(\\|)?)\n" \
+ "((\\|)?([^\n]+\\|)+[^\n]+(\\|)?(\n|$))+" ) ) {
+ len = RLENGTH; st = RSTART;
+ #initialize empty arrays
+ split("", talign); split("", tarray);
+ cols = 0; cnt=0; ttext = "";
+
+ # table header and alignment
+ split( gensub( /(^\||\|$)/, "", "g", \
+ gensub( /(^|[^\\])\\\|/, "\\1\\|", "g", \
+ substr(block, 1, match(block, /(\n|$)/)) \
+ )), tarray, /\|/);
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+ cols = split( \
+ gensub( /(^\||\|$)/, "", "g", \
+ substr(block, 1, match(block, /(\n|$)/)) \
+ ), talign, /[+\|]/);
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+
+ for( cnt = 1; cnt < cols; cnt++ ) {
+ if (match(talign[cnt], /:-+:/)) talign[cnt]="center";
+ else if (match(talign[cnt], /-+:/)) talign[cnt]="right";
+ else if (match(talign[cnt], /:-+/)) talign[cnt]="left";
+ else talign[cnt]="";
+ }
+
+ ttext = "\n"
+ for (cnt = 1; cnt < cols; cnt++)
+ ttext = ttext "" inline(tarray[cnt]) " "
+ ttext = ttext " \n \n"
+
+ while ( match(block, "^((\\|)?([^\n]+\\|)+[^\n]+(\\|)?(\n|$))+" ) ){
+ split( gensub( /(^\||\|$)/, "", "g", \
+ gensub( /(^|[^\\])\\\|/, "\\1\\|", "g", \
+ substr(block, 1, match(block, /(\n|$)/)) \
+ )), tarray, /\|/);
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+
+ ttext = ttext ""
+ for (cnt = 1; cnt < cols; cnt++)
+ ttext = ttext "" inline(tarray[cnt]) " "
+ ttext = ttext " \n"
+ }
+ return "\n" _block(block);
+
+ # Grid Tables (pandoc)
+ # (with, and without header)
+ } else if ( match( block, "^\\+(-+\\+)+\n" \
+ "(\\|([^\n]+\\|)+\n)+" \
+ "(\\+(:?=+:?\\+)+)\n" \
+ "((\\|([^\n]+\\|)+\n)+" \
+ "\\+(-+\\+)+(\n|$))+" \
+ ) || \
+ match( block, "^()()()" \
+ "(\\+(:?-+:?\\+)+)\n" \
+ "((\\|([^\n]+\\|)+\n)+" \
+ "\\+(-+\\+)+(\n|$))+" \
+ ) ) {
+ len = RLENGTH; st = RSTART;
+ #initialize empty arrays
+ split("", talign); split("", tarray); split("", tread);
+ cols = 0; cnt=0; ttext = "";
+
+ # Column Count
+ cols = split( gensub( "^(\\+(:?-+:?\\+)+)(\n.*)*$", "\\1", 1, block), tread, /\+/) - 2;
+ # debug(" Cols: " gensub( "^(\\+(:?-+:?\\+)+)(\n.*)*$", "\\1", 1, block ));
+
+ # table alignment
+ split( gensub( "^(.*\n)?\\+((:?=+:?\\+|(:-+|-+:|:-+:)\\+)+)(\n.*)$", "\\2", "g", block ), talign, /\+/ );
+ # debug("Align: " gensub( "^(.*\n)?\\+((:?=+:?\\+|(:-+|-+:|:-+:)\\+)+)(\n.*)$", "\\2", "g", block ));
+
+ for (cnt = 1; cnt <= cols; cnt++) {
+ if (match(talign[cnt], /:(-+|=+):/)) talign[cnt]="center";
+ else if (match(talign[cnt], /(-+|=+):/)) talign[cnt]="right";
+ else if (match(talign[cnt], /:(-+|=+)/ )) talign[cnt]="left";
+ else talign[cnt]="";
+ }
+
+ if ( match(block, "^\\+(-+\\+)+\n" \
+ "(\\|([^\n]+\\|)+\n)+" \
+ "\\+(:?=+:?\\+)+\n" \
+ "((\\|([^\n]+\\|)+\n)+" \
+ "\\+(-+\\+)+(\n|$))+" \
+ ) ) {
+ # table header
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+ while ( match(block, "^\\|([^\n]+\\|)+\n") ) {
+ split( gensub( /(^\||\|$)/, "", "g", \
+ gensub( /(^|[^\\])\\\|/, "\\1\\|", "g", \
+ substr(block, 1, match(block, /(\n|$)/)) \
+ )), tread, /\|/);
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+ for (cnt = 1; cnt <= cols; cnt++)
+ tarray[cnt] = tarray[cnt] "\n" tread[cnt];
+ }
+
+ ttext = "\n"
+ for (cnt = 1; cnt <= cols; cnt++)
+ ttext = ttext "" _nblock(tarray[cnt]) " "
+ ttext = ttext " \n "
+ }
+
+ # table body
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+ ttext = ttext " \n"
+
+ while ( match(block, /^((\|([^\n]+\|)+\n)+\+(-+\+)+(\n|$))+/ ) ){
+ split("", tarray);
+ while ( match(block, /^\|([^\n]+\|)+\n/) ) {
+ split( gensub( /(^\||\|$)/, "", "g", \
+ gensub( /(^|[^\\])\\\|/, "\\1\\|", "g", \
+ substr(block, 1, match(block, /(\n|$)/)) \
+ )), tread, /\|/);
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+ for (cnt = 1; cnt <= cols; cnt++)
+ tarray[cnt] = tarray[cnt] "\n" tread[cnt];
+ }
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+
+ ttext = ttext ""
+ for (cnt = 1; cnt <= cols; cnt++)
+ ttext = ttext "" _nblock(tarray[cnt]) " "
+ ttext = ttext " \n"
+ }
+ return "\n" _nblock(block);
+
# Line Blocks (pandoc)
} else if ( match(block, /^\| [^\n]*(\n|$)(\| [^\n]*(\n|$)|[ \t]+[^\n[:space:]][^\n]*(\n|$))*/) ) {
len = RLENGTH; st = RSTART;
- code = substr(block, 1, len);
- gsub(/\n[[:space:]]+/, " ", code);
- gsub(/\n\| /, "\n", code);
- gsub(/^\| |\n$/, "", code);
- return "" gensub(/\n/, " \n", "g", inline( code )) "
\n" \
+ text = substr(block, 1, len);
+ gsub(/\n[[:space:]]+/, " ", text);
+ gsub(/\n\| /, "\n", text);
+ gsub(/^\| |\n$/, "", text);
+ return "" gensub(/\n/, " \n", "g", inline( text )) "
\n" \
_block( substr( block, len + 1) );
# Indented Code Block
- } else if ( match(block, /^( |\t)[^\n]+(\n|$)(( |\t)[^\n]+(\n|$)|[ \t]*(\n|$))*/) ) {
+ } else if ( match(block, /^( |\t)( *\t*[^ \t\n]+ *\t*)+(\n|$)(( |\t)[^\n]+(\n|$)|[ \t]*(\n|$))*/) ) {
len = RLENGTH; st = RSTART;
code = substr(block, 1, len);
gsub(/(^|\n)( |\t)/, "\n", code);
@@ -305,13 +530,34 @@ function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent ) {
return "" HTML( code ) "
\n" \
_block( substr( block, len + 1 ) );
+ # Fenced Divs (pandoc, custom)
+ } else if ( match( block, /^(:::+)/ ) ) {
+ guard = substr( block, 1, RLENGTH );
+ code = gensub(/^[^\n]+\n/, "", 1, block);
+ attrib = gensub(/^:::+[ \t]*\{?[ \t]*([^\}\n]*)\}?[ \t]*\n.*$/, "\\1", 1, block);
+ gsub(/[^a-zA-Z0-9_-]+/, " ", attrib);
+ gsub(/(^ | $)/, "", attrib);
+ if ( match(code, "(^|\n)" guard "+(\n|$)" ) ) {
+ len = RLENGTH; st = RSTART;
+ return "" _nblock( substr(code, 1, st - 1) ) "
\n" \
+ _block( substr( code, st + len ) );
+ } else {
+ match( block, /(^|\n)[[:space:]]*(\n|$)/ ) || match( block, /$/ );
+ len = RLENGTH; st = RSTART;
+ return "" inline( substr(block, 1, st - 1) ) "
\n" \
+ _block( substr(block, st + len) );
+ }
+
# Fenced Code Block (pandoc)
} else if ( match( block, /^(~~~+|```+)/ ) ) {
guard = substr( block, 1, RLENGTH );
code = gensub(/^[^\n]+\n/, "", 1, block);
+ attrib = gensub(/^(~~~+|```+)[ \t]*\{?[ \t]*([^\}\n]*)\}?[ \t]*\n.*$/, "\\2", 1, block);
+ gsub(/[^a-zA-Z0-9_-]+/, " ", attrib);
+ gsub(/(^ | $)/, "", attrib);
if ( match(code, "(^|\n)" guard "+(\n|$)" ) ) {
len = RLENGTH; st = RSTART;
- return "" HTML( substr(code, 1, st - 1) ) "
\n" \
+ return "" HTML( substr(code, 1, st - 1) ) "
\n" \
_block( substr( code, st + len ) );
} else {
match( block, /(^|\n)[[:space:]]*(\n|$)/ ) || match( block, /$/ );
@@ -344,32 +590,170 @@ function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent ) {
gsub("(^|\n) {0," indent "}", "\n", list);
return "\n\n" _list( substr(list, 2) ) " \n" _block( block );
- # First Order Heading
- } else if ( match( block, /^[^\n]+\n===+(\n|$)/ ) ) {
+ # Definition list
+ } else if (match( block, "^(([ \t]*\n)*[^:\n \t][^\n]+\n" \
+ "([ \t]*\n)* ? ? ?:[ \t][^\n]+(\n|$)" \
+ "(([ \t]*\n)* ? ? ?:[ \t][^\n]+(\n|$)" \
+ "|[^:\n \t][^\n]+(\n|$)" \
+ "|( ? ? ?\t| +)[^\n]+(\n|$)" \
+ "|([ \t]*\n)+( ? ? ?\t| +)[^\n]+(\n|$))*)+" \
+ )) {
+ list = substr( block, 1, RLENGTH); block = substr( block, RLENGTH + 1);
+ return "\n\n" _dlist( list ) " \n" _block( block );
+
+ # First Order Heading H1 + Attrib
+ } else if ( match( block, /^([^\n]+)([ \t]*\{([^\}\n]+)\})\n===+(\n|$)/ ) ) {
len = RLENGTH;
- return "" inline( gensub( /\n.*$/, "", "g", block ) ) " \n\n" \
+ text = gensub(/^([^\n]+)([ \t]*\{([^\}\n]+)\})\n===+(\n.*)?$/, "\\1", 1, block)
+ attrib = gensub(/^([^\n]+)([ \t]*\{([^\}\n]+)\})\n===+(\n.*)?$/, "\\3", 1, block)
+ gsub(/[^a-zA-Z0-9_-]+/, " ", attrib); gsub(/(^ | $)/, "", attrib);
+
+ return headline(1, text, attrib) \
_block( substr( block, len + 1 ) );
- # Second Order Heading
- } else if ( match( block, /^[^\n]+\n---+(\n|$)/ ) ) {
+ # First Order Heading H1
+ } else if ( match( block, /^([^\n]+)\n===+(\n|$)/ ) ) {
len = RLENGTH;
- return "" inline( gensub( /\n.*$/, "", "g", block ) ) " \n\n" \
+ text = gensub(/^([^\n]+)\n===+(\n.*)?$/, "\\1", 1, block)
+
+ return headline(1, text, 0) \
+ _block( substr( block, len + 1 ) );
+
+ # Second Order Heading H2 + Attrib
+ } else if ( match( block, /^([^\n]+)([ \t]*\{([^\}\n]+)\})\n---+(\n|$)/ ) ) {
+ len = RLENGTH;
+ text = gensub(/^([^\n]+)([ \t]*\{([^\}\n]+)\})\n---+(\n.*)?$/, "\\1", 1, block)
+ attrib = gensub(/^([^\n]+)([ \t]*\{([^\}\n]+)\})\n---+(\n.*)?$/, "\\3", 1, block)
+ gsub(/[^a-zA-Z0-9_-]+/, " ", attrib); gsub(/(^ | $)/, "", attrib);
+
+ return headline(2, text, attrib) \
_block( substr( block, len + 1) );
- # Nth Order Heading
- } else if ( match( block, /^#{1,6}[[:space:]]*[^\n]+([[:space:]]*#*)(\n|$)/ ) ) {
+ # Second Order Heading H2
+ } else if ( match( block, /^([^\n]+)\n---+(\n|$)/ ) ) {
len = RLENGTH;
- hlvl = length( gensub( /^(#{1,6}).*$/, "\\1", "g", block ) );
- htxt = gensub( /[[:space:]]*#*$/, "", "1", gensub( /^#{1,6}[[:space:]]*([^\n]+)([[:space:]]*#*)\n.*$/, "\\1", "g", block ) )
- return "" inline( htxt ) " \n\n" \
+ text = gensub(/^([^\n]+)\n---+(\n.*)?$/, "\\1", 1, block)
+
+ return headline(2, text, 0) \
_block( substr( block, len + 1) );
- # Plain paragraph
- } else {
- match( block, /(^|\n)[[:space:]]*(\n|$)/ ) || match( block, /$/ );
+ # Nth Order Heading H1 H2 H3 H4 H5 H6 + Attrib
+ } else if ( match( block, /^(#{1,6})[ \t]*(([^ \t\n]+|[ \t]+[^ \t\n#]|[ \t]+#+[ \t]*[^ \t\n#])+)[ \t]*#*([ \t]*\{([a-zA-Z \t-]*)\})(\n|$)/ ) ) {
+ len = RLENGTH;
+ n = gensub(/^(#{1,6})[ \t]*(([^ \t\n]+|[ \t]+[^ \t\n#]|[ \t]+#+[ \t]*[^ \t\n#])+)[ \t]*#*([ \t]*\{([a-zA-Z \t-]*)\})(\n.*)?$/, "\\1", 1, block);
+ text = gensub(/^(#{1,6})[ \t]*(([^ \t\n]+|[ \t]+[^ \t\n#]|[ \t]+#+[ \t]*[^ \t\n#])+)[ \t]*#*([ \t]*\{([a-zA-Z \t-]*)\})(\n.*)?$/, "\\2", 1, block);
+ attrib = gensub(/^(#{1,6})[ \t]*(([^ \t\n]+|[ \t]+[^ \t\n#]|[ \t]+#+[ \t]*[^ \t\n#])+)[ \t]*#*([ \t]*\{([a-zA-Z \t-]*)\})(\n.*)?$/, "\\5", 1, block);
+ gsub(/[^a-zA-Z0-9_-]+/, " ", attrib); gsub(/(^ | $)/, "", attrib);
+
+ return headline( length(n), text, attrib ) \
+ _block( substr( block, len + 1) );
+
+ # Nth Order Heading H1 H2 H3 H4 H5 H6
+ } else if ( match( block, /^(#{1,6})[ \t]*(([^ \t\n]+|[ \t]+[^ \t\n#]|[ \t]+#+[ \t]*[^ \t\n#])+)[ \t]*#*(\n|$)/ ) ) {
+ len = RLENGTH;
+ n = gensub(/^(#{1,6})[ \t]*(([^ \t\n]+|[ \t]+[^ \t\n#]|[ \t]+#+[ \t]*[^ \t\n#])+)[ \t]*#*(\n.*)?$/, "\\1", 1, block);
+ text = gensub(/^(#{1,6})[ \t]*(([^ \t\n]+|[ \t]+[^ \t\n#]|[ \t]+#+[ \t]*[^ \t\n#])+)[ \t]*#*(\n.*)?$/, "\\2", 1, block);
+
+ return headline( length(n), text, 0 ) \
+ _block( substr( block, len + 1) );
+
+ # block images (wrapped in )
+ } else if ( match(block, /^!\[([^]]*)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)(\{([a-zA-Z \t-]*)\})?(\n|$)/) ) {
+ len = RLENGTH;
+ text = gensub(/^!\[([^]]*)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)(\{([a-zA-Z \t-]*)\})?(\n.*)?$/, "\\1", "g", block);
+ href = gensub(/^!\[([^]]*)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)(\{([a-zA-Z \t-]*)\})?(\n.*)?$/, "\\2", "g", block);
+ title = gensub(/^!\[([^]]*)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)(\{([a-zA-Z \t-]*)\})?(\n.*)?$/, "\\4", "g", block);
+ attrib = gensub(/^!\[([^]]*)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)(\{([a-zA-Z \t-]*)\})?(\n.*)?$/, "\\6", "g", block);
+ if ( title && attrib ) {
+ return "" \
+ " " \
+ "" inline(title) " " \
+ " \n\n" \
+ _block( substr( block, len + 1) );
+ } else if ( title ) {
+ return "" \
+ " " \
+ "" inline(title) " " \
+ " \n\n" \
+ _block( substr( block, len + 1) );
+ } else if ( attrib ) {
+ return "" \
+ " " \
+ " \n\n" \
+ _block( substr( block, len + 1) );
+ } else {
+ return "" \
+ " " \
+ " \n\n" \
+ _block( substr( block, len + 1) );
+ }
+
+ # reference style images (block)
+ } else if ( match(line, /^!\[([^]]*)\] ?\[([^]]*)\](\n|$)/ ) ) {
+ len = RLENGTH;
+ text = gensub(/^!\[([^\n]*)\] ?\[([^\n]*)\](\n.*)?$/, "\\1", 1, block);
+ id = gensub(/^!\[([^\n]*)\] ?\[([^\n]*)\](\n.*)?$/, "\\2", 1, block);
+ if ( ! id ) id = text;
+ if ( rl_href[id] && rl_title[id] ) {
+ return "" \
+ " " \
+ "" inline(rl_title[id]) " " \
+ " \n\n" \
+ _block( substr( block, len + 1) );
+ } else if ( rl_href[id] ) {
+ return "" \
+ " " \
+ " \n\n" \
+ _block( substr( block, len + 1) );
+ } else {
+ return "" HTML(substr(block, 1, len)) "
\n" _block( substr(block, len + 1) );
+ }
+
+ # Macros (standalone <> calls handled as block, so they are not wrapped in paragraph)
+ } else if ( AllowMacros && match( block, /^<<(([^>]|>[^>])+)>>(\n|$)/) ) {
+ len = RLENGTH;
+ text = gensub(/^<<(([^>]|>[^>])+)>>(\n.*)?$/, "\\1", 1, block);
+ return macro(text) _block(substr(block, len + 1) );
+
+ # Split paragraphs
+ } else if ( match( block, /(^|\n)[[:space:]]*(\n|$)/) ) {
len = RLENGTH; st = RSTART;
- return "" inline( substr(block, 1, st - 1) ) "
\n" \
+ return _block( substr(block, 1, st - 1) ) "\n" \
_block( substr(block, st + len) );
+
+ # Horizontal rule
+ } else if ( match( block, /(^|\n) ? ? ?((\* *){3,}|(- *){3,}|(_ *){3,})($|\n)/) ) {
+ len = RLENGTH; st = RSTART;
+ return _block(substr(block, 1, st - 1)) " \n" _block(substr(block, st + len));
+
+ # Plain paragraph
+ } else {
+ return "" inline(block) "
\n";
+ }
+}
+
+function _dlist (block, LOCAL, len, st, text, indent, p) {
+ if (match( block, "^([ \t]*\n)*[^:\n \t][^\n]+\n" )) {
+ len = RLENGTH; text = substr(block, 1, len);
+ gsub( "(^\n*|\n*$)", "", text );
+ return "" inline( text ) " \n" _dlist( substr(block, len + 1) );
+ } else if (match( block, "^([ \t]*\n)* ? ? ?:[ \t][^\n]+(\n|$)" \
+ "([^:\n \t][^\n]+(\n|$)" \
+ "|( ? ? ?\t| +)[^\n]+(\n|$)" \
+ "|([ \t]*\n)+( ? ? ?\t| +)[^\n]+(\n|$))*" \
+ )) {
+ len = RLENGTH; text = substr(block, 1, len);
+ if (match( text, "^ ? ? ?:[ \t][^\n]+(\n|$)" \
+ "([^:\n \t][^\n]+(\n|$)" \
+ "|( ? ? ?\t| +)[^\t\n ][^\n]*(\n|$))*$" \
+ )) p = 1; else p = 0;
+ sub( "^([ \t]*\n)*", "", text);
+ match(text, "^ ? ? ?:(\t| +)"); indent = RLENGTH;
+ sub( "^ ? ? ?:(\t| +)", "", text);
+ gsub( "(^|\n) {0," indent "}", "\n", text );
+ text = _block(text);
+ if (p) gsub("(^\n*|
\n*$)", "", text);
+ return "" text " \n" _dlist( substr(block, len + 1) );
}
}
@@ -389,23 +773,49 @@ function _list( block, last, LOCAL, p) {
# if this should be a paragraph item
# either previous item (last) or current item (p) contains blank lines
if (match(last, /\n[[:space:]]*\n/) || match(p, /\n[[:space:]]*\n/) ) {
- last = p; p = _block(p);
+ last = p; p = _nblock(p);
} else {
- last = p; p = _block(p);
+ last = p; p = _nblock(p);
sub( /^/, "", p );
sub( /<\/p>\n/, "", p );
}
sub( /\n$/, "", p );
- return "
" p " \n" _list( block, last );
+
+ # Task List (pandoc, custom)
+ if ( p ~ /^\[ \].*/ ) { return " " \
+ substr(p, 4) " \n" _list( block, last );
+ } else if ( p ~ /^\[-\].*/ ) { return " " \
+ substr(p, 4) " \n" _list( block, last );
+ } else if ( p ~ /^\[\?\].*/ ) { return " " \
+ substr(p, 4) " \n" _list( block, last );
+ } else if ( p ~ /^\[\/\].*/ ) { return " " \
+ substr(p, 4) " \n" _list( block, last );
+ } else if ( p ~ /^\[[xX]\].*/ ) { return " " \
+ substr(p, 4) " \n" _list( block, last );
+ } else if ( p ~ /^\[ \].*/ ) { return "
" \
+ substr(p, 7) "
\n" _list( block, last );
+ } else if ( p ~ /^\[-\].*/ ) { return "
" \
+ substr(p, 7) "
\n" _list( block, last );
+ } else if ( p ~ /^\[\?\].*/ ) { return "
" \
+ substr(p, 7) "
\n" _list( block, last );
+ } else if ( p ~ /^\[\/\].*/ ) { return "
" \
+ substr(p, 7) "
\n" _list( block, last );
+ } else if ( p ~ /^\[[xX]\].*/ ) { return "
" \
+ substr(p, 7) "
\n" _list( block, last );
+ } else { return "" p " \n" _list( block, last ); }
}
BEGIN {
# Global Vars
file = ""; rl_href[""] = ""; rl_title[""] = "";
if (ENVIRON["MD_HTML"] == "true") { AllowHTML = "true"; }
+ HL[1] = 0; HL[2] = 0; HL[3] = 0; HL[4] = 0; HL[5] = 0; HL[6] = 0;
+ # hls = "0 0 0 0 0 0";
# Buffering of full file ist necessary, e.g. to find reference links
while (getline) { file = file $0 "\n"; }
+ # Clean up MS-DOS line breaks
+ gsub(/\r\n/, "\n", file);
# Fill array of reference links
f = file; rl_id;
@@ -424,5 +834,5 @@ BEGIN {
# for (n in rl_href) { debug(n " | " rl_href[n] " | " rl_title[n] ); }
# Run Block Processing -> The Actual Markdown!
- printf "%s", _block( file );
+ printf "%s", _nblock( file );
}