1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642 |
- #!/usr/bin/env perl
- #
- # Markdown -- A text-to-HTML conversion tool for web writers
- #
- # Copyright (c) 2004-2005 John Gruber
- # <http://daringfireball.net/projects/markdown/>
- #
- package Markdown;
- require 5.006_000;
- use strict;
- use warnings;
- use Digest::MD5 qw(md5_hex);
- use vars qw($VERSION);
- $VERSION = '1.0.2b7';
- # Tue 29 Aug 2006
- ## Disabled; causes problems under Perl 5.6.1:
- # use utf8;
- # binmode( STDOUT, ":utf8" ); # c.f.: http://acis.openlib.org/dev/perl-unicode-struggle.html
- #
- # Global default settings:
- #
- my $g_empty_element_suffix = " />"; # Change to ">" for HTML output
- my $g_tab_width = 4;
- #
- # Globals:
- #
- # Regex to match balanced [brackets]. See Friedl's
- # "Mastering Regular Expressions", 2nd Ed., pp. 328-331.
- my $g_nested_brackets;
- $g_nested_brackets = qr{
- (?> # Atomic matching
- [^\[\]]+ # Anything other than brackets
- |
- \[
- (??{ $g_nested_brackets }) # Recursive set of nested brackets
- \]
- )*
- }x;
- # Table of hash values for escaped characters:
- my %g_escape_table;
- foreach my $char (split //, '\\`*_{}[]()>#+-.!') {
- $g_escape_table{$char} = md5_hex($char);
- }
- # Global hashes, used by various utility routines
- my %g_urls;
- my %g_titles;
- my %g_html_blocks;
- # Used to track when we're inside an ordered or unordered list
- # (see _ProcessListItems() for details):
- my $g_list_level = 0;
- #### Blosxom plug-in interface ##########################################
- # Set $g_blosxom_use_meta to 1 to use Blosxom's meta plug-in to determine
- # which posts Markdown should process, using a "meta-markup: markdown"
- # header. If it's set to 0 (the default), Markdown will process all
- # entries.
- my $g_blosxom_use_meta = 0;
- sub start { 1; }
- sub story {
- my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
- if ( (! $g_blosxom_use_meta) or
- (defined($meta::markup) and ($meta::markup =~ /^\s*markdown\s*$/i))
- ){
- $$body_ref = Markdown($$body_ref);
- }
- 1;
- }
- #### Movable Type plug-in interface #####################################
- eval {require MT}; # Test to see if we're running in MT.
- unless ($@) {
- require MT;
- import MT;
- require MT::Template::Context;
- import MT::Template::Context;
- eval {require MT::Plugin}; # Test to see if we're running >= MT 3.0.
- unless ($@) {
- require MT::Plugin;
- import MT::Plugin;
- my $plugin = new MT::Plugin({
- name => "Markdown",
- description => "A plain-text-to-HTML formatting plugin. (Version: $VERSION)",
- doc_link => 'http://daringfireball.net/projects/markdown/'
- });
- MT->add_plugin( $plugin );
- }
- MT::Template::Context->add_container_tag(MarkdownOptions => sub {
- my $ctx = shift;
- my $args = shift;
- my $builder = $ctx->stash('builder');
- my $tokens = $ctx->stash('tokens');
- if (defined ($args->{'output'}) ) {
- $ctx->stash('markdown_output', lc $args->{'output'});
- }
- defined (my $str = $builder->build($ctx, $tokens) )
- or return $ctx->error($builder->errstr);
- $str; # return value
- });
- MT->add_text_filter('markdown' => {
- label => 'Markdown',
- docs => 'http://daringfireball.net/projects/markdown/',
- on_format => sub {
- my $text = shift;
- my $ctx = shift;
- my $raw = 0;
- if (defined $ctx) {
- my $output = $ctx->stash('markdown_output');
- if (defined $output && $output =~ m/^html/i) {
- $g_empty_element_suffix = ">";
- $ctx->stash('markdown_output', '');
- }
- elsif (defined $output && $output eq 'raw') {
- $raw = 1;
- $ctx->stash('markdown_output', '');
- }
- else {
- $raw = 0;
- $g_empty_element_suffix = " />";
- }
- }
- $text = $raw ? $text : Markdown($text);
- $text;
- },
- });
- # If SmartyPants is loaded, add a combo Markdown/SmartyPants text filter:
- my $smartypants;
- {
- no warnings "once";
- $smartypants = $MT::Template::Context::Global_filters{'smarty_pants'};
- }
- if ($smartypants) {
- MT->add_text_filter('markdown_with_smartypants' => {
- label => 'Markdown With SmartyPants',
- docs => 'http://daringfireball.net/projects/markdown/',
- on_format => sub {
- my $text = shift;
- my $ctx = shift;
- if (defined $ctx) {
- my $output = $ctx->stash('markdown_output');
- if (defined $output && $output eq 'html') {
- $g_empty_element_suffix = ">";
- }
- else {
- $g_empty_element_suffix = " />";
- }
- }
- $text = Markdown($text);
- $text = $smartypants->($text, '1');
- },
- });
- }
- }
- else {
- #### BBEdit/command-line text filter interface ##########################
- # Needs to be hidden from MT (and Blosxom when running in static mode).
- # We're only using $blosxom::version once; tell Perl not to warn us:
- no warnings 'once';
- unless ( defined($blosxom::version) ) {
- use warnings;
- #### Check for command-line switches: #################
- my %cli_opts;
- use Getopt::Long;
- Getopt::Long::Configure('pass_through');
- GetOptions(\%cli_opts,
- 'version',
- 'shortversion',
- 'html4tags',
- );
- if ($cli_opts{'version'}) { # Version info
- print "\nThis is Markdown, version $VERSION.\n";
- print "Copyright 2004 John Gruber\n";
- print "http://daringfireball.net/projects/markdown/\n\n";
- exit 0;
- }
- if ($cli_opts{'shortversion'}) { # Just the version number string.
- print $VERSION;
- exit 0;
- }
- if ($cli_opts{'html4tags'}) { # Use HTML tag style instead of XHTML
- $g_empty_element_suffix = ">";
- }
- #### Process incoming text: ###########################
- my $text;
- {
- local $/; # Slurp the whole file
- $text = <>;
- }
- print Markdown($text);
- }
- }
- sub Markdown {
- #
- # Main function. The order in which other subs are called here is
- # essential. Link and image substitutions need to happen before
- # _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
- # and <img> tags get encoded.
- #
- my $text = shift;
- # Clear the global hashes. If we don't clear these, you get conflicts
- # from other articles when generating a page which contains more than
- # one article (e.g. an index page that shows the N most recent
- # articles):
- %g_urls = ();
- %g_titles = ();
- %g_html_blocks = ();
- # Standardize line endings:
- $text =~ s{\r\n}{\n}g; # DOS to Unix
- $text =~ s{\r}{\n}g; # Mac to Unix
- # Make sure $text ends with a couple of newlines:
- $text .= "\n\n";
- # Convert all tabs to spaces.
- $text = _Detab($text);
- # Strip any lines consisting only of spaces and tabs.
- # This makes subsequent regexen easier to write, because we can
- # match consecutive blank lines with /\n+/ instead of something
- # contorted like /[ \t]*\n+/ .
- $text =~ s/^[ \t]+$//mg;
- # Turn block-level HTML blocks into hash entries
- $text = _HashHTMLBlocks($text);
- # Strip link definitions, store in hashes.
- $text = _StripLinkDefinitions($text);
- $text = _RunBlockGamut($text);
- $text = _UnescapeSpecialChars($text);
- return $text . "\n";
- }
- sub _StripLinkDefinitions {
- #
- # Strips link definitions from text, stores the URLs and titles in
- # hash references.
- #
- my $text = shift;
- my $less_than_tab = $g_tab_width - 1;
- # Link defs are in the form: ^[id]: url "optional title"
- while ($text =~ s{
- ^[ ]{0,$less_than_tab}\[(.+)\]: # id = $1
- [ \t]*
- \n? # maybe *one* newline
- [ \t]*
- <?(\S+?)>? # url = $2
- [ \t]*
- \n? # maybe one newline
- [ \t]*
- (?:
- (?<=\s) # lookbehind for whitespace
- ["(]
- (.+?) # title = $3
- [")]
- [ \t]*
- )? # title is optional
- (?:\n+|\Z)
- }
- {}mx) {
- $g_urls{lc $1} = _EncodeAmpsAndAngles( $2 ); # Link IDs are case-insensitive
- if ($3) {
- $g_titles{lc $1} = $3;
- $g_titles{lc $1} =~ s/"/"/g;
- }
- }
- return $text;
- }
- sub _HashHTMLBlocks {
- my $text = shift;
- my $less_than_tab = $g_tab_width - 1;
- # Hashify HTML blocks:
- # We only want to do this for block-level HTML tags, such as headers,
- # lists, and tables. That's because we still want to wrap <p>s around
- # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
- # phrase emphasis, and spans. The list of tags we're looking for is
- # hard-coded:
- my $block_tags = qr{
- (?:
- p | div | h[1-6] | blockquote | pre | table |
- dl | ol | ul | script | noscript | form |
- fieldset | iframe | math | ins | del
- )
- }x;
- my $tag_attrs = qr{
- (?: # Match one attr name/value pair
- \s+ # There needs to be at least some whitespace
- # before each attribute name.
- [\w.:_-]+ # Attribute name
- \s*=\s*
- (["']) # Attribute quoter
- .+? # Attribute value
- \1 # Closing quoter
- )* # Zero or more
- }x;
- my $empty_tag = qr{< \w+ $tag_attrs \s* />}xms;
- my $open_tag = qr{< $block_tags $tag_attrs \s* >}xms;
- my $close_tag = undef; # let Text::Balanced handle this
- use Text::Balanced qw(gen_extract_tagged);
- my $extract_block = gen_extract_tagged($open_tag, $close_tag, undef, { ignore => [$empty_tag] });
- my @chunks;
- ## TO-DO: the 0,3 on the next line ought to respect the
- ## tabwidth, or else, we should mandate 4-space tabwidth and
- ## be done with it:
- while ($text =~ s{^(([ ]{0,3}<)?.*\n)}{}m) {
- my $cur_line = $1;
- if (defined $2) {
- # current line could be start of code block
- my ($tag, $remainder) = $extract_block->($cur_line . $text);
- if ($tag) {
- my $key = md5_hex($tag);
- $g_html_blocks{$key} = $tag;
- push @chunks, "\n\n" . $key . "\n\n";
- $text = $remainder;
- }
- else {
- # No tag match, so toss $cur_line into @chunks
- push @chunks, $cur_line;
- }
- }
- else {
- # current line could NOT be start of code block
- push @chunks, $cur_line;
- }
- }
- push @chunks, $text; # Whatever is left.
- $text = join '', @chunks;
- # Special case just for <hr />. It was easier to make a special case than
- # to make the other regex more complicated.
- $text =~ s{
- (?:
- (?<=\n\n) # Starting after a blank line
- | # or
- \A\n? # the beginning of the doc
- )
- ( # save in $1
- [ ]{0,$less_than_tab}
- <(hr) # start tag = $2
- \b # word break
- ([^<>])*? #
- /?> # the matching end tag
- [ \t]*
- (?=\n{2,}|\Z) # followed by a blank line or end of document
- )
- }{
- my $key = md5_hex($1);
- $g_html_blocks{$key} = $1;
- "\n\n" . $key . "\n\n";
- }egx;
- # Special case for standalone HTML comments:
- $text =~ s{
- (?:
- (?<=\n\n) # Starting after a blank line
- | # or
- \A\n? # the beginning of the doc
- )
- ( # save in $1
- [ ]{0,$less_than_tab}
- (?s:
- <!
- (--.*?--\s*)+
- >
- )
- [ \t]*
- (?=\n{2,}|\Z) # followed by a blank line or end of document
- )
- }{
- my $key = md5_hex($1);
- $g_html_blocks{$key} = $1;
- "\n\n" . $key . "\n\n";
- }egx;
- # PHP and ASP-style processor instructions (<?…?> and <%…%>)
- $text =~ s{
- (?:
- (?<=\n\n) # Starting after a blank line
- | # or
- \A\n? # the beginning of the doc
- )
- ( # save in $1
- [ ]{0,$less_than_tab}
- (?s:
- <([?%]) # $2
- .*?
- \2>
- )
- [ \t]*
- (?=\n{2,}|\Z) # followed by a blank line or end of document
- )
- }{
- my $key = md5_hex($1);
- $g_html_blocks{$key} = $1;
- "\n\n" . $key . "\n\n";
- }egx;
- return $text;
- }
- sub _RunBlockGamut {
- #
- # These are all the transformations that form block-level
- # tags like paragraphs, headers, and list items.
- #
- my $text = shift;
- $text = _DoHeaders($text);
- # Do Horizontal Rules:
- $text =~ s{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
- $text =~ s{^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
- $text =~ s{^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
- $text = _DoLists($text);
- $text = _DoCodeBlocks($text);
- $text = _DoBlockQuotes($text);
- # We already ran _HashHTMLBlocks() before, in Markdown(), but that
- # was to escape raw HTML in the original Markdown source. This time,
- # we're escaping the markup we've just created, so that we don't wrap
- # <p> tags around block-level tags.
- $text = _HashHTMLBlocks($text);
- $text = _FormParagraphs($text);
- return $text;
- }
- sub _RunSpanGamut {
- #
- # These are all the transformations that occur *within* block-level
- # tags like paragraphs, headers, and list items.
- #
- my $text = shift;
- $text = _DoCodeSpans($text);
- $text = _EscapeSpecialCharsWithinTagAttributes($text);
- $text = _EncodeBackslashEscapes($text);
- # Process anchor and image tags. Images must come first,
- # because ![foo][f] looks like an anchor.
- $text = _DoImages($text);
- $text = _DoAnchors($text);
- # Make links out of things like `<http://example.com/>`
- # Must come after _DoAnchors(), because you can use < and >
- # delimiters in inline links like [this](<url>).
- $text = _DoAutoLinks($text);
- $text = _EncodeAmpsAndAngles($text);
- $text = _DoItalicsAndBold($text);
- # Do hard breaks:
- $text =~ s/ {2,}\n/ <br$g_empty_element_suffix\n/g;
- return $text;
- }
- sub _EscapeSpecialCharsWithinTagAttributes {
- #
- # Within tags -- meaning between < and > -- encode [\ ` * _] so they
- # don't conflict with their use in Markdown for code, italics and strong.
- # We're replacing each such character with its corresponding MD5 checksum
- # value; this is likely overkill, but it should prevent us from colliding
- # with the escape values by accident.
- #
- my $text = shift;
- my $tokens ||= _TokenizeHTML($text);
- $text = ''; # rebuild $text from the tokens
- foreach my $cur_token (@$tokens) {
- if ($cur_token->[0] eq "tag") {
- $cur_token->[1] =~ s! \\ !$g_escape_table{'\\'}!gx;
- $cur_token->[1] =~ s{ (?<=.)</?code>(?=.) }{$g_escape_table{'`'}}gx;
- $cur_token->[1] =~ s! \* !$g_escape_table{'*'}!gx;
- $cur_token->[1] =~ s! _ !$g_escape_table{'_'}!gx;
- }
- $text .= $cur_token->[1];
- }
- return $text;
- }
- sub _DoAnchors {
- #
- # Turn Markdown link shortcuts into XHTML <a> tags.
- #
- my $text = shift;
- #
- # First, handle reference-style links: [link text] [id]
- #
- $text =~ s{
- ( # wrap whole match in $1
- \[
- ($g_nested_brackets) # link text = $2
- \]
- [ ]? # one optional space
- (?:\n[ ]*)? # one optional newline followed by spaces
- \[
- (.*?) # id = $3
- \]
- )
- }{
- my $result;
- my $whole_match = $1;
- my $link_text = $2;
- my $link_id = lc $3;
- if ($link_id eq "") {
- $link_id = lc $link_text; # for shortcut links like [this][].
- }
- if (defined $g_urls{$link_id}) {
- my $url = $g_urls{$link_id};
- $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid
- $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold.
- $result = "<a href=\"$url\"";
- if ( defined $g_titles{$link_id} ) {
- my $title = $g_titles{$link_id};
- $title =~ s! \* !$g_escape_table{'*'}!gx;
- $title =~ s! _ !$g_escape_table{'_'}!gx;
- $result .= " title=\"$title\"";
- }
- $result .= ">$link_text</a>";
- }
- else {
- $result = $whole_match;
- }
- $result;
- }xsge;
- #
- # Next, inline-style links: [link text](url "optional title")
- #
- $text =~ s{
- ( # wrap whole match in $1
- \[
- ($g_nested_brackets) # link text = $2
- \]
- \( # literal paren
- [ \t]*
- <?(.*?)>? # href = $3
- [ \t]*
- ( # $4
- (['"]) # quote char = $5
- (.*?) # Title = $6
- \5 # matching quote
- [ \t]* # ignore any spaces/tabs between closing quote and )
- )? # title is optional
- \)
- )
- }{
- my $result;
- my $whole_match = $1;
- my $link_text = $2;
- my $url = $3;
- my $title = $6;
- $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid
- $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold.
- $result = "<a href=\"$url\"";
- if (defined $title) {
- $title =~ s/"/"/g;
- $title =~ s! \* !$g_escape_table{'*'}!gx;
- $title =~ s! _ !$g_escape_table{'_'}!gx;
- $result .= " title=\"$title\"";
- }
- $result .= ">$link_text</a>";
- $result;
- }xsge;
- #
- # Last, handle reference-style shortcuts: [link text]
- # These must come last in case you've also got [link test][1]
- # or [link test](/foo)
- #
- $text =~ s{
- ( # wrap whole match in $1
- \[
- ([^\[\]]+) # link text = $2; can't contain '[' or ']'
- \]
- )
- }{
- my $result;
- my $whole_match = $1;
- my $link_text = $2;
- (my $link_id = lc $2) =~ s{[ ]?\n}{ }g; # lower-case and turn embedded newlines into spaces
- if (defined $g_urls{$link_id}) {
- my $url = $g_urls{$link_id};
- $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid
- $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold.
- $result = "<a href=\"$url\"";
- if ( defined $g_titles{$link_id} ) {
- my $title = $g_titles{$link_id};
- $title =~ s! \* !$g_escape_table{'*'}!gx;
- $title =~ s! _ !$g_escape_table{'_'}!gx;
- $result .= " title=\"$title\"";
- }
- $result .= ">$link_text</a>";
- }
- else {
- $result = $whole_match;
- }
- $result;
- }xsge;
- return $text;
- }
- sub _DoImages {
- #
- # Turn Markdown image shortcuts into <img> tags.
- #
- my $text = shift;
- #
- # First, handle reference-style labeled images: ![alt text][id]
- #
- $text =~ s{
- ( # wrap whole match in $1
- !\[
- (.*?) # alt text = $2
- \]
- [ ]? # one optional space
- (?:\n[ ]*)? # one optional newline followed by spaces
- \[
- (.*?) # id = $3
- \]
- )
- }{
- my $result;
- my $whole_match = $1;
- my $alt_text = $2;
- my $link_id = lc $3;
- if ($link_id eq "") {
- $link_id = lc $alt_text; # for shortcut links like ![this][].
- }
- $alt_text =~ s/"/"/g;
- if (defined $g_urls{$link_id}) {
- my $url = $g_urls{$link_id};
- $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid
- $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold.
- $result = "<img src=\"$url\" alt=\"$alt_text\"";
- if (defined $g_titles{$link_id}) {
- my $title = $g_titles{$link_id};
- $title =~ s! \* !$g_escape_table{'*'}!gx;
- $title =~ s! _ !$g_escape_table{'_'}!gx;
- $result .= " title=\"$title\"";
- }
- $result .= $g_empty_element_suffix;
- }
- else {
- # If there's no such link ID, leave intact:
- $result = $whole_match;
- }
- $result;
- }xsge;
- #
- # Next, handle inline images: 
- # Don't forget: encode * and _
- $text =~ s{
- ( # wrap whole match in $1
- !\[
- (.*?) # alt text = $2
- \]
- \s? # One optional whitespace character
- \( # literal paren
- [ \t]*
- <?(\S+?)>? # src url = $3
- [ \t]*
- ( # $4
- (['"]) # quote char = $5
- (.*?) # title = $6
- \5 # matching quote
- [ \t]*
- )? # title is optional
- \)
- )
- }{
- my $result;
- my $whole_match = $1;
- my $alt_text = $2;
- my $url = $3;
- my $title = '';
- if (defined($6)) {
- $title = $6;
- }
- $alt_text =~ s/"/"/g;
- $title =~ s/"/"/g;
- $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid
- $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold.
- $result = "<img src=\"$url\" alt=\"$alt_text\"";
- if (defined $title) {
- $title =~ s! \* !$g_escape_table{'*'}!gx;
- $title =~ s! _ !$g_escape_table{'_'}!gx;
- $result .= " title=\"$title\"";
- }
- $result .= $g_empty_element_suffix;
- $result;
- }xsge;
- return $text;
- }
- sub _DoHeaders {
- my $text = shift;
- # Setext-style headers:
- # Header 1
- # ========
- #
- # Header 2
- # --------
- #
- $text =~ s{ ^(.+)[ \t]*\n=+[ \t]*\n+ }{
- "<h1>" . _RunSpanGamut($1) . "</h1>\n\n";
- }egmx;
- $text =~ s{ ^(.+)[ \t]*\n-+[ \t]*\n+ }{
- "<h2>" . _RunSpanGamut($1) . "</h2>\n\n";
- }egmx;
- # atx-style headers:
- # # Header 1
- # ## Header 2
- # ## Header 2 with closing hashes ##
- # ...
- # ###### Header 6
- #
- $text =~ s{
- ^(\#{1,6}) # $1 = string of #'s
- [ \t]*
- (.+?) # $2 = Header text
- [ \t]*
- \#* # optional closing #'s (not counted)
- \n+
- }{
- my $h_level = length($1);
- "<h$h_level>" . _RunSpanGamut($2) . "</h$h_level>\n\n";
- }egmx;
- return $text;
- }
- sub _DoLists {
- #
- # Form HTML ordered (numbered) and unordered (bulleted) lists.
- #
- my $text = shift;
- my $less_than_tab = $g_tab_width - 1;
- # Re-usable patterns to match list item bullets and number markers:
- my $marker_ul = qr/[*+-]/;
- my $marker_ol = qr/\d+[.]/;
- my $marker_any = qr/(?:$marker_ul|$marker_ol)/;
- # Re-usable pattern to match any entirel ul or ol list:
- my $whole_list = qr{
- ( # $1 = whole list
- ( # $2
- [ ]{0,$less_than_tab}
- (${marker_any}) # $3 = first list item marker
- [ \t]+
- )
- (?s:.+?)
- ( # $4
- \z
- |
- \n{2,}
- (?=\S)
- (?! # Negative lookahead for another list item marker
- [ \t]*
- ${marker_any}[ \t]+
- )
- )
- )
- }mx;
- # We use a different prefix before nested lists than top-level lists.
- # See extended comment in _ProcessListItems().
- #
- # Note: There's a bit of duplication here. My original implementation
- # created a scalar regex pattern as the conditional result of the test on
- # $g_list_level, and then only ran the $text =~ s{...}{...}egmx
- # substitution once, using the scalar as the pattern. This worked,
- # everywhere except when running under MT on my hosting account at Pair
- # Networks. There, this caused all rebuilds to be killed by the reaper (or
- # perhaps they crashed, but that seems incredibly unlikely given that the
- # same script on the same server ran fine *except* under MT. I've spent
- # more time trying to figure out why this is happening than I'd like to
- # admit. My only guess, backed up by the fact that this workaround works,
- # is that Perl optimizes the substition when it can figure out that the
- # pattern will never change, and when this optimization isn't on, we run
- # afoul of the reaper. Thus, the slightly redundant code that uses two
- # static s/// patterns rather than one conditional pattern.
- if ($g_list_level) {
- $text =~ s{
- ^
- $whole_list
- }{
- my $list = $1;
- my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";
- # Turn double returns into triple returns, so that we can make a
- # paragraph for the last item in a list, if necessary:
- $list =~ s/\n{2,}/\n\n\n/g;
- my $result = _ProcessListItems($list, $marker_any);
- # Trim any trailing whitespace, to put the closing `</$list_type>`
- # up on the preceding line, to get it past the current stupid
- # HTML block parser. This is a hack to work around the terrible
- # hack that is the HTML block parser.
- $result =~ s{\s+$}{};
- $result = "<$list_type>" . $result . "</$list_type>\n";
- $result;
- }egmx;
- }
- else {
- $text =~ s{
- (?:(?<=\n\n)|\A\n?)
- $whole_list
- }{
- my $list = $1;
- my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";
- # Turn double returns into triple returns, so that we can make a
- # paragraph for the last item in a list, if necessary:
- $list =~ s/\n{2,}/\n\n\n/g;
- my $result = _ProcessListItems($list, $marker_any);
- $result = "<$list_type>\n" . $result . "</$list_type>\n";
- $result;
- }egmx;
- }
- return $text;
- }
- sub _ProcessListItems {
- #
- # Process the contents of a single ordered or unordered list, splitting it
- # into individual list items.
- #
- my $list_str = shift;
- my $marker_any = shift;
- # The $g_list_level global keeps track of when we're inside a list.
- # Each time we enter a list, we increment it; when we leave a list,
- # we decrement. If it's zero, we're not in a list anymore.
- #
- # We do this because when we're not inside a list, we want to treat
- # something like this:
- #
- # I recommend upgrading to version
- # 8. Oops, now this line is treated
- # as a sub-list.
- #
- # As a single paragraph, despite the fact that the second line starts
- # with a digit-period-space sequence.
- #
- # Whereas when we're inside a list (or sub-list), that line will be
- # treated as the start of a sub-list. What a kludge, huh? This is
- # an aspect of Markdown's syntax that's hard to parse perfectly
- # without resorting to mind-reading. Perhaps the solution is to
- # change the syntax rules such that sub-lists must start with a
- # starting cardinal number; e.g. "1." or "a.".
- $g_list_level++;
- # trim trailing blank lines:
- $list_str =~ s/\n{2,}\z/\n/;
- $list_str =~ s{
- (\n)? # leading line = $1
- (^[ \t]*) # leading whitespace = $2
- ($marker_any) [ \t]+ # list marker = $3
- ((?s:.+?) # list item text = $4
- (\n{1,2}))
- (?= \n* (\z | \2 ($marker_any) [ \t]+))
- }{
- my $item = $4;
- my $leading_line = $1;
- my $leading_space = $2;
- if ($leading_line or ($item =~ m/\n{2,}/)) {
- $item = _RunBlockGamut(_Outdent($item));
- }
- else {
- # Recursion for sub-lists:
- $item = _DoLists(_Outdent($item));
- chomp $item;
- $item = _RunSpanGamut($item);
- }
- "<li>" . $item . "</li>\n";
- }egmx;
- $g_list_level--;
- return $list_str;
- }
- sub _DoCodeBlocks {
- #
- # Process Markdown `<pre><code>` blocks.
- #
- my $text = shift;
- $text =~ s{
- (?:\n\n|\A)
- ( # $1 = the code block -- one or more lines, starting with a space/tab
- (?:
- (?:[ ]{$g_tab_width} | \t) # Lines must start with a tab or a tab-width of spaces
- .*\n+
- )+
- )
- ((?=^[ ]{0,$g_tab_width}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
- }{
- my $codeblock = $1;
- my $result; # return value
- $codeblock = _EncodeCode(_Outdent($codeblock));
- $codeblock = _Detab($codeblock);
- $codeblock =~ s/\A\n+//; # trim leading newlines
- $codeblock =~ s/\n+\z//; # trim trailing newlines
- $result = "\n\n<pre><code>" . $codeblock . "\n</code></pre>\n\n";
- $result;
- }egmx;
- return $text;
- }
- sub _DoCodeSpans {
- #
- # * Backtick quotes are used for <code></code> spans.
- #
- # * You can use multiple backticks as the delimiters if you want to
- # include literal backticks in the code span. So, this input:
- #
- # Just type ``foo `bar` baz`` at the prompt.
- #
- # Will translate to:
- #
- # <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
- #
- # There's no arbitrary limit to the number of backticks you
- # can use as delimters. If you need three consecutive backticks
- # in your code, use four for delimiters, etc.
- #
- # * You can use spaces to get literal backticks at the edges:
- #
- # ... type `` `bar` `` ...
- #
- # Turns to:
- #
- # ... type <code>`bar`</code> ...
- #
- my $text = shift;
- $text =~ s@
- (?<!\\) # Character before opening ` can't be a backslash
- (`+) # $1 = Opening run of `
- (.+?) # $2 = The code block
- (?<!`)
- \1 # Matching closer
- (?!`)
- @
- my $c = "$2";
- $c =~ s/^[ \t]*//g; # leading whitespace
- $c =~ s/[ \t]*$//g; # trailing whitespace
- $c = _EncodeCode($c);
- "<code>$c</code>";
- @egsx;
- return $text;
- }
- sub _EncodeCode {
- #
- # Encode/escape certain characters inside Markdown code runs.
- # The point is that in code, these characters are literals,
- # and lose their special Markdown meanings.
- #
- local $_ = shift;
- # Encode all ampersands; HTML entities are not
- # entities within a Markdown code span.
- s/&/&/g;
- # Encode $'s, but only if we're running under Blosxom.
- # (Blosxom interpolates Perl variables in article bodies.)
- {
- no warnings 'once';
- if (defined($blosxom::version)) {
- s/\$/$/g;
- }
- }
- # Do the angle bracket song and dance:
- s! < !<!gx;
- s! > !>!gx;
- # Now, escape characters that are magic in Markdown:
- s! \* !$g_escape_table{'*'}!gx;
- s! _ !$g_escape_table{'_'}!gx;
- s! { !$g_escape_table{'{'}!gx;
- s! } !$g_escape_table{'}'}!gx;
- s! \[ !$g_escape_table{'['}!gx;
- s! \] !$g_escape_table{']'}!gx;
- s! \\ !$g_escape_table{'\\'}!gx;
- return $_;
- }
- sub _DoItalicsAndBold {
- my $text = shift;
- # <strong> must go first:
- $text =~ s{ (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 }
- {<strong>$2</strong>}gsx;
- $text =~ s{ (\*|_) (?=\S) (.+?) (?<=\S) \1 }
- {<em>$2</em>}gsx;
- return $text;
- }
- sub _DoBlockQuotes {
- my $text = shift;
- $text =~ s{
- ( # Wrap whole match in $1
- (
- ^[ \t]*>[ \t]? # '>' at the start of a line
- .+\n # rest of the first line
- (.+\n)* # subsequent consecutive lines
- \n* # blanks
- )+
- )
- }{
- my $bq = $1;
- $bq =~ s/^[ \t]*>[ \t]?//gm; # trim one level of quoting
- $bq =~ s/^[ \t]+$//mg; # trim whitespace-only lines
- $bq = _RunBlockGamut($bq); # recurse
- $bq =~ s/^/ /g;
- # These leading spaces screw with <pre> content, so we need to fix that:
- $bq =~ s{
- (\s*<pre>.+?</pre>)
- }{
- my $pre = $1;
- $pre =~ s/^ //mg;
- $pre;
- }egsx;
- "<blockquote>\n$bq\n</blockquote>\n\n";
- }egmx;
- return $text;
- }
- sub _FormParagraphs {
- #
- # Params:
- # $text - string to process with html <p> tags
- #
- my $text = shift;
- # Strip leading and trailing lines:
- $text =~ s/\A\n+//;
- $text =~ s/\n+\z//;
- my @grafs = split(/\n{2,}/, $text);
- #
- # Wrap <p> tags.
- #
- foreach (@grafs) {
- unless (defined( $g_html_blocks{$_} )) {
- $_ = _RunSpanGamut($_);
- s/^([ \t]*)/<p>/;
- $_ .= "</p>";
- }
- }
- #
- # Unhashify HTML blocks
- #
- # foreach my $graf (@grafs) {
- # my $block = $g_html_blocks{$graf};
- # if (defined $block) {
- # $graf = $block;
- # }
- # }
- foreach my $graf (@grafs) {
- # Modify elements of @grafs in-place...
- my $block = $g_html_blocks{$graf};
- if (defined $block) {
- $graf = $block;
- if ($block =~ m{
- \A
- ( # $1 = <div> tag
- <div \s+
- [^>]*
- \b
- markdown\s*=\s* (['"]) # $2 = attr quote char
- 1
- \2
- [^>]*
- >
- )
- ( # $3 = contents
- .*
- )
- (</div>) # $4 = closing tag
- \z
- }xms
- ) {
- my ($div_open, $div_content, $div_close) = ($1, $3, $4);
- # We can't call Markdown(), because that resets the hash;
- # that initialization code should be pulled into its own sub, though.
- $div_content = _HashHTMLBlocks($div_content);
- $div_content = _StripLinkDefinitions($div_content);
- $div_content = _RunBlockGamut($div_content);
- $div_content = _UnescapeSpecialChars($div_content);
- $div_open =~ s{\smarkdown\s*=\s*(['"]).+?\1}{}ms;
- $graf = $div_open . "\n" . $div_content . "\n" . $div_close;
- }
- }
- }
- return join "\n\n", @grafs;
- }
- sub _EncodeAmpsAndAngles {
- # Smart processing for ampersands and angle brackets that need to be encoded.
- my $text = shift;
- # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
- # http://bumppo.net/projects/amputator/
- $text =~ s/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/&/g;
- # Encode naked <'s
- $text =~ s{<(?![a-z/?\$!])}{<}gi;
- return $text;
- }
- sub _EncodeBackslashEscapes {
- #
- # Parameter: String.
- # Returns: The string, with after processing the following backslash
- # escape sequences.
- #
- local $_ = shift;
- s! \\\\ !$g_escape_table{'\\'}!gx; # Must process escaped backslashes first.
- s! \\` !$g_escape_table{'`'}!gx;
- s! \\\* !$g_escape_table{'*'}!gx;
- s! \\_ !$g_escape_table{'_'}!gx;
- s! \\\{ !$g_escape_table{'{'}!gx;
- s! \\\} !$g_escape_table{'}'}!gx;
- s! \\\[ !$g_escape_table{'['}!gx;
- s! \\\] !$g_escape_table{']'}!gx;
- s! \\\( !$g_escape_table{'('}!gx;
- s! \\\) !$g_escape_table{')'}!gx;
- s! \\> !$g_escape_table{'>'}!gx;
- s! \\\# !$g_escape_table{'#'}!gx;
- s! \\\+ !$g_escape_table{'+'}!gx;
- s! \\\- !$g_escape_table{'-'}!gx;
- s! \\\. !$g_escape_table{'.'}!gx;
- s{ \\! }{$g_escape_table{'!'}}gx;
- return $_;
- }
- sub _DoAutoLinks {
- my $text = shift;
- $text =~ s{<((https?|ftp|dict):[^'">\s]+)>}{<a href="$1">$1</a>}gi;
- # Email addresses: <address@domain.foo>
- $text =~ s{
- <
- (?:mailto:)?
- (
- [-.\w]+
- \@
- [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
- )
- >
- }{
- _EncodeEmailAddress( _UnescapeSpecialChars($1) );
- }egix;
- return $text;
- }
- sub _EncodeEmailAddress {
- #
- # Input: an email address, e.g. "foo@example.com"
- #
- # Output: the email address as a mailto link, with each character
- # of the address encoded as either a decimal or hex entity, in
- # the hopes of foiling most address harvesting spam bots. E.g.:
- #
- # <a href="mailto:foo@e
- # xample.com">foo
- # @example.com</a>
- #
- # Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
- # mailing list: <http://tinyurl.com/yu7ue>
- #
- my $addr = shift;
- srand;
- my @encode = (
- sub { '&#' . ord(shift) . ';' },
- sub { '&#x' . sprintf( "%X", ord(shift) ) . ';' },
- sub { shift },
- );
- $addr = "mailto:" . $addr;
- $addr =~ s{(.)}{
- my $char = $1;
- if ( $char eq '@' ) {
- # this *must* be encoded. I insist.
- $char = $encode[int rand 1]->($char);
- } elsif ( $char ne ':' ) {
- # leave ':' alone (to spot mailto: later)
- my $r = rand;
- # roughly 10% raw, 45% hex, 45% dec
- $char = (
- $r > .9 ? $encode[2]->($char) :
- $r < .45 ? $encode[1]->($char) :
- $encode[0]->($char)
- );
- }
- $char;
- }gex;
- $addr = qq{<a href="$addr">$addr</a>};
- $addr =~ s{">.+?:}{">}; # strip the mailto: from the visible part
- return $addr;
- }
- sub _UnescapeSpecialChars {
- #
- # Swap back in all the special characters we've hidden.
- #
- my $text = shift;
- while( my($char, $hash) = each(%g_escape_table) ) {
- $text =~ s/$hash/$char/g;
- }
- return $text;
- }
- sub _TokenizeHTML {
- #
- # Parameter: String containing HTML markup.
- # Returns: Reference to an array of the tokens comprising the input
- # string. Each token is either a tag (possibly with nested,
- # tags contained therein, such as <a href="<MTFoo>">, or a
- # run of text between tags. Each element of the array is a
- # two-element array; the first is either 'tag' or 'text';
- # the second is the actual value.
- #
- #
- # Derived from the _tokenize() subroutine from Brad Choate's MTRegex plugin.
- # <http://www.bradchoate.com/past/mtregex.php>
- #
- my $str = shift;
- my $pos = 0;
- my $len = length $str;
- my @tokens;
- my $depth = 6;
- my $nested_tags = join('|', ('(?:<[a-z/!$](?:[^<>]') x $depth) . (')*>)' x $depth);
- my $match = qr/(?s: <! ( -- .*? -- \s* )+ > ) | # comment
- (?s: <\? .*? \?> ) | # processing instruction
- $nested_tags/ix; # nested tags
- while ($str =~ m/($match)/g) {
- my $whole_tag = $1;
- my $sec_start = pos $str;
- my $tag_start = $sec_start - length $whole_tag;
- if ($pos < $tag_start) {
- push @tokens, ['text', substr($str, $pos, $tag_start - $pos)];
- }
- push @tokens, ['tag', $whole_tag];
- $pos = pos $str;
- }
- push @tokens, ['text', substr($str, $pos, $len - $pos)] if $pos < $len;
- return \@tokens;
- }
- sub _Outdent {
- #
- # Remove one level of line-leading tabs or spaces
- #
- my $text = shift;
- $text =~ s/^(\t|[ ]{1,$g_tab_width})//gm;
- return $text;
- }
- sub _Detab {
- #
- # Cribbed from a post by Bart Lateur:
- # <http://www.nntp.perl.org/group/perl.macperl.anyperl/154>
- #
- my $text = shift;
- $text =~ s{(.*?)\t}{$1.(' ' x ($g_tab_width - length($1) % $g_tab_width))}ge;
- return $text;
- }
- 1;
- __END__
- =pod
- =head1 NAME
- B<Markdown>
- =head1 SYNOPSIS
- B<Markdown.pl> [ B<--html4tags> ] [ B<--version> ] [ B<-shortversion> ]
- [ I<file> ... ]
- =head1 DESCRIPTION
- Markdown is a text-to-HTML filter; it translates an easy-to-read /
- easy-to-write structured text format into HTML. Markdown's text format
- is most similar to that of plain text email, and supports features such
- as headers, *emphasis*, code blocks, blockquotes, and links.
- Markdown's syntax is designed not as a generic markup language, but
- specifically to serve as a front-end to (X)HTML. You can use span-level
- HTML tags anywhere in a Markdown document, and you can use block level
- HTML tags (like <div> and <table> as well).
- For more information about Markdown's syntax, see:
- http://daringfireball.net/projects/markdown/
- =head1 OPTIONS
- Use "--" to end switch parsing. For example, to open a file named "-z", use:
- Markdown.pl -- -z
- =over 4
- =item B<--html4tags>
- Use HTML 4 style for empty element tags, e.g.:
- <br>
- instead of Markdown's default XHTML style tags, e.g.:
- <br />
- =item B<-v>, B<--version>
- Display Markdown's version number and copyright information.
- =item B<-s>, B<--shortversion>
- Display the short-form version number.
- =back
- =head1 BUGS
- To file bug reports or feature requests (other than topics listed in the
- Caveats section above) please send email to:
- support@daringfireball.net
- Please include with your report: (1) the example input; (2) the output
- you expected; (3) the output Markdown actually produced.
- =head1 VERSION HISTORY
- See the readme file for detailed release notes for this version.
- 1.0.2b7
- + Changed shebang line from "/usr/bin/perl" to "/usr/bin/env perl"
-
- + Now only trim trailing newlines from code blocks, instead of trimming
- all trailing whitespace characters.
- 1.0.2b6 - Mon 03 Apr 2006
- + Fixed bad performance bug in new `Text::Balanced`-based block-level parser.
- 1.0.2b5 - Thu 08 Dec 2005
- + Fixed bug where this:
-
- [text](http://m.com "title" )
-
- wasn't working as expected, because the parser wasn't allowing for spaces
- before the closing paren.
- 1.0.2b4 - Thu 08 Sep 2005
- + Filthy hack to support markdown='1' in div tags, because I need it
- to write today's fireball.
-
- + First crack at a new, smarter, block-level HTML parser.
- 1.0.2b3 - Thu 28 Apr 2005
- + _DoAutoLinks() now supports the 'dict://' URL scheme.
- + PHP- and ASP-style processor instructions are now protected as
- raw HTML blocks.
- <? ... ?>
- <% ... %>
- + Workarounds for regressions introduced with fix for "backticks within
- tags" bug in 1.0.2b1. The fix is to allow `...` to be turned into
- <code>...</code> within an HTML tag attribute, and then to turn
- these spurious `<code>` tags back into literal backtick characters
- in _EscapeSpecialCharsWithinTagAttributes().
- The regression was caused because in the fix, we moved
- _EscapeSpecialCharsWithinTagAttributes() ahead of _DoCodeSpans()
- in _RunSpanGamut(), but that's no good. We need to process code
- spans first, otherwise we can get tripped up by something like this:
- `<test a="` content of attribute `">`
- 1.0.2b2 - 20 Mar 2005
- + Fix for nested sub-lists in list-paragraph mode. Previously we got
- a spurious extra level of `<p>` tags for something like this:
- * this
- * sub
- that
- + Experimental support for [this] as a synonym for [this][].
- (Note to self: No test yet for this.)
- Be sure to test, e.g.: [permutations of this sort of [thing][].]
- 1.0.2b1 - 28 Feb 2005
- + Fix for backticks within HTML tag: <span attr='`ticks`'>like this</span>
- + Fix for escaped backticks still triggering code spans:
- There are two raw backticks here: \` and here: \`, not a code span
- 1.0.1 - 14 Dec 2004
- 1.0 - 28 Aug 2004
- =head1 AUTHOR
- John Gruber
- http://daringfireball.net
- PHP port and other contributions by Michel Fortin
- http://michelf.com
- =head1 COPYRIGHT AND LICENSE
- Copyright (c) 2003-2005 John Gruber
- <http://daringfireball.net/>
- All rights reserved.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name "Markdown" nor the names of its contributors may
- be used to endorse or promote products derived from this software
- without specific prior written permission.
- This software is provided by the copyright holders and contributors "as
- is" and any express or implied warranties, including, but not limited
- to, the implied warranties of merchantability and fitness for a
- particular purpose are disclaimed. In no event shall the copyright owner
- or contributors be liable for any direct, indirect, incidental, special,
- exemplary, or consequential damages (including, but not limited to,
- procurement of substitute goods or services; loss of use, data, or
- profits; or business interruption) however caused and on any theory of
- liability, whether in contract, strict liability, or tort (including
- negligence or otherwise) arising in any way out of the use of this
- software, even if advised of the possibility of such damage.
- =cut
|