]> git.mdlowis.com Git - projs/mikedlowis.github.io/commitdiff
Initial commit
authorMichael D. Lowis <mike@mdlowis.com>
Tue, 4 Aug 2015 01:30:52 +0000 (21:30 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Tue, 4 Aug 2015 01:30:52 +0000 (21:30 -0400)
Makefile [new file with mode: 0644]
pages.static/index.html [new file with mode: 0644]
pages/index.md [new file with mode: 0644]
scripts/Markdown.pl [new file with mode: 0755]
scripts/md2html.awk [new file with mode: 0755]
scripts/sw [new file with mode: 0755]
scripts/whereis [new file with mode: 0755]
style.css [new file with mode: 0644]
sw.conf [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..bd20752
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
+export PATH := $(PWD)/scripts/:$(PATH)
+
+all:
+       $(PWD)/scripts/sw ./pages
+
+clean:
+       rm -rf site.static
+
+## sw - suckless webframework - 2012 - MIT License - nibble <develsec.org>
+#
+#DESTDIR?=
+#PREFIX?=/usr/local
+#P=${DESTDIR}/${PREFIX}
+#
+#all: sw.conf
+#
+#sw.conf:
+#      cp sw.conf.def sw.conf
+#
+#install:
+#      mkdir -p ${P}/bin
+#      sed -e "s,/usr/bin/awk,`./whereis awk`,g" md2html.awk > ${P}/bin/md2html.awk
+#      chmod +x ${P}/bin/md2html.awk
+#      cp -f sw ${P}/bin/sw
+#      chmod +x ${P}/bin/sw
diff --git a/pages.static/index.html b/pages.static/index.html
new file mode 100644 (file)
index 0000000..a03d4ae
--- /dev/null
@@ -0,0 +1,116 @@
+<!doctype html>
+<html>
+<head>
+<title>mdlowis</title>
+<link rel="icon" href="/favicon.png" type="image/png">
+<meta charset="UTF-8">
+<style type="text/css">
+/* Based on werc.cat-v.org, suckless.org and garbe.us */
+
+/* General */
+body {
+       color: #aaa;
+       font-family: sans-serif;
+       font-size: 80%;
+       margin: 0;
+       padding: 0
+}
+
+/* Header */
+.header { background-color: #ccc; border: 0; }
+.header a { border: 0; color: #111; text-decoration: none; }
+.midHeader img { border: 0; }
+
+.headerTitle { font-size: 1.6em; font-weight: bold; margin: 0 0 0 0.5em; padding: 0.5em; }
+.headerTitle a { border: 0; text-decoration: none; }
+
+.headerSubTitle { font-size: 0.6em; font-weight: normal; margin-left: 1em; }
+
+/* Side */
+#side-bar {
+       width: 100%;
+       clear: both;
+       border: 0;
+       margin: 0;
+       padding: 0;
+       background-color: #ddd;
+}
+
+#side-bar ul {
+       margin: 0;
+       padding: 0.3em;
+       list-style-type: none;
+       list-style-image: none;
+       border: 0;
+}
+
+#side-bar li { display: inline; line-height: 1.6em; white-space: nowrap; }
+
+#side-bar ul li a {
+       margin: 0;
+       padding: 0.1em 1ex 0.1em 1ex;
+       color: #336699;
+       background-color: transparent;
+       text-decoration: none;
+       font-size: 1em;
+       border: 0;
+}
+
+#side-bar ul li a:hover { color: #111; text-decoration: none; }
+/* Main Copy */
+#main {
+       max-width: 70em;
+       color: #111;
+       margin: 0 auto 0 2em;
+       padding: 1em 3em 2em 1em;
+       border: 0;
+}
+
+#main a { color: #336699; text-decoration: none; }
+#main a:hover { text-decoration: underline; }
+#main h1, #main-copy h2 { color: #666; }
+#main ul { list-style-type: square; }
+
+/* Footer */
+#footer {
+       background-color: #ddd;
+       color: #111;
+       font-size: 91%;
+       padding: 2em;
+       clear: both;
+}
+
+#footer .left { text-align: left; float: left; clear: left; }
+#footer .right { text-align: right; }
+#footer a { color: #111; text-decoration: none; }
+#footer a:hover { text-decoration: underline; }
+
+abbr, acronym { border-bottom: 1px dotted #333; cursor: help; }
+blockquote { border-left: 1px solid #333; font-style: italic; padding: 1em; }
+hr { border-width: 0 0 0.1em 0; border-color: #666; }
+
+code, pre { font-size: 1.1em }
+pre { margin-left: 2em; }
+</style>
+</head>
+<body>
+<div class="header">
+<h1 class="headerTitle">
+<a href="index.html">mdlowis</a> <span class="headerSubtitle"></span>
+</h1>
+</div>
+<div id="side-bar">
+<ul>
+</ul>
+</div>
+<div id="main">
+<h1>Hello</h1>
+
+<p>aslkdjalksjdlakjsdlakjsdlkjas</p>
+</div>
+<div id="footer">
+<div class="right"><a href="https://github.com/jroimartin/sw">Powered by sw</a></div>
+</div>
+</body>
+</html>
diff --git a/pages/index.md b/pages/index.md
new file mode 100644 (file)
index 0000000..73c1069
--- /dev/null
@@ -0,0 +1,2 @@
+# Hello
+aslkdjalksjdlakjsdlakjsdlkjas
diff --git a/scripts/Markdown.pl b/scripts/Markdown.pl
new file mode 100755 (executable)
index 0000000..e4c8469
--- /dev/null
@@ -0,0 +1,1450 @@
+#!/usr/bin/perl
+
+#
+# Markdown -- A text-to-HTML conversion tool for web writers
+#
+# Copyright (c) 2004 John Gruber
+# <http://daringfireball.net/projects/markdown/>
+#
+
+
+package Markdown;
+require 5.006_000;
+use strict;
+use warnings;
+
+use Digest::MD5 qw(md5_hex);
+use vars qw($VERSION);
+$VERSION = '1.0.1';
+# Tue 14 Dec 2004
+
+## Disabled; causes problems under Perl 5.6.1:
+# use utf8;
+# binmode( STDOUT, ":utf8" );  # c.f.: http://acis.openlib.org/dev/perl-unicode-struggle.html
+
+
+#
+# Global default settings:
+#
+my $g_empty_element_suffix = " />";     # Change to ">" for HTML output
+my $g_tab_width = 4;
+
+
+#
+# Globals:
+#
+
+# Regex to match balanced [brackets]. See Friedl's
+# "Mastering Regular Expressions", 2nd Ed., pp. 328-331.
+my $g_nested_brackets;
+$g_nested_brackets = qr{
+       (?>                                                             # Atomic matching
+          [^\[\]]+                                                     # Anything other than brackets
+        | 
+          \[
+                (??{ $g_nested_brackets })             # Recursive set of nested brackets
+          \]
+       )*
+}x;
+
+
+# Table of hash values for escaped characters:
+my %g_escape_table;
+foreach my $char (split //, '\\`*_{}[]()>#+-.!') {
+       $g_escape_table{$char} = md5_hex($char);
+}
+
+
+# Global hashes, used by various utility routines
+my %g_urls;
+my %g_titles;
+my %g_html_blocks;
+
+# Used to track when we're inside an ordered or unordered list
+# (see _ProcessListItems() for details):
+my $g_list_level = 0;
+
+
+#### Blosxom plug-in interface ##########################################
+
+# Set $g_blosxom_use_meta to 1 to use Blosxom's meta plug-in to determine
+# which posts Markdown should process, using a "meta-markup: markdown"
+# header. If it's set to 0 (the default), Markdown will process all
+# entries.
+my $g_blosxom_use_meta = 0;
+
+sub start { 1; }
+sub story {
+       my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
+
+       if ( (! $g_blosxom_use_meta) or
+            (defined($meta::markup) and ($meta::markup =~ /^\s*markdown\s*$/i))
+            ){
+                       $$body_ref  = Markdown($$body_ref);
+     }
+     1;
+}
+
+
+#### Movable Type plug-in interface #####################################
+eval {require MT};  # Test to see if we're running in MT.
+unless ($@) {
+    require MT;
+    import  MT;
+    require MT::Template::Context;
+    import  MT::Template::Context;
+
+       eval {require MT::Plugin};  # Test to see if we're running >= MT 3.0.
+       unless ($@) {
+               require MT::Plugin;
+               import  MT::Plugin;
+               my $plugin = new MT::Plugin({
+                       name => "Markdown",
+                       description => "A plain-text-to-HTML formatting plugin. (Version: $VERSION)",
+                       doc_link => 'http://daringfireball.net/projects/markdown/'
+               });
+               MT->add_plugin( $plugin );
+       }
+
+       MT::Template::Context->add_container_tag(MarkdownOptions => sub {
+               my $ctx  = shift;
+               my $args = shift;
+               my $builder = $ctx->stash('builder');
+               my $tokens = $ctx->stash('tokens');
+
+               if (defined ($args->{'output'}) ) {
+                       $ctx->stash('markdown_output', lc $args->{'output'});
+               }
+
+               defined (my $str = $builder->build($ctx, $tokens) )
+                       or return $ctx->error($builder->errstr);
+               $str;           # return value
+       });
+
+       MT->add_text_filter('markdown' => {
+               label     => 'Markdown',
+               docs      => 'http://daringfireball.net/projects/markdown/',
+               on_format => sub {
+                       my $text = shift;
+                       my $ctx  = shift;
+                       my $raw  = 0;
+                   if (defined $ctx) {
+                       my $output = $ctx->stash('markdown_output'); 
+                               if (defined $output  &&  $output =~ m/^html/i) {
+                                       $g_empty_element_suffix = ">";
+                                       $ctx->stash('markdown_output', '');
+                               }
+                               elsif (defined $output  &&  $output eq 'raw') {
+                                       $raw = 1;
+                                       $ctx->stash('markdown_output', '');
+                               }
+                               else {
+                                       $raw = 0;
+                                       $g_empty_element_suffix = " />";
+                               }
+                       }
+                       $text = $raw ? $text : Markdown($text);
+                       $text;
+               },
+       });
+
+       # If SmartyPants is loaded, add a combo Markdown/SmartyPants text filter:
+       my $smartypants;
+
+       {
+               no warnings "once";
+               $smartypants = $MT::Template::Context::Global_filters{'smarty_pants'};
+       }
+
+       if ($smartypants) {
+               MT->add_text_filter('markdown_with_smartypants' => {
+                       label     => 'Markdown With SmartyPants',
+                       docs      => 'http://daringfireball.net/projects/markdown/',
+                       on_format => sub {
+                               my $text = shift;
+                               my $ctx  = shift;
+                               if (defined $ctx) {
+                                       my $output = $ctx->stash('markdown_output'); 
+                                       if (defined $output  &&  $output eq 'html') {
+                                               $g_empty_element_suffix = ">";
+                                       }
+                                       else {
+                                               $g_empty_element_suffix = " />";
+                                       }
+                               }
+                               $text = Markdown($text);
+                               $text = $smartypants->($text, '1');
+                       },
+               });
+       }
+}
+else {
+#### BBEdit/command-line text filter interface ##########################
+# Needs to be hidden from MT (and Blosxom when running in static mode).
+
+    # We're only using $blosxom::version once; tell Perl not to warn us:
+       no warnings 'once';
+    unless ( defined($blosxom::version) ) {
+               use warnings;
+
+               #### Check for command-line switches: #################
+               my %cli_opts;
+               use Getopt::Long;
+               Getopt::Long::Configure('pass_through');
+               GetOptions(\%cli_opts,
+                       'version',
+                       'shortversion',
+                       'html4tags',
+               );
+               if ($cli_opts{'version'}) {             # Version info
+                       print "\nThis is Markdown, version $VERSION.\n";
+                       print "Copyright 2004 John Gruber\n";
+                       print "http://daringfireball.net/projects/markdown/\n\n";
+                       exit 0;
+               }
+               if ($cli_opts{'shortversion'}) {                # Just the version number string.
+                       print $VERSION;
+                       exit 0;
+               }
+               if ($cli_opts{'html4tags'}) {                   # Use HTML tag style instead of XHTML
+                       $g_empty_element_suffix = ">";
+               }
+
+
+               #### Process incoming text: ###########################
+               my $text;
+               {
+                       local $/;               # Slurp the whole file
+                       $text = <>;
+               }
+        print Markdown($text);
+    }
+}
+
+
+
+sub Markdown {
+#
+# Main function. The order in which other subs are called here is
+# essential. Link and image substitutions need to happen before
+# _EscapeSpecialChars(), so that any *'s or _'s in the <a>
+# and <img> tags get encoded.
+#
+       my $text = shift;
+
+       # Clear the global hashes. If we don't clear these, you get conflicts
+       # from other articles when generating a page which contains more than
+       # one article (e.g. an index page that shows the N most recent
+       # articles):
+       %g_urls = ();
+       %g_titles = ();
+       %g_html_blocks = ();
+
+
+       # Standardize line endings:
+       $text =~ s{\r\n}{\n}g;  # DOS to Unix
+       $text =~ s{\r}{\n}g;    # Mac to Unix
+
+       # Make sure $text ends with a couple of newlines:
+       $text .= "\n\n";
+
+       # Convert all tabs to spaces.
+       $text = _Detab($text);
+
+       # Strip any lines consisting only of spaces and tabs.
+       # This makes subsequent regexen easier to write, because we can
+       # match consecutive blank lines with /\n+/ instead of something
+       # contorted like /[ \t]*\n+/ .
+       $text =~ s/^[ \t]+$//mg;
+
+       # Turn block-level HTML blocks into hash entries
+       $text = _HashHTMLBlocks($text);
+
+       # Strip link definitions, store in hashes.
+       $text = _StripLinkDefinitions($text);
+
+       $text = _RunBlockGamut($text);
+
+       $text = _UnescapeSpecialChars($text);
+
+       return $text . "\n";
+}
+
+
+sub _StripLinkDefinitions {
+#
+# Strips link definitions from text, stores the URLs and titles in
+# hash references.
+#
+       my $text = shift;
+       my $less_than_tab = $g_tab_width - 1;
+
+       # Link defs are in the form: ^[id]: url "optional title"
+       while ($text =~ s{
+                                               ^[ ]{0,$less_than_tab}\[(.+)\]: # id = $1
+                                                 [ \t]*
+                                                 \n?                           # maybe *one* newline
+                                                 [ \t]*
+                                               <?(\S+?)>?                      # url = $2
+                                                 [ \t]*
+                                                 \n?                           # maybe one newline
+                                                 [ \t]*
+                                               (?:
+                                                       (?<=\s)                 # lookbehind for whitespace
+                                                       ["(]
+                                                       (.+?)                   # title = $3
+                                                       [")]
+                                                       [ \t]*
+                                               )?      # title is optional
+                                               (?:\n+|\Z)
+                                       }
+                                       {}mx) {
+               $g_urls{lc $1} = _EncodeAmpsAndAngles( $2 );    # Link IDs are case-insensitive
+               if ($3) {
+                       $g_titles{lc $1} = $3;
+                       $g_titles{lc $1} =~ s/"/&quot;/g;
+               }
+       }
+
+       return $text;
+}
+
+
+sub _HashHTMLBlocks {
+       my $text = shift;
+       my $less_than_tab = $g_tab_width - 1;
+
+       # Hashify HTML blocks:
+       # We only want to do this for block-level HTML tags, such as headers,
+       # lists, and tables. That's because we still want to wrap <p>s around
+       # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
+       # phrase emphasis, and spans. The list of tags we're looking for is
+       # hard-coded:
+       my $block_tags_a = qr/p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del/;
+       my $block_tags_b = qr/p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math/;
+
+       # First, look for nested blocks, e.g.:
+       #       <div>
+       #               <div>
+       #               tags for inner block must be indented.
+       #               </div>
+       #       </div>
+       #
+       # The outermost tags must start at the left margin for this to match, and
+       # the inner nested divs must be indented.
+       # We need to do this before the next, more liberal match, because the next
+       # match will start at the first `<div>` and stop at the first `</div>`.
+       $text =~ s{
+                               (                                               # save in $1
+                                       ^                                       # start of line  (with /m)
+                                       <($block_tags_a)        # start tag = $2
+                                       \b                                      # word break
+                                       (.*\n)*?                        # any number of lines, minimally matching
+                                       </\2>                           # the matching end tag
+                                       [ \t]*                          # trailing spaces/tabs
+                                       (?=\n+|\Z)      # followed by a newline or end of document
+                               )
+                       }{
+                               my $key = md5_hex($1);
+                               $g_html_blocks{$key} = $1;
+                               "\n\n" . $key . "\n\n";
+                       }egmx;
+
+
+       #
+       # Now match more liberally, simply from `\n<tag>` to `</tag>\n`
+       #
+       $text =~ s{
+                               (                                               # save in $1
+                                       ^                                       # start of line  (with /m)
+                                       <($block_tags_b)        # start tag = $2
+                                       \b                                      # word break
+                                       (.*\n)*?                        # any number of lines, minimally matching
+                                       .*</\2>                         # the matching end tag
+                                       [ \t]*                          # trailing spaces/tabs
+                                       (?=\n+|\Z)      # followed by a newline or end of document
+                               )
+                       }{
+                               my $key = md5_hex($1);
+                               $g_html_blocks{$key} = $1;
+                               "\n\n" . $key . "\n\n";
+                       }egmx;
+       # Special case just for <hr />. It was easier to make a special case than
+       # to make the other regex more complicated.     
+       $text =~ s{
+                               (?:
+                                       (?<=\n\n)               # Starting after a blank line
+                                       |                               # or
+                                       \A\n?                   # the beginning of the doc
+                               )
+                               (                                               # save in $1
+                                       [ ]{0,$less_than_tab}
+                                       <(hr)                           # start tag = $2
+                                       \b                                      # word break
+                                       ([^<>])*?                       # 
+                                       /?>                                     # the matching end tag
+                                       [ \t]*
+                                       (?=\n{2,}|\Z)           # followed by a blank line or end of document
+                               )
+                       }{
+                               my $key = md5_hex($1);
+                               $g_html_blocks{$key} = $1;
+                               "\n\n" . $key . "\n\n";
+                       }egx;
+
+       # Special case for standalone HTML comments:
+       $text =~ s{
+                               (?:
+                                       (?<=\n\n)               # Starting after a blank line
+                                       |                               # or
+                                       \A\n?                   # the beginning of the doc
+                               )
+                               (                                               # save in $1
+                                       [ ]{0,$less_than_tab}
+                                       (?s:
+                                               <!
+                                               (--.*?--\s*)+
+                                               >
+                                       )
+                                       [ \t]*
+                                       (?=\n{2,}|\Z)           # followed by a blank line or end of document
+                               )
+                       }{
+                               my $key = md5_hex($1);
+                               $g_html_blocks{$key} = $1;
+                               "\n\n" . $key . "\n\n";
+                       }egx;
+
+
+       return $text;
+}
+
+
+sub _RunBlockGamut {
+#
+# These are all the transformations that form block-level
+# tags like paragraphs, headers, and list items.
+#
+       my $text = shift;
+
+       $text = _DoHeaders($text);
+
+       # Do Horizontal Rules:
+       $text =~ s{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
+       $text =~ s{^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
+       $text =~ s{^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
+
+       $text = _DoLists($text);
+
+       $text = _DoCodeBlocks($text);
+
+       $text = _DoBlockQuotes($text);
+
+       # We already ran _HashHTMLBlocks() before, in Markdown(), but that
+       # was to escape raw HTML in the original Markdown source. This time,
+       # we're escaping the markup we've just created, so that we don't wrap
+       # <p> tags around block-level tags.
+       $text = _HashHTMLBlocks($text);
+
+       $text = _FormParagraphs($text);
+
+       return $text;
+}
+
+
+sub _RunSpanGamut {
+#
+# These are all the transformations that occur *within* block-level
+# tags like paragraphs, headers, and list items.
+#
+       my $text = shift;
+
+       $text = _DoCodeSpans($text);
+
+       $text = _EscapeSpecialChars($text);
+
+       # Process anchor and image tags. Images must come first,
+       # because ![foo][f] looks like an anchor.
+       $text = _DoImages($text);
+       $text = _DoAnchors($text);
+
+       # Make links out of things like `<http://example.com/>`
+       # Must come after _DoAnchors(), because you can use < and >
+       # delimiters in inline links like [this](<url>).
+       $text = _DoAutoLinks($text);
+
+       $text = _EncodeAmpsAndAngles($text);
+
+       $text = _DoItalicsAndBold($text);
+
+       # Do hard breaks:
+       $text =~ s/ {2,}\n/ <br$g_empty_element_suffix\n/g;
+
+       return $text;
+}
+
+
+sub _EscapeSpecialChars {
+       my $text = shift;
+       my $tokens ||= _TokenizeHTML($text);
+
+       $text = '';   # rebuild $text from the tokens
+#      my $in_pre = 0;  # Keep track of when we're inside <pre> or <code> tags.
+#      my $tags_to_skip = qr!<(/?)(?:pre|code|kbd|script|math)[\s>]!;
+
+       foreach my $cur_token (@$tokens) {
+               if ($cur_token->[0] eq "tag") {
+                       # Within tags, encode * and _ so they don't conflict
+                       # with their use in Markdown for italics and strong.
+                       # We're replacing each such character with its
+                       # corresponding MD5 checksum value; this is likely
+                       # overkill, but it should prevent us from colliding
+                       # with the escape values by accident.
+                       $cur_token->[1] =~  s! \* !$g_escape_table{'*'}!gx;
+                       $cur_token->[1] =~  s! _  !$g_escape_table{'_'}!gx;
+                       $text .= $cur_token->[1];
+               } else {
+                       my $t = $cur_token->[1];
+                       $t = _EncodeBackslashEscapes($t);
+                       $text .= $t;
+               }
+       }
+       return $text;
+}
+
+
+sub _DoAnchors {
+#
+# Turn Markdown link shortcuts into XHTML <a> tags.
+#
+       my $text = shift;
+
+       #
+       # First, handle reference-style links: [link text] [id]
+       #
+       $text =~ s{
+               (                                       # wrap whole match in $1
+                 \[
+                   ($g_nested_brackets)        # link text = $2
+                 \]
+
+                 [ ]?                          # one optional space
+                 (?:\n[ ]*)?           # one optional newline followed by spaces
+
+                 \[
+                   (.*?)               # id = $3
+                 \]
+               )
+       }{
+               my $result;
+               my $whole_match = $1;
+               my $link_text   = $2;
+               my $link_id     = lc $3;
+
+               if ($link_id eq "") {
+                       $link_id = lc $link_text;     # for shortcut links like [this][].
+               }
+
+               if (defined $g_urls{$link_id}) {
+                       my $url = $g_urls{$link_id};
+                       $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
+                       $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
+                       $result = "<a href=\"$url\"";
+                       if ( defined $g_titles{$link_id} ) {
+                               my $title = $g_titles{$link_id};
+                               $title =~ s! \* !$g_escape_table{'*'}!gx;
+                               $title =~ s!  _ !$g_escape_table{'_'}!gx;
+                               $result .=  " title=\"$title\"";
+                       }
+                       $result .= ">$link_text</a>";
+               }
+               else {
+                       $result = $whole_match;
+               }
+               $result;
+       }xsge;
+
+       #
+       # Next, inline-style links: [link text](url "optional title")
+       #
+       $text =~ s{
+               (                               # wrap whole match in $1
+                 \[
+                   ($g_nested_brackets)        # link text = $2
+                 \]
+                 \(                    # literal paren
+                       [ \t]*
+                       <?(.*?)>?       # href = $3
+                       [ \t]*
+                       (                       # $4
+                         (['"])        # quote char = $5
+                         (.*?)         # Title = $6
+                         \5            # matching quote
+                       )?                      # title is optional
+                 \)
+               )
+       }{
+               my $result;
+               my $whole_match = $1;
+               my $link_text   = $2;
+               my $url                 = $3;
+               my $title               = $6;
+
+               $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
+               $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
+               $result = "<a href=\"$url\"";
+
+               if (defined $title) {
+                       $title =~ s/"/&quot;/g;
+                       $title =~ s! \* !$g_escape_table{'*'}!gx;
+                       $title =~ s!  _ !$g_escape_table{'_'}!gx;
+                       $result .=  " title=\"$title\"";
+               }
+
+               $result .= ">$link_text</a>";
+
+               $result;
+       }xsge;
+
+       return $text;
+}
+
+
+sub _DoImages {
+#
+# Turn Markdown image shortcuts into <img> tags.
+#
+       my $text = shift;
+
+       #
+       # First, handle reference-style labeled images: ![alt text][id]
+       #
+       $text =~ s{
+               (                               # wrap whole match in $1
+                 !\[
+                   (.*?)               # alt text = $2
+                 \]
+
+                 [ ]?                          # one optional space
+                 (?:\n[ ]*)?           # one optional newline followed by spaces
+
+                 \[
+                   (.*?)               # id = $3
+                 \]
+
+               )
+       }{
+               my $result;
+               my $whole_match = $1;
+               my $alt_text    = $2;
+               my $link_id     = lc $3;
+
+               if ($link_id eq "") {
+                       $link_id = lc $alt_text;     # for shortcut links like ![this][].
+               }
+
+               $alt_text =~ s/"/&quot;/g;
+               if (defined $g_urls{$link_id}) {
+                       my $url = $g_urls{$link_id};
+                       $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
+                       $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
+                       $result = "<img src=\"$url\" alt=\"$alt_text\"";
+                       if (defined $g_titles{$link_id}) {
+                               my $title = $g_titles{$link_id};
+                               $title =~ s! \* !$g_escape_table{'*'}!gx;
+                               $title =~ s!  _ !$g_escape_table{'_'}!gx;
+                               $result .=  " title=\"$title\"";
+                       }
+                       $result .= $g_empty_element_suffix;
+               }
+               else {
+                       # If there's no such link ID, leave intact:
+                       $result = $whole_match;
+               }
+
+               $result;
+       }xsge;
+
+       #
+       # Next, handle inline images:  ![alt text](url "optional title")
+       # Don't forget: encode * and _
+
+       $text =~ s{
+               (                               # wrap whole match in $1
+                 !\[
+                   (.*?)               # alt text = $2
+                 \]
+                 \(                    # literal paren
+                       [ \t]*
+                       <?(\S+?)>?      # src url = $3
+                       [ \t]*
+                       (                       # $4
+                         (['"])        # quote char = $5
+                         (.*?)         # title = $6
+                         \5            # matching quote
+                         [ \t]*
+                       )?                      # title is optional
+                 \)
+               )
+       }{
+               my $result;
+               my $whole_match = $1;
+               my $alt_text    = $2;
+               my $url                 = $3;
+               my $title               = '';
+               if (defined($6)) {
+                       $title          = $6;
+               }
+
+               $alt_text =~ s/"/&quot;/g;
+               $title    =~ s/"/&quot;/g;
+               $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
+               $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
+               $result = "<img src=\"$url\" alt=\"$alt_text\"";
+               if (defined $title) {
+                       $title =~ s! \* !$g_escape_table{'*'}!gx;
+                       $title =~ s!  _ !$g_escape_table{'_'}!gx;
+                       $result .=  " title=\"$title\"";
+               }
+               $result .= $g_empty_element_suffix;
+
+               $result;
+       }xsge;
+
+       return $text;
+}
+
+
+sub _DoHeaders {
+       my $text = shift;
+
+       # Setext-style headers:
+       #         Header 1
+       #         ========
+       #  
+       #         Header 2
+       #         --------
+       #
+       $text =~ s{ ^(.+)[ \t]*\n=+[ \t]*\n+ }{
+               "<h1>"  .  _RunSpanGamut($1)  .  "</h1>\n\n";
+       }egmx;
+
+       $text =~ s{ ^(.+)[ \t]*\n-+[ \t]*\n+ }{
+               "<h2>"  .  _RunSpanGamut($1)  .  "</h2>\n\n";
+       }egmx;
+
+
+       # atx-style headers:
+       #       # Header 1
+       #       ## Header 2
+       #       ## Header 2 with closing hashes ##
+       #       ...
+       #       ###### Header 6
+       #
+       $text =~ s{
+                       ^(\#{1,6})      # $1 = string of #'s
+                       [ \t]*
+                       (.+?)           # $2 = Header text
+                       [ \t]*
+                       \#*                     # optional closing #'s (not counted)
+                       \n+
+               }{
+                       my $h_level = length($1);
+                       "<h$h_level>"  .  _RunSpanGamut($2)  .  "</h$h_level>\n\n";
+               }egmx;
+
+       return $text;
+}
+
+
+sub _DoLists {
+#
+# Form HTML ordered (numbered) and unordered (bulleted) lists.
+#
+       my $text = shift;
+       my $less_than_tab = $g_tab_width - 1;
+
+       # Re-usable patterns to match list item bullets and number markers:
+       my $marker_ul  = qr/[*+-]/;
+       my $marker_ol  = qr/\d+[.]/;
+       my $marker_any = qr/(?:$marker_ul|$marker_ol)/;
+
+       # Re-usable pattern to match any entirel ul or ol list:
+       my $whole_list = qr{
+               (                                                               # $1 = whole list
+                 (                                                             # $2
+                       [ ]{0,$less_than_tab}
+                       (${marker_any})                         # $3 = first list item marker
+                       [ \t]+
+                 )
+                 (?s:.+?)
+                 (                                                             # $4
+                         \z
+                       |
+                         \n{2,}
+                         (?=\S)
+                         (?!                                           # Negative lookahead for another list item marker
+                               [ \t]*
+                               ${marker_any}[ \t]+
+                         )
+                 )
+               )
+       }mx;
+
+       # We use a different prefix before nested lists than top-level lists.
+       # See extended comment in _ProcessListItems().
+       #
+       # Note: There's a bit of duplication here. My original implementation
+       # created a scalar regex pattern as the conditional result of the test on
+       # $g_list_level, and then only ran the $text =~ s{...}{...}egmx
+       # substitution once, using the scalar as the pattern. This worked,
+       # everywhere except when running under MT on my hosting account at Pair
+       # Networks. There, this caused all rebuilds to be killed by the reaper (or
+       # perhaps they crashed, but that seems incredibly unlikely given that the
+       # same script on the same server ran fine *except* under MT. I've spent
+       # more time trying to figure out why this is happening than I'd like to
+       # admit. My only guess, backed up by the fact that this workaround works,
+       # is that Perl optimizes the substition when it can figure out that the
+       # pattern will never change, and when this optimization isn't on, we run
+       # afoul of the reaper. Thus, the slightly redundant code to that uses two
+       # static s/// patterns rather than one conditional pattern.
+
+       if ($g_list_level) {
+               $text =~ s{
+                               ^
+                               $whole_list
+                       }{
+                               my $list = $1;
+                               my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";
+                               # Turn double returns into triple returns, so that we can make a
+                               # paragraph for the last item in a list, if necessary:
+                               $list =~ s/\n{2,}/\n\n\n/g;
+                               my $result = _ProcessListItems($list, $marker_any);
+                               $result = "<$list_type>\n" . $result . "</$list_type>\n";
+                               $result;
+                       }egmx;
+       }
+       else {
+               $text =~ s{
+                               (?:(?<=\n\n)|\A\n?)
+                               $whole_list
+                       }{
+                               my $list = $1;
+                               my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";
+                               # Turn double returns into triple returns, so that we can make a
+                               # paragraph for the last item in a list, if necessary:
+                               $list =~ s/\n{2,}/\n\n\n/g;
+                               my $result = _ProcessListItems($list, $marker_any);
+                               $result = "<$list_type>\n" . $result . "</$list_type>\n";
+                               $result;
+                       }egmx;
+       }
+
+
+       return $text;
+}
+
+
+sub _ProcessListItems {
+#
+#      Process the contents of a single ordered or unordered list, splitting it
+#      into individual list items.
+#
+
+       my $list_str = shift;
+       my $marker_any = shift;
+
+
+       # The $g_list_level global keeps track of when we're inside a list.
+       # Each time we enter a list, we increment it; when we leave a list,
+       # we decrement. If it's zero, we're not in a list anymore.
+       #
+       # We do this because when we're not inside a list, we want to treat
+       # something like this:
+       #
+       #               I recommend upgrading to version
+       #               8. Oops, now this line is treated
+       #               as a sub-list.
+       #
+       # As a single paragraph, despite the fact that the second line starts
+       # with a digit-period-space sequence.
+       #
+       # Whereas when we're inside a list (or sub-list), that line will be
+       # treated as the start of a sub-list. What a kludge, huh? This is
+       # an aspect of Markdown's syntax that's hard to parse perfectly
+       # without resorting to mind-reading. Perhaps the solution is to
+       # change the syntax rules such that sub-lists must start with a
+       # starting cardinal number; e.g. "1." or "a.".
+
+       $g_list_level++;
+
+       # trim trailing blank lines:
+       $list_str =~ s/\n{2,}\z/\n/;
+
+
+       $list_str =~ s{
+               (\n)?                                                   # leading line = $1
+               (^[ \t]*)                                               # leading whitespace = $2
+               ($marker_any) [ \t]+                    # list marker = $3
+               ((?s:.+?)                                               # list item text   = $4
+               (\n{1,2}))
+               (?= \n* (\z | \2 ($marker_any) [ \t]+))
+       }{
+               my $item = $4;
+               my $leading_line = $1;
+               my $leading_space = $2;
+
+               if ($leading_line or ($item =~ m/\n{2,}/)) {
+                       $item = _RunBlockGamut(_Outdent($item));
+               }
+               else {
+                       # Recursion for sub-lists:
+                       $item = _DoLists(_Outdent($item));
+                       chomp $item;
+                       $item = _RunSpanGamut($item);
+               }
+
+               "<li>" . $item . "</li>\n";
+       }egmx;
+
+       $g_list_level--;
+       return $list_str;
+}
+
+
+
+sub _DoCodeBlocks {
+#
+#      Process Markdown `<pre><code>` blocks.
+#      
+
+       my $text = shift;
+
+       $text =~ s{
+                       (?:\n\n|\A)
+                       (                   # $1 = the code block -- one or more lines, starting with a space/tab
+                         (?:
+                           (?:[ ]{$g_tab_width} | \t)  # Lines must start with a tab or a tab-width of spaces
+                           .*\n+
+                         )+
+                       )
+                       ((?=^[ ]{0,$g_tab_width}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
+               }{
+                       my $codeblock = $1;
+                       my $result; # return value
+
+                       $codeblock = _EncodeCode(_Outdent($codeblock));
+                       $codeblock = _Detab($codeblock);
+                       $codeblock =~ s/\A\n+//; # trim leading newlines
+                       $codeblock =~ s/\s+\z//; # trim trailing whitespace
+
+                       $result = "\n\n<pre><code>" . $codeblock . "\n</code></pre>\n\n";
+
+                       $result;
+               }egmx;
+
+       return $text;
+}
+
+
+sub _DoCodeSpans {
+#
+#      *       Backtick quotes are used for <code></code> spans.
+# 
+#      *       You can use multiple backticks as the delimiters if you want to
+#              include literal backticks in the code span. So, this input:
+#     
+#         Just type ``foo `bar` baz`` at the prompt.
+#     
+#      Will translate to:
+#     
+#         <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
+#     
+#              There's no arbitrary limit to the number of backticks you
+#              can use as delimters. If you need three consecutive backticks
+#              in your code, use four for delimiters, etc.
+#
+#      *       You can use spaces to get literal backticks at the edges:
+#     
+#         ... type `` `bar` `` ...
+#     
+#      Turns to:
+#     
+#         ... type <code>`bar`</code> ...
+#
+
+       my $text = shift;
+
+       $text =~ s@
+                       (`+)            # $1 = Opening run of `
+                       (.+?)           # $2 = The code block
+                       (?<!`)
+                       \1                      # Matching closer
+                       (?!`)
+               @
+                       my $c = "$2";
+                       $c =~ s/^[ \t]*//g; # leading whitespace
+                       $c =~ s/[ \t]*$//g; # trailing whitespace
+                       $c = _EncodeCode($c);
+                       "<code>$c</code>";
+               @egsx;
+
+       return $text;
+}
+
+
+sub _EncodeCode {
+#
+# Encode/escape certain characters inside Markdown code runs.
+# The point is that in code, these characters are literals,
+# and lose their special Markdown meanings.
+#
+    local $_ = shift;
+
+       # Encode all ampersands; HTML entities are not
+       # entities within a Markdown code span.
+       s/&/&amp;/g;
+
+       # Encode $'s, but only if we're running under Blosxom.
+       # (Blosxom interpolates Perl variables in article bodies.)
+       {
+               no warnings 'once';
+       if (defined($blosxom::version)) {
+               s/\$/&#036;/g;  
+       }
+    }
+
+
+       # Do the angle bracket song and dance:
+       s! <  !&lt;!gx;
+       s! >  !&gt;!gx;
+
+       # Now, escape characters that are magic in Markdown:
+       s! \* !$g_escape_table{'*'}!gx;
+       s! _  !$g_escape_table{'_'}!gx;
+       s! {  !$g_escape_table{'{'}!gx;
+       s! }  !$g_escape_table{'}'}!gx;
+       s! \[ !$g_escape_table{'['}!gx;
+       s! \] !$g_escape_table{']'}!gx;
+       s! \\ !$g_escape_table{'\\'}!gx;
+
+       return $_;
+}
+
+
+sub _DoItalicsAndBold {
+       my $text = shift;
+
+       # <strong> must go first:
+       $text =~ s{ (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 }
+               {<strong>$2</strong>}gsx;
+
+       $text =~ s{ (\*|_) (?=\S) (.+?) (?<=\S) \1 }
+               {<em>$2</em>}gsx;
+
+       return $text;
+}
+
+
+sub _DoBlockQuotes {
+       my $text = shift;
+
+       $text =~ s{
+                 (                                                             # Wrap whole match in $1
+                       (
+                         ^[ \t]*>[ \t]?                        # '>' at the start of a line
+                           .+\n                                        # rest of the first line
+                         (.+\n)*                                       # subsequent consecutive lines
+                         \n*                                           # blanks
+                       )+
+                 )
+               }{
+                       my $bq = $1;
+                       $bq =~ s/^[ \t]*>[ \t]?//gm;    # trim one level of quoting
+                       $bq =~ s/^[ \t]+$//mg;                  # trim whitespace-only lines
+                       $bq = _RunBlockGamut($bq);              # recurse
+
+                       $bq =~ s/^/  /g;
+                       # These leading spaces screw with <pre> content, so we need to fix that:
+                       $bq =~ s{
+                                       (\s*<pre>.+?</pre>)
+                               }{
+                                       my $pre = $1;
+                                       $pre =~ s/^  //mg;
+                                       $pre;
+                               }egsx;
+
+                       "<blockquote>\n$bq\n</blockquote>\n\n";
+               }egmx;
+
+
+       return $text;
+}
+
+
+sub _FormParagraphs {
+#
+#      Params:
+#              $text - string to process with html <p> tags
+#
+       my $text = shift;
+
+       # Strip leading and trailing lines:
+       $text =~ s/\A\n+//;
+       $text =~ s/\n+\z//;
+
+       my @grafs = split(/\n{2,}/, $text);
+
+       #
+       # Wrap <p> tags.
+       #
+       foreach (@grafs) {
+               unless (defined( $g_html_blocks{$_} )) {
+                       $_ = _RunSpanGamut($_);
+                       s/^([ \t]*)/<p>/;
+                       $_ .= "</p>";
+               }
+       }
+
+       #
+       # Unhashify HTML blocks
+       #
+       foreach (@grafs) {
+               if (defined( $g_html_blocks{$_} )) {
+                       $_ = $g_html_blocks{$_};
+               }
+       }
+
+       return join "\n\n", @grafs;
+}
+
+
+sub _EncodeAmpsAndAngles {
+# Smart processing for ampersands and angle brackets that need to be encoded.
+
+       my $text = shift;
+
+       # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
+       #   http://bumppo.net/projects/amputator/
+       $text =~ s/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/&amp;/g;
+
+       # Encode naked <'s
+       $text =~ s{<(?![a-z/?\$!])}{&lt;}gi;
+
+       return $text;
+}
+
+
+sub _EncodeBackslashEscapes {
+#
+#   Parameter:  String.
+#   Returns:    The string, with after processing the following backslash
+#               escape sequences.
+#
+    local $_ = shift;
+
+    s! \\\\  !$g_escape_table{'\\'}!gx;                # Must process escaped backslashes first.
+    s! \\`   !$g_escape_table{'`'}!gx;
+    s! \\\*  !$g_escape_table{'*'}!gx;
+    s! \\_   !$g_escape_table{'_'}!gx;
+    s! \\\{  !$g_escape_table{'{'}!gx;
+    s! \\\}  !$g_escape_table{'}'}!gx;
+    s! \\\[  !$g_escape_table{'['}!gx;
+    s! \\\]  !$g_escape_table{']'}!gx;
+    s! \\\(  !$g_escape_table{'('}!gx;
+    s! \\\)  !$g_escape_table{')'}!gx;
+    s! \\>   !$g_escape_table{'>'}!gx;
+    s! \\\#  !$g_escape_table{'#'}!gx;
+    s! \\\+  !$g_escape_table{'+'}!gx;
+    s! \\\-  !$g_escape_table{'-'}!gx;
+    s! \\\.  !$g_escape_table{'.'}!gx;
+    s{ \\!  }{$g_escape_table{'!'}}gx;
+
+    return $_;
+}
+
+
+sub _DoAutoLinks {
+       my $text = shift;
+
+       $text =~ s{<((https?|ftp):[^'">\s]+)>}{<a href="$1">$1</a>}gi;
+
+       # Email addresses: <address@domain.foo>
+       $text =~ s{
+               <
+        (?:mailto:)?
+               (
+                       [-.\w]+
+                       \@
+                       [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
+               )
+               >
+       }{
+               _EncodeEmailAddress( _UnescapeSpecialChars($1) );
+       }egix;
+
+       return $text;
+}
+
+
+sub _EncodeEmailAddress {
+#
+#      Input: an email address, e.g. "foo@example.com"
+#
+#      Output: the email address as a mailto link, with each character
+#              of the address encoded as either a decimal or hex entity, in
+#              the hopes of foiling most address harvesting spam bots. E.g.:
+#
+#        <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
+#       x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
+#       &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
+#
+#      Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
+#      mailing list: <http://tinyurl.com/yu7ue>
+#
+
+       my $addr = shift;
+
+       srand;
+       my @encode = (
+               sub { '&#' .                 ord(shift)   . ';' },
+               sub { '&#x' . sprintf( "%X", ord(shift) ) . ';' },
+               sub {                            shift          },
+       );
+
+       $addr = "mailto:" . $addr;
+
+       $addr =~ s{(.)}{
+               my $char = $1;
+               if ( $char eq '@' ) {
+                       # this *must* be encoded. I insist.
+                       $char = $encode[int rand 1]->($char);
+               } elsif ( $char ne ':' ) {
+                       # leave ':' alone (to spot mailto: later)
+                       my $r = rand;
+                       # roughly 10% raw, 45% hex, 45% dec
+                       $char = (
+                               $r > .9   ?  $encode[2]->($char)  :
+                               $r < .45  ?  $encode[1]->($char)  :
+                                                        $encode[0]->($char)
+                       );
+               }
+               $char;
+       }gex;
+
+       $addr = qq{<a href="$addr">$addr</a>};
+       $addr =~ s{">.+?:}{">}; # strip the mailto: from the visible part
+
+       return $addr;
+}
+
+
+sub _UnescapeSpecialChars {
+#
+# Swap back in all the special characters we've hidden.
+#
+       my $text = shift;
+
+       while( my($char, $hash) = each(%g_escape_table) ) {
+               $text =~ s/$hash/$char/g;
+       }
+    return $text;
+}
+
+
+sub _TokenizeHTML {
+#
+#   Parameter:  String containing HTML markup.
+#   Returns:    Reference to an array of the tokens comprising the input
+#               string. Each token is either a tag (possibly with nested,
+#               tags contained therein, such as <a href="<MTFoo>">, or a
+#               run of text between tags. Each element of the array is a
+#               two-element array; the first is either 'tag' or 'text';
+#               the second is the actual value.
+#
+#
+#   Derived from the _tokenize() subroutine from Brad Choate's MTRegex plugin.
+#       <http://www.bradchoate.com/past/mtregex.php>
+#
+
+    my $str = shift;
+    my $pos = 0;
+    my $len = length $str;
+    my @tokens;
+
+    my $depth = 6;
+    my $nested_tags = join('|', ('(?:<[a-z/!$](?:[^<>]') x $depth) . (')*>)' x  $depth);
+    my $match = qr/(?s: <! ( -- .*? -- \s* )+ > ) |  # comment
+                   (?s: <\? .*? \?> ) |              # processing instruction
+                   $nested_tags/ix;                   # nested tags
+
+    while ($str =~ m/($match)/g) {
+        my $whole_tag = $1;
+        my $sec_start = pos $str;
+        my $tag_start = $sec_start - length $whole_tag;
+        if ($pos < $tag_start) {
+            push @tokens, ['text', substr($str, $pos, $tag_start - $pos)];
+        }
+        push @tokens, ['tag', $whole_tag];
+        $pos = pos $str;
+    }
+    push @tokens, ['text', substr($str, $pos, $len - $pos)] if $pos < $len;
+    \@tokens;
+}
+
+
+sub _Outdent {
+#
+# Remove one level of line-leading tabs or spaces
+#
+       my $text = shift;
+
+       $text =~ s/^(\t|[ ]{1,$g_tab_width})//gm;
+       return $text;
+}
+
+
+sub _Detab {
+#
+# Cribbed from a post by Bart Lateur:
+# <http://www.nntp.perl.org/group/perl.macperl.anyperl/154>
+#
+       my $text = shift;
+
+       $text =~ s{(.*?)\t}{$1.(' ' x ($g_tab_width - length($1) % $g_tab_width))}ge;
+       return $text;
+}
+
+
+1;
+
+__END__
+
+
+=pod
+
+=head1 NAME
+
+B<Markdown>
+
+
+=head1 SYNOPSIS
+
+B<Markdown.pl> [ B<--html4tags> ] [ B<--version> ] [ B<-shortversion> ]
+    [ I<file> ... ]
+
+
+=head1 DESCRIPTION
+
+Markdown is a text-to-HTML filter; it translates an easy-to-read /
+easy-to-write structured text format into HTML. Markdown's text format
+is most similar to that of plain text email, and supports features such
+as headers, *emphasis*, code blocks, blockquotes, and links.
+
+Markdown's syntax is designed not as a generic markup language, but
+specifically to serve as a front-end to (X)HTML. You can  use span-level
+HTML tags anywhere in a Markdown document, and you can use block level
+HTML tags (like <div> and <table> as well).
+
+For more information about Markdown's syntax, see:
+
+    http://daringfireball.net/projects/markdown/
+
+
+=head1 OPTIONS
+
+Use "--" to end switch parsing. For example, to open a file named "-z", use:
+
+       Markdown.pl -- -z
+
+=over 4
+
+
+=item B<--html4tags>
+
+Use HTML 4 style for empty element tags, e.g.:
+
+    <br>
+
+instead of Markdown's default XHTML style tags, e.g.:
+
+    <br />
+
+
+=item B<-v>, B<--version>
+
+Display Markdown's version number and copyright information.
+
+
+=item B<-s>, B<--shortversion>
+
+Display the short-form version number.
+
+
+=back
+
+
+
+=head1 BUGS
+
+To file bug reports or feature requests (other than topics listed in the
+Caveats section above) please send email to:
+
+    support@daringfireball.net
+
+Please include with your report: (1) the example input; (2) the output
+you expected; (3) the output Markdown actually produced.
+
+
+=head1 VERSION HISTORY
+
+See the readme file for detailed release notes for this version.
+
+1.0.1 - 14 Dec 2004
+
+1.0 - 28 Aug 2004
+
+
+=head1 AUTHOR
+
+    John Gruber
+    http://daringfireball.net
+
+    PHP port and other contributions by Michel Fortin
+    http://michelf.com
+
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (c) 2003-2004 John Gruber   
+<http://daringfireball.net/>   
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name "Markdown" nor the names of its contributors may
+  be used to endorse or promote products derived from this software
+  without specific prior written permission.
+
+This software is provided by the copyright holders and contributors "as
+is" and any express or implied warranties, including, but not limited
+to, the implied warranties of merchantability and fitness for a
+particular purpose are disclaimed. In no event shall the copyright owner
+or contributors be liable for any direct, indirect, incidental, special,
+exemplary, or consequential damages (including, but not limited to,
+procurement of substitute goods or services; loss of use, data, or
+profits; or business interruption) however caused and on any theory of
+liability, whether in contract, strict liability, or tort (including
+negligence or otherwise) arising in any way out of the use of this
+software, even if advised of the possibility of such damage.
+
+=cut
diff --git a/scripts/md2html.awk b/scripts/md2html.awk
new file mode 100755 (executable)
index 0000000..20e7f1b
--- /dev/null
@@ -0,0 +1,427 @@
+#!/usr/bin/awk -f
+#
+# by: Jesus Galan (yiyus) 2009
+#
+# Usage: md2html.awk file.md > file.html
+# See: http://4l77.com/src/md2html.awk
+
+function eschtml(t) {
+    gsub("&", "\\&amp;", t);
+    gsub("<", "\\&lt;", t);
+    return t;
+}
+
+function oprint(t){
+    if(nr == 0)
+        print t;
+    else
+        otext = otext "\n" t;
+}
+
+function subref(id){
+    for(; nr > 0 && sub("<<" id, ref[id], otext); nr--);
+    if(nr == 0 && otext) {
+        print otext;
+        otext = "";
+    }
+}
+
+function nextil(t) {
+    if(!match(t, /[`<&\[*_\\-]|(\!\[)/))
+        return t;
+    t1 = substr(t, 1, RSTART - 1);
+    tag = substr(t, RSTART, RLENGTH);
+    t2 = substr(t, RSTART + RLENGTH);
+    if(ilcode && tag != "`")
+        return eschtml(t1 tag) nextil(t2);
+    # Backslash escaping
+    if(tag == "\\"){
+        if(match(t2, /^[\\`*_{}\[\]()#+\-\.!]/)){
+            tag = substr(t2, 1, 1);
+            t2 = substr(t2, 2);
+        }
+        return t1 tag nextil(t2);
+    }
+    # Dashes
+    if(tag == "-"){
+        if(sub(/^-/, "", t2))
+            tag = "&#8212;";
+        return t1 tag nextil(t2);
+    }
+    # Inline Code
+    if(tag == "`"){
+        if(sub(/^`/, "", t2)){
+            if(!match(t2, /``/))
+                return t1 "&#8221;" nextil(t2);
+            ilcode2 = !ilcode2;
+        }
+        else if(ilcode2)
+            return t1 tag nextil(t2);
+        tag = "<code>";
+        if(ilcode){
+            t1 = eschtml(t1);
+            tag = "</code>";
+        }
+        ilcode = !ilcode;
+        return t1 tag nextil(t2);
+    }
+    if(tag == "<"){
+    # Autolinks
+        if(match(t2, /^[^   ]+[\.@][^   ]+>/)){
+            url = eschtml(substr(t2, 1, RLENGTH - 1));
+            t2 = substr(t2, RLENGTH + 1);
+            linktext = url;
+            if(match(url, /@/) && !match(url, /^mailto:/))
+                url = "mailto:" url;
+            return t1 "<a href=\"" url "\">" linktext "</a>" nextil(t2);
+        }
+    # Html tags
+        if(match(t2, /^[A-Za-z\/!][^>]*>/)){
+            tag = tag substr(t2, RSTART, RLENGTH);
+            t2 = substr(t2, RLENGTH + 1);
+            return t1 tag nextil(t2);
+        }
+        return t1 "&lt;" nextil(t2);
+    }
+    # Html special entities
+    if(tag == "&"){
+        if(match(t2, /^#?[A-Za-z0-9]+;/)){
+            tag = tag substr(t2, RSTART, RLENGTH);
+            t2 = substr(t2, RLENGTH + 1);
+            return t1 tag nextil(t2);
+        }
+        return t1 "&amp;" nextil(t2);
+    }
+    # Images
+    if(tag == "!["){
+        if(!match(t2, /(\[.*\])|(\(.*\))/))
+            return t1 tag nextil(t2);
+        match(t2, /^[^\]]*/);
+        alt = substr(t2, 1, RLENGTH);
+        t2 = substr(t2, RLENGTH + 2);
+        if(match(t2, /^\(/)){
+            # Inline
+            sub(/^\(/, "", t2);
+            match(t2, /^[^\)]+/);
+            url = eschtml(substr(t2, 1, RLENGTH));
+            t2 = substr(t2, RLENGTH + 2);
+            title = "";
+            if(match(url, /[    ]+\".*\"[   ]*$/)) {
+                title = substr(url, RSTART, RLENGTH);
+                url = substr(url, 1, RSTART - 1);
+                match(title, /\".*\"/);
+                title = " title=\"" substr(title, RSTART + 1, RLENGTH - 2) "\"";
+            }
+            if(match(url, /^<.*>$/))
+                url = substr(url, 2, RLENGTH - 2);
+            return t1 "<img src=\"" url "\" alt=\"" alt "\"" title " />" nextil(t2);
+        }
+        else{
+            # Referenced
+            sub(/^ ?\[/, "", t2);
+            id = alt;
+            if(match(t2, /^[^\]]+/))
+                id = substr(t2, 1, RLENGTH);
+            t2 = substr(t2, RLENGTH + 2);
+            if(ref[id])
+                r = ref[id];
+            else{
+                r = "<<" id;
+                nr++;
+            }
+            return t1 "<img src=\"" r "\" alt=\"" alt "\" />" nextil(t2);
+        }
+    }
+    # Links
+    if(tag == "["){
+        if(!match(t2, /(\[.*\])|(\(.*\))/))
+            return t1 tag nextil(t2);
+        match(t2, /^[^\]]*(\[[^\]]*\][^\]]*)*/);
+        linktext = substr(t2, 1, RLENGTH);
+        t2 = substr(t2, RLENGTH + 2);
+        if(match(t2, /^\(/)){
+            # Inline
+            match(t2, /^[^\)]+(\([^\)]+\)[^\)]*)*/);
+            url = substr(t2, 2, RLENGTH - 1);
+            pt2 = substr(t2, RLENGTH + 2);
+            title = "";
+            if(match(url, /[    ]+\".*\"[   ]*$/)) {
+                title = substr(url, RSTART, RLENGTH);
+                url = substr(url, 1, RSTART - 1);
+                match(title, /\".*\"/);
+                title = " title=\"" substr(title, RSTART + 1, RLENGTH - 2) "\"";
+            }
+            if(match(url, /^<.*>$/))
+                url = substr(url, 2, RLENGTH - 2);
+            url = eschtml(url);
+            return t1 "<a href=\"" url "\"" title ">" nextil(linktext) "</a>" nextil(pt2);
+        }
+        else{
+            # Referenced
+            sub(/^ ?\[/, "", t2);
+            id = linktext;
+            if(match(t2, /^[^\]]+/))
+                id = substr(t2, 1, RLENGTH);
+            t2 = substr(t2, RLENGTH + 2);
+            if(ref[id])
+                r = ref[id];
+            else{
+                r = "<<" id;
+                nr++;
+            }
+            pt2 = t2;
+            return t1 "<a href=\"" r "\" />" nextil(linktext) "</a>" nextil(pt2);
+        }
+    }
+    # Emphasis
+    if(match(tag, /[*_]/)){
+        ntag = tag;
+        if(sub("^" tag, "", t2)){
+            if(stag[ns] == tag && match(t2, "^" tag))
+                t2 = tag t2;
+            else
+                ntag = tag tag
+        }
+        n = length(ntag);
+        tag = (n == 2) ? "strong" : "em";
+        if(match(t1, / $/) && match(t2, /^ /))
+            return t1 tag nextil(t2);
+        if(stag[ns] == ntag){
+            tag = "/" tag;
+            ns--;
+        }
+        else
+            stag[++ns] = ntag;
+        tag = "<" tag ">";
+        return t1 tag nextil(t2);
+    }
+}
+
+function inline(t) {
+    ilcode = 0;
+    ilcode2 = 0;
+    ns = 0;
+
+    return nextil(t);
+}
+
+function printp(tag) {
+    if(!match(text, /^[     ]*$/)){
+        text = inline(text);
+        if(tag != "")
+            oprint("<" tag ">" text "</" tag ">");
+        else
+            oprint(text);
+    }
+    text = "";
+}
+
+BEGIN {
+    blank = 0;
+    code = 0;
+    hr = 0;
+    html = 0;
+    nl = 0;
+    nr = 0;
+    otext = "";
+    text = "";
+    par = "p";
+}
+
+# References
+!code && /^ *\[[^\]]*\]:[   ]+/ {
+    sub(/^ *\[/, "");
+    match($0, /\]/);
+    id = substr($0, 1, RSTART - 1);
+    sub(id "\\]:[   ]+", "");
+    title = "";
+    if(match($0, /\".*\"$/))
+        title = "\" title=\"" substr($0, RSTART + 1, RLENGTH - 2);
+    sub(/[  ]+\".*\"$/, "");
+    url = eschtml($0);
+    ref[id] = url title;
+
+    subref(id);
+    next;
+}
+
+# html
+!html && /^<(address|blockquote|center|dir|div|dl|fieldset|form|h[1-6r]|\
+isindex|menu|noframes|noscript|ol|p|pre|table|ul|!--)/ {
+    if(code)
+        oprint("</pre></code>");
+    for(; !text && block[nl] == "blockquote"; nl--)
+        oprint("</blockquote>");
+    match($0, /^<(address|blockquote|center|dir|div|dl|fieldset|form|h[1-6r]|\
+    isindex|menu|noframes|noscript|ol|p|pre|table|ul|!--)/);
+    htag = substr($0, 2, RLENGTH - 1);
+    if(!match($0, "(<\\/" htag ">)|((^<hr ?\\/?)|(--)>$)"))
+        html = 1;
+    if(html && match($0, /^<hr/))
+        hr = 1;
+    oprint($0);
+    next;
+}
+
+html && (/(^<\/(address|blockquote|center|dir|div|dl|fieldset|form|h[1-6r]|\
+isindex|menu|noframes|noscript|ol|p|pre|table|ul).*)|(--)>$/ ||
+(hr && />$/)) {
+    html = 0;
+    hr = 0;
+    oprint($0);
+    next;
+}
+
+html {
+    oprint($0);
+    next;
+}
+
+# List and quote blocks
+
+#   Remove indentation
+{
+    for(nnl = 0; nnl < nl; nnl++)
+        if((match(block[nnl + 1], /[ou]l/) && !sub(/^(    | )/, "")) || \
+        (block[nnl + 1] == "blockquote" && !sub(/^> ?/, "")))
+            break;
+}
+nnl < nl && !blank && text && ! /^ ? ? ?([*+-]|([0-9]+\.)+)( +| )/ { nnl = nl; }
+#   Quote blocks
+{
+    while(sub(/^> /, ""))
+        nblock[++nnl] = "blockquote";
+}
+#   Horizontal rules
+{ hr = 0; }
+(blank || (!text && !code)) && /^ ? ? ?([-*_][  ]*)([-*_][  ]*)([-*_][  ]*)+$/ {
+    if(code){
+        oprint("</pre></code>");
+        code = 0;
+    }
+    blank = 0;
+    nnl = 0;
+    hr = 1;
+}
+#   List items
+block[nl] ~ /[ou]l/ && /^$/ {
+    blank = 1;
+    next;
+}
+{ newli = 0; }
+!hr && (nnl != nl || !text || block[nl] ~ /[ou]l/) && /^ ? ? ?[*+-]( +| )/ {
+    sub(/^ ? ? ?[*+-]( +|   )/, "");
+    nnl++;
+    nblock[nnl] = "ul";
+    newli = 1;
+}
+(nnl != nl || !text || block[nl] ~ /[ou]l/) && /^ ? ? ?([0-9]+\.)+( +|  )/ {
+    sub(/^ ? ? ?([0-9]+\.)+( +| )/, "");
+    nnl++;
+    nblock[nnl] = "ol";
+    newli = 1;
+}
+newli {
+    if(blank && nnl == nl && !par)
+        par = "p";
+    blank = 0;
+    printp(par);
+    if(nnl == nl && block[nl] == nblock[nl])
+        oprint("</li><li>");
+}
+blank && ! /^$/ {
+    if(match(block[nnl], /[ou]l/) && !par)
+        par = "p";
+    printp(par);
+    par = "p";
+    blank = 0;
+}
+
+# Close old blocks and open new ones
+nnl != nl || nblock[nl] != block[nl] {
+    if(code){
+        oprint("</pre></code>");
+        code = 0;
+    }
+    printp(par);
+    b = (nnl > nl) ? nblock[nnl] : block[nnl];
+    par = (match(b, /[ou]l/)) ? "" : "p";
+}
+nnl < nl || (nnl == nl && nblock[nl] != block[nl]) {
+    for(; nl > nnl || (nnl == nl && pblock[nl] != block[nl]); nl--){
+        if(match(block[nl], /[ou]l/))
+            oprint("</li>");
+        oprint("</" block[nl] ">");
+    }
+}
+nnl > nl {
+    for(; nl < nnl; nl++){
+        block[nl + 1] = nblock[nl + 1];
+        oprint("<" block[nl + 1] ">");
+        if(match(block[nl + 1], /[ou]l/))
+            oprint("<li>");
+    }
+}
+hr {
+    oprint("<hr>");
+    next;
+}
+
+# Code blocks
+code && /^$/ {
+    if(blanK)
+        oprint("");
+    blank = 1;
+    next;
+}
+!text && sub(/^(    |    )/, "") {
+    if(blanK)
+        oprint("");
+    blank = 0;
+    if(!code)
+        oprint("<code><pre>");
+    code = 1;
+    $0 = eschtml($0);
+    oprint($0);
+    next;
+}
+code {
+    oprint("</pre></code>");
+    code = 0;
+}
+
+# Setex-style Headers
+text && /^=+$/ {printp("h1"); next;}
+text && /^-+$/ {printp("h2"); next;}
+
+# Atx-Style headers
+/^#+/ && (!newli || par=="p" || /^##/) {
+    for(n = 0; n < 6 && sub(/^# */, ""); n++)
+        sub(/#$/, "");
+    par = "h" n;
+}
+
+# Paragraph
+/^$/ {
+    printp(par);
+    par = "p";
+    next;
+}
+
+# Add text
+{ text = (text ? text " " : "") $0; }
+
+END {
+    if(code){
+        oprint("</pre></code>");
+        code = 0;
+    }
+    printp(par);
+    for(; nl > 0; nl--){
+        if(match(block[nl], /[ou]l/))
+            oprint("</li>");
+        oprint("</" block[nl] ">");
+    }
+    gsub(/<<[^\"]*/, "", otext);
+    print(otext);
+}
diff --git a/scripts/sw b/scripts/sw
new file mode 100755 (executable)
index 0000000..ed28413
--- /dev/null
@@ -0,0 +1,106 @@
+#!/bin/sh
+# sw - suckless webframework - 2012 - MIT License - nibble <develsec.org>
+
+sw_filter() {
+    for b in $BL; do
+        [ "$b" = "$1" ] && return 0
+    done
+}
+
+sw_main() {
+    $MDHANDLER $1
+}
+
+sw_menu() {
+    echo "<ul>"
+    [ -z "`echo $1 | grep index.md`" ] && echo "<li><a href=\"index.html\">.</a></li>"
+    [ "`dirname $1`" != "." ] && echo "<li><a href=\"../index.html\">..</a></li>"
+    FILES=`ls \`dirname $1\` | sed -e 's,.md$,.html,g'`
+    for i in $FILES ; do
+        sw_filter $i && continue
+        NAME=`echo $i | sed -e 's/\..*$//' -e 's/_/ /g'`
+        [ -z "`echo $i | grep '\..*$'`" ] && i="$i/index.html"
+        echo "<li><a href=\"$i\">$NAME</a></li>"
+    done
+    echo "</ul>"
+}
+
+sw_page() {
+    # Header
+    cat << _header_
+<!doctype html>
+<html>
+<head>
+<title>${TITLE}</title>
+<link rel="icon" href="/favicon.png" type="image/png">
+<meta charset="UTF-8">
+_header_
+    # Stylesheet
+    sw_style
+    cat << _header_
+</head>
+<body>
+<div class="header">
+<h1 class="headerTitle">
+<a href="`echo $1 | sed -e 's,[^/]*/,../,g' -e 's,[^/]*.md$,index.html,g'`">${TITLE}</a> <span class="headerSubtitle">${SUBTITLE}</span>
+</h1>
+</div>
+_header_
+    # Menu
+    echo "<div id=\"side-bar\">"
+    sw_menu $1
+    echo "</div>"
+    # Body
+    echo "<div id=\"main\">"
+    sw_main $1
+    echo "</div>"
+    # Footer
+    cat << _footer_
+<div id="footer">
+<div class="right"><a href="https://github.com/jroimartin/sw">Powered by sw</a></div>
+</div>
+</body>
+</html>
+_footer_
+}
+
+sw_style() {
+    if [ -f $CDIR/$STYLE ]; then
+        echo '<style type="text/css">'
+        cat $CDIR/$STYLE
+        echo '</style>'
+    fi
+}
+
+# Set input dir
+IDIR="`echo $1 | sed -e 's,/*$,,'`"
+if [ -z "$IDIR" ] || [ ! -d $IDIR ]; then
+    echo "Usage: sw [dir]"
+    exit 1
+fi
+
+# Load config file
+if [ ! -f $PWD/sw.conf ]; then
+    echo "Cannot find sw.conf in current directory"
+    exit 1
+fi
+. $PWD/sw.conf
+
+# Setup output dir structure
+CDIR=$PWD
+ODIR="$CDIR/`basename $IDIR`.static"
+rm -rf $ODIR
+mkdir -p $ODIR
+cp -rf $IDIR/* $ODIR
+rm -f `find $ODIR -type f -iname '*.md'`
+
+# Parse files
+cd $IDIR
+FILES=`find . -iname '*.md' | sed -e 's,^\./,,'`
+for a in $FILES; do
+    b="$ODIR/`echo $a | sed -e 's,.md$,.html,g'`"
+    echo "* $a"
+    sw_page $a > $b;
+done
+
+exit 0
diff --git a/scripts/whereis b/scripts/whereis
new file mode 100755 (executable)
index 0000000..9cf5412
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+[ -z "$1" ] && exit 1
+IFS=:
+for a in $PATH ; do
+  if [ -e "$a/$1" ]; then
+    echo "$a/$1"
+    break
+  fi
+done
diff --git a/style.css b/style.css
new file mode 100644 (file)
index 0000000..03eb739
--- /dev/null
+++ b/style.css
@@ -0,0 +1,87 @@
+/* Based on werc.cat-v.org, suckless.org and garbe.us */
+
+/* General */
+body {
+       color: #aaa;
+       font-family: sans-serif;
+       font-size: 80%;
+       margin: 0;
+       padding: 0
+}
+
+/* Header */
+.header { background-color: #ccc; border: 0; }
+.header a { border: 0; color: #111; text-decoration: none; }
+.midHeader img { border: 0; }
+
+.headerTitle { font-size: 1.6em; font-weight: bold; margin: 0 0 0 0.5em; padding: 0.5em; }
+.headerTitle a { border: 0; text-decoration: none; }
+
+.headerSubTitle { font-size: 0.6em; font-weight: normal; margin-left: 1em; }
+
+/* Side */
+#side-bar {
+       width: 100%;
+       clear: both;
+       border: 0;
+       margin: 0;
+       padding: 0;
+       background-color: #ddd;
+}
+
+#side-bar ul {
+       margin: 0;
+       padding: 0.3em;
+       list-style-type: none;
+       list-style-image: none;
+       border: 0;
+}
+
+#side-bar li { display: inline; line-height: 1.6em; white-space: nowrap; }
+
+#side-bar ul li a {
+       margin: 0;
+       padding: 0.1em 1ex 0.1em 1ex;
+       color: #336699;
+       background-color: transparent;
+       text-decoration: none;
+       font-size: 1em;
+       border: 0;
+}
+
+#side-bar ul li a:hover { color: #111; text-decoration: none; }
+/* Main Copy */
+#main {
+       max-width: 70em;
+       color: #111;
+       margin: 0 auto 0 2em;
+       padding: 1em 3em 2em 1em;
+       border: 0;
+}
+
+#main a { color: #336699; text-decoration: none; }
+#main a:hover { text-decoration: underline; }
+#main h1, #main-copy h2 { color: #666; }
+#main ul { list-style-type: square; }
+
+/* Footer */
+#footer {
+       background-color: #ddd;
+       color: #111;
+       font-size: 91%;
+       padding: 2em;
+       clear: both;
+}
+
+#footer .left { text-align: left; float: left; clear: left; }
+#footer .right { text-align: right; }
+#footer a { color: #111; text-decoration: none; }
+#footer a:hover { text-decoration: underline; }
+
+abbr, acronym { border-bottom: 1px dotted #333; cursor: help; }
+blockquote { border-left: 1px solid #333; font-style: italic; padding: 1em; }
+hr { border-width: 0 0 0.1em 0; border-color: #666; }
+
+code, pre { font-size: 1.1em }
+pre { margin-left: 2em; }
diff --git a/sw.conf b/sw.conf
new file mode 100644 (file)
index 0000000..4d9e4bd
--- /dev/null
+++ b/sw.conf
@@ -0,0 +1,9 @@
+# sw - suckless webframework - 2012 - nibble <develsec.org>
+
+# Configuration
+TITLE="mdlowis"        # Site title
+SUBTITLE=""            # Site subtitle
+BL="index.html images" # Black list
+STYLE="style.css"      # Stylesheet name
+# External apps
+MDHANDLER="Markdown.pl" # md handler