X-Git-Url: http://git.plutz.net/?p=cgilite;a=blobdiff_plain;f=markdown.awk;h=361e6005c3fc42fa5e1f81859d69ec59d0c0a138;hp=a5c2eb1f587d79f203df3858819d33da565236bf;hb=6c1784b157a0990a9ad04ebce2176989e0fabcd0;hpb=b2cf4a3c3d0022d1abbeb69909500ea3147cbd10 diff --git a/markdown.awk b/markdown.awk index a5c2eb1..361e600 100755 --- a/markdown.awk +++ b/markdown.awk @@ -23,7 +23,8 @@ # - [x] Blockquotes # - [x] Lists (ordered, unordered) # - [x] Code blocks (using indention) -# - [ ] Horizontal rules +# - [x] Horizontal rules +# - [x] Verbatim HTML block (disabled by default) # # Basic Markdown - Inline elements: # --------------------------------- @@ -31,13 +32,18 @@ # - [x] Reference style links # - [x] Emphasis *em*/**strong** (*Asterisk*, _Underscore_) # - [x] `code`, also ``code containing `backticks` `` -# - [ ] Images / reference style images +# - [x] Images / reference style images # - [x] # - [x] backslash escapes +# - [x] Verbatim HTML inline (disabled by default) +# - [x] HTML escaping +# +# NOTE: Set the environment variable MD_HTML=true to enable verbatim HTML # # Extensions - Block elements: # ---------------------------- # - ? Heading identifiers (php md, pandoc) +# - [x] Automatic heading identifiers (custom) # - [x] Fenced code blocks (php md, pandoc) # - [-] Fenced code attributes # - [ ] Tables @@ -46,7 +52,7 @@ # - ? Grid table (pandoc) # - ? Pipe table (php md pandoc) # - [x] Line blocks (pandoc) -# - [ ] Task lists (pandoc) +# - [x] Task lists (pandoc) # - [ ] Definition lists (php md, pandoc) # - [-] Numbered example lists (pandoc) # - [-] Metadata blocks (pandoc) @@ -146,6 +152,32 @@ function inline( line, LOCAL, len, code, href, guard ) { return "" substr(line, 1, len) inline( substr(line, len + 1) ); } + # inline images + } else if ( match(line, /^!\[([^]]+)\]\(([^"\)]+)([ \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) ); + } else { + return "\""" inline( substr( line, len + 1) ); + } + + # reference style images + } else if ( match(line, /^!\[([^]]+)\] ?\[([^]]*)\]/ ) ) { + len = RLENGTH; + text = gensub(/^!\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\1", 1, line); + id = gensub(/^!\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\2", 1, line); + if ( ! id ) id = text; + if ( rl_href[id] && rl_title[id] ) { + return "\""" inline( substr( line, len + 1) ); + } else if ( rl_href[id] ) { + return "\""" inline( substr( line, len + 1) ); + } else { + return "" substr(line, 1, len) inline( substr(line, len + 1) ); + } + # ~~strikeout~~ (pandoc) } else if ( match(line, /^~~([[:graph:]]|[[:graph:]]([^~]|~[^~])*[[:graph:]])~~/) ) { len = RLENGTH; @@ -171,7 +203,7 @@ function inline( line, LOCAL, len, code, href, guard ) { return "" inline( substr( line, 3, len - 4 ) ) "" inline( substr( line, len + 1 ) ); # __strong__ - } else if ( match(line, "^__(([^_[:space:]]|" ieu ")|([^_[:space:]]|" ieu ")(" nu "|" ieu ")*([^_[:space:]]|" ieu "))__[[:space:]]") ) { + } else if ( match(line, "^__(([^_[:space:]]|" ieu ")|([^_[:space:]]|" ieu ")(" nu "|" ieu ")*([^_[:space:]]|" ieu "))__[[:space:][:punct:]]") ) { len = RLENGTH; return "" inline( substr( line, 3, len - 5 ) ) "" inline( substr( line, len) ); @@ -186,7 +218,7 @@ function inline( line, LOCAL, len, code, href, guard ) { return "" inline( substr( line, 2, len - 2 ) ) "" inline( substr( line, len + 1 ) ); # _em_ - } else if ( match(line, "^_(([^_[:space:]]|" isu ")|([^_[:space:]]|" isu ")(" nu "|" isu ")*([^_[:space:]]|" isu "))_[[:space:]]") ) { + } else if ( match(line, "^_(([^_[:space:]]|" isu ")|([^_[:space:]]|" isu ")(" nu "|" isu ")*([^_[:space:]]|" isu "))_[[:space:][:punct:]]") ) { len = RLENGTH; return "" inline( substr( line, 2, len - 3 ) ) "" inline( substr( line, len ) ); @@ -195,6 +227,20 @@ function inline( line, LOCAL, len, code, href, guard ) { len = RLENGTH; return "" inline( substr( line, 2, len - 2 ) ) "" 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; + return substr( line, 1, len ) inline(substr(line, len + 1)); + + # Escape lone HTML character + } else if ( match( line, /^[&<>"']/) ) { + return HTML(substr(line, 1, 1)) inline(substr(line, 2)); + # continue walk over string } else { return substr(line, 1, 1) inline( substr(line, 2) ); @@ -206,6 +252,28 @@ function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent ) { if ( block == "" ) { return ""; + + # HTML #2 #3 #4 $5 + } else if ( AllowHTML && match( block, /(^|\n) ? ? ?(|$)|<\?([^\?]|\?[^>])*(\?>|$)|]*(>|$)|])*(\]\]>|$))/) ) { + len = RLENGTH; st = RSTART; + return _block(substr(block, 1, st - 1)) substr(block, st, len) _block(substr(block, st + len)); + + # HTML #6 + } else if ( AllowHTML && match( tolower(block), /(^|\n) ? ? ?<\/?(address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[123456]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)([[:space:]\n>]|\/>)([^\n]|\n[ \t]*[^\n])*(\n[[:space:]]*\n|$)/) ) { + len = RLENGTH; st = RSTART; + return _block(substr(block, 1, st - 1)) substr(block, st, len) _block(substr(block, st + len)); + + # HTML #1 + } else if ( AllowHTML && match( tolower(block), /(^|\n) ? ? ?<(script|pre|style)([[:space:]\n>]).*(<\/script>|<\/pre>|<\/style>|$)/) ) { + len = RLENGTH; st = RSTART; + match( tolower(substr(block, st, len)), /(<\/script>|<\/pre>|<\/style>)/); + len = RSTART + RLENGTH; + return _block(substr(block, 1, st - 1)) substr(block, st, len) _block(substr(block, st + len)); + + # HTML #7 + } else if ( AllowHTML && match( block, /^ ? ? ?(<\/[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:]]*\/?>)([[:space:]]*\n)([^\n]|\n[ \t]*[^\n])*(\n[[:space:]]*\n|$)/) ) { + len = RLENGTH; st = RSTART; + return substr(block, st, len) _block(substr(block, st + len)); # Blockquote (leading >) } else if ( match( block, /^> /) ) { @@ -275,23 +343,32 @@ function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent ) { # First Order Heading } else if ( match( block, /^[^\n]+\n===+(\n|$)/ ) ) { len = RLENGTH; - return "

" inline( gensub( /\n.*$/, "", "g", block ) ) "

\n\n" \ + HL[1]++; HL[2] = 0; HL[3] = 0; HL[4] = 0; HL[5] = 0; HL[6] = 0; + return "

" inline( gensub( /\n.*$/, "", "g", block ) ) "

\n\n" \ _block( substr( block, len + 1 ) ); # Second Order Heading } else if ( match( block, /^[^\n]+\n---+(\n|$)/ ) ) { len = RLENGTH; - return "

" inline( gensub( /\n.*$/, "", "g", block ) ) "

\n\n" \ + HL[2]++; HL[3] = 0; HL[4] = 0; HL[5] = 0; HL[6] = 0; + return "

" inline( gensub( /\n.*$/, "", "g", block ) ) "

\n\n" \ _block( substr( block, len + 1) ); # Nth Order Heading - } else if ( match( block, /^#{1,6}[[:space:]]*[^\n]+([[:space:]]*#*)(\n|$)/ ) ) { + } else if ( match( block, /^#{1,6}[ \t]*[^\n]+([ \t]*#*)(\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" \ + htxt = gensub(/^#{1,6}[ \t]*(([^ \t\n]+|[ \t]+[^ \t\n#]|[ \t]+#+[^\n#])+)([ \t]*#*)(\n.*)?$/, "\\1", 1, block); + HL[hlvl]++; for ( n = hlvl + 1; n < 7; n++) { HL[n] = 0;} + hid = HL[1]; for ( n = 2; n <= hlvl; n++) { hid = hid "." HL[n] ; } + return "" inline( htxt ) "\n\n" \ _block( substr( block, len + 1) ); + # 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 { match( block, /(^|\n)[[:space:]]*(\n|$)/ ) || match( block, /$/ ); @@ -324,15 +401,25 @@ function _list( block, last, LOCAL, p) { sub( /<\/p>\n/, "", p ); } sub( /\n$/, "", p ); + + # Task List (pandoc) + if ( p ~ /^\[ \].*/ ) { p = "" substr(p, 4); } + else if ( p ~ /^\[[xX]\].*/ ) { p = "" substr(p, 4); } + else if ( p ~ /^

\[ \].*/ ) { p = "

" substr(p, 7); } + else if ( p ~ /^

\[[xX]\].*/ ) { p = "

" substr(p, 7); } 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; # 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;