X-Git-Url: https://git.plutz.net//?a=blobdiff_plain;f=markdown.awk;h=34879d25c22ba3498633c366374a9feeac6be943;hb=7f444e3275361651eb5e0b89f09fd3afd8113e9f;hp=100242cb1ebb9b0e2a706a1025dbc552c51c190b;hpb=fa6be3f1cfe2df6e0fc89f26dc1100f53ce2c061;p=cgilite
diff --git a/markdown.awk b/markdown.awk
index 100242c..34879d2 100755
--- a/markdown.awk
+++ b/markdown.awk
@@ -5,6 +5,20 @@
# Meant to support all features of John Grubers basic Markdown
# + a number of common extensions, mostly inspired by Pandoc Markdown
+# Copyright 2021 - 2023 Paul Hänsch
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED âAS ISâ AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
# Supported Features / TODO:
# ==========================
# [x] done [ ] todo [-] not planned ? unsure
@@ -38,7 +52,10 @@
#
# Extensions - Block elements:
# ----------------------------
+# - [x] Automatic
terminates section
# - [x] Automatic heading identifiers (custom)
# - [x] Fenced code blocks (php md, pandoc)
# - [x] Fenced code attributes
@@ -52,7 +69,7 @@
# - [x] Pipe table (php md, pandoc)
# - [x] Line blocks (pandoc)
# - [x] Task lists (pandoc, custom)
-# - [ ] Definition lists (php md, pandoc)
+# - [x] Definition lists (php md, pandoc)
# - [-] Numbered example lists (pandoc)
# - [-] Metadata blocks (pandoc)
# - [x] Metadata blocks (custom)
@@ -89,12 +106,13 @@ function HTML ( text ) {
return text;
}
-function URL ( text ) {
+function URL ( text, sharp ) {
gsub( /&/, "%26", text );
gsub( /"/, "%22", text );
gsub( /'/, "%27", text );
+ gsub( /`/, "%60", text );
gsub( /\?/, "%3F", text );
- gsub( /#/, "%23", text );
+ if (sharp) gsub( /#/, "%23", text );
gsub( /\[/, "%5B", text );
gsub( /\]/, "%5D", text );
gsub( / /, "%20", text );
@@ -103,20 +121,13 @@ function URL ( text ) {
return text;
}
-function inline( line, LOCAL, len, code, href, guard ) {
- nu = "(\\\\\\\\|\\\\[^\\\\]|[^\\\\_]|_[[:alnum:]])*" # not underline (except when escaped)
- na = "(\\\\\\\\|\\\\[^\\\\]|[^\\\\\\*])*" # not asterisk (except when escaped)
- ieu = "_([^_[:space:]]|[^_[:space:]]" nu "[^_[:space:]])_" # inner (underline)
- isu = "__([^_[:space:]]|[^_[:space:]]" nu "[^_[:space:]])__" # inner (underline)
- iea = "\\*([^\\*[:space:]]|[^\\*[:space:]]" na "[^\\*[:space:]])\\*" # inner (asterisk)
- isa = "\\*\\*([^\\*[:space:]]|[^\\*[:space:]]" na "[^\\*[:space:]])\\*\\*" # inner (asterisk)
-
+function inline( line, LOCAL, len, text, 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) );
+ } else if ( line ~ /^\\./) {
+ return HTML(substr(line, 2, 1)) inline( substr(line, 3) );
# hard brakes
} else if ( match(line, /^ \n/) ) {
@@ -130,47 +141,59 @@ 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)
+ gsub( /^ | $/, "", code)
# escape HTML within code span
gsub( /&/, "\\&", code ); gsub( /, "\\<", code ); gsub( />/, "\\>", code );
return "" code "
" inline( substr( line, len + 1 ) )
}
+ # Macros
+ } else if ( match( line, /^<<([^>]|>[^>])+>>/ ) ) {
+ len = RLENGTH;
+ return "" HTML( substr( line, 3, len - 4 ) ) "
" inline(substr(line, len + 1));
+
# Wiki style links
- } else if ( match( line, /^\[\[([^\]\|]+)(\|([^\]]+))?\]\]/) ) {
+ } else if ( match( line, /^\[\[([^]|]+)(\|[^]]+)?\]\]/) ) {
len = RLENGTH;
- href = gensub(/^\[\[([^\]\|]+)(\|([^\]]+))?\]\]/, "\\1", 1, substr(line, 1, len) );
- text = gensub(/^\[\[([^\]\|]+)(\|([^\]]+))?\]\]/, "\\3", 1, substr(line, 1, len) );
+ 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) );
+ 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 = URL( substr( line, 2, len - 2) );
+ href = HTML( 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) );
+ href = HTML( substr( line, 2, len - 2) );
return "" href "" 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;
+ return substr( line, 1, len) inline(substr(line, len + 1));
+
# inline links
- # ,_______________________Image____________________________,
- } else if ( match(line, /^\[([^]]+|!\[[^]]*\]\([^"\)]+([ \t]+"[^"]+")?\)(\{[a-zA-Z \t-]*\})?)\]\(([^"\)]+)([[:space:]]+"([^"]+)")?\)/) ) {
+ } else if ( match(line, "^" lii "\\([\n\t ]*" lid "([\n\t ]+" lit ")?[\n\t ]*\\)") ) {
len = RLENGTH;
- 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) );
- } else {
- return "" inline( text ) "" inline( substr( line, len + 1) );
- }
+ text = href = title = substr( line, 1, len);
+ sub("^\\[", "", text); sub("\\]\\([\n\t ]*" lid "([\n\t ]+" lit ")?[\n\t ]*\\)$", "", text);
+ sub("^" lii "\\([\n\t ]*", "", href); sub("([\n\t ]+" lit ")?[\n\t ]*\\)$", "", href);
+ sub("^" lii "\\([\n\t ]*" lid, "", title); sub("[\n\t ]*\\)$", "", title); sub("^[\n\t ]+", "", title);
+
+ if ( match(href, /^<.*>$/) ) { sub(/^, "", href); sub(/>$/, "", href); }
+ if ( match(title, /^".*"$/) ) { sub(/^"/, "", title); sub(/"$/, "", title); }
+ else if ( match(title, /^'.*'$/) ) { sub(/^'/, "", title); sub(/'$/, "", title); }
+ else if ( match(title, /^\(.*\)$/) ) { sub(/^\(/, "", title); sub(/\)$/, "", title); }
+
+ gsub(/\\/, "", href); gsub(/\\/, "", title); gsub(/[\n\t]+/, " ", title);
+
+ return "" \
+ inline( text ) "" inline( substr( line, len + 1) );
# reference style links
} else if ( match(line, /^\[([^]]+)\] ?\[([^]]*)\]/ ) ) {
@@ -179,33 +202,41 @@ function inline( line, LOCAL, len, code, href, guard ) {
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 "" HTML(substr(line, 1, len)) inline( substr(line, len + 1) );
}
# inline images
- } else if ( match(line, /^!\[([^]]*)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)(\{([a-zA-Z \t-]*)\})?/) ) {
- len = RLENGTH;
- 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) );
- }
+ } else if ( match(line, "^!" lix "\\([\n\t ]*" lid "([\n\t ]+" lit ")?[\n\t ]*\\)(\\{[a-zA-Z \t-]*\\})?") ) {
+ len = RLENGTH; text = href = title = attrib = substr( line, 1, len);
+
+ sub("^!\\[", "", text);
+ sub("\\]\\([\n\t ]*" lid "([\n\t ]+" lit ")?[\n\t ]*\\)(\\{[a-zA-Z \t-]*\\})?$", "", text);
+
+ sub("^!" lix "\\([\n\t ]*", "", href);
+ sub("([\n\t ]+" lit ")?[\n\t ]*\\)(\\{[a-zA-Z \t-]*\\})?$", "", href);
+
+ sub("^!" lix "\\([\n\t ]*" lid, "", title);
+ sub("[\n\t ]*\\)(\\{[a-zA-Z \t-]*\\})?$", "", title);
+ sub("^[\n\t ]+", "", title);
+
+ sub("^!" lix "\\([\n\t ]*" lid "([\n\t ]+" lit ")?[\n\t ]*\\)", "", attrib);
+ sub(/^\{[ \t]*/, "", attrib); sub(/[ \t]*\}$/, "", attrib); gsub(/[ \t]+/, " ", attrib);
+
+ if ( match(href, /^<.*>$/) ) { sub(/^, "", href); sub(/>$/, "", href); }
+ if ( match(title, /^".*"$/) ) { sub(/^"/, "", title); sub(/"$/, "", title); }
+ else if ( match(title, /^'.*'$/) ) { sub(/^'/, "", title); sub(/'$/, "", title); }
+ else if ( match(title, /^\(.*\)$/) ) { sub(/^\(/, "", title); sub(/\)$/, "", title); }
+
+ gsub(/^[\t ]+$/, "", text); gsub(/\\/, "", href);
+ gsub(/\\/, "", title); gsub(/[\n\t]+/, " ", title);
+
+ return "
" inline( substr( line, len + 1) );
# reference style images
} else if ( match(line, /^!\[([^]]*)\] ?\[([^]]*)\]/ ) ) {
@@ -214,10 +245,10 @@ function inline( line, LOCAL, len, code, href, guard ) {
id = gensub(/^!\[([^\n]*)\] ?\[([^\n]*)\].*/, "\\2", 1, substr(line, 1, len) );
if ( ! id ) id = text;
if ( rl_href[id] && rl_title[id] ) {
- return "
" \
+ return "
" \
inline( substr( line, len + 1) );
} else if ( rl_href[id] ) {
- return "
" \
+ return "
" \
inline( substr( line, len + 1) );
} else {
return "" HTML(substr(line, 1, len)) inline( substr(line, len + 1) );
@@ -272,16 +303,6 @@ 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;
- return substr( line, 1, len) inline(substr(line, len + 1));
-
# Literal HTML entities
} else if ( match( line, /^&([a-zA-Z]{2,32}|#[0-9]{1,7}|#[xX][0-9a-fA-F]{1,6});/) ) {
len = RLENGTH;
@@ -305,8 +326,43 @@ function inline( line, LOCAL, len, code, href, guard ) {
}
}
-function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent, attrib ) {
- gsub( /^\n+|\n+$/, "", block );
+function headline( hlvl, htxt, attrib, LOCAL, sec, n, HL) {
+ match(hstack, /([0-9]+( [0-9]+){5})$/); split( substr(hstack, RSTART), HL);
+
+ for ( n = hlvl; n <= 6; n++ ) { sec = sec (HL[n]?"
\n" _block( gensub( /(^|\n)> /, "\n", "g", substr(block, 1, st - 1) ) ) "\n\n" \ - _block( substr(block, st + len) ); + text = substr(block, 1, st - 1); gsub( /(^|\n)> /, "\n", text ); + text = _nblock( text ); gsub( /^\n|\n$/, "", text ) + return "
" text "\n\n" _block( substr(block, st + len) ); # Pipe Tables (pandoc / php md / gfm ) } else if ( match(block, "^((\\|)?([^\n]+\\|)+[^\n]+(\\|)?)\n" \ - "((\\|)?:?(-+:?[\\|+])+:?-+:?(\\|)?)\n" \ + "((\\|)?(:?-+:?[\\|+])+:?-+:?(\\|)?)\n" \ "((\\|)?([^\n]+\\|)+[^\n]+(\\|)?(\n|$))+" ) ) { len = RLENGTH; st = RSTART; #initialize empty arrays @@ -399,22 +456,25 @@ function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent, attrib "(\\|([^\n]+\\|)+\n)+" \ "(\\+(:?=+:?\\+)+)\n" \ "((\\|([^\n]+\\|)+\n)+" \ - "\\+(-+\\+)+(\n|$))+", \ - tread \ + "\\+(-+\\+)+(\n|$))+" \ ) || \ match( block, "^()()()" \ "(\\+(:?-+:?\\+)+)\n" \ "((\\|([^\n]+\\|)+\n)+" \ - "\\+(-+\\+)+(\n|$))+", \ - tread \ + "\\+(-+\\+)+(\n|$))+" \ ) ) { len = RLENGTH; st = RSTART; #initialize empty arrays - split("", talign); split("", tarray); # split("", tread); + 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 - cols = split( gensub( /(^\+|\+$)/, "", "g", tread[4] ), talign, /\+/ ); + 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"; @@ -432,9 +492,9 @@ function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent, attrib # table header block = substr(block, match(block, /(\n|$)/) + 1 ); while ( match(block, "^\\|([^\n]+\\|)+\n") ) { - cols = split( gensub( /(^\||\|$)/, "", "g", \ - gensub( /(^|[^\\])\\\|/, "\\1\\|", "g", \ - substr(block, 1, match(block, /(\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++) @@ -443,7 +503,7 @@ function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent, attrib ttext = "\n
" HTML(text) "
" _block(substr(block, len + 1) );
+
+ # 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/, "", p ); - sub( /<\/p>\n/, "", p ); +function _list (block, mark, p, LOCAL, len, st, text, indent, task) { + if ( match(block, "^([ \t]*\n)*$")) return; + + match(block, "^" mark "[ \t]"); indent = RLENGTH; + sub("^" mark "[ \t]", "", block); + + if (match(block, /\n[ \t]*\n/)) p = 1; + + match( block, "\n" mark "[ \t][^\n]+(\n|$)" ); + st = (RLENGTH == -1) ? length(block) + 1 : RSTART; + text = substr(block, 1, st); block = substr(block, st + 1); + + gsub("\n {0," indent "}", "\n", text); + + task = match( text, /^\[ \]/ ) ? "
(
]|[^p]|<[^/]|[^<])*\n$" )) + gsub( "(^|
\n$)", "", text); + + return task text "(
]|[^p]|<[^/]|[^<])*\n$" )) + gsub( "(^|
\n$)", "", text); + + return "\[ \].*/ ) { return "
" \ - substr(p, 7) "
\[-\].*/ ) { return "
" \ - substr(p, 7) "
\[\?\].*/ ) { return "
" \ - substr(p, 7) "
\[\/\].*/ ) { return "
" \ - substr(p, 7) "
\[[xX]\].*/ ) { return "
" \ - substr(p, 7) "