X-Git-Url: http://git.plutz.net/?a=blobdiff_plain;f=markdown.awk;h=29dc64d48e6432100228c90a858463f88f645f8d;hb=d45722a72e37c6b214877a81d53b22bfb422a900;hp=973818067497779dc286f234b1c1ed489f2e4dfe;hpb=b089a33c27ef1af2b4d5816d4af7ef7809210a02;p=cgilite
diff --git a/markdown.awk b/markdown.awk
index 9738180..29dc64d 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,26 @@
#
# Extensions - Block elements:
# ----------------------------
+# - [x] Automatic
\n" inline( substr(line, RLENGTH + 1) );
+ return "
\n" inline( substr(line, RLENGTH + 1) );
# ``code spans``
} else if ( match( line, /^`+/) ) {
@@ -113,68 +132,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 +242,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 +274,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 +289,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 +307,23 @@ function inline( line, LOCAL, len, code, href, guard ) {
}
}
-function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent ) {
+# Nested Block, resets heading counters
+function _nblock( block, LOCAL, hlsav, sec ) {
+ # Keeping arrays in a local scope is awkward, so we serialize the HL array
+ # into the scalar hlsav
+ hlsav = HL[1] " " HL[2] " " HL[3] " " HL[4] " " HL[5] " " HL[6];
+ HL[1] = 0; HL[2] = 0; HL[3] = 0; HL[4] = 0; HL[5] = 0; HL[6] = 0;
+
+ # Block Level
+ blvl++;
+
+ block = _block( block );
+ sec = ""; for ( n = 1; n <= 6; n++ ) { sec = sec (HL[n]?"
\n" _block( gensub( /(^|\n)> /, "\n", "g", substr(block, 1, st - 1) ) ) "\n\n" \ + return "
\n" _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
" 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 "" 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 +571,124 @@ function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent ) {
gsub("(^|\n) {0," indent "}", "\n", list);
return "\n" HTML(substr(block, 1, len)) "
\n" _block( substr(block, len + 1) ); + } + + # Macros (standalone <" 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)) "" inline(block) "
\n"; } } @@ -389,26 +708,44 @@ 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 ); - # 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 "
\[ \].*/ ) { return "
" \ + substr(p, 7) "
\[-\].*/ ) { return "
" \ + substr(p, 7) "
\[\?\].*/ ) { return "
" \ + substr(p, 7) "
\[\/\].*/ ) { return "
" \ + substr(p, 7) "
\[[xX]\].*/ ) { return "
" \ + substr(p, 7) "