Clean up own directory access
[multimarkdown] / bin / MultiMarkdown.pl
1 #!/usr/bin/env perl
2
3 # MultiMarkdown -- A modification of John Gruber's original Markdown
4 #       that adds new features and an output format that can more readily
5 #       be converted into other document formats
6 #
7 # $Id: MultiMarkdown.pl 492 2008-01-18 23:08:43Z fletcher $
8 #
9 # Original Code Copyright (c) 2004-2007 John Gruber
10 #       <http://daringfireball.net/projects/markdown/>
11 #
12 # MultiMarkdown changes Copyright (c) 2005-2008 Fletcher T. Penney
13 #       <http://fletcherpenney.net/>
14 #
15 # MultiMarkdown Version 2.0.b5
16 #
17 # Based on Markdown.pl 1.0.2b8 -  Wed 09 May 2007
18 #
19 #
20 #       TODO: Change math mode delimiter?
21 #       TODO: WikiWords inside of MMD links are converted to wiki links
22 #       TODO: Still need to get the glossary working in non-memoir documents
23 #       TODO: A mechanism to include arbitrary code (LaTeX, etc) without being "ugly"
24 #       TODO: Look into discussion re: assigning classes to div's/span's on Markdown list.
25 #       TODO: Should I just scrap the WikiWords feature to get rid of all the trouble it causes?
26 #       TODO: Improve support for tables with long items and overall width in LaTeX
27 #       TODO: Need a syntax for long table cells in MMD, even if no rowspan feature yet
28 #       TODO: Create utilities to convert MMD tables to/from tab-delimited
29
30
31 package Markdown;
32 require 5.006_000;
33 use strict;
34 use warnings;
35 use File::Basename;
36
37 # Include ASCIIMathML.pm
38         my $me = $0;                            # Where am I?
39
40         # Am I running in Windoze?
41         my $os = $^O;
42
43         if ($os =~ /MSWin/) {
44                 $me = dirname($me)."\\";        # Get just the directory portion
45         } else {
46                 $me = dirname(readlink($me))."/";       # Get just the directory portion
47         }
48
49         require $me ."ASCIIMathML.pm";
50
51 use Digest::MD5 qw(md5_hex);
52 use vars qw($VERSION $g_use_metadata $g_use_wiki_links $g_base_url
53         $g_bibliography_title $g_allow_mathml $g_base_header_level $mathParser);
54 $VERSION = '2.0.b5';
55
56 $mathParser = new Text::ASCIIMathML();
57
58 ## Disabled; causes problems under Perl 5.6.1:
59 # use utf8;
60 # binmode( STDOUT, ":utf8" );  # c.f.: http://acis.openlib.org/dev/perl-unicode-struggle.html
61
62 #
63 # Global default settings:
64 #
65 my $g_empty_element_suffix = " />";     # Change to ">" for HTML output
66 my $g_tab_width = 4;
67 my $g_allow_mathml = 1;
68 my $g_base_header_level = 1;
69 my $g_wikilinks_kill_switch = 1;                # WikiLinks may become deprecated; this is the first step
70
71 #
72 # Globals:
73 #
74
75 # Reusable patterns to match balanced [brackets] and (parens). See
76 # Friedl's "Mastering Regular Expressions", 2nd Ed., pp. 328-331.
77 my ($g_nested_brackets, $g_nested_parens);
78 $g_nested_brackets = qr{
79         (?>                                                             # Atomic matching
80            [^\[\]]+                                                     # Anything other than brackets
81          | 
82            \[
83                  (??{ $g_nested_brackets })             # Recursive set of nested brackets
84            \]
85         )*
86 }x;
87
88 # Doesn't allow for whitespace, because we're using it to match URLs:
89 $g_nested_parens = qr{
90         (?>                                                             # Atomic matching
91            [^()\s]+                                                     # Anything other than parens or whitespace
92          | 
93            \(
94                  (??{ $g_nested_parens })               # Recursive set of nested brackets
95            \)
96         )*
97 }x;
98
99
100 # Table of hash values for escaped characters:
101 my %g_escape_table;
102 foreach my $char (split //, '\\`*_{}[]()>#+-.!') {
103         $g_escape_table{$char} = md5_hex($char);
104 }
105
106
107 # Global hashes, used by various utility routines
108 my %g_urls = ();
109 my %g_titles= ();
110 my %g_html_blocks = ();
111 my %g_metadata = ();
112 my %g_metadata_newline = ();
113 my %g_crossrefs = ();
114 my %g_footnotes = ();
115 my %g_attributes = ();
116 my @g_used_footnotes = ();
117 my $g_footnote_counter = 0;
118
119 my $g_citation_counter = 0;
120 my @g_used_references = ();
121 my %g_references = ();
122 $g_bibliography_title = "Bibliography";
123
124 $g_use_metadata = 1;
125 $g_metadata_newline{default} = "\n";
126 $g_metadata_newline{keywords} = ", ";
127 my $g_document_format = "";
128
129 # For use with WikiWords and [[Wiki Links]]
130 $g_use_wiki_links = 0;
131 $g_base_url = "";               # This is the base url to be used for WikiLinks
132 my $g_temp_no_wikiwords = 0;
133
134 # NOTE:
135 # You can use \WikiWord to prevent a WikiWord from being treated as a link
136
137
138 # Used to track when we're inside an ordered or unordered list
139 # (see _ProcessListItems() for details):
140 my $g_list_level = 0;
141
142
143 #### Blosxom plug-in interface ##########################################
144
145 # Set $g_blosxom_use_meta to 1 to use Blosxom's meta plug-in to determine
146 # which posts Markdown should process, using a "meta-markup: markdown"
147 # header. If it's set to 0 (the default), Markdown will process all
148 # entries.
149 my $g_blosxom_use_meta = 0;
150
151 sub start { 1; }
152 sub story {
153         my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
154
155         if ( (! $g_blosxom_use_meta) or
156              (defined($meta::markup) and ($meta::markup =~ /^\s*markdown\s*$/i))
157              ){
158                         $$body_ref  = Markdown($$body_ref);
159      }
160      1;
161 }
162
163
164 #### Movable Type plug-in interface #####################################
165 eval {require MT};  # Test to see if we're running in MT.
166 unless ($@) {
167     require MT;
168     import  MT;
169     require MT::Template::Context;
170     import  MT::Template::Context;
171
172         eval {require MT::Plugin};  # Test to see if we're running >= MT 3.0.
173         unless ($@) {
174                 require MT::Plugin;
175                 import  MT::Plugin;
176                 my $plugin = new MT::Plugin({
177                         name => "Markdown",
178                         description => "A plain-text-to-HTML formatting plugin. (Version: $VERSION)",
179                         doc_link => 'http://daringfireball.net/projects/markdown/'
180                 });
181                 MT->add_plugin( $plugin );
182         }
183
184         MT::Template::Context->add_container_tag(MarkdownOptions => sub {
185                 my $ctx  = shift;
186                 my $args = shift;
187                 my $builder = $ctx->stash('builder');
188                 my $tokens = $ctx->stash('tokens');
189
190                 if (defined ($args->{'output'}) ) {
191                         $ctx->stash('markdown_output', lc $args->{'output'});
192                 }
193
194                 defined (my $str = $builder->build($ctx, $tokens) )
195                         or return $ctx->error($builder->errstr);
196                 $str;           # return value
197         });
198
199         MT->add_text_filter('markdown' => {
200                 label     => 'Markdown',
201                 docs      => 'http://daringfireball.net/projects/markdown/',
202                 on_format => sub {
203                         my $text = shift;
204                         my $ctx  = shift;
205                         my $raw  = 0;
206                     if (defined $ctx) {
207                         my $output = $ctx->stash('markdown_output'); 
208                                 if (defined $output  &&  $output =~ m/^html/i) {
209                                         $g_empty_element_suffix = ">";
210                                         $ctx->stash('markdown_output', '');
211                                 }
212                                 elsif (defined $output  &&  $output eq 'raw') {
213                                         $raw = 1;
214                                         $ctx->stash('markdown_output', '');
215                                 }
216                                 else {
217                                         $raw = 0;
218                                         $g_empty_element_suffix = " />";
219                                 }
220                         }
221                         $text = $raw ? $text : Markdown($text);
222                         $text;
223                 },
224         });
225
226         # If SmartyPants is loaded, add a combo Markdown/SmartyPants text filter:
227         my $smartypants;
228
229         {
230                 no warnings "once";
231                 $smartypants = $MT::Template::Context::Global_filters{'smarty_pants'};
232         }
233
234         if ($smartypants) {
235                 MT->add_text_filter('markdown_with_smartypants' => {
236                         label     => 'Markdown With SmartyPants',
237                         docs      => 'http://daringfireball.net/projects/markdown/',
238                         on_format => sub {
239                                 my $text = shift;
240                                 my $ctx  = shift;
241                                 if (defined $ctx) {
242                                         my $output = $ctx->stash('markdown_output'); 
243                                         if (defined $output  &&  $output eq 'html') {
244                                                 $g_empty_element_suffix = ">";
245                                         }
246                                         else {
247                                                 $g_empty_element_suffix = " />";
248                                         }
249                                 }
250                                 $text = Markdown($text);
251                                 $text = $smartypants->($text, '1');
252                         },
253                 });
254         }
255 }
256 else {
257 #### BBEdit/command-line text filter interface ##########################
258 # Needs to be hidden from MT (and Blosxom when running in static mode).
259
260     # We're only using $blosxom::version once; tell Perl not to warn us:
261         no warnings 'once';
262     unless ( defined($blosxom::version) ) {
263                 use warnings;
264
265                 #### Check for command-line switches: #################
266                 my %cli_opts;
267                 use Getopt::Long;
268                 Getopt::Long::Configure('pass_through');
269                 GetOptions(\%cli_opts,
270                         'version',
271                         'shortversion',
272                         'html4tags',
273                 );
274                 if ($cli_opts{'version'}) {             # Version info
275                         print "\nThis is Markdown, version $VERSION.\n";
276                         print "Copyright 2004 John Gruber\n";
277                         print "http://daringfireball.net/projects/markdown/\n\n";
278                         exit 0;
279                 }
280                 if ($cli_opts{'shortversion'}) {                # Just the version number string.
281                         print $VERSION;
282                         exit 0;
283                 }
284                 if ($cli_opts{'html4tags'}) {                   # Use HTML tag style instead of XHTML
285                         $g_empty_element_suffix = ">";
286                 }
287
288
289                 #### Process incoming text: ###########################
290                 my $text;
291                 {
292                         local $/;               # Slurp the whole file
293                         $text = <>;
294                 }
295         print Markdown($text);
296     }
297 }
298
299
300
301 sub Markdown {
302 #
303 # Main function. The order in which other subs are called here is
304 # essential. Link and image substitutions need to happen before
305 # _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
306 # and <img> tags get encoded.
307 #
308         my $text = shift;
309
310         # Clear the global hashes. If we don't clear these, you get conflicts
311         # from other articles when generating a page which contains more than
312         # one article (e.g. an index page that shows the N most recent
313         # articles):
314         %g_urls = ();
315         %g_titles = ();
316         %g_html_blocks = ();
317         %g_metadata = ();
318         %g_crossrefs = ();
319         %g_footnotes = ();
320         @g_used_footnotes = ();
321         @g_used_references = ();
322
323
324         # Standardize line endings:
325         $text =~ s{\r\n}{\n}g;  # DOS to Unix
326         $text =~ s{\r}{\n}g;    # Mac to Unix
327
328         # Make sure $text ends with a couple of newlines:
329         $text .= "\n\n";
330
331         # Convert all tabs to spaces.
332         $text = _Detab($text);
333
334         # Strip any lines consisting only of spaces and tabs.
335         # This makes subsequent regexen easier to write, because we can
336         # match consecutive blank lines with /\n+/ instead of something
337         # contorted like /[ \t]*\n+/ .
338         $text =~ s/^[ \t]+$//mg;
339
340         # Strip leading blank lines
341         $text =~ s/^\n+//s;
342         
343         # Strip out MetaData
344         $text = _ParseMetaData($text) if $g_use_metadata;
345
346         # And recheck for leading blank lines
347         $text =~ s/^\n+//s;
348                 
349         # Turn block-level HTML blocks into hash entries
350         $text = _HashHTMLBlocks($text);
351
352         # Strip footnote and link definitions, store in hashes.
353         $text = _StripFootnoteDefinitions($text);
354
355         $text = _StripLinkDefinitions($text);
356
357         _GenerateImageCrossRefs($text);
358         
359         $text = _StripMarkdownReferences($text);
360
361         $text = _RunBlockGamut($text);
362         
363         $text = _DoMarkdownCitations($text);
364         
365         $text = _DoFootnotes($text);
366         
367         $text = _UnescapeSpecialChars($text);
368         
369         # Clean encoding within HTML comments
370         $text = _UnescapeComments($text);
371         
372         # This must follow _UnescapeSpecialChars
373         $text = _UnescapeWikiWords($text);
374         
375         $text = _FixFootnoteParagraphs($text);
376         $text .= _PrintFootnotes();
377         
378         $text .= _PrintMarkdownBibliography();
379                 
380         $text = _ConvertCopyright($text);
381         
382         if (lc($g_document_format) =~ /^complete\s*$/) {
383                 return xhtmlMetaData() . "<body>\n\n" . $text . "\n</body>\n</html>";
384         } else {
385                 return textMetaData() . $text . "\n";
386         }
387         
388 }
389
390
391 sub _StripLinkDefinitions {
392 #
393 # Strips link definitions from text, stores the URLs and titles in
394 # hash references.
395 #
396         my $text = shift;
397         my $less_than_tab = $g_tab_width - 1;
398
399         # Link defs are in the form: ^[id]: url "optional title"
400         while ($text =~ s{
401                                                 # Pattern altered for MultiMarkdown
402                                                 # in order to not match citations or footnotes
403                                                 ^[ ]{0,$less_than_tab}\[([^#^].*)\]:    # id = $1
404                                                   [ \t]*
405                                                   \n?                           # maybe *one* newline
406                                                   [ \t]*
407                                                 <?(\S+?)>?                      # url = $2
408                                                   [ \t]*
409                                                   \n?                           # maybe one newline
410                                                   [ \t]*
411                                                 (?:
412                                                         (?<=\s)                 # lookbehind for whitespace
413                                                         ["(]
414                                                         (.+?)                   # title = $3
415                                                         [")]
416                                                         [ \t]*
417                                                 )?      # title is optional
418                                                 
419                                                 # MultiMarkdown addition for attribute support
420                                                 \n?
421                                                 (                               # Attributes = $4
422                                                         (?<=\s)                 # lookbehind for whitespace
423                                                         (([ \t]*\n)?[ \t]*((\S+=\S+)|(\S+=".*?")))*
424                                                 )?
425                                                 [ \t]*
426                                                 # /addition
427                                                 (?:\n+|\Z)
428                                         }
429                                         {}mx) {
430                 $g_urls{lc $1} = _EncodeAmpsAndAngles( $2 );    # Link IDs are case-insensitive
431                 if ($3) {
432                         $g_titles{lc $1} = $3;
433                         $g_titles{lc $1} =~ s/"/&quot;/g;
434                 }
435                 
436                 # MultiMarkdown addition "
437                 if ($4) {
438                         $g_attributes{lc $1} = $4;
439                 }
440                 # /addition
441         }
442
443         return $text;
444 }
445
446
447 sub _HashHTMLBlocks {
448         my $text = shift;
449         my $less_than_tab = $g_tab_width - 1;
450
451         # Hashify HTML blocks:
452         # We only want to do this for block-level HTML tags, such as headers,
453         # lists, and tables. That's because we still want to wrap <p>s around
454         # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
455         # phrase emphasis, and spans. The list of tags we're looking for is
456         # hard-coded:
457         my $block_tags = qr{
458                   (?:
459                         p         |  div     |  h[1-6]  |  blockquote  |  pre       |  table  |
460                         dl        |  ol      |  ul      |  script      |  noscript  |  form   |
461                         fieldset  |  iframe     |  ins         |  del
462                   )
463                 }x;                     # MultiMarkdown does not include `math` in the above list so that 
464                                         # Equations can optionally be included in separate paragraphs
465
466         my $tag_attrs = qr{
467                                                 (?:                             # Match one attr name/value pair
468                                                         \s+                             # There needs to be at least some whitespace
469                                                                                         # before each attribute name.
470                                                         [\w.:_-]+               # Attribute name
471                                                         \s*=\s*
472                                                         (?:
473                                                                 ".+?"           # "Attribute value"
474                                                          |
475                                                                 '.+?'           # 'Attribute value'
476                                                         )
477                                                 )*                              # Zero or more
478                                         }x;
479
480         my $empty_tag = qr{< \w+ $tag_attrs \s* />}xms;
481         my $open_tag =  qr{< $block_tags $tag_attrs \s* >}xms;
482         my $close_tag = undef;  # let Text::Balanced handle this
483
484         use Text::Balanced qw(gen_extract_tagged);
485         my $extract_block = gen_extract_tagged($open_tag, $close_tag, undef, { ignore => [$empty_tag] });
486
487         my @chunks;
488         ## TO-DO: the 0,3 on the next line ought to respect the
489         ## tabwidth, or else, we should mandate 4-space tabwidth and
490         ## be done with it:
491         while ($text =~ s{^(([ ]{0,3}<)?.*\n)}{}m) {
492                 my $cur_line = $1;
493                 if (defined $2) {
494                         # current line could be start of code block
495
496                         my ($tag, $remainder) = $extract_block->($cur_line . $text);
497                         if ($tag) {
498                                 my $key = md5_hex($tag);
499                                 $g_html_blocks{$key} = $tag;
500                                 push @chunks, "\n\n" . $key . "\n\n";
501                                 $text = $remainder;
502                         }
503                         else {
504                                 # No tag match, so toss $cur_line into @chunks
505                                 push @chunks, $cur_line;
506                         }
507                 }
508                 else {
509                         # current line could NOT be start of code block
510                         push @chunks, $cur_line;
511                 }
512
513         }
514         push @chunks, $text; # Whatever is left.
515
516         $text = join '', @chunks;
517
518
519
520         # Special case just for <hr />. It was easier to make a special case than
521         # to make the other regex more complicated.     
522         $text =~ s{
523                                 (?:
524                                         (?<=\n\n)               # Starting after a blank line
525                                         |                               # or
526                                         \A\n?                   # the beginning of the doc
527                                 )
528                                 (                                               # save in $1
529                                         [ ]{0,$less_than_tab}
530                                         <(hr)                           # start tag = $2
531                                         \b                                      # word break
532                                         ([^<>])*?                       # 
533                                         /?>                                     # the matching end tag
534                                         [ \t]*
535                                         (?=\n{2,}|\Z)           # followed by a blank line or end of document
536                                 )
537                         }{
538                                 my $key = md5_hex($1);
539                                 $g_html_blocks{$key} = $1;
540                                 "\n\n" . $key . "\n\n";
541                         }egx;
542
543         # Special case for standalone HTML comments:
544         $text =~ s{
545                                 (?:
546                                         (?<=\n\n)               # Starting after a blank line
547                                         |                               # or
548                                         \A\n?                   # the beginning of the doc
549                                 )
550                                 (                                               # save in $1
551                                         [ ]{0,$less_than_tab}
552                                         (?s:
553                                                 <!
554                                                 (--.*?--\s*)+
555                                                 >
556                                         )
557                                         [ \t]*
558                                         (?=\n{2,}|\Z)           # followed by a blank line or end of document
559                                 )
560                         }{
561                                 my $key = md5_hex($1);
562                                 $g_html_blocks{$key} = $1;
563                                 "\n\n" . $key . "\n\n";
564                         }egx;
565
566         # PHP and ASP-style processor instructions (<?…?> and <%…%>)
567         $text =~ s{
568                                 (?:
569                                         (?<=\n\n)               # Starting after a blank line
570                                         |                               # or
571                                         \A\n?                   # the beginning of the doc
572                                 )
573                                 (                                               # save in $1
574                                         [ ]{0,$less_than_tab}
575                                         (?s:
576                                                 <([?%])                 # $2
577                                                 .*?
578                                                 \2>
579                                         )
580                                         [ \t]*
581                                         (?=\n{2,}|\Z)           # followed by a blank line or end of document
582                                 )
583                         }{
584                                 my $key = md5_hex($1);
585                                 $g_html_blocks{$key} = $1;
586                                 "\n\n" . $key . "\n\n";
587                         }egx;
588
589
590         return $text;
591 }
592
593
594 sub _RunBlockGamut {
595 #
596 # These are all the transformations that form block-level
597 # tags like paragraphs, headers, and list items.
598 #
599         my $text = shift;
600
601         $text = _DoHeaders($text);
602
603         # Do tables first to populate the table id's for cross-refs
604         # Escape <pre><code> so we don't get greedy with tables
605         $text = _DoTables($text);
606         
607         # And now, protect our tables
608         $text = _HashHTMLBlocks($text);
609
610         # Do Horizontal Rules:
611         $text =~ s{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
612         $text =~ s{^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
613         $text =~ s{^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
614
615         $text = _DoDefinitionLists($text);
616         $text = _DoLists($text);
617         $text = _DoCodeBlocks($text);
618         $text = _DoBlockQuotes($text);
619
620         # We already ran _HashHTMLBlocks() before, in Markdown(), but that
621         # was to escape raw HTML in the original Markdown source. This time,
622         # we're escaping the markup we've just created, so that we don't wrap
623         # <p> tags around block-level tags.
624         $text = _HashHTMLBlocks($text);
625         $text = _FormParagraphs($text);
626
627         return $text;
628 }
629
630
631 sub _RunSpanGamut {
632 #
633 # These are all the transformations that occur *within* block-level
634 # tags like paragraphs, headers, and list items.
635 #
636         my $text = shift;
637
638         $text = _DoCodeSpans($text);
639         $text = _DoMathSpans($text);
640         $text = _EscapeSpecialCharsWithinTagAttributes($text);
641         $text = _EncodeBackslashEscapes($text);
642
643         # Process anchor and image tags. Images must come first,
644         # because ![foo][f] looks like an anchor.
645         $text = _DoImages($text);
646         $text = _DoAnchors($text);
647
648         # Process WikiWords
649         if ($g_use_wiki_links && !$g_temp_no_wikiwords && !$g_wikilinks_kill_switch) {
650                 $text = _DoWikiLinks($text);
651                 
652                 # And then reprocess anchors and images
653                 $text = _DoImages($text);
654                 $text = _DoAnchors($text);
655         }
656         
657
658         # Make links out of things like `<http://example.com/>`
659         # Must come after _DoAnchors(), because you can use < and >
660         # delimiters in inline links like [this](<url>).
661         $text = _DoAutoLinks($text);
662         $text = _EncodeAmpsAndAngles($text);
663         $text = _DoItalicsAndBold($text);
664
665         # Do hard breaks:
666         $text =~ s/ {2,}\n/ <br$g_empty_element_suffix\n/g;
667
668         return $text;
669 }
670
671
672 sub _EscapeSpecialCharsWithinTagAttributes {
673 #
674 # Within tags -- meaning between < and > -- encode [\ ` * _] so they
675 # don't conflict with their use in Markdown for code, italics and strong.
676 # We're replacing each such character with its corresponding MD5 checksum
677 # value; this is likely overkill, but it should prevent us from colliding
678 # with the escape values by accident.
679 #
680         my $text = shift;
681         my $tokens ||= _TokenizeHTML($text);
682         $text = '';   # rebuild $text from the tokens
683
684         foreach my $cur_token (@$tokens) {
685                 if ($cur_token->[0] eq "tag") {
686                         $cur_token->[1] =~  s! \\ !$g_escape_table{'\\'}!gx;
687                         $cur_token->[1] =~  s{ (?<=.)</?code>(?=.)  }{$g_escape_table{'`'}}gx;
688                         $cur_token->[1] =~  s! \* !$g_escape_table{'*'}!gx;
689                         $cur_token->[1] =~  s! _  !$g_escape_table{'_'}!gx;
690                 }
691                 $text .= $cur_token->[1];
692         }
693         return $text;
694 }
695
696
697 sub _DoAnchors {
698 #
699 # Turn Markdown link shortcuts into XHTML <a> tags.
700 #
701         my $text = shift;
702
703         #
704         # First, handle reference-style links: [link text] [id]
705         #
706         $text =~ s{
707                 (                                       # wrap whole match in $1
708                   \[
709                     ($g_nested_brackets)        # link text = $2
710                   \]
711
712                   [ ]?                          # one optional space
713                   (?:\n[ ]*)?           # one optional newline followed by spaces
714
715                   \[
716                     (.*?)               # id = $3
717                   \]
718                 )
719         }{
720                 my $result;
721                 my $whole_match = $1;
722                 my $link_text   = $2;
723                 my $link_id     = lc $3;
724
725                 if ($link_id eq "") {
726                         $link_id = lc $link_text;     # for shortcut links like [this][].
727                 }
728
729                 # Allow automatic cross-references to headers
730                 my $label = Header2Label($link_id);
731                 if (defined $g_urls{$link_id}) {
732                         my $url = $g_urls{$link_id};
733                         $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
734                         $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
735                         $result = "<a href=\"$url\"";
736                         if ( defined $g_titles{$link_id} ) {
737                                 my $title = $g_titles{$link_id};
738                                 $title =~ s! \* !$g_escape_table{'*'}!gx;
739                                 $title =~ s!  _ !$g_escape_table{'_'}!gx;
740                                 $result .=  " title=\"$title\"";
741                         }
742                         $result .= _DoAttributes($label);
743                         $result .= ">$link_text</a>";
744                 } elsif (defined $g_crossrefs{$label}) {
745                         my $url = $g_crossrefs{$label};
746                         $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
747                         $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
748                         $result = "<a href=\"$url\"";
749                         if ( defined $g_titles{$label} ) {
750                                 my $title = $g_titles{$label};
751                                 $title =~ s! \* !$g_escape_table{'*'}!gx;
752                                 $title =~ s!  _ !$g_escape_table{'_'}!gx;
753                                 $result .=  " title=\"$title\"";
754                         }
755                         $result .= _DoAttributes($label);
756                         $result .= ">$link_text</a>";
757                 } else {
758                         $result = $whole_match;
759                 }
760                 $result;
761         }xsge;
762
763         #
764         # Next, inline-style links: [link text](url "optional title")
765         #
766         $text =~ s{
767                 (                               # wrap whole match in $1
768                   \[
769                     ($g_nested_brackets)        # link text = $2
770                   \]
771                   \(                    # literal paren
772                         [ \t]*
773                         ($g_nested_parens)              # href = $3
774                         [ \t]*
775                         (                       # $4
776                           (['"])        # quote char = $5
777                           (.*?)         # Title = $6
778                           \5            # matching quote
779                           [ \t]*        # ignore any spaces/tabs between closing quote and )
780                         )?                      # title is optional
781                   \)
782                 )
783         }{
784                 my $result;
785                 my $whole_match = $1;
786                 my $link_text   = $2;
787                 my $url                 = $3;
788                 my $title               = $6;
789
790                 $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
791                 $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
792                 $url =~ s{^<(.*)>$}{$1};                                        # Remove <>'s surrounding URL, if present
793                 $result = "<a href=\"$url\"";
794
795                 if (defined $title) {
796                         $title =~ s/"/&quot;/g;
797                         $title =~ s! \* !$g_escape_table{'*'}!gx;
798                         $title =~ s!  _ !$g_escape_table{'_'}!gx;
799                         $result .=  " title=\"$title\"";
800                 }
801                 $result .= ">$link_text</a>";
802
803                 $result;
804         }xsge;
805
806         #
807         # Last, handle reference-style shortcuts: [link text]
808         # These must come last in case you've also got [link test][1]
809         # or [link test](/foo)
810         #
811         $text =~ s{
812                 (                                       # wrap whole match in $1
813                   \[
814                     ([^\[\]]+)          # link text = $2; can't contain '[' or ']'
815                   \]
816                 )
817         }{
818                 my $result;
819                 my $whole_match = $1;
820                 my $link_text   = $2;
821                 (my $link_id = lc $2) =~ s{[ ]?\n}{ }g; # lower-case and turn embedded newlines into spaces
822
823                 # Allow automatic cross-references to headers
824                 my $label = Header2Label($link_id);
825                 if (defined $g_urls{$link_id}) {
826                         my $url = $g_urls{$link_id};
827                         $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
828                         $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
829                         $result = "<a href=\"$url\"";
830                         if ( defined $g_titles{$link_id} ) {
831                                 my $title = $g_titles{$link_id};
832                                 $title =~ s! \* !$g_escape_table{'*'}!gx;
833                                 $title =~ s!  _ !$g_escape_table{'_'}!gx;
834                                 $result .=  " title=\"$title\"";
835                         }
836                         $result .= _DoAttributes($link_id);
837                         $result .= ">$link_text</a>";
838                 } elsif (defined $g_crossrefs{$label}) {
839                         my $url = $g_crossrefs{$label};
840                         $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
841                         $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
842                         $result = "<a href=\"$url\"";
843                         if ( defined $g_titles{$label} ) {
844                                 my $title = $g_titles{$label};
845                                 $title =~ s! \* !$g_escape_table{'*'}!gx;
846                                 $title =~ s!  _ !$g_escape_table{'_'}!gx;
847                                 $result .=  " title=\"$title\"";
848                         }
849                         $result .= _DoAttributes($label);
850                         $result .= ">$link_text</a>";
851                 } else {
852                         $result = $whole_match;
853                 }
854                 $result;
855         }xsge;
856
857         return $text;
858 }
859
860
861 sub _DoImages {
862 #
863 # Turn Markdown image shortcuts into <img> tags.
864 #
865         my $text = shift;
866
867         #
868         # First, handle reference-style labeled images: ![alt text][id]
869         #
870         $text =~ s{
871                 (                               # wrap whole match in $1
872                   !\[
873                     (.*?)               # alt text = $2
874                   \]
875
876                   [ ]?                          # one optional space
877                   (?:\n[ ]*)?           # one optional newline followed by spaces
878
879                   \[
880                     (.*?)               # id = $3
881                   \]
882
883                 )
884         }{
885                 my $result;
886                 my $whole_match = $1;
887                 my $alt_text    = $2;
888                 my $link_id     = lc $3;
889
890                 if ($link_id eq "") {
891                         $link_id = lc $alt_text;     # for shortcut links like ![this][].
892                 }
893
894                 $alt_text =~ s/"/&quot;/g;
895                 if (defined $g_urls{$link_id}) {
896                         my $url = $g_urls{$link_id};
897                         $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
898                         $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
899                         
900                         my $label = Header2Label($alt_text);
901                         $g_crossrefs{$label} = "#$label";
902                         if (! defined $g_titles{$link_id}) {
903                                 $g_titles{$link_id} = $alt_text;
904                         }
905                         
906                         $result = "<img id=\"$label\" src=\"$url\" alt=\"$alt_text\"";
907                         if (defined $g_titles{$link_id}) {
908                                 my $title = $g_titles{$link_id};
909                                 $title =~ s! \* !$g_escape_table{'*'}!gx;
910                                 $title =~ s!  _ !$g_escape_table{'_'}!gx;
911                                 $result .=  " title=\"$title\"";
912                         }
913                         $result .= _DoAttributes($link_id);
914                         $result .= $g_empty_element_suffix;             
915                 }
916                 else {
917                         # If there's no such link ID, leave intact:
918                         $result = $whole_match;
919                 }
920
921                 $result;
922         }xsge;
923
924         #
925         # Next, handle inline images:  ![alt text](url "optional title")
926         # Don't forget: encode * and _
927
928         $text =~ s{
929                 (                               # wrap whole match in $1
930                   !\[
931                     (.*?)               # alt text = $2
932                   \]
933                   \s?                   # One optional whitespace character
934                   \(                    # literal paren
935                         [ \t]*
936                         ($g_nested_parens)              # href = $3
937                         [ \t]*
938                         (                       # $4
939                           (['"])        # quote char = $5
940                           (.*?)         # title = $6
941                           \5            # matching quote
942                           [ \t]*
943                         )?                      # title is optional
944                   \)
945                 )
946         }{
947                 my $result;
948                 my $whole_match = $1;
949                 my $alt_text    = $2;
950                 my $url                 = $3;
951                 my $title               = (defined $6) ? $6 : '';
952
953                 $alt_text =~ s/"/&quot;/g;
954                 $title    =~ s/"/&quot;/g;
955                 $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
956                 $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
957                 $url =~ s{^<(.*)>$}{$1};                                        # Remove <>'s surrounding URL, if present
958
959                 my $label = Header2Label($alt_text);
960                 $g_crossrefs{$label} = "#$label";
961 #               $g_titles{$label} = $alt_text;                  # I think this line should not be here
962                         
963                 $result = "<img id=\"$label\" src=\"$url\" alt=\"$alt_text\"";
964                 if (defined $title) {
965                         $title =~ s! \* !$g_escape_table{'*'}!gx;
966                         $title =~ s!  _ !$g_escape_table{'_'}!gx;
967                         $result .=  " title=\"$title\"";
968                 }
969                 $result .= $g_empty_element_suffix;
970
971                 $result;
972         }xsge;
973
974         return $text;
975 }
976
977
978 sub _DoHeaders {
979         my $text = shift;
980         my $header = "";
981         my $label = "";
982         my $idString = "";
983         
984         # Don't do Wiki Links in Headers
985         $g_temp_no_wikiwords = 1;
986         
987         # Setext-style headers:
988         #         Header 1
989         #         ========
990         #  
991         #         Header 2
992         #         --------
993         #
994         $text =~ s{ ^(.+?)(?:\s\[([^\[]*?)\])?[ \t]*\n=+[ \t]*\n+ }{
995                 if (defined $2) {
996                         $label = Header2Label($2);
997                 } else {
998                         $label = Header2Label($1);
999                 }
1000                 $header = _RunSpanGamut($1);
1001                 $header =~ s/^\s*//s;
1002                 
1003                 if ($label ne "") {
1004                         $g_crossrefs{$label} = "#$label";
1005                         $g_titles{$label} = $header;
1006                         $idString = " id=\"$label\"";                   
1007                 } else {
1008                         $idString = "";
1009                 }
1010                 
1011                 "<h1$idString>"  .  $header  .  "</h1>\n\n";
1012         }egmx;
1013
1014         $text =~ s{ ^(.+?)(?:\s*\[([^\[]*?)\])?[ \t]*\n-+[ \t]*\n+ }{
1015                 if (defined $2) {
1016                         $label = Header2Label($2);
1017                 } else {
1018                         $label = Header2Label($1);
1019                 }
1020                 $header = _RunSpanGamut($1);
1021                 $header =~ s/^\s*//s;
1022                 
1023                 if ($label ne "") {
1024                         $g_crossrefs{$label} = "#$label";
1025                         $g_titles{$label} = $header;
1026                         $idString = " id=\"$label\"";                   
1027                 } else {
1028                         $idString = "";
1029                 }
1030                 
1031                 "<h2$idString>"  .  $header  .  "</h2>\n\n";
1032         }egmx;
1033
1034
1035         # atx-style headers:
1036         #       # Header 1
1037         #       ## Header 2
1038         #       ## Header 2 with closing hashes ##
1039         #       ...
1040         #       ###### Header 6
1041         #
1042         $text =~ s{
1043                         ^(\#{1,6})      # $1 = string of #'s
1044                         [ \t]*
1045                         (.+?)           # $2 = Header text
1046                         [ \t]*
1047                         (?:\[([^\[]*?)\])?      # $3 = optional label for cross-reference
1048                         [ \t]*
1049                         \#*                     # optional closing #'s (not counted)
1050                         \n+
1051                 }{
1052                         my $h_level = length($1) + $g_base_header_level - 1;
1053                         if (defined $3) {
1054                                 $label = Header2Label($3);
1055                         } else {
1056                                 $label = Header2Label($2);
1057                         }
1058                         $header = _RunSpanGamut($2);
1059                         $header =~ s/^\s*//s;
1060                         
1061                         if ($label ne "") {
1062                                 $g_crossrefs{$label} = "#$label";
1063                                 $g_titles{$label} = $header;
1064                                 $idString = " id=\"$label\"";                   
1065                         } else {
1066                                 $idString = "";
1067                         }
1068
1069                         "<h$h_level$idString>"  .  $header  .  "</h$h_level>\n\n";
1070                 }egmx;
1071
1072         # Can now process Wiki Links again
1073         $g_temp_no_wikiwords = 0;
1074
1075         return $text;
1076 }
1077
1078
1079 sub _DoLists {
1080 #
1081 # Form HTML ordered (numbered) and unordered (bulleted) lists.
1082 #
1083         my $text = shift;
1084         my $less_than_tab = $g_tab_width - 1;
1085
1086         # Re-usable patterns to match list item bullets and number markers:
1087         my $marker_ul  = qr/[*+-]/;
1088         my $marker_ol  = qr/\d+[.]/;
1089         my $marker_any = qr/(?:$marker_ul|$marker_ol)/;
1090
1091         # Re-usable pattern to match any entirel ul or ol list:
1092         my $whole_list = qr{
1093                 (                                                               # $1 = whole list
1094                   (                                                             # $2
1095                         [ ]{0,$less_than_tab}
1096                         (${marker_any})                         # $3 = first list item marker
1097                         [ \t]+
1098                   )
1099                   (?s:.+?)
1100                   (                                                             # $4
1101                           \z
1102                         |
1103                           \n{2,}
1104                           (?=\S)
1105                           (?!                                           # Negative lookahead for another list item marker
1106                                 [ \t]*
1107                                 ${marker_any}[ \t]+
1108                           )
1109                   )
1110                 )
1111         }mx;
1112
1113         # We use a different prefix before nested lists than top-level lists.
1114         # See extended comment in _ProcessListItems().
1115         #
1116         # Note: There's a bit of duplication here. My original implementation
1117         # created a scalar regex pattern as the conditional result of the test on
1118         # $g_list_level, and then only ran the $text =~ s{...}{...}egmx
1119         # substitution once, using the scalar as the pattern. This worked,
1120         # everywhere except when running under MT on my hosting account at Pair
1121         # Networks. There, this caused all rebuilds to be killed by the reaper (or
1122         # perhaps they crashed, but that seems incredibly unlikely given that the
1123         # same script on the same server ran fine *except* under MT. I've spent
1124         # more time trying to figure out why this is happening than I'd like to
1125         # admit. My only guess, backed up by the fact that this workaround works,
1126         # is that Perl optimizes the substition when it can figure out that the
1127         # pattern will never change, and when this optimization isn't on, we run
1128         # afoul of the reaper. Thus, the slightly redundant code that uses two
1129         # static s/// patterns rather than one conditional pattern.
1130
1131         if ($g_list_level) {
1132                 $text =~ s{
1133                                 ^
1134                                 $whole_list
1135                         }{
1136                                 my $list = $1;
1137                                 my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";
1138
1139                                 # Turn double returns into triple returns, so that we can make a
1140                                 # paragraph for the last item in a list, if necessary:
1141                                 $list =~ s/\n{2,}/\n\n\n/g;
1142                                 my $result = _ProcessListItems($list, $marker_any);
1143
1144                                 # Trim any trailing whitespace, to put the closing `</$list_type>`
1145                                 # up on the preceding line, to get it past the current stupid
1146                                 # HTML block parser. This is a hack to work around the terrible
1147                                 # hack that is the HTML block parser.
1148                                 $result =~ s{\s+$}{};
1149                                 $result = "<$list_type>" . $result . "</$list_type>\n";
1150                                 $result;
1151                         }egmx;
1152         }
1153         else {
1154                 $text =~ s{
1155                                 (?:(?<=\n\n)|\A\n?)
1156                                 $whole_list
1157                         }{
1158                                 my $list = $1;
1159                                 my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";
1160                                 # Turn double returns into triple returns, so that we can make a
1161                                 # paragraph for the last item in a list, if necessary:
1162                                 $list =~ s/\n{2,}/\n\n\n/g;
1163                                 my $result = _ProcessListItems($list, $marker_any);
1164                                 $result = "<$list_type>\n" . $result . "</$list_type>\n";
1165                                 $result;
1166                         }egmx;
1167         }
1168
1169
1170         return $text;
1171 }
1172
1173
1174 sub _ProcessListItems {
1175 #
1176 #       Process the contents of a single ordered or unordered list, splitting it
1177 #       into individual list items.
1178 #
1179
1180         my $list_str = shift;
1181         my $marker_any = shift;
1182
1183
1184         # The $g_list_level global keeps track of when we're inside a list.
1185         # Each time we enter a list, we increment it; when we leave a list,
1186         # we decrement. If it's zero, we're not in a list anymore.
1187         #
1188         # We do this because when we're not inside a list, we want to treat
1189         # something like this:
1190         #
1191         #               I recommend upgrading to version
1192         #               8. Oops, now this line is treated
1193         #               as a sub-list.
1194         #
1195         # As a single paragraph, despite the fact that the second line starts
1196         # with a digit-period-space sequence.
1197         #
1198         # Whereas when we're inside a list (or sub-list), that line will be
1199         # treated as the start of a sub-list. What a kludge, huh? This is
1200         # an aspect of Markdown's syntax that's hard to parse perfectly
1201         # without resorting to mind-reading. Perhaps the solution is to
1202         # change the syntax rules such that sub-lists must start with a
1203         # starting cardinal number; e.g. "1." or "a.".
1204
1205         $g_list_level++;
1206
1207         # trim trailing blank lines:
1208         $list_str =~ s/\n{2,}\z/\n/;
1209
1210
1211         $list_str =~ s{
1212                 (\n)?                                                   # leading line = $1
1213                 (^[ \t]*)                                               # leading whitespace = $2
1214                 ($marker_any) [ \t]+                    # list marker = $3
1215                 ((?s:.+?)                                               # list item text   = $4
1216                 (\n{1,2}))
1217                 (?= \n* (\z | \2 ($marker_any) [ \t]+))
1218         }{
1219                 my $item = $4;
1220                 my $leading_line = $1;
1221                 my $leading_space = $2;
1222
1223                 if ($leading_line or ($item =~ m/\n{2,}/)) {
1224                         $item = _RunBlockGamut(_Outdent($item));
1225                 }
1226                 else {
1227                         # Recursion for sub-lists:
1228                         $item = _DoLists(_Outdent($item));
1229                         chomp $item;
1230                         $item = _RunSpanGamut($item);
1231                 }
1232
1233                 "<li>" . $item . "</li>\n";
1234         }egmx;
1235
1236         $g_list_level--;
1237         return $list_str;
1238 }
1239
1240
1241
1242 sub _DoCodeBlocks {
1243 #
1244 #       Process Markdown `<pre><code>` blocks.
1245 #       
1246
1247         my $text = shift;
1248
1249         $text =~ s{
1250                         (?:\n\n|\A)
1251                         (                   # $1 = the code block -- one or more lines, starting with a space/tab
1252                           (?:
1253                             (?:[ ]{$g_tab_width} | \t)  # Lines must start with a tab or a tab-width of spaces
1254                             .*\n+
1255                           )+
1256                         )
1257                         ((?=^[ ]{0,$g_tab_width}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
1258                 }{
1259                         my $codeblock = $1;
1260                         my $result; # return value
1261
1262                         $codeblock = _EncodeCode(_Outdent($codeblock));
1263                         $codeblock = _Detab($codeblock);
1264                         $codeblock =~ s/\A\n+//; # trim leading newlines
1265                         $codeblock =~ s/\n+\z//; # trim trailing newlines
1266
1267                         $result = "\n\n<pre><code>" . $codeblock . "</code></pre>\n\n"; # CHANGED: Removed newline for MMD
1268
1269                         $result;
1270                 }egmx;
1271
1272         return $text;
1273 }
1274
1275
1276 sub _DoCodeSpans {
1277 #
1278 #       *       Backtick quotes are used for <code></code> spans.
1279
1280 #       *       You can use multiple backticks as the delimiters if you want to
1281 #               include literal backticks in the code span. So, this input:
1282 #     
1283 #         Just type ``foo `bar` baz`` at the prompt.
1284 #     
1285 #       Will translate to:
1286 #     
1287 #         <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
1288 #     
1289 #               There's no arbitrary limit to the number of backticks you
1290 #               can use as delimters. If you need three consecutive backticks
1291 #               in your code, use four for delimiters, etc.
1292 #
1293 #       *       You can use spaces to get literal backticks at the edges:
1294 #     
1295 #         ... type `` `bar` `` ...
1296 #     
1297 #       Turns to:
1298 #     
1299 #         ... type <code>`bar`</code> ...
1300 #
1301
1302         my $text = shift;
1303
1304         $text =~ s@
1305                         (?<!\\)         # Character before opening ` can't be a backslash
1306                         (`+)            # $1 = Opening run of `
1307                         (.+?)           # $2 = The code block
1308                         (?<!`)
1309                         \1                      # Matching closer
1310                         (?!`)
1311                 @
1312                         my $c = "$2";
1313                         $c =~ s/^[ \t]*//g; # leading whitespace
1314                         $c =~ s/[ \t]*$//g; # trailing whitespace
1315                         $c = _EncodeCode($c);
1316                         "<code>$c</code>";
1317                 @egsx;
1318
1319         return $text;
1320 }
1321
1322
1323 sub _EncodeCode {
1324 #
1325 # Encode/escape certain characters inside Markdown code runs.
1326 # The point is that in code, these characters are literals,
1327 # and lose their special Markdown meanings.
1328 #
1329     local $_ = shift;
1330
1331         # Protect Wiki Links in Code Blocks
1332         if (!$g_wikilinks_kill_switch) {
1333                 my $WikiWord = qr'[A-Z]+[a-z\x80-\xff]+[A-Z][A-Za-z\x80-\xff]*';
1334                 s/(\A\\?|\s\\?)($WikiWord)/$1\\$2/gx;
1335         }
1336         
1337         # Encode all ampersands; HTML entities are not
1338         # entities within a Markdown code span.
1339         s/&/&amp;/g;
1340
1341         # Encode $'s, but only if we're running under Blosxom.
1342         # (Blosxom interpolates Perl variables in article bodies.)
1343         {
1344                 no warnings 'once';
1345         if (defined($blosxom::version)) {
1346                 s/\$/&#036;/g;  
1347         }
1348     }
1349
1350
1351         # Do the angle bracket song and dance:
1352         s! <  !&lt;!gx;
1353         s! >  !&gt;!gx;
1354
1355         # Now, escape characters that are magic in Markdown:
1356         s! \* !$g_escape_table{'*'}!gx;
1357         s! _  !$g_escape_table{'_'}!gx;
1358         s! {  !$g_escape_table{'{'}!gx;
1359         s! }  !$g_escape_table{'}'}!gx;
1360         s! \[ !$g_escape_table{'['}!gx;
1361         s! \] !$g_escape_table{']'}!gx;
1362         s! \\ !$g_escape_table{'\\'}!gx;
1363
1364         return $_;
1365 }
1366
1367
1368 sub _DoItalicsAndBold {
1369         my $text = shift;
1370
1371         # Cave in - `*` and `_` behave differently...  We'll see how it works out
1372         
1373         
1374         # <strong> must go first:
1375         $text =~ s{ (?<!\w) (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 }
1376                 {<strong>$2</strong>}gsx;
1377
1378         $text =~ s{ (?<!\w) (\*|_) (?=\S) (.+?) (?<=\S) \1 }
1379                 {<em>$2</em>}gsx;
1380
1381         # And now, a second pass to catch nested strong and emphasis special cases
1382         $text =~ s{ (?<!\w) (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 }
1383                 {<strong>$2</strong>}gsx;
1384
1385         $text =~ s{ (?<!\w) (\*|_) (?=\S) (.+?) (?<=\S) \1 }
1386                 {<em>$2</em>}gsx;
1387
1388         # And now, allow `*` in the middle of words
1389
1390         # <strong> must go first:
1391         $text =~ s{ (\*\*) (?=\S) (.+?[*]*) (?<=\S) \1 }
1392                 {<strong>$2</strong>}gsx;
1393
1394         $text =~ s{ (\*) (?=\S) (.+?) (?<=\S) \1 }
1395                 {<em>$2</em>}gsx;
1396
1397         return $text;
1398 }
1399
1400
1401 sub _DoBlockQuotes {
1402         my $text = shift;
1403
1404         $text =~ s{
1405                   (                                                             # Wrap whole match in $1
1406                         (
1407                           ^[ \t]*>[ \t]?                        # '>' at the start of a line
1408                             .+\n                                        # rest of the first line
1409                           (.+\n)*                                       # subsequent consecutive lines
1410                           \n*                                           # blanks
1411                         )+
1412                   )
1413                 }{
1414                         my $bq = $1;
1415                         $bq =~ s/^[ \t]*>[ \t]?//gm;    # trim one level of quoting
1416                         $bq =~ s/^[ \t]+$//mg;                  # trim whitespace-only lines
1417                         $bq = _RunBlockGamut($bq);              # recurse
1418
1419                         $bq =~ s/^/  /g;
1420                         # These leading spaces screw with <pre> content, so we need to fix that:
1421                         $bq =~ s{
1422                                         (\s*<pre>.+?</pre>)
1423                                 }{
1424                                         my $pre = $1;
1425                                         $pre =~ s/^  //mg;
1426                                         $pre;
1427                                 }egsx;
1428
1429                         "<blockquote>\n$bq\n</blockquote>\n\n";
1430                 }egmx;
1431
1432
1433         return $text;
1434 }
1435
1436
1437 sub _FormParagraphs {
1438 #
1439 #       Params:
1440 #               $text - string to process with html <p> tags
1441 #
1442         my $text = shift;
1443
1444         # Strip leading and trailing lines:
1445         $text =~ s/\A\n+//;
1446         $text =~ s/\n+\z//;
1447
1448         my @grafs = split(/\n{2,}/, $text);
1449
1450         #
1451         # Wrap <p> tags.
1452         #
1453         foreach (@grafs) {
1454                 unless (defined( $g_html_blocks{$_} )) {
1455                         $_ = _RunSpanGamut($_);
1456                         s/^([ \t]*)/<p>/;
1457                         $_ .= "</p>";
1458                 }
1459         }
1460
1461         #
1462         # Unhashify HTML blocks
1463         #
1464 #       foreach my $graf (@grafs) {
1465 #               my $block = $g_html_blocks{$graf};
1466 #               if (defined $block) {
1467 #                       $graf = $block;
1468 #               }
1469 #       }
1470
1471         foreach my $graf (@grafs) {
1472                 # Modify elements of @grafs in-place...
1473                 my $block = $g_html_blocks{$graf};
1474                 if (defined $block) {
1475                         $graf = $block;
1476                         if ($block =~ m{
1477                                                         \A
1478                                                         (                                                       # $1 = <div> tag
1479                                                           <div  \s+
1480                                                           [^>]*
1481                                                           \b
1482                                                           markdown\s*=\s*  (['"])       #       $2 = attr quote char
1483                                                           1
1484                                                           \2
1485                                                           [^>]*
1486                                                           >
1487                                                         )
1488                                                         (                                                       # $3 = contents
1489                                                         .*
1490                                                         )
1491                                                         (</div>)                                        # $4 = closing tag
1492                                                         \z
1493
1494                                                         }xms
1495                                 ) {
1496                                 my ($div_open, $div_content, $div_close) = ($1, $3, $4);
1497
1498                                 # We can't call Markdown(), because that resets the hash;
1499                                 # that initialization code should be pulled into its own sub, though.
1500                                 $div_content = _HashHTMLBlocks($div_content);
1501                                 $div_content = _StripLinkDefinitions($div_content);
1502                                 $div_content = _RunBlockGamut($div_content);
1503                                 $div_content = _UnescapeSpecialChars($div_content);
1504
1505                                 $div_open =~ s{\smarkdown\s*=\s*(['"]).+?\1}{}ms;
1506
1507                                 $graf = $div_open . "\n" . $div_content . "\n" . $div_close;
1508                         }
1509                 }
1510         }
1511
1512
1513         return join "\n\n", @grafs;
1514 }
1515
1516
1517 sub _EncodeAmpsAndAngles {
1518 # Smart processing for ampersands and angle brackets that need to be encoded.
1519
1520         my $text = shift;
1521
1522         # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1523         #   http://bumppo.net/projects/amputator/
1524         $text =~ s/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/&amp;/g;
1525
1526         # Encode naked <'s
1527         $text =~ s{<(?![a-z/?\$!])}{&lt;}gi;
1528
1529         return $text;
1530 }
1531
1532
1533 sub _EncodeBackslashEscapes {
1534 #
1535 #   Parameter:  String.
1536 #   Returns:    The string, with after processing the following backslash
1537 #               escape sequences.
1538 #
1539     local $_ = shift;
1540
1541     s! \\\\  !$g_escape_table{'\\'}!gx;         # Must process escaped backslashes first.
1542     s! \\`   !$g_escape_table{'`'}!gx;
1543     s! \\\*  !$g_escape_table{'*'}!gx;
1544     s! \\_   !$g_escape_table{'_'}!gx;
1545     s! \\\{  !$g_escape_table{'{'}!gx;
1546     s! \\\}  !$g_escape_table{'}'}!gx;
1547     s! \\\[  !$g_escape_table{'['}!gx;
1548     s! \\\]  !$g_escape_table{']'}!gx;
1549     s! \\\(  !$g_escape_table{'('}!gx;
1550     s! \\\)  !$g_escape_table{')'}!gx;
1551     s! \\>   !$g_escape_table{'>'}!gx;
1552     s! \\\#  !$g_escape_table{'#'}!gx;
1553     s! \\\+  !$g_escape_table{'+'}!gx;
1554     s! \\\-  !$g_escape_table{'-'}!gx;
1555     s! \\\.  !$g_escape_table{'.'}!gx;
1556     s{ \\!  }{$g_escape_table{'!'}}gx;
1557
1558     return $_;
1559 }
1560
1561
1562 sub _DoAutoLinks {
1563         my $text = shift;
1564
1565         $text =~ s{<((https?|ftp|dict):[^'">\s]+)>}{<a href="$1">$1</a>}gi;
1566
1567         # Email addresses: <address@domain.foo>
1568         $text =~ s{
1569                 <
1570         (?:mailto:)?
1571                 (
1572                         [-.\w]+
1573                         \@
1574                         [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
1575                 )
1576                 >
1577         }{
1578                 _EncodeEmailAddress( _UnescapeSpecialChars($1) );
1579         }egix;
1580
1581         return $text;
1582 }
1583
1584
1585 sub _EncodeEmailAddress {
1586 #
1587 #       Input: an email address, e.g. "foo@example.com"
1588 #
1589 #       Output: the email address as a mailto link, with each character
1590 #               of the address encoded as either a decimal or hex entity, in
1591 #               the hopes of foiling most address harvesting spam bots. E.g.:
1592 #
1593 #         <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
1594 #       x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
1595 #       &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
1596 #
1597 #       Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
1598 #       mailing list: <http://tinyurl.com/yu7ue>
1599 #
1600
1601         my $addr = shift;
1602
1603         srand;
1604         my @encode = (
1605                 sub { '&#' .                 ord(shift)   . ';' },
1606                 sub { '&#x' . sprintf( "%X", ord(shift) ) . ';' },
1607                 sub {                            shift          },
1608         );
1609
1610         $addr = "mailto:" . $addr;
1611
1612         $addr =~ s{(.)}{
1613                 my $char = $1;
1614                 if ( $char eq '@' ) {
1615                         # this *must* be encoded. I insist.
1616                         $char = $encode[int rand 1]->($char);
1617                 } elsif ( $char ne ':' ) {
1618                         # leave ':' alone (to spot mailto: later)
1619                         my $r = rand;
1620                         # roughly 10% raw, 45% hex, 45% dec
1621                         $char = (
1622                                 $r > .9   ?  $encode[2]->($char)  :
1623                                 $r < .45  ?  $encode[1]->($char)  :
1624                                                          $encode[0]->($char)
1625                         );
1626                 }
1627                 $char;
1628         }gex;
1629
1630         $addr = qq{<a href="$addr">$addr</a>};
1631         $addr =~ s{">.+?:}{">}; # strip the mailto: from the visible part
1632
1633         return $addr;
1634 }
1635
1636
1637 sub _UnescapeSpecialChars {
1638 #
1639 # Swap back in all the special characters we've hidden.
1640 #
1641         my $text = shift;
1642
1643         while( my($char, $hash) = each(%g_escape_table) ) {
1644                 $text =~ s/$hash/$char/g;
1645         }
1646     return $text;
1647 }
1648
1649
1650 sub _TokenizeHTML {
1651 #
1652 #   Parameter:  String containing HTML markup.
1653 #   Returns:    Reference to an array of the tokens comprising the input
1654 #               string. Each token is either a tag (possibly with nested,
1655 #               tags contained therein, such as <a href="<MTFoo>">, or a
1656 #               run of text between tags. Each element of the array is a
1657 #               two-element array; the first is either 'tag' or 'text';
1658 #               the second is the actual value.
1659 #
1660 #
1661 #   Derived from the _tokenize() subroutine from Brad Choate's MTRegex plugin.
1662 #       <http://www.bradchoate.com/past/mtregex.php>
1663 #
1664
1665     my $str = shift;
1666     my $pos = 0;
1667     my $len = length $str;
1668     my @tokens;
1669
1670     my $depth = 6;
1671     my $nested_tags = join('|', ('(?:<[a-z/!$](?:[^<>]') x $depth) . (')*>)' x  $depth);
1672     my $match = qr/(?s: <! ( -- .*? -- \s* )+ > ) |  # comment
1673                    (?s: <\? .*? \?> ) |              # processing instruction
1674                    $nested_tags/ix;                   # nested tags
1675
1676     while ($str =~ m/($match)/g) {
1677         my $whole_tag = $1;
1678         my $sec_start = pos $str;
1679         my $tag_start = $sec_start - length $whole_tag;
1680         if ($pos < $tag_start) {
1681             push @tokens, ['text', substr($str, $pos, $tag_start - $pos)];
1682         }
1683         push @tokens, ['tag', $whole_tag];
1684         $pos = pos $str;
1685     }
1686     push @tokens, ['text', substr($str, $pos, $len - $pos)] if $pos < $len;
1687
1688     return \@tokens;
1689 }
1690
1691
1692 sub _Outdent {
1693 #
1694 # Remove one level of line-leading tabs or spaces
1695 #
1696         my $text = shift;
1697
1698         $text =~ s/^(\t|[ ]{1,$g_tab_width})//gm;
1699         return $text;
1700 }
1701
1702
1703 sub _Detab {
1704 #
1705 # Cribbed from a post by Bart Lateur:
1706 # <http://www.nntp.perl.org/group/perl.macperl.anyperl/154>
1707 #
1708         my $text = shift;
1709
1710         $text =~ s{(.*?)\t}{$1.(' ' x ($g_tab_width - length($1) % $g_tab_width))}ge;
1711         return $text;
1712 }
1713
1714 #
1715 # MultiMarkdown Routines
1716 #
1717
1718 sub _ParseMetaData {
1719         my $text = shift;
1720         my $clean_text = "";
1721         
1722         my ($inMetaData, $currentKey) = (1,'');
1723         
1724         foreach my $line ( split /\n/, $text ) {
1725                 $line =~ /^$/ and $inMetaData = 0 and $clean_text .= $line and next;
1726                 if ($inMetaData) {
1727                         if ($line =~ /^([a-zA-Z0-9][0-9a-zA-Z _-]*?):\s*(.*)$/ ) {
1728                                 $currentKey = $1;
1729                                 $currentKey =~ s/  / /g;
1730                                 $g_metadata{$currentKey} = $2;
1731                                 if (lc($currentKey) eq "format") {
1732                                         $g_document_format = lc($g_metadata{$currentKey});
1733                                 }
1734                                 if (lc($currentKey) eq "base url") {
1735                                         $g_base_url = $g_metadata{$currentKey};
1736                                 }
1737                                 if (lc($currentKey) eq "use wikilinks") {
1738                                         if (lc($g_metadata{$currentKey}) eq "true" ||
1739                                                 $g_metadata{$currentKey} eq "1") {
1740                                                         $g_use_wiki_links = 1;
1741                                                 }
1742                                 }
1743                                 if (lc($currentKey) eq "bibliography title") {
1744                                         $g_bibliography_title = $g_metadata{$currentKey};
1745                                         $g_bibliography_title =~ s/\s*$//;
1746                                 }
1747                                 if (lc($currentKey) eq "base header level") {
1748                                         $g_base_header_level = $g_metadata{$currentKey};
1749                                 }
1750                                 if (!$g_metadata_newline{$currentKey}) {
1751                                         $g_metadata_newline{$currentKey} = $g_metadata_newline{default};
1752                                 }
1753                         } else {
1754                                 if ($currentKey eq "") {
1755                                         # No metadata present
1756                                         $clean_text .= "$line\n";
1757                                         $inMetaData = 0;
1758                                         next;
1759                                 }
1760                                 if ($line =~ /^\s*(.+)$/ ) {
1761                                         $g_metadata{$currentKey} .= "$g_metadata_newline{$currentKey}$1";
1762                                 }
1763                         }
1764                 } else {
1765                         $clean_text .= "$line\n";
1766                 }
1767         }
1768                 
1769         return $clean_text;
1770 }
1771
1772 sub _StripFootnoteDefinitions {
1773         my $text = shift;
1774         my $less_than_tab = $g_tab_width - 1;
1775
1776         while ($text =~ s{
1777                 \n\[\^([^\n]+?)\]\:[ \t]*# id = $1
1778                 \n?
1779                 (.*?)\n{1,2}            # end at new paragraph
1780                 ((?=\n[ ]{0,$less_than_tab}\S)|\Z)      # Lookahead for non-space at line-start, or end of doc
1781         }
1782         {\n}sx)
1783         {
1784                 my $id = $1;
1785                 my $footnote = "$2\n";
1786                 $footnote =~ s/^[ ]{0,$g_tab_width}//gm;
1787         
1788                 $g_footnotes{id2footnote($id)} = $footnote;
1789         }
1790         
1791         return $text;
1792 }
1793
1794 sub _DoFootnotes {
1795         my $text = shift;
1796         
1797         # First, run routines that get skipped in footnotes
1798         foreach my $label (sort keys %g_footnotes) {
1799                 my $footnote = _RunBlockGamut($g_footnotes{$label});
1800
1801                 $footnote = _DoMarkdownCitations($footnote);
1802                 $g_footnotes{$label} = $footnote;
1803         }
1804         
1805         $text =~ s{
1806                 \[\^(.+?)\]             # id = $1
1807         }{
1808                 my $result;
1809                 my $id = id2footnote($1);
1810                 if (defined $g_footnotes{$id} ) {
1811                         $g_footnote_counter++;
1812                         if ($g_footnotes{$id} =~ /^glossary:/i) {
1813                                 $result = "<a href=\"#fn:$id\" id=\"fnref:$id\" class=\"footnote glossary\">$g_footnote_counter</a>";
1814                         } else {
1815                                 $result = "<a href=\"#fn:$id\" id=\"fnref:$id\" class=\"footnote\">$g_footnote_counter</a>";
1816                         }
1817                         push (@g_used_footnotes,$id);
1818                 }
1819                 $result;
1820         }xsge;
1821         
1822         return $text;
1823 }
1824
1825 sub _FixFootnoteParagraphs {
1826         my $text = shift;
1827         
1828         $text =~ s/^\<p\>\<\/footnote\>/<\/footnote>/gm;
1829         
1830         return $text;
1831 }
1832
1833 sub _PrintFootnotes{
1834         my $footnote_counter = 0;
1835         my $result = "";
1836         
1837         foreach my $id (@g_used_footnotes) {
1838                 $footnote_counter++;
1839                 my $footnote = $g_footnotes{$id};
1840                 my $footnote_closing_tag = "";
1841
1842                 $footnote =~ s/(\<\/(p(re)?|ol|ul)\>)$//;
1843                 $footnote_closing_tag = $1;
1844                 
1845                 if ($footnote =~ s/^glossary:\s*//i) {
1846                         # Add some formatting for glossary entries
1847
1848                         $footnote =~ s{
1849                                 ^(.*?)                          # $1 = term
1850                                 \s*
1851                                 (?:\(([^\(\)]*)\)[^\n]*)?               # $2 = optional sort key
1852                                 \n
1853                         }{
1854                                 my $glossary = "<span class=\"glossary name\">$1</span>";
1855                                 
1856                                 if ($2) {
1857                                         $glossary.="<span class=\"glossary sort\" style=\"display:none\">$2</span>";
1858                                 };
1859                                 
1860                                 $glossary . ":<p>";     
1861                         }egsx;
1862
1863                         $result.="<li id=\"fn:$id\">$footnote<a href=\"#fnref:$id\" class=\"reversefootnote\">&#160;&#8617;</a>$footnote_closing_tag</li>\n\n";
1864                 } else {
1865                         $result.="<li id=\"fn:$id\">$footnote<a href=\"#fnref:$id\" class=\"reversefootnote\">&#160;&#8617;</a>$footnote_closing_tag</li>\n\n";
1866                 }
1867         }
1868         $result .= "</ol>\n</div>";
1869
1870         if ($footnote_counter > 0) {
1871                 $result = "\n\n<div class=\"footnotes\">\n<hr$g_empty_element_suffix\n<ol>\n\n".$result;
1872         } else {
1873                 $result = "";
1874         }       
1875         
1876         $result= _UnescapeSpecialChars($result);
1877         return $result;
1878 }
1879
1880 sub Header2Label {
1881         my $header = shift;
1882         my $label = lc $header;
1883         $label =~ s/[^A-Za-z0-9:_.-]//g;                # Strip illegal characters
1884         while ($label =~ s/^[^A-Za-z]//g)
1885                 {};             # Strip illegal leading characters
1886         return $label;
1887 }
1888
1889 sub id2footnote {
1890         # Since we prepend "fn:", we can allow leading digits in footnotes
1891         my $id = shift;
1892         my $footnote = lc $id;
1893         $footnote =~ s/[^A-Za-z0-9:_.-]//g;             # Strip illegal characters
1894         return $footnote;
1895 }
1896
1897
1898 sub xhtmlMetaData {
1899         my $result = qq{<?xml version="1.0" encoding="UTF-8" ?>\n};
1900
1901         # This screws up xsltproc - make sure to use `-nonet -novalid` if you
1902         #       have difficulty
1903         if ($g_allow_mathml) {
1904                  $result .= qq{<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN"\n\t"http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd">
1905 \n};
1906         
1907                 $result.= qq{<html xmlns="http://www.w3.org/1999/xhtml">\n\t<head>\n};
1908         } else {
1909                 $result .= qq{<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n};
1910
1911                 $result.= qq!<html xmlns="http://www.w3.org/1999/xhtml">\n\t<head>\n!;
1912         }
1913         
1914         $result.= "\t\t<!-- Processed by MultiMarkdown -->\n";
1915         
1916         foreach my $key (sort keys %g_metadata ) {
1917                 # Strip trailing spaces
1918                 $g_metadata{$key} =~ s/(\s)*$//s;
1919                 
1920                 # Strip spaces from key
1921                 my $export_key = $key;
1922                 $export_key =~ s/\s//g;
1923                 
1924                 if (lc($key) eq "title") {
1925                         $result.= "\t\t<title>" . _EncodeAmpsAndAngles($g_metadata{$key}) . "</title>\n";
1926                 } elsif (lc($key) eq "css") {
1927                         $result.= "\t\t<link type=\"text/css\" rel=\"stylesheet\" href=\"$g_metadata{$key}\"$g_empty_element_suffix\n";
1928                 } elsif (lc($export_key) eq "xhtmlheader") {
1929                         $result .= "\t\t$g_metadata{$key}\n";
1930                 } else {
1931                         $result.= qq!\t\t<meta name="$export_key" content="$g_metadata{$key}"$g_empty_element_suffix\n!;
1932                 }
1933         }
1934         $result.= "\t</head>\n";
1935         
1936         return $result;
1937 }
1938
1939 sub textMetaData {
1940         my $result = "";
1941         
1942         foreach my $key (sort keys %g_metadata ) {
1943                 $result .= "$key: $g_metadata{$key}\n";
1944         }
1945         $result =~ s/\s*\n/<br \/>\n/g;
1946         
1947         if ($result ne "") {
1948                 $result.= "\n";
1949         }
1950         
1951         return $result;
1952 }
1953
1954 sub _ConvertCopyright{
1955         my $text = shift;
1956         # Convert to an XML compatible form of copyright symbol
1957         
1958         $text =~ s/&copy;/&#xA9;/gi;
1959         
1960         return $text;
1961 }
1962
1963 sub _CreateWikiLink {
1964         my $title = shift;
1965         
1966         my $id = $title;
1967                 $id =~ s/ /_/g;
1968                 $id =~ s/__+/_/g;
1969                 $id =~ s/^_//g;
1970                 $id =~ s/_$//;
1971
1972         $title =~ s/_/ /g;
1973                 
1974         return "[$title]($g_base_url$id)";
1975 }
1976
1977 sub _DoWikiLinks {
1978         my $text = shift;
1979         my $WikiWord = '[A-Z]+[a-z\x80-\xff]+[A-Z][A-Za-z\x80-\xff]*';
1980         my $FreeLinkPattern = "([-,.()' _0-9A-Za-z\x80-\xff]+)";
1981         
1982         if ($g_wikilinks_kill_switch) {
1983                 return $text;
1984         }       
1985         
1986         if ($g_use_wiki_links) {
1987                 # FreeLinks
1988                 $text =~ s{
1989                         \[\[($FreeLinkPattern)\]\]
1990                 }{
1991                         my $label = $1;
1992                         $label =~ s{
1993                                 ([\s\>])($WikiWord)
1994                         }{
1995                                 $1 ."\\" . $2
1996                         }xsge;
1997                         
1998                         _CreateWikiLink($label)
1999                 }xsge;
2000         }
2001         
2002         # WikiWords
2003         if ($g_use_wiki_links) {
2004                 $text =~ s{
2005                         ([\s])($WikiWord)
2006                 }{
2007                         $1 . _CreateWikiLink($2)
2008                 }xsge;
2009                 
2010                 # Catch WikiWords at beginning of text
2011                 $text =~ s{^($WikiWord)
2012                 }{
2013                         _CreateWikiLink($1)
2014                 }xse;
2015         }
2016         
2017         
2018         return $text;
2019 }
2020
2021 sub _UnescapeWikiWords {
2022         my $text = shift;
2023         my $WikiWord = '[A-Z]+[a-z\x80-\xff]+[A-Z][A-Za-z\x80-\xff]*';
2024
2025         if ($g_wikilinks_kill_switch) {
2026                 return $text;
2027         }
2028                 
2029         # Unescape escaped WikiWords
2030         # This should occur whether wikilinks are enabled or not
2031         $text =~ s/(?<=\B)\\($WikiWord)/$1/g;
2032         
2033         return $text;
2034 }
2035
2036
2037 sub _DoTables {
2038         my $text = shift;
2039         my $less_than_tab = $g_tab_width - 1;
2040         
2041         # Algorithm inspired by PHP Markdown Extra's table support
2042         # <http://www.michelf.com/projects/php-markdown/>
2043                 
2044         # Reusable regexp's to match table
2045         
2046         my $line_start = qr{
2047                 [ ]{0,$less_than_tab}
2048         }mx;
2049         
2050         my $table_row = qr{
2051                 [^\n]*?\|[^\n]*?\n
2052         }mx;
2053                 
2054         my $first_row = qr{
2055                 $line_start
2056                 \S+.*?\|.*?\n
2057         }mx;
2058         
2059         my $table_rows = qr{
2060                 (\n?$table_row)
2061         }mx;
2062         
2063         my $table_caption = qr{
2064                 $line_start
2065                 \[.*?\][ \t]*\n
2066         }mx;
2067         
2068         my $table_divider = qr{
2069                 $line_start
2070                 [\|\-\+\:\.][ \-\+\|\:\.]* \| [ \-\+\|\:\.]* 
2071         }mx;
2072         
2073         my $whole_table = qr{
2074                 ($table_caption)?               # Optional caption
2075                 ($first_row                             # First line must start at beginning
2076                 ($table_row)*?)?                # Header Rows
2077                 $table_divider                  # Divider/Alignment definitions
2078                 $table_rows+                    # Body Rows
2079                 ($table_caption)?               # Optional caption
2080         }mx;
2081         
2082         
2083         # Find whole tables, then break them up and process them
2084         
2085         $text =~ s{
2086                 ^($whole_table)                 # Whole table in $1
2087                 (\n|\Z)                                 # End of file or 2 blank lines
2088         }{
2089                 my $table = $1;
2090                 
2091                 # Clean extra spaces at end of lines - 
2092                 #       they cause the processing to choke
2093                 $table =~ s/[\t ]*\n/\n/gs;
2094                 
2095                 my $result = "<table>\n";
2096                 my @alignments;
2097                 my $use_row_header = 1;
2098                 
2099                 # Add Caption, if present
2100                 
2101                 if ($table =~ s/^$line_start\[\s*(.*?)\s*\](\[\s*(.*?)\s*\])?[ \t]*$//m) {
2102                         my $table_id = "";
2103                         if (defined $3) {
2104                                 # add caption id to cross-ref list
2105                                 $table_id = Header2Label($3);
2106                         } else {
2107                                 # use caption as the id
2108                                 $table_id = Header2Label($1);
2109                         }
2110                         $result .= "<caption id=\"$table_id\">" . _RunSpanGamut($1). "</caption>\n";
2111                                 
2112                         $g_crossrefs{$table_id} = "#$table_id";
2113                         $g_titles{$table_id} = "$1";
2114                 }
2115                                 
2116                 # If a second "caption" is present, treat it as a summary
2117                 # However, this is not valid in XHTML 1.0 Strict
2118                 # But maybe in future
2119                 
2120                 # A summary might be longer than one line
2121                 if ($table =~ s/\n$line_start\[\s*(.*?)\s*\][ \t]*\n/\n/s) {
2122                         # $result .= "<summary>" . _RunSpanGamut($1) . "</summary>\n";
2123                 }
2124                 
2125                 # Now, divide table into header, alignment, and body
2126
2127                 # First, add leading \n in case there is no header
2128                 
2129                 $table = "\n" . $table;
2130                 
2131                 # Need to be greedy
2132                 
2133                 $table =~ s/\n($table_divider)\n(($table_rows)+)//s;
2134
2135                 my $body = "";
2136                 my $alignment_string = $1;
2137                 if (defined $2){
2138                         $body = $2;
2139                 }
2140
2141                 # Process column alignment
2142                 while ($alignment_string =~ /\|?\s*(.+?)\s*(\||\Z)/gs) {
2143                         my $cell = _RunSpanGamut($1);
2144                         if ($cell =~ /\+/){
2145                                 $result .= "<col class=\"extended\"";
2146                         } else {
2147                                 $result .= "<col";
2148                         }
2149                         if ($cell =~ /\:$/) {
2150                                 if ($cell =~ /^\:/) {
2151                                         $result .= " align=\"center\"$g_empty_element_suffix\n";
2152                                         push(@alignments,"center");
2153                                 } else {
2154                                         $result .= " align=\"right\"$g_empty_element_suffix\n";
2155                                         push(@alignments,"right");
2156                                 }
2157                         } else {
2158                                 if ($cell =~ /^\:/) {
2159                                         $result .= " align=\"left\"$g_empty_element_suffix\n";
2160                                         push(@alignments,"left");
2161                                 } else {
2162                                         if (($cell =~ /^\./) || ($cell =~ /\.$/)) {
2163                                                 $result .= " align=\"char\"$g_empty_element_suffix\n";
2164                                                 push(@alignments,"char");
2165                                         } else {
2166                                                 $result .= "$g_empty_element_suffix\n";
2167                                                 push(@alignments,"");
2168                                         }
2169                                 }
2170                         }
2171                 }
2172                 
2173                 # Process headers
2174                 $table =~ s/^\n+//s;
2175                 
2176                 $result .= "<thead>\n";
2177                 
2178                 # Strip blank lines
2179                 $table =~ s/\n[ \t]*\n/\n/g;
2180                 
2181                 foreach my $line (split(/\n/, $table)) {
2182                         # process each line (row) in table
2183                         $result .= "<tr>\n";
2184                         my $count=0;
2185                         while ($line =~ /\|?\s*([^\|]+?)\s*(\|+|\Z)/gs) {
2186                                 # process contents of each cell
2187                                 my $cell = _RunSpanGamut($1);
2188                                 my $ending = $2;
2189                                 my $colspan = "";
2190                                 if ($ending =~ s/^\s*(\|{2,})\s*$/$1/) {
2191                                         $colspan = " colspan=\"" . length($ending) . "\"";
2192                                 }
2193                                 $result .= "\t<th$colspan>$cell</th>\n";
2194                                 if ( $count == 0) {
2195                                         if ($cell =~ /^\s*$/) {
2196                                                 $use_row_header = 1;
2197                                         } else {
2198                                                 $use_row_header = 0;
2199                                         }
2200                                 }
2201                                 $count++;
2202                         }
2203                         $result .= "</tr>\n";
2204                 }
2205                 
2206                 # Process body
2207                 
2208                 $result .= "</thead>\n<tbody>\n";
2209
2210                 foreach my $line (split(/\n/, $body)) {
2211                         # process each line (row) in table
2212                         if ($line =~ /^\s*$/) {
2213                                 $result .= "</tbody>\n\n<tbody>\n";
2214                                 next;
2215                         }
2216                         $result .= "<tr>\n";
2217                         my $count=0;
2218                         while ($line =~ /\|?\s*([^\|]+?)\s*(\|+|\Z)/gs) {
2219                                 # process contents of each cell
2220                                 my $cell = _RunSpanGamut($1);
2221                                 my $ending = "";
2222                                 if ($2 ne ""){
2223                                         $ending = $2;
2224                                 }
2225                                 my $colspan = "";
2226                                 my $cell_type = "td";
2227                                 if ($count == 0 && $use_row_header == 1) {
2228                                         $cell_type = "th";
2229                                 }
2230                                 if ($ending =~ s/^\s*(\|{2,})\s*$/$1/) {
2231                                         $colspan = " colspan=\"" . length($ending) . "\"";
2232                                 }
2233                                 if ($alignments[$count] !~ /^\s*$/) {
2234                                         $result .= "\t<$cell_type$colspan align=\"$alignments[$count]\">$cell</$cell_type>\n";
2235                                 } else {
2236                                         $result .= "\t<$cell_type$colspan>$cell</$cell_type>\n";
2237                                         }
2238                                 $count++;
2239                         }
2240                         $result .= "</tr>\n";
2241                 }
2242
2243                 # Strip out empty <thead> sections
2244                 $result =~ s/<thead>\s*<\/thead>\s*//s;
2245
2246                 # Handle pull-quotes
2247
2248                 # This might be too specific for my needs.  If others want it
2249                 # removed, I am open to discussion.
2250
2251                 $result =~ s/<table>\s*<col \/>\s*<tbody>/<table class="pull-quote">\n<col \/>\n<tbody>/s;
2252                 
2253                 $result .= "</tbody>\n</table>\n";
2254                 $result
2255         }egmx;
2256         
2257         my $table_body = qr{
2258                 (                                                               # wrap whole match in $2
2259                         
2260                         (.*?\|.*?)\n                                    # wrap headers in $3
2261                         
2262                         [ ]{0,$less_than_tab}
2263                         ($table_divider)        # alignment in $4
2264                         
2265                         (                                                       # wrap cells in $5
2266                                 $table_rows
2267                         )
2268                 )
2269         }mx;
2270         
2271         return $text;
2272 }
2273
2274
2275 sub _DoAttributes{
2276         my $id = shift;
2277         my $result = "";
2278         
2279         if (defined $g_attributes{$id}) {
2280                 my $attributes = $g_attributes{$id};
2281                 while ($attributes =~ s/(\S+)="(.*?)"//) {
2282                         $result .= " $1=\"$2\"";
2283                 }
2284                 while ($attributes =~ /(\S+)=(\S+)/g) {
2285                         $result .= " $1=\"$2\"";
2286                 }
2287         }
2288         
2289         return $result;
2290 }
2291
2292
2293 sub _StripMarkdownReferences {
2294         my $text = shift;
2295         my $less_than_tab = $g_tab_width - 1;
2296
2297         while ($text =~ s{
2298                 \n\[\#(.+?)\]:[ \t]*    # id = $1
2299                 \n?
2300                 (.*?)\n{1,2}                    # end at new paragraph
2301                 ((?=\n[ ]{0,$less_than_tab}\S)|\Z)      # Lookahead for non-space at line-start, or end of doc
2302         }
2303         {\n}sx)
2304         {
2305                 my $id = $1;
2306                 my $reference = "$2\n";
2307
2308                 $reference =~ s/^[ ]{0,$g_tab_width}//gm;
2309                 
2310                 $reference = _RunBlockGamut($reference);
2311
2312                 # strip leading and trailing <p> tags (they will be added later)
2313                 $reference =~ s/^\<p\>//s;
2314                 $reference =~ s/\<\/p\>\s*$//s;
2315                 
2316                 $g_references{$id} = $reference;
2317         }
2318         
2319         return $text;
2320 }
2321
2322 sub _DoMarkdownCitations {
2323         my $text = shift;
2324         
2325         $text =~ s{                             # Allow for citations without locator to be written
2326                 \[\#([^\[]*?)\]         # in usual manner, e.g. [#author][] rather than
2327                 [ ]?                            # [][#author]
2328                 (?:\n[ ]*)?
2329                 \[\s*\]
2330         }{
2331                 "[][#$1]";
2332         }xsge;
2333         
2334         $text =~ s{
2335                 \[([^\[]*?)\]           # citation text = $1
2336                 [ ]?                    # one optional space
2337                 (?:\n[ ]*)?             # one optional newline followed by spaces
2338                 \[\#(.*?)\]             # id = $2
2339         }{
2340                 my $result;
2341                 my $anchor_text = $1;
2342                 my $id = $2;
2343                 my $count;
2344
2345                 # implement equivalent to \citet
2346                 my $textual_string = "";
2347                 if ($anchor_text =~ s/^(.*?);\s*//) {
2348                         $textual_string = "<span class=\"textual citation\">$1</span>";
2349                 }
2350
2351                 if (defined $g_references{$id} ) {
2352                         my $citation_counter=0;
2353                         
2354                         # See if citation has been used before
2355                         foreach my $old_id (@g_used_references) {
2356                                 $citation_counter++;
2357                                 $count = $citation_counter if ($old_id eq $id);
2358                         }
2359         
2360                         if (! defined $count) {
2361                                 $g_citation_counter++;
2362                                 $count = $g_citation_counter;
2363                                 push (@g_used_references,$id);
2364                         }
2365                         
2366                         $result = "<span class=\"markdowncitation\">$textual_string (<a href=\"#$id\">$count</a>";
2367                         
2368                         if ($anchor_text ne "") {
2369                                 $result .=", <span class=\"locator\">$anchor_text</span>";
2370                         }
2371                         
2372                         $result .= ")</span>";
2373                 } else {
2374                         # No reference exists
2375                         $result = "<span class=\"externalcitation\">$textual_string (<a id=\"$id\">$id</a>";
2376
2377                         if ($anchor_text ne "") {
2378                                 $result .=", <span class=\"locator\">$anchor_text</span>";
2379                         }
2380                         
2381                         $result .= ")</span>";
2382                 }
2383                 
2384                 if (Header2Label($anchor_text) eq "notcited"){
2385                         $result = "<span class=\"notcited\" id=\"$id\"/>";
2386                 }
2387                 $result;
2388         }xsge;
2389         
2390         return $text;
2391
2392 }
2393
2394 sub _PrintMarkdownBibliography{
2395         my $citation_counter = 0;
2396         my $result;
2397         
2398         foreach my $id (@g_used_references) {
2399                 $citation_counter++;
2400                 $result.="<div id=\"$id\"><p>[$citation_counter] <span class=\"item\">$g_references{$id}</span></p></div>\n\n";
2401         }
2402         $result .= "</div>";
2403
2404         if ($citation_counter > 0) {
2405                 $result = "\n\n<div class=\"bibliography\">\n<hr$g_empty_element_suffix\n<p>$g_bibliography_title</p>\n\n".$result;
2406         } else {
2407                 $result = "";
2408         }       
2409         
2410         return $result;
2411 }
2412
2413 sub _GenerateImageCrossRefs {
2414         my $text = shift;
2415
2416         #
2417         # First, handle reference-style labeled images: ![alt text][id]
2418         #
2419         $text =~ s{
2420                 (                               # wrap whole match in $1
2421                   !\[
2422                     (.*?)               # alt text = $2
2423                   \]
2424
2425                   [ ]?                          # one optional space
2426                   (?:\n[ ]*)?           # one optional newline followed by spaces
2427
2428                   \[
2429                     (.*?)               # id = $3
2430                   \]
2431
2432                 )
2433         }{
2434                 my $result;
2435                 my $whole_match = $1;
2436                 my $alt_text    = $2;
2437                 my $link_id     = lc $3;
2438
2439                 if ($link_id eq "") {
2440                         $link_id = lc $alt_text;     # for shortcut links like ![this][].
2441                 }
2442
2443                 $alt_text =~ s/"/&quot;/g;
2444                 if (defined $g_urls{$link_id}) {
2445                         my $label = Header2Label($alt_text);
2446                         $g_crossrefs{$label} = "#$label";
2447                 }
2448                 else {
2449                         # If there's no such link ID, leave intact:
2450                         $result = $whole_match;
2451                 }
2452
2453                 $whole_match;
2454         }xsge;
2455
2456         #
2457         # Next, handle inline images:  ![alt text](url "optional title")
2458         # Don't forget: encode * and _
2459
2460         $text =~ s{
2461                 (                               # wrap whole match in $1
2462                   !\[
2463                     (.*?)               # alt text = $2
2464                   \]
2465                   \(                    # literal paren
2466                         [ \t]*
2467                         <?(\S+?)>?      # src url = $3
2468                         [ \t]*
2469                         (                       # $4
2470                           (['"])        # quote char = $5 '
2471                           (.*?)         # title = $6
2472                           \5            # matching quote
2473                           [ \t]*
2474                         )?                      # title is optional
2475                   \)
2476                 )
2477         }{
2478                 my $result;
2479                 my $whole_match = $1;
2480                 my $alt_text    = $2;
2481
2482                 $alt_text =~ s/"/&quot;/g;
2483                 my $label = Header2Label($alt_text);
2484                 $g_crossrefs{$label} = "#$label";
2485                 $whole_match;
2486         }xsge;
2487
2488         return $text;
2489 }
2490
2491 sub _FindMathEquations{
2492         my $text = shift;
2493         
2494         $text =~ s{
2495                 (\<math[^\>]*)id=\"(.*?)\">     # "
2496         }{
2497                 my $label = Header2Label($2);
2498                 my $header = _RunSpanGamut($2);
2499                 
2500                 $g_crossrefs{$label} = "#$label";
2501                 $g_titles{$label} = $header;
2502                 
2503                 $1 . "id=\"$label\">";
2504         }xsge;
2505         
2506         return $text;
2507 }
2508
2509 sub _DoMathSpans {
2510         # Based on Gruber's _DoCodeSpans
2511         
2512         my $text = shift;
2513         my $display_as_block = 0;       
2514         $display_as_block = 1 if ($text =~ /^<<[^\>\>]*>>$/);
2515
2516         $text =~ s{
2517                         (?<!\\)         # Character before opening << can't be a backslash
2518                         (<<)            # $1 = Opening 
2519                         (.+?)           # $2 = The code block
2520                         (?:\[(.+)\])?   # $3 = optional label
2521                         (>>)
2522                 }{
2523                         my $m = "$2";
2524                         my $label = "";
2525                         my @attr = (xmlns=>"http://www.w3.org/1998/Math/MathML");
2526                         
2527                         if (defined $3) {
2528                                 $label = Header2Label($3);
2529                                 my $header = _RunSpanGamut($3);
2530
2531                                 $g_crossrefs{$label} = "#$label";
2532                                 $g_titles{$label} = $header;
2533                         }
2534                         $m =~ s/^[ \t]*//g; # leading whitespace
2535                         $m =~ s/[ \t]*$//g; # trailing whitespace
2536                         push(@attr,(id=>"$label")) if ($label ne "");
2537                         push(@attr,(display=>"block")) if ($display_as_block == 1);
2538
2539                         $m = $mathParser->TextToMathML($m,\@attr); 
2540                         "$m";
2541                 }egsx;
2542
2543         return $text;
2544 }
2545
2546 sub _DoDefinitionLists {
2547         # Uses the syntax proposed by Michel Fortin in PHP Markdown Extra
2548         
2549         my $text = shift;
2550         my $less_than_tab = $g_tab_width -1;
2551         
2552         my $line_start = qr{
2553                 [ ]{0,$less_than_tab}
2554         }mx;
2555         
2556         my $term = qr{
2557                 $line_start
2558                 [^:\s][^\n]*\n
2559         }sx;
2560         
2561         my $definition = qr{
2562                 \n?[ ]{0,$less_than_tab}
2563                 \:[ \t]+(.*?)\n
2564                 ((?=\n*[ ]{0,$less_than_tab}\S)|\n\n|\Z)        # Lookahead for non-space at line-start,
2565                                                                                                         # two returns, or end of doc
2566         }sx;
2567         
2568         my $definition_block = qr{
2569                 ((?:$term)+)                            # $1 = one or more terms
2570                 ((?:$definition)+)                      # $2 = by one or more definitions
2571         }sx;
2572         
2573         my $definition_list = qr{
2574                 (?:$definition_block\n*)+               # One ore more definition blocks
2575         }sx;
2576         
2577         $text =~ s{
2578                 ($definition_list)                      # $1 = the whole list
2579         }{
2580                 my $list = $1;
2581                 my $result = $1;
2582                 
2583                 $list =~ s{
2584                         (?:$definition_block)\n*
2585                 }{
2586                         my $terms = $1;
2587                         my $defs = $2;
2588
2589                         $terms =~ s{
2590                                 [ ]{0,$less_than_tab}
2591                                 (.*)
2592                                 \s*
2593                         }{
2594                                 my $term = $1;
2595                                 my $result = "";
2596                                 $term =~ s/^\s*(.*?)\s*$/$1/;
2597                                 if ($term !~ /^\s*$/){
2598                                         $result = "<dt>" . _RunSpanGamut($1) . "</dt>\n";
2599                                 }
2600                                 $result;
2601                         }xmge;
2602                         
2603                         $defs =~ s{
2604                                 $definition
2605                         }{
2606                                 my $def = $1 . "\n";
2607                                 $def =~ s/^[ ]{0,$g_tab_width}//gm;
2608                                 "<dd>\n" . _RunBlockGamut($def) . "\n</dd>\n";
2609                         }xsge;
2610                         
2611                         $terms . $defs . "\n";
2612                 }xsge;
2613                 
2614                 "<dl>\n" . $list . "</dl>\n\n";
2615         }xsge;
2616         
2617         return $text
2618 }
2619
2620 sub _UnescapeComments{
2621         # Remove encoding inside comments
2622         # Based on proposal by Toras Doran (author of Text::MultiMarkdown)
2623
2624         my $text = shift;
2625         $text =~ s{
2626                 (?<=<!--) # Begin comment
2627                 (.*?)     # Anything inside
2628                 (?=-->)   # End comments
2629         }{
2630                 my $t = $1;
2631                 $t =~ s/&amp;/&/g;
2632                 $t =~ s/&lt;/</g;
2633                 $t;
2634         }egsx;
2635
2636         return $text;
2637 }
2638
2639 1;
2640
2641 __END__
2642
2643
2644 =pod
2645
2646 =head1 NAME
2647
2648 B<MultiMarkdown>
2649
2650
2651 =head1 SYNOPSIS
2652
2653 B<MultiMarkdown.pl> [ B<--html4tags> ] [ B<--version> ] [ B<-shortversion> ]
2654     [ I<file> ... ]
2655
2656
2657 =head1 DESCRIPTION
2658
2659 Markdown is a text-to-HTML filter; it translates an easy-to-read /
2660 easy-to-write structured text format into HTML. Markdown's text format
2661 is most similar to that of plain text email, and supports features such
2662 as headers, *emphasis*, code blocks, blockquotes, and links.
2663
2664 Markdown's syntax is designed not as a generic markup language, but
2665 specifically to serve as a front-end to (X)HTML. You can  use span-level
2666 HTML tags anywhere in a Markdown document, and you can use block level
2667 HTML tags (like <div> and <table> as well).
2668
2669 For more information about Markdown's syntax, see:
2670
2671     http://daringfireball.net/projects/markdown/
2672
2673
2674 =head1 OPTIONS
2675
2676 Use "--" to end switch parsing. For example, to open a file named "-z", use:
2677
2678         Markdown.pl -- -z
2679
2680 =over 4
2681
2682
2683 =item B<--html4tags>
2684
2685 Use HTML 4 style for empty element tags, e.g.:
2686
2687     <br>
2688
2689 instead of Markdown's default XHTML style tags, e.g.:
2690
2691     <br />
2692
2693
2694 =item B<-v>, B<--version>
2695
2696 Display Markdown's version number and copyright information.
2697
2698
2699 =item B<-s>, B<--shortversion>
2700
2701 Display the short-form version number.
2702
2703
2704 =back
2705
2706
2707
2708 =head1 BUGS
2709
2710 To file bug reports or feature requests (other than topics listed in the
2711 Caveats section above) please send email to:
2712
2713     support@daringfireball.net (for Markdown issues)
2714     
2715     fletcher@fletcherpenney.net (for MultiMarkdown issues)
2716
2717 Please include with your report: (1) the example input; (2) the output
2718 you expected; (3) the output Markdown actually produced.
2719
2720
2721 =head1 VERSION HISTORY
2722
2723 See the readme file for detailed release notes for this version.
2724
2725 1.0.2b8 - Wed 09 May 2007
2726
2727         +       Fixed bug with nested raw HTML tags that contained
2728                 attributes. The problem is that it uses a backreference in
2729                 the expression that it passes to gen_extract_tagged, which
2730                 is broken when Text::Balanced wraps it in parentheses.
2731
2732                 Thanks to Matt Kraai for the patch.
2733         
2734         +       Now supports URLs containing literal parentheses, such as:
2735         
2736                         http://en.wikipedia.org/wiki/WIMP_(computing)
2737                 
2738                 Such parentheses may be arbitrarily nested, but must be
2739                 balanced.
2740
2741
2742 1.0.2b7
2743
2744         +       Changed shebang line from "/usr/bin/perl" to "/usr/bin/env perl"
2745         
2746         +       Now only trim trailing newlines from code blocks, instead of trimming
2747                 all trailing whitespace characters.
2748
2749
2750 1.0.2b6 - Mon 03 Apr 2006
2751
2752         +       Fixed bad performance bug in new `Text::Balanced`-based block-level parser.
2753
2754
2755 1.0.2b5 - Thu 08 Dec 2005
2756
2757         +       Fixed bug where this:
2758         
2759                         [text](http://m.com "title" )
2760                         
2761                 wasn't working as expected, because the parser wasn't allowing for spaces
2762                 before the closing paren.
2763
2764
2765 1.0.2b4 - Thu 08 Sep 2005
2766
2767         +       Filthy hack to support markdown='1' in div tags, because I need it
2768                 to write today's fireball.
2769         
2770         +       First crack at a new, smarter, block-level HTML parser.
2771
2772 1.0.2b3 - Thu 28 Apr 2005
2773
2774         +       _DoAutoLinks() now supports the 'dict://' URL scheme.
2775
2776         +       PHP- and ASP-style processor instructions are now protected as
2777                 raw HTML blocks.
2778
2779                         <? ... ?>
2780                         <% ... %>
2781
2782         +       Workarounds for regressions introduced with fix for "backticks within
2783                 tags" bug in 1.0.2b1. The fix is to allow `...` to be turned into
2784                 <code>...</code> within an HTML tag attribute, and then to turn
2785                 these spurious `<code>` tags back into literal backtick characters
2786                 in _EscapeSpecialCharsWithinTagAttributes().
2787
2788                 The regression was caused because in the fix, we moved
2789                 _EscapeSpecialCharsWithinTagAttributes() ahead of _DoCodeSpans()
2790                 in _RunSpanGamut(), but that's no good. We need to process code
2791                 spans first, otherwise we can get tripped up by something like this:
2792
2793                         `<test a="` content of attribute `">`
2794
2795
2796 1.0.2b2 - 20 Mar 2005
2797
2798         +       Fix for nested sub-lists in list-paragraph mode. Previously we got
2799                 a spurious extra level of `<p>` tags for something like this:
2800
2801                         *       this
2802
2803                                 *       sub
2804
2805                                 that
2806
2807         +       Experimental support for [this] as a synonym for [this][].
2808                 (Note to self: No test yet for this.)
2809                 Be sure to test, e.g.: [permutations of this sort of [thing][].]
2810
2811
2812 1.0.2b1 - 28  Feb 2005
2813
2814         +       Fix for backticks within HTML tag: <span attr='`ticks`'>like this</span>
2815
2816         +       Fix for escaped backticks still triggering code spans:
2817
2818                         There are two raw backticks here: \` and here: \`, not a code span
2819
2820 1.0.1 - 14 Dec 2004
2821
2822 1.0 - 28 Aug 2004
2823
2824
2825 =head1 AUTHOR
2826
2827     John Gruber
2828     http://daringfireball.net/
2829
2830     PHP port and other contributions by Michel Fortin
2831     http://michelf.com/
2832
2833     MultiMarkdown changes by Fletcher Penney
2834     http://fletcherpenney.net/
2835
2836 =head1 COPYRIGHT AND LICENSE
2837
2838 Original Markdown Code Copyright (c) 2003-2007 John Gruber   
2839 <http://daringfireball.net/>   
2840 All rights reserved.
2841
2842 MultiMarkdown changes Copyright (c) 2005-2007 Fletcher T. Penney   
2843 <http://fletcherpenney.net/>   
2844 All rights reserved.
2845
2846 Redistribution and use in source and binary forms, with or without
2847 modification, are permitted provided that the following conditions are
2848 met:
2849
2850 * Redistributions of source code must retain the above copyright notice,
2851   this list of conditions and the following disclaimer.
2852
2853 * Redistributions in binary form must reproduce the above copyright
2854   notice, this list of conditions and the following disclaimer in the
2855   documentation and/or other materials provided with the distribution.
2856
2857 * Neither the name "Markdown" nor the names of its contributors may
2858   be used to endorse or promote products derived from this software
2859   without specific prior written permission.
2860
2861 This software is provided by the copyright holders and contributors "as
2862 is" and any express or implied warranties, including, but not limited
2863 to, the implied warranties of merchantability and fitness for a
2864 particular purpose are disclaimed. In no event shall the copyright owner
2865 or contributors be liable for any direct, indirect, incidental, special,
2866 exemplary, or consequential damages (including, but not limited to,
2867 procurement of substitute goods or services; loss of use, data, or
2868 profits; or business interruption) however caused and on any theory of
2869 liability, whether in contract, strict liability, or tort (including
2870 negligence or otherwise) arising in any way out of the use of this
2871 software, even if advised of the possibility of such damage.
2872
2873 =cut
2874
2875
2876 Possibilities for 'THE'
2877
2878 $TE     239
2879 THE     192
2880 $RE     143
2881 INE     142
2882 BLE     131
2883 ODE     98
2884 ABE     96
2885 TLE     90
2886 OTE     87
2887 APE     86
2888 G_E     79
2889 USE     79
2890 ADE     72
2891  RE     71
2892 ITE     65
2893  NE     60
2894  BE     55
2895  WE     52
2896         RE      49
2897  HE     47
2898 NTE     47
2899 NCE     46
2900 ACE     45
2901 _TE     45
2902  TE     44
2903 ORE     38
2904 LSE     37
2905  DE     36
2906 OLE     35
2907 ARE     34
2908 } E     33
2909 ATE     33
2910 OCE     31
2911 _ME     31
2912 (DE     31
2913  LE     31
2914 STE     29
2915  SE     29
2916 ERE     29
2917 ASE     28
2918 UTE     28
2919 $LE     26
2920 PRE     26
2921 ONE     26
2922 EME     25
2923 $KE     24
2924 RRE     24
2925 IRE     24
2926 SPE     24
2927 OKE     24
2928 YPE     22
2929 Y_E     21
2930 ILE     20
2931 TKE     20
2932 AGE     20
2933 $CE     19
2934 RKE     19
2935 $HE     19
2936 CHE     18
2937 NME     18
2938 SRE     18
2939 HRE     18
2940 CTE     18
2941 WHE     18
2942 SGE     17
2943 _NE     17
2944 IDE     17
2945 IKE     17
2946 $DE     17
2947  GE     16
2948 AME     16
2949         }E      16
2950  PE     15
2951 RSE     15
2952 # E     15
2953 R E     14
2954 YLE     14
2955  _E     14
2956 .NE     14
2957 URE     14
2958 OVE     14
2959 TTE     14
2960 _RE     14
2961 _HE     14
2962 'RE     13
2963 'VE     13
2964 PLE     13
2965 _LE     13
2966 OPE     13
2967 CRE     12
2968 CKE     12
2969 -LE     11
2970 CLE     11
2971 O E     11
2972 AVE     11
2973 NDE     11
2974 OWE     10
2975 S E     10
2976 ) E     10
2977 SHE     10
2978 IVE     10
2979 , E     10
2980  $E     10
2981 OME     10
2982 UME     10
2983                 E       10
2984  VE     10
2985 NGE     10
2986 AKE     9
2987 GLE     9
2988 LUE     9
2989 FLE     9
2990 UNE     9
2991 DLE     9
2992 DRE     8
2993 LLE     8
2994 OJE     8
2995 D E     8
2996 NNE     8
2997 MBE     8
2998 LTE     8
2999 RDE     8
3000 =HE     8
3001 OSE     8
3002  ME     7
3003 IME     7
3004 TRE     7
3005 -RE     7
3006 NVE     7
3007 T E     7
3008 QUE     6
3009 NSE     6
3010 UBE     6
3011 Y E     6
3012 NRE     6
3013 $ME     6
3014 AHE     6
3015 N E     6
3016  FE     6
3017 UDE     6
3018  KE     6
3019 RVE     5
3020 SSE     5
3021 PPE     5
3022 $VE     5
3023 TIE     5
3024  CE     5
3025 FTE     5
3026 ($E     5
3027 LDE     5
3028 IZE     5
3029 TDE     4
3030 RPE     4
3031 :TE     4
3032 RIE     4
3033 ICE     4
3034 NLE     4
3035 //E     4
3036 GGE     4
3037 _KE     4
3038 MME     4
3039 DDE     4
3040 GRE     4
3041 RTE     4
3042 TVE     4
3043 H E     3
3044 _DE     3
3045 /PE     3
3046 MPE     3
3047 IXE     3
3048 FRE     3
3049 TWE     3
3050 XTE     3
3051 BBE     3
3052 N_E     3
3053 KDE     3
3054         BE      3
3055 {<E     3
3056 XPE     3
3057 YBE     3
3058 UCE     3
3059 ISE     3
3060 'TE     3
3061 RCE     3
3062 :VE     3
3063 BRE     3
3064 . E     3
3065 </E     3
3066  ZE     2
3067 DGE     2
3068 TME     2
3069 DOE     2
3070 N/E     2
3071 -VE     2
3072 FFE     2
3073         NE      2
3074 <HE     2
3075 $SE     2
3076 KBE     2
3077 "TE     2
3078 }GE     2
3079 SUE     2
3080 LIE     2
3081  YE     2
3082 {DE     2
3083 F E     2
3084 XCE     2
3085         _E      2
3086 "LE     2
3087 "RE     2
3088         $E      2
3089 \"E     2
3090 OHE     2
3091 SIE     2
3092 BSE     2
3093 : E     2
3094 ->E     2
3095 G E     2
3096 "CE     2
3097 {RE     2
3098 X2E     2
3099 RGE     2
3100 LME     2
3101 E E     2
3102 IGE     2
3103 ULE     2
3104 _GE     2
3105         DE      2
3106 $NE     2
3107  *E     2
3108         GE      2
3109 EXE     2
3110 U E     1
3111 MGE     1
3112   E     1
3113 {KE     1
3114 (GE     1
3115 "$E     1
3116 - E     1
3117 -ME     1
3118 RFE     1
3119 .}E     1
3120 (HE     1
3121 L E     1
3122 FIE     1
3123 XSE     1
3124 "ME     1
3125 [$E     1
3126 O-E     1
3127 SEE     1
3128 /HE     1
3129 DSE     1
3130 [TE     1
3131 D-E     1
3132 <ME     1
3133 MTE     1
3134 DIE     1
3135 T-E     1
3136 >TE     1
3137 TNE     1
3138 .PE     1
3139 CPE     1
3140 -DE     1
3141 O@E     1
3142 WNE     1
3143 IPE     1
3144 RNE     1
3145 -PE     1
3146 GUE     1
3147 [PE     1
3148 /DE     1
3149 7UE     1
3150 GNE     1
3151 `TE     1
3152         @E      1
3153 RUE     1
3154  @E     1
3155 (SE     1
3156 (RE     1
3157 +       E       1
3158  (E     1
3159 LRE     1
3160 DME     1
3161 __E     1
3162 KRE     1
3163 ALE     1
3164 LHE     1
3165 <TE     1
3166 = E     1
3167 (VE     1
3168 X E     1
3169 OZE     1
3170 " E     1
3171 -TE     1
3172 ] E     1
3173 ; E     1