rdiscount-2.1.8/0000755000004100000410000000000012477017312013570 5ustar www-datawww-datardiscount-2.1.8/Rakefile0000644000004100000410000001500512477017312015236 0ustar www-datawww-datarequire 'date' require 'rake/clean' require 'digest/md5' task :default => :test # ========================================================== # Ruby Extension # ========================================================== DLEXT = RbConfig::MAKEFILE_CONFIG['DLEXT'] RUBYDIGEST = Digest::MD5.hexdigest(`ruby --version`) file "ext/ruby-#{RUBYDIGEST}" do |f| rm_f FileList["ext/ruby-*"] touch f.name end CLEAN.include "ext/ruby-*" file 'ext/Makefile' => FileList['ext/*.{c,h,rb}', "ext/ruby-#{RUBYDIGEST}"] do chdir('ext') { ruby 'extconf.rb' } end CLEAN.include 'ext/Makefile', 'ext/mkmf.log' file "ext/rdiscount.#{DLEXT}" => FileList["ext/Makefile"] do |f| sh 'cd ext && make clean && make && rm -rf conftest.dSYM' end CLEAN.include 'ext/*.{o,bundle,so,dll}' file "lib/rdiscount.#{DLEXT}" => "ext/rdiscount.#{DLEXT}" do |f| cp f.prerequisites, "lib/", :preserve => true end desc 'Build the rdiscount extension' task :build => "lib/rdiscount.#{DLEXT}" # ========================================================== # Manual # ========================================================== file 'man/rdiscount.1' => 'man/rdiscount.1.ronn' do sh "ronn --manual=RUBY -b man/rdiscount.1.ronn" end CLOBBER.include 'man/rdiscount.1' desc 'Build manpages' task :man => 'man/rdiscount.1' # ========================================================== # Testing # ========================================================== require 'rake/testtask' Rake::TestTask.new('test:unit') do |t| t.test_files = FileList['test/*_test.rb'] t.ruby_opts += ['-rubygems'] if defined? Gem end task 'test:unit' => [:build] desc 'Run conformance tests (MARKDOWN_TEST_VER=1.0)' task 'test:conformance' => [:build] do |t| script = "#{pwd}/bin/rdiscount" test_version = ENV['MARKDOWN_TEST_VER'] || '1.0.3' lib_dir = "#{pwd}/lib" chdir("test/MarkdownTest_#{test_version}") do result = `RUBYLIB=#{lib_dir} ./MarkdownTest.pl --script='#{script}' --tidy` print result fail unless result.include? "; 0 failed." end end desc 'Run version 1.0 conformance suite' task 'test:conformance:1.0' => [:build] do |t| ENV['MARKDOWN_TEST_VER'] = '1.0' Rake::Task['test:conformance'].invoke end desc 'Run 1.0.3 conformance suite' task 'test:conformance:1.0.3' => [:build] do |t| ENV['MARKDOWN_TEST_VER'] = '1.0.3' Rake::Task['test:conformance'].invoke end desc 'Run unit and conformance tests' task :test => %w[test:unit test:conformance] desc 'Run benchmarks' task :benchmark => :build do |t| $:.unshift 'lib' load 'test/benchmark.rb' end # ========================================================== # Documentation # ========================================================== desc 'Generate API documentation' task :doc => 'doc/index.html' file 'doc/index.html' => FileList['lib/*.rb'] do |f| sh((<<-end).gsub(/\s+/, ' ')) hanna --charset utf8 --fmt html --inline-source --line-numbers \ --main RDiscount --op doc --title 'RDiscount API Documentation' \ #{f.prerequisites.join(' ')} end end CLEAN.include 'doc' # ========================================================== # Update package's Discount sources # ========================================================== desc 'Gather required discount sources into extension directory' task :gather => 'discount/markdown.h' do |t| # Files unique to /ext that should not be overridden rdiscount_ext_files = [ "config.h", "extconf.rb", "rdiscount.c", ] # Files in /discount that have a main function and should not be copied to /ext discount_c_files_with_main_function = [ "main.c", "makepage.c", "mkd2html.c", "theme.c", ] # Ensure configure.sh was run if not File.exists? 'discount/mkdio.h' abort "discount/mkdio.h not found. Did you run ./configure.sh in the discount directory?" end # Delete all *.c and *.h files from ext that are not specific to RDiscount. Dir.chdir("ext") do c_files_to_delete = Dir["*.c"].select { |e| !rdiscount_ext_files.include? e } h_files_to_delete = Dir["*.h"].select { |e| !rdiscount_ext_files.include? e } rm (c_files_to_delete + h_files_to_delete), :verbose => true end # Copy all *.c and *.h files from discount -> ext except those that # RDiscount overrides. Also exclude Discount files with main functions. Dir.chdir("discount") do c_files_to_copy = Dir["*.c"].select { |e| (!rdiscount_ext_files.include? e) && (!discount_c_files_with_main_function.include? e) } h_files_to_copy = Dir["*.h"].select { |e| !rdiscount_ext_files.include? e } cp (c_files_to_copy + h_files_to_copy), '../ext/', :preserve => true, :verbose => true end # Copy special files from discount -> ext cp 'discount/blocktags', 'ext/' cp 'discount/VERSION', 'ext/' # Copy man page cp 'discount/markdown.7', 'man/' end file 'discount/markdown.h' do |t| abort "The discount submodule is required. See the file BUILDING for getting set up." end # PACKAGING ================================================================= require 'rubygems' $spec = eval(File.read('rdiscount.gemspec')) def package(ext='') "pkg/rdiscount-#{$spec.version}" + ext end desc 'Build packages' task :package => %w[.gem .tar.gz].map {|e| package(e)} desc 'Build and install as local gem' task :install => package('.gem') do sh "gem install #{package('.gem')}" end directory 'pkg/' file package('.gem') => %w[pkg/ rdiscount.gemspec] + $spec.files do |f| sh "gem build rdiscount.gemspec" mv File.basename(f.name), f.name end file package('.tar.gz') => %w[pkg/] + $spec.files do |f| sh "git archive --format=tar HEAD | gzip > #{f.name}" end # GEMSPEC HELPERS ========================================================== def source_version line = File.read('lib/rdiscount.rb')[/^\s*VERSION = .*/] line.match(/.*VERSION = '(.*)'/)[1] end file 'rdiscount.gemspec' => FileList['Rakefile','lib/rdiscount.rb'] do |f| # read spec file and split out manifest section spec = File.read(f.name) head, manifest, tail = spec.split(" # = MANIFEST =\n") head.sub!(/\.version = '.*'/, ".version = '#{source_version}'") head.sub!(/\.date = '.*'/, ".date = '#{Date.today.to_s}'") # determine file list from git ls-files files = `git ls-files`. split("\n"). sort. reject{ |file| file =~ /^\./ || file =~ /^test\/MarkdownTest/ }. map{ |file| " #{file}" }. join("\n") # piece file back together and write... manifest = " s.files = %w[\n#{files}\n ]\n" spec = [head,manifest,tail].join(" # = MANIFEST =\n") File.open(f.name, 'w') { |io| io.write(spec) } puts "updated #{f.name}" end rdiscount-2.1.8/bin/0000755000004100000410000000000012477017312014340 5ustar www-datawww-datardiscount-2.1.8/bin/rdiscount0000755000004100000410000000063112477017312016300 0ustar www-datawww-data#!/usr/bin/env ruby # Usage: rdiscount [...] # Convert one or more Markdown files to HTML and write to standard output. With # no or when is '-', read Markdown source text from standard input. if ARGV.include?('--help') File.read(__FILE__).split("\n").grep(/^# /).each do |line| puts line[2..-1] end exit 0 end require 'rdiscount' STDOUT.write(RDiscount.new(ARGF.read).to_html) rdiscount-2.1.8/lib/0000755000004100000410000000000012477017312014336 5ustar www-datawww-datardiscount-2.1.8/lib/rdiscount.rb0000644000004100000410000000710012477017312016673 0ustar www-datawww-data# Discount is an implementation of John Gruber's Markdown markup # language in C. It implements all of the language as described in # {Markdown Syntax}[http://daringfireball.net/projects/markdown/syntax] # and passes the Markdown 1.0 test suite. The RDiscount extension makes # the Discount processor available via a Ruby C Extension library. # # == Usage # # RDiscount implements the basic protocol popularized by RedCloth and adopted # by BlueCloth: # require 'rdiscount' # markdown = RDiscount.new("Hello World!") # puts markdown.to_html # # == Replacing BlueCloth # # Inject RDiscount into your BlueCloth-using code by replacing your bluecloth # require statements with the following: # begin # require 'rdiscount' # BlueCloth = RDiscount # rescue LoadError # require 'bluecloth' # end # class RDiscount VERSION = '2.1.8' # Original Markdown formatted text. attr_reader :text # Set true to have smarty-like quote translation performed. attr_accessor :smart # Do not output ") assert_equal "

Hello

\n", rd.to_html end def test_that_tables_can_have_leading_and_trailing_pipes rd = RDiscount.new(<\n\n\n A \n B \n\n\n\n\n C \n D \n\n\n\n\n", rd.to_html end def test_that_gfm_code_blocks_work rd = RDiscount.new(<line 1\n\nline 2\n\n", rd.to_html end def test_that_gfm_code_blocks_work_with_language rd = RDiscount.new(<line 1\n\nline 2\n\n", rd.to_html end def test_that_pandoc_code_blocks_work rd = RDiscount.new(<line 1\n\nline 2\n\n", rd.to_html end def test_that_discount_definition_lists_work rd = RDiscount.new(<
tag1
tag2
data.
EOS end def test_that_extra_definition_lists_work rd = RDiscount.new(<
tag1
data
EOS end def test_that_emphasis_beside_international_characters_detected rd = RDiscount.new(%(*foo ä bar*)) assert_equal %(

foo ä bar

\n), rd.to_html rd = RDiscount.new(%(*ä foobar*)) assert_equal %(

ä foobar

\n), rd.to_html rd = RDiscount.new(%(*foobar ä*)) assert_equal %(

foobar ä

\n), rd.to_html end end rdiscount-2.1.8/man/0000755000004100000410000000000012477017312014343 5ustar www-datawww-datardiscount-2.1.8/man/rdiscount.10000644000004100000410000000124012477017312016434 0ustar www-datawww-data.\" generated with Ronn/v0.6.8 .\" http://github.com/rtomayko/ronn/ . .TH "RDISCOUNT" "1" "April 2010" "" "RUBY" . .SH "NAME" \fBrdiscount\fR \- humane markup to HTML conversion tool . .SH "SYNOPSIS" \fBrdiscount\fR [\fIfile\fR\.\.\.] . .SH "DESCRIPTION" The \fBrdiscount\fR utility reads one or more markdown(7)\-formatted text \fIfile\fRs and writes HTML to standard output\. With no \fIfile\fR, or when \fIfile\fR is \'\-\', \fBrdiscount\fR reads source text from standard input\. . .SH "RETURN VALUES" The \fBrdiscount\fR utility exits 0 on success, and > 0 if an error occurs\. . .SH "SEE ALSO" markdown(7) . .SH "AUTHOR" Ryan Tomayko \fIhttp://tomayko\.com/about\fR rdiscount-2.1.8/man/markdown.70000644000004100000410000007117612477017312016271 0ustar www-datawww-data.\" .Dd Dec 22, 2007 .Dt MARKDOWN 7 .Os MASTODON .Sh NAME .Nm Markdown .Nd The Markdown text formatting syntax .Sh DESCRIPTION .Ss Philosophy .Nm Markdown is intended to be as easy-to-read and easy-to-write as is feasible. .Pp Readability, however, is emphasized above all else. A Markdown-formatted document should be publishable as-is, as plain text, without looking like it's been marked up with tags or formatting instructions. While Markdown's syntax has been influenced by several existing text-to-HTML filters -- including .Em Setext , .Em atx , .Em Textile , .Em reStructuredText , .Em Grutatext , and .Em EtText \-\- the single biggest source of inspiration for Markdown's syntax is the format of plain text email. .Pp To this end, Markdown's syntax is comprised entirely of punctuation characters, which punctuation characters have been carefully chosen so as to look like what they mean. E.g., asterisks around a word actually look like *emphasis*. Markdown lists look like, well, lists. Even blockquotes look like quoted passages of text, assuming you've ever used email. .Ss Inline HTML Markdown's syntax is intended for one purpose: to be used as a format for .Em writing for the web. .Pp .Nm is not a replacement for HTML, or even close to it. Its syntax is very small, corresponding only to a very small subset of HTML tags. The idea is .Em not to create a syntax that makes it easier to insert HTML tags. In my opinion, HTML tags are already easy to insert. The idea for Markdown is to make it easy to read, write, and edit prose. HTML is a .Em publishing format; Markdown is a .Em writing format. Thus, Markdown's formatting syntax only addresses issues that can be conveyed in plain text. .Pp For any markup that is not covered by Markdown's syntax, you simply use HTML itself. There's no need to preface it or delimit it to indicate that you're switching from Markdown to HTML; you just use the tags. .Pp The only restrictions are that block-level HTML elements -- e.g. .Li \
, .Li \ , .Li \
 ,
.Li \

, etc. -- must be separated from surrounding content by blank lines, and the start and end tags of the block should not be indented with tabs or spaces. Markdown is smart enough not to add extra (unwanted) .Li \

tags around HTML block-level tags. .Pp For example, to add an HTML table to a Markdown article: .Bd -literal -offset indent This is a regular paragraph.

Foo
This is another regular paragraph. .Ed .Pp Note that Markdown formatting syntax is not processed within block-level HTML tags. E.g., you can't use Markdown-style .Li *emphasis* inside an HTML block. .Pp Span-level HTML tags -- e.g. .Li \ , .Li \ , or .Li \ \-\- can be used anywhere in a Markdown paragraph, list item, or header. If you want, you can even use HTML tags instead of Markdown formatting; e.g. if you'd prefer to use HTML .Li \ or .Li \ tags instead of Markdown's link or image syntax, go right ahead. .Pp Unlike block-level HTML tags, Markdown syntax *is* processed within span-level tags. .Ss Automatic Escaping for Special Characters In HTML, there are two characters that demand special treatment: `<` and `&`. Left angle brackets are used to start tags; ampersands are used to denote HTML entities. If you want to use them as literal characters, you must escape them as entities, e.g. `<`, and `&`. .Pp Ampersands in particular are bedeviling for web writers. If you want to write about 'AT&T', you need to write '`AT&T`'. You even need to escape ampersands within URLs. Thus, if you want to link to: .Bd -literal -offset indent http://images.google.com/images?num=30&q=larry+bird .Ed .Pp you need to encode the URL as: .Bd -literal -offset indent http://images.google.com/images?num=30&q=larry+bird .Ed .Pp in your anchor tag `href` attribute. Needless to say, this is easy to forget, and is probably the single most common source of HTML validation errors in otherwise well-marked-up web sites. .Pp .Nm allows you to use these characters naturally, taking care of all the necessary escaping for you. If you use an ampersand as part of an HTML entity, it remains unchanged; otherwise it will be translated into `&`. .Pp So, if you want to include a copyright symbol in your article, you can write: .Bd -literal -offset indent © .Ed .Pp and Markdown will leave it alone. But if you write: .Bd -literal -offset indent AT&T .Ed .Pp .Nm will translate it to: .Bd -literal -offset indent AT&T .Ed .Pp Similarly, because Markdown supports inline HTML, if you use angle brackets as delimiters for HTML tags, Markdown will treat them as such. But if you write: .Bd -literal -offset indent 4 < 5 .Ed .Pp .Nm will translate it to: .Bd -literal -offset indent 4 < 5 .Ed .Pp However, inside Markdown code spans and blocks, angle brackets and ampersands are *always* encoded automatically. This makes it easy to use Markdown to write about HTML code. (As opposed to raw HTML, which is a terrible format for writing about HTML syntax, because every single `<` and `&` in your example code needs to be escaped.) .Sh Block Elements .Ss Paragraphs and Line Breaks .Pp A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a blank line -- a line containing nothing but spaces or tabs is considered blank.) Normal paragraphs should not be indented with spaces or tabs. .Pp The implication of the .Qq one or more consecutive lines of text rule is that Markdown supports .Qq hard-wrapped Dtext paragraphs. This differs significantly from most other text-to-HTML formatters (including Movable Type's .Qq Convert Line Breaks option) which translate every line break character in a paragraph into a `
` tag. .Pp When you *do* want to insert a `
` break tag using Markdown, you end a line with two or more spaces, then type return. .Pp Yes, this takes a tad more effort to create a `
`, but a simplistic "every line break is a `
`" rule wouldn't work for Markdown. Markdown's email-style .Sx blockquoting and multi-paragraph .Sx list items work best -- and look better -- when you format them with hard breaks. .Ss Headers .Nm supports two styles of headers, .Em Setext and .Em atx . .Pp Setext-style headers are .Sq underlined using equal signs (for first-level headers) and dashes (for second-level headers). For example: .Bd -literal -offset indent This is an H1 ============= This is an H2 ------------- .Ed .Pp Any number of underlining `=`'s or `-`'s will work. .Pp Atx-style headers use 1-6 hash characters at the start of the line, corresponding to header levels 1-6. For example: .Bd -literal -offset indent # This is an H1 ## This is an H2 ###### This is an H6 .Ed .Pp Optionally, you may .Qq close atx-style headers. This is purely cosmetic -- you can use this if you think it looks better. The closing hashes don't even need to match the number of hashes used to open the header. (The number of opening hashes determines the header level.) : .Bd -literal -offset indent # This is an H1 # ## This is an H2 ## ### This is an H3 ###### .Ed .Pp .Ss Blockquotes .Nm uses email-style `>` characters for blockquoting. If you're familiar with quoting passages of text in an email message, then you know how to create a blockquote in Markdown. It looks best if you hard wrap the text and put a `>` before every line: .Bd -literal -offset indent > This is a blockquote with two paragraphs. Lorem ipsum > dolor sit amet, consectetuer adipiscing elit. Aliquam > hendrerit mi posuere lectus. Vestibulum enim wisi, > viverra nec, fringilla in, laoreet vitae, risus. > > Donec sit amet nisl. Aliquam semper ipsum sit amet > velit. Suspendisse id sem consectetuer libero luctus > adipiscing. .Ed .Pp .Nm allows you to be lazy and only put the `>` before the first line of a hard-wrapped paragraph: .Bd -literal -offset indent > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse id sem consectetuer libero luctus adipiscing. .Ed .Pp Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by adding additional levels of `>`: .Bd -literal -offset indent > This is the first level of quoting. > > > This is nested blockquote. > > Back to the first level. .Ed .Pp Blockquotes can contain other Markdown elements, including headers, lists, and code blocks: .Bd -literal -offset indent > ## This is a header. > > 1. This is the first list item. > 2. This is the second list item. > > Here's some example code: > > return shell_exec("echo $input | $markdown_script"); .Ed .Pp Any decent text editor should make email-style quoting easy. For example, with BBEdit, you can make a selection and choose Increase Quote Level from the Text menu. .Ss Lists .Nm supports ordered (numbered) and unordered (bulleted) lists. .Pp Unordered lists use asterisks, pluses, and hyphens -- interchangably \-- as list markers: .Bd -literal -offset indent * Red * Green * Blue .Ed .Pp is equivalent to: .Bd -literal -offset indent + Red + Green + Blue .Ed .Pp and: .Bd -literal -offset indent - Red - Green - Blue .Ed .Pp Ordered lists use numbers followed by periods: .Bd -literal -offset indent 1. Bird 2. McHale 3. Parish .Ed .Pp It's important to note that the actual numbers you use to mark the list have no effect on the HTML output Markdown produces. The HTML Markdown produces from the above list is: .Bd -literal -offset indent
  1. Bird
  2. McHale
  3. Parish
.Ed .Pp If you instead wrote the list in Markdown like this: .Bd -literal -offset indent 1. Bird 1. McHale 1. Parish .Ed .Pp or even: .Bd -literal -offset indent 3. Bird 1. McHale 8. Parish .Ed .Pp you'd get the exact same HTML output. The point is, if you want to, you can use ordinal numbers in your ordered Markdown lists, so that the numbers in your source match the numbers in your published HTML. But if you want to be lazy, you don't have to. .Pp If you do use lazy list numbering, however, you should still start the list with the number 1. At some point in the future, Markdown may support starting ordered lists at an arbitrary number. .Pp List markers typically start at the left margin, but may be indented by up to three spaces. List markers must be followed by one or more spaces or a tab. .Pp To make lists look nice, you can wrap items with hanging indents: .Bd -literal -offset indent * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse id sem consectetuer libero luctus adipiscing. .Ed .Pp But if you want to be lazy, you don't have to: .Bd -literal -offset indent * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse id sem consectetuer libero luctus adipiscing. .Ed .Pp If list items are separated by blank lines, Markdown will wrap the items in `

` tags in the HTML output. For example, this input: .Bd -literal -offset indent * Bird * Magic .Ed .Pp will turn into: .Bd -literal -offset indent

  • Bird
  • Magic
.Ed .Pp But this: .Bd -literal -offset indent * Bird * Magic .Ed .Pp will turn into: .Bd -literal -offset indent
  • Bird

  • Magic

.Ed .Pp List items may consist of multiple paragraphs. Each subsequent paragraph in a list item must be intended by either 4 spaces or one tab: .Bd -literal -offset indent 1. This is a list item with two paragraphs. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. Donec sit amet nisl. Aliquam semper ipsum sit amet velit. 2. Suspendisse id sem consectetuer libero luctus adipiscing. .Ed .Pp It looks nice if you indent every line of the subsequent paragraphs, but here again, Markdown will allow you to be lazy: .Bd -literal -offset indent * This is a list item with two paragraphs. This is the second paragraph in the list item. You're only required to indent the first line. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. * Another item in the same list. .Ed .Pp To put a blockquote within a list item, the blockquote's `>` delimiters need to be indented: .Bd -literal -offset indent * A list item with a blockquote: > This is a blockquote > inside a list item. .Ed .Pp To put a code block within a list item, the code block needs to be indented *twice* -- 8 spaces or two tabs: .Bd -literal -offset indent * A list item with a code block: .Ed .Pp It's worth noting that it's possible to trigger an ordered list by accident, by writing something like this: .Bd -literal -offset indent 1986. What a great season. .Ed .Pp In other words, a *number-period-space* sequence at the beginning of a line. To avoid this, you can backslash-escape the period: .Bd -literal -offset indent 1986\\. What a great season. .Ed .Pp .Ss Code Blocks Pre-formatted code blocks are used for writing about programming or markup source code. Rather than forming normal paragraphs, the lines of a code block are interpreted literally. Markdown wraps a code block in both `
` and `` tags.
.Pp
To produce a code block in Markdown, simply indent every line of the
block by at least 4 spaces or 1 tab. For example, given this input:
.Bd -literal -offset indent
    This is a normal paragraph:

        This is a code block.
.Ed
.Pp
.Nm
will generate:
.Bd -literal -offset indent
    

This is a normal paragraph:

This is a code block.
    
.Ed .Pp One level of indentation -- 4 spaces or 1 tab -- is removed from each line of the code block. For example, this: .Bd -literal -offset indent Here is an example of AppleScript: tell application "Foo" beep end tell .Ed .Pp will turn into: .Bd -literal -offset indent

Here is an example of AppleScript:

tell application "Foo"
        beep
    end tell
    
.Ed .Pp A code block continues until it reaches a line that is not indented (or the end of the article). .Pp Within a code block, ampersands (`&`) and angle brackets (`<` and `>`) are automatically converted into HTML entities. This makes it very easy to include example HTML source code using Markdown -- just paste it and indent it, and Markdown will handle the hassle of encoding the ampersands and angle brackets. For example, this: .Bd -literal -offset indent .Ed .Pp will turn into: .Bd -literal -offset indent
<div class="footer">
        &copy; 2004 Foo Corporation
    </div>
    
.Ed .Pp Regular Markdown syntax is not processed within code blocks. E.g., asterisks are just literal asterisks within a code block. This means it's also easy to use Markdown to write about Markdown's own syntax. .Ss Horizontal Rules You can produce a horizontal rule tag (`
`) by placing three or more hyphens, asterisks, or underscores on a line by themselves. If you wish, you may use spaces between the hyphens or asterisks. Each of the following lines will produce a horizontal rule: .Bd -literal -offset indent * * * *** ***** - - - --------------------------------------- .Ed .Pp .Sh Span Elements .Ss Links .Nm supports two style of links: .Em inline and .Em reference . .Pp In both styles, the link text is delimited by [square brackets]. .Pp To create an inline link, use a set of regular parentheses immediately after the link text's closing square bracket. Inside the parentheses, put the URL where you want the link to point, along with an *optional* title for the link, surrounded in quotes. For example: .Bd -literal -offset indent This is [an example](http://example.com/ "Title") inline link. [This link](http://example.net/) has no title attribute. .Ed .Pp Will produce: .Bd -literal -offset indent

This is an example inline link.

This link has no title attribute.

.Ed .Pp If you're referring to a local resource on the same server, you can use relative paths: .Bd -literal -offset indent See my [About](/about/) page for details. .Ed .Pp Reference-style links use a second set of square brackets, inside which you place a label of your choosing to identify the link: .Bd -literal -offset indent This is [an example][id] reference-style link. .Ed .Pp You can optionally use a space to separate the sets of brackets: .Bd -literal -offset indent This is [an example] [id] reference-style link. .Ed .Pp Then, anywhere in the document, you define your link label like this, on a line by itself: .Bd -literal -offset indent [id]: http://example.com/ "Optional Title Here" .Ed .Pp That is: .Bl -bullet .It Square brackets containing the link identifier (optionally indented from the left margin using up to three spaces); .It followed by a colon; .It followed by one or more spaces (or tabs); .It followed by the URL for the link; .It optionally followed by a title attribute for the link, enclosed in double or single quotes, or enclosed in parentheses. .El .Pp The following three link definitions are equivalent: .Bd -literal -offset indent [foo]: http://example.com/ "Optional Title Here" [foo]: http://example.com/ 'Optional Title Here' [foo]: http://example.com/ (Optional Title Here) .Ed .Pp .Em Note : There is a known bug in Markdown.pl 1.0.1 which prevents single quotes from being used to delimit link titles. .Pp The link URL may, optionally, be surrounded by angle brackets: .Bd -literal -offset indent [id]: "Optional Title Here" .Ed .Pp You can put the title attribute on the next line and use extra spaces or tabs for padding, which tends to look better with longer URLs: .Bd -literal -offset indent [id]: http://example.com/longish/path/to/resource/here "Optional Title Here" .Ed .Pp Link definitions are only used for creating links during Markdown processing, and are stripped from your document in the HTML output. .Pp Link definition names may constist of letters, numbers, spaces, and punctuation -- but they are .Em not case sensitive. E.g. these two links: .Bd -literal -offset indent [link text][a] [link text][A] .Ed .Pp are equivalent. .Pp The .Em implicit link name shortcut allows you to omit the name of the link, in which case the link text itself is used as the name. Just use an empty set of square brackets -- e.g., to link the word .Qq Google to the google.com web site, you could simply write: .Bd -literal -offset indent [Google][] .Ed .Pp And then define the link: .Bd -literal -offset indent [Google]: http://google.com/ .Ed .Pp Because link names may contain spaces, this shortcut even works for multiple words in the link text: .Bd -literal -offset indent Visit [Daring Fireball][] for more information. .Ed .Pp And then define the link: .Bd -literal -offset indent [Daring Fireball]: http://daringfireball.net/ .Ed .Pp Link definitions can be placed anywhere in your Markdown document. I tend to put them immediately after each paragraph in which they're used, but if you want, you can put them all at the end of your document, sort of like footnotes. .Pp Here's an example of reference links in action: .Bd -literal -offset indent I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]. [1]: http://google.com/ "Google" [2]: http://search.yahoo.com/ "Yahoo Search" [3]: http://search.msn.com/ "MSN Search" .Ed .Pp Using the implicit link name shortcut, you could instead write: .Bd -literal -offset indent I get 10 times more traffic from [Google][] than from [Yahoo][] or [MSN][]. [google]: http://google.com/ "Google" [yahoo]: http://search.yahoo.com/ "Yahoo Search" [msn]: http://search.msn.com/ "MSN Search" .Ed .Pp Both of the above examples will produce the following HTML output: .Bd -literal -offset indent

I get 10 times more traffic from Google than from Yahoo or MSN.

.Ed .Pp For comparison, here is the same paragraph written using Markdown's inline link style: .Bd -literal -offset indent I get 10 times more traffic from [Google](http://google.com/ "Google") than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or [MSN](http://search.msn.com/ "MSN Search"). .Ed .Pp The point of reference-style links is not that they're easier to write. The point is that with reference-style links, your document source is vastly more readable. Compare the above examples: using reference-style links, the paragraph itself is only 81 characters long; with inline-style links, it's 176 characters; and as raw HTML, it's 234 characters. In the raw HTML, there's more markup than there is text. .Pp With Markdown's reference-style links, a source document much more closely resembles the final output, as rendered in a browser. By allowing you to move the markup-related metadata out of the paragraph, you can add links without interrupting the narrative flow of your prose. .Ss Emphasis Markdown treats asterisks (`*`) and underscores (`_`) as indicators of emphasis. Text wrapped with one `*` or `_` will be wrapped with an HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML `` tag. E.g., this input: .Bd -literal -offset indent *single asterisks* _single underscores_ **double asterisks** __double underscores__ .Ed .Pp will produce: .Bd -literal -offset indent single asterisks single underscores double asterisks double underscores .Ed .Pp You can use whichever style you prefer; the lone restriction is that the same character must be used to open and close an emphasis span. .Pp Emphasis can be used in the middle of a word: .Bd -literal -offset indent un*fucking*believable .Ed .Pp But if you surround an `*` or `_` with spaces, it'll be treated as a literal asterisk or underscore. .Pp To produce a literal asterisk or underscore at a position where it would otherwise be used as an emphasis delimiter, you can backslash escape it: .Bd -literal -offset indent \\*this text is surrounded by literal asterisks\\* .Ed .Pp .Ss Code To indicate a span of code, wrap it with backtick quotes (`` ` ``). Unlike a pre-formatted code block, a code span indicates code within a normal paragraph. For example: .Bd -literal -offset indent Use the `printf()` function. .Ed .Pp will produce: .Bd -literal -offset indent

Use the printf() function.

.Ed .Pp To include a literal backtick character within a code span, you can use multiple backticks as the opening and closing delimiters: .Bd -literal -offset indent ``There is a literal backtick (`) here.`` .Ed .Pp which will produce this: .Bd -literal -offset indent

There is a literal backtick (`) here.

.Ed .Pp The backtick delimiters surrounding a code span may include spaces -- one after the opening, one before the closing. This allows you to place literal backtick characters at the beginning or end of a code span: .Bd -literal -offset indent A single backtick in a code span: `` ` `` A backtick-delimited string in a code span: `` `foo` `` .Ed .Pp will produce: .Bd -literal -offset indent

A single backtick in a code span: `

A backtick-delimited string in a code span: `foo`

.Ed .Pp With a code span, ampersands and angle brackets are encoded as HTML entities automatically, which makes it easy to include example HTML tags. Markdown will turn this: .Bd -literal -offset indent Please don't use any `` tags. .Ed .Pp into: .Bd -literal -offset indent

Please don't use any <blink> tags.

.Ed .Pp You can write this: .Bd -literal -offset indent `—` is the decimal-encoded equivalent of `—`. .Ed .Pp to produce: .Bd -literal -offset indent

&#8212; is the decimal-encoded equivalent of &mdash;.

.Ed .Pp .Ss Images Admittedly, it's fairly difficult to devise a .Qq natural syntax for placing images into a plain text document format. .Pp Markdown uses an image syntax that is intended to resemble the syntax for links, allowing for two styles: .Em inline and .Em reference . .Pp Inline image syntax looks like this: .Bd -literal -offset indent ![Alt text](/path/to/img.jpg) ![Alt text](/path/to/img.jpg =Optional size "Optional title") .Ed .Pp That is: .Bl -bullet .It An exclamation mark: `!`; .It followed by a set of square brackets, containing the `alt` attribute text for the image; .It followed by a set of parentheses, containing the URL or path to the image, an optional `size` attribute (in .Ar width Li c Ar height format) prefixed with a `=`, and an optional `title` attribute enclosed in double or single quotes. .El .Pp Reference-style image syntax looks like this: .Bd -literal -offset indent ![Alt text][id] .Ed .Pp Where .Qq id is the name of a defined image reference. Image references are defined using syntax identical to link references: .Bd -literal -offset indent [id]: url/to/image =Optional size "Optional title attribute" .Ed .Pp .Sh Miscellaneous .Ss Automatic Links .Nm supports a shortcut style for creating .Qq automatic links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this: .Bd -literal -offset indent .Ed .Pp .Nm will turn this into: .Bd -literal -offset indent http://example.com/ .Ed .Pp Automatic links for email addresses work similarly, except that Markdown will also perform a bit of randomized decimal and hex entity-encoding to help obscure your address from address-harvesting spambots. For example, Markdown will turn this: .Bd -literal -offset indent .Ed .Pp into something like this: .Bd -literal -offset indent address@exa mple.com .Ed .Pp which will render in a browser as a clickable link to .Qq address@example.com . .Pp (This sort of entity-encoding trick will indeed fool many, if not most, address-harvesting bots, but it definitely won't fool all of them. It's better than nothing, but an address published in this way will probably eventually start receiving spam.) .Ss Backslash Escapes .Nm allows you to use backslash escapes to generate literal characters which would otherwise have special meaning in Markdown's formatting syntax. For example, if you wanted to surround a word with literal asterisks (instead of an HTML `` tag), you add backslashes before the asterisks, like this: .Bd -literal -offset indent \\*literal asterisks\\* .Ed .Pp .Nm provides backslash escapes for the following characters: .Bl -tag -compact .It \&\ backslash .It \` backtick .It * asterisk .It _ underscore .It \{\} curly braces .It [] square brackets .It () parentheses .It # hash mark .It + plus sign .It \- minus sign (hyphen) .It \. dot .It \! exclamation mark .El .Sh BUGS .Nm assumes that tabs are set to 4 spaces. .Sh AUTHOR John Gruber .%T http://daringfireball.net/ .Sh SEE ALSO .Xr markdown 1 , .Xr markdown 3 , .Xr mkd-callbacks 3 , .Xr mkd-functions 3 , .Xr mkd-extensions 7 . .Pp .%T http://daringfireball.net/projects/markdown .br .%T http://docutils.sourceforge.net/mirror/setext.html .br .%T http://www.aaronsw.com/2002/atx/ .br .%T http://textism.com/tools/textile/ .br .%T http://docutils.sourceforge.net/rst.html .br .%T http://www.triptico.com/software/grutatxt.html .br .%T http://ettext.taint.org/doc/ rdiscount-2.1.8/man/rdiscount.1.ronn0000644000004100000410000000102712477017312017412 0ustar www-datawww-datardiscount(1) -- humane markup to HTML conversion tool ===================================================== ## SYNOPSIS `rdiscount` [...] ## DESCRIPTION The `rdiscount` utility reads one or more markdown(7)-formatted text s and writes HTML to standard output. With no , or when is '-', `rdiscount` reads source text from standard input. ## RETURN VALUES The `rdiscount` utility exits 0 on success, and > 0 if an error occurs. ## SEE ALSO markdown(7) ## AUTHOR Ryan Tomayko rdiscount-2.1.8/BUILDING0000644000004100000410000001147212477017312014715 0ustar www-datawww-dataBUILDING rdiscount ================== You'll be needing Ruby, rake, and a basic build environment. Build the rdiscount extension for tests and local development: $ rake build Use your rdiscount working copy when running ruby programs: $ export RUBYLIB=~/rdiscount/lib:$RUBYLIB $ ruby some-program.rb Gathering changes from an upstream discount clone requires first grabbing the discount submodule into the root of the project and then running the rake gather task to copy discount source files into the ext/ directory: $ git submodule update --init Submodule 'discount' (git://github.com/davidfstr/discount.git) registered for path 'discount' Cloning into discount... $ cd discount $ ./configure.sh --with-fenced-code --with-github-tags --with-dl=both $ make # ensure it compiles $ cd .. $ rake gather $ rake build UPGRADING Discount ================== The most common maintenance task is upgrading the version of Discount that RDiscount is using. Before doing anything, make sure you can build the current (unmodified) version of RDiscount. See the section above for details. Update the Discount submodule to the desired version: $ cd discount $ git fetch $ git checkout v2.0.7.x # insert desired version $ cd .. Copy the new Discount sources to the appropriate directories for RDiscount: $ rake gather Update rdiscount.gemspec to include all *.c, *.h, and *.rb files in ext. This must be done manually. Here's a quick way to get the full list: $ echo ext/*.c ext/*.h ext/*.rb ext/blocktags ext/VERSION | tr ' ' "\n" | sort Build the RDiscount gem. If you get errors related to missing files in ext, make sure you updated the gemspec correctly in the previous step. $ gem build rdiscount.gemspec Install this new gem locally. It is recommended that you use RVM to create an isolated installation environment. If you get an error after the line "Building native extensions", see the troubleshooting section below. $ rvm ruby@rdiscount --create # recommended; requires RVM $ gem install rdiscount-*.gem Make sure the gem can be imported: $ ruby -e 'require "rdiscount"' Make sure the tests (still) pass: $ rake test Worked? Swell! The hard part is past. Check the Discount release notes to determine whether it has gained any new features that should be exposed through the RDiscount Ruby interface (lib/rdiscount.rb), such as new MKD_* flags or configure flags. If so, update the Ruby interface. If the ./configure.sh line needs to be changed to support new features, you will need to port some #defines from discount/config.h to ext/config.h manually to get RDiscount's embedded Discount to use the same configure flags. For new Discount extensions via new MKD_* flags, you will need to update: * lib/rdiscount.rb with new accessors and * the rb_rdiscount__get_flags function in ext/rdiscount.c with new accessor-to-flag mappings for each new extension. You should also look for RDiscount-specific bugs & feature requests in the GitHub tracker and fix a few. If any bugs were fixed or features added be sure to also add new tests! And don't forget to rerun the preexisting tests. Update the CHANGELOG. Update rdiscount.gemspec with the new RDiscount version number and date. Also update the VERSION constant in lib/rdiscount.rb. Push that change as the final commit with a message in the format "2.0.7 release". Tag the release commit: $ git tag 2.0.7 # insert desired version Rebuild the gem file and push it to RubyGems. $ gem build rdiscount.gemspec $ gem push rdiscount-NEW_VERSION.gem Announce the new release! For releases with new features it is recommended to write a full blog post. Troubleshooting Native Extension Issues --------------------------------------- The most likely place where errors will crop up is when you attempt to build the C extension. If this happens, you will have to debug manually. Below are a few recommended sanity checks: Ensure the Makefile is generated correctly: $ cd ext $ ruby extconf.rb Ensure make succeeds: $ make If you get linker errors related to there being duplicate symbols for _main, you probably need to update the deploy target of the Rakefile to exclude new *.c files from Discount that have a main function. For issues related to config.h or extconf.rb, there was probably some change to Discount's configure.sh that broke them. You will probably need to update these files in ext/ manually. For other errors, you'll have to investigate yourself. Common error classes: * 'ext/configure.sh' fails: - Create a patch to the upstream Discount. * Some files missing from ext/ that are present in discount/: - Update 'rake deploy' target to copy more files. * Some files missing when `gem build` is run: - Update gemspec to enumerate the correct files in ext/. rdiscount-2.1.8/ext/0000755000004100000410000000000012477017312014370 5ustar www-datawww-datardiscount-2.1.8/ext/generate.c0000644000004100000410000011125712477017312016335 0ustar www-datawww-data/* markdown: a C implementation of John Gruber's Markdown markup language. * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include #include #include #include "config.h" #include "cstring.h" #include "markdown.h" #include "amalloc.h" typedef int (*stfu)(const void*,const void*); typedef void (*spanhandler)(MMIOT*,int); /* forward declarations */ static void text(MMIOT *f); static Paragraph *display(Paragraph*, MMIOT*); /* externals from markdown.c */ int __mkd_footsort(Footnote *, Footnote *); /* * push text into the generator input buffer */ static void push(char *bfr, int size, MMIOT *f) { while ( size-- > 0 ) EXPAND(f->in) = *bfr++; } /* * push a character into the generator input buffer */ static void pushc(char c, MMIOT *f) { EXPAND(f->in) = c; } /* look characters ahead of the cursor. */ static inline int peek(MMIOT *f, int i) { i += (f->isp-1); return (i >= 0) && (i < S(f->in)) ? T(f->in)[i] : EOF; } /* pull a byte from the input buffer */ static inline int pull(MMIOT *f) { return ( f->isp < S(f->in) ) ? T(f->in)[f->isp++] : EOF; } /* return a pointer to the current position in the input buffer. */ static inline char* cursor(MMIOT *f) { return T(f->in) + f->isp; } static inline int isthisspace(MMIOT *f, int i) { int c = peek(f, i); if ( c == EOF ) return 1; if ( c & 0x80 ) return 0; return isspace(c) || (c < ' '); } static inline int isthisalnum(MMIOT *f, int i) { int c = peek(f, i); return (c != EOF) && isalnum(c); } static inline int isthisnonword(MMIOT *f, int i) { return isthisspace(f, i) || ispunct(peek(f,i)); } /* return/set the current cursor position */ #define mmiotseek(f,x) (f->isp = x) #define mmiottell(f) (f->isp) /* move n characters forward ( or -n characters backward) in the input buffer. */ static void shift(MMIOT *f, int i) { if (f->isp + i >= 0 ) f->isp += i; } /* Qchar() */ static void Qchar(int c, MMIOT *f) { block *cur; if ( S(f->Q) == 0 ) { cur = &EXPAND(f->Q); memset(cur, 0, sizeof *cur); cur->b_type = bTEXT; } else cur = &T(f->Q)[S(f->Q)-1]; EXPAND(cur->b_text) = c; } /* Qstring() */ static void Qstring(char *s, MMIOT *f) { while (*s) Qchar(*s++, f); } /* Qwrite() */ static void Qwrite(char *s, int size, MMIOT *f) { while (size-- > 0) Qchar(*s++, f); } /* Qprintf() */ static void Qprintf(MMIOT *f, char *fmt, ...) { char bfr[80]; va_list ptr; va_start(ptr,fmt); vsnprintf(bfr, sizeof bfr, fmt, ptr); va_end(ptr); Qstring(bfr, f); } /* Qem() */ static void Qem(MMIOT *f, char c, int count) { block *p = &EXPAND(f->Q); memset(p, 0, sizeof *p); p->b_type = (c == '*') ? bSTAR : bUNDER; p->b_char = c; p->b_count = count; memset(&EXPAND(f->Q), 0, sizeof(block)); } /* generate html from a markup fragment */ void ___mkd_reparse(char *bfr, int size, int flags, MMIOT *f, char *esc) { MMIOT sub; struct escaped e; ___mkd_initmmiot(&sub, f->footnotes); sub.flags = f->flags | flags; sub.cb = f->cb; sub.ref_prefix = f->ref_prefix; if ( esc ) { sub.esc = &e; e.up = f->esc; e.text = esc; } else sub.esc = f->esc; push(bfr, size, &sub); pushc(0, &sub); S(sub.in)--; text(&sub); ___mkd_emblock(&sub); Qwrite(T(sub.out), S(sub.out), f); ___mkd_freemmiot(&sub, f->footnotes); } /* * check the escape list for special cases */ static int escaped(MMIOT *f, char c) { struct escaped *thing = f->esc; while ( thing ) { if ( strchr(thing->text, c) ) return 1; thing = thing->up; } return 0; } /* * write out a url, escaping problematic characters */ static void puturl(char *s, int size, MMIOT *f, int display) { unsigned char c; while ( size-- > 0 ) { c = *s++; if ( c == '\\' && size-- > 0 ) { c = *s++; if ( !( ispunct(c) || isspace(c) ) ) Qchar('\\', f); } if ( c == '&' ) Qstring("&", f); else if ( c == '<' ) Qstring("<", f); else if ( c == '"' ) Qstring("%22", f); else if ( isalnum(c) || ispunct(c) || (display && isspace(c)) ) Qchar(c, f); else if ( c == MKD_EOLN ) /* untokenize hard return */ Qstring(" ", f); else Qprintf(f, "%%%02X", c); } } /* advance forward until the next character is not whitespace */ static int eatspace(MMIOT *f) { int c; for ( ; ((c=peek(f, 1)) != EOF) && isspace(c); pull(f) ) ; return c; } /* (match (a (nested (parenthetical (string.))))) */ static int parenthetical(int in, int out, MMIOT *f) { int size, indent, c; for ( indent=1,size=0; indent; size++ ) { if ( (c = pull(f)) == EOF ) return EOF; else if ( (c == '\\') && (peek(f,1) == out || peek(f,1) == in) ) { ++size; pull(f); } else if ( c == in ) ++indent; else if ( c == out ) --indent; } return size ? (size-1) : 0; } /* extract a []-delimited label from the input stream. */ static int linkylabel(MMIOT *f, Cstring *res) { char *ptr = cursor(f); int size; if ( (size = parenthetical('[',']',f)) != EOF ) { T(*res) = ptr; S(*res) = size; return 1; } return 0; } /* see if the quote-prefixed linky segment is actually a title. */ static int linkytitle(MMIOT *f, char quote, Footnote *ref) { int whence = mmiottell(f); char *title = cursor(f); char *e; register int c; while ( (c = pull(f)) != EOF ) { e = cursor(f); if ( c == quote ) { if ( (c = eatspace(f)) == ')' ) { T(ref->title) = 1+title; S(ref->title) = (e-title)-2; return 1; } } } mmiotseek(f, whence); return 0; } /* extract a =HHHxWWW size from the input stream */ static int linkysize(MMIOT *f, Footnote *ref) { int height=0, width=0; int whence = mmiottell(f); int c; if ( isspace(peek(f,0)) ) { pull(f); /* eat '=' */ for ( c = pull(f); isdigit(c); c = pull(f)) width = (width * 10) + (c - '0'); if ( c == 'x' ) { for ( c = pull(f); isdigit(c); c = pull(f)) height = (height*10) + (c - '0'); if ( isspace(c) ) c = eatspace(f); if ( (c == ')') || ((c == '\'' || c == '"') && linkytitle(f, c, ref)) ) { ref->height = height; ref->width = width; return 1; } } } mmiotseek(f, whence); return 0; } /* extract a <...>-encased url from the input stream. * (markdown 1.0.2b8 compatibility; older versions * of markdown treated the < and > as syntactic * sugar that didn't have to be there. 1.0.2b8 * requires a closing >, and then falls into the * title or closing ) */ static int linkybroket(MMIOT *f, int image, Footnote *p) { int c; int good = 0; T(p->link) = cursor(f); for ( S(p->link)=0; (c = pull(f)) != '>'; ++S(p->link) ) { /* pull in all input until a '>' is found, or die trying. */ if ( c == EOF ) return 0; else if ( (c == '\\') && ispunct(peek(f,2)) ) { ++S(p->link); pull(f); } } c = eatspace(f); /* next nonspace needs to be a title, a size, or ) */ if ( ( c == '\'' || c == '"' ) && linkytitle(f,c,p) ) good=1; else if ( image && (c == '=') && linkysize(f,p) ) good=1; else good=( c == ')' ); if ( good ) { if ( peek(f, 1) == ')' ) pull(f); ___mkd_tidy(&p->link); } return good; } /* linkybroket */ /* extract a (-prefixed url from the input stream. * the label is either of the format ``, where I * extract until I find a >, or it is of the format * `text`, where I extract until I reach a ')', a quote, * or (if image) a '=' */ static int linkyurl(MMIOT *f, int image, Footnote *p) { int c; int mayneedtotrim=0; if ( (c = eatspace(f)) == EOF ) return 0; if ( c == '<' ) { pull(f); if ( !(f->flags & MKD_1_COMPAT) ) return linkybroket(f,image,p); mayneedtotrim=1; } T(p->link) = cursor(f); for ( S(p->link)=0; (c = peek(f,1)) != ')'; ++S(p->link) ) { if ( c == EOF ) return 0; else if ( (c == '"' || c == '\'') && linkytitle(f, c, p) ) break; else if ( image && (c == '=') && linkysize(f, p) ) break; else if ( (c == '\\') && ispunct(peek(f,2)) ) { ++S(p->link); pull(f); } pull(f); } if ( peek(f, 1) == ')' ) pull(f); ___mkd_tidy(&p->link); if ( mayneedtotrim && (T(p->link)[S(p->link)-1] == '>') ) --S(p->link); return 1; } /* prefixes for */ static struct _protocol { char *name; int nlen; } protocol[] = { #define _aprotocol(x) { x, (sizeof x)-1 } _aprotocol( "https:" ), _aprotocol( "http:" ), _aprotocol( "news:" ), _aprotocol( "ftp:" ), #undef _aprotocol }; #define NRPROTOCOLS (sizeof protocol / sizeof protocol[0]) static int isautoprefix(char *text, int size) { int i; struct _protocol *p; for (i=0, p=protocol; i < NRPROTOCOLS; i++, p++) if ( (size >= p->nlen) && strncasecmp(text, p->name, p->nlen) == 0 ) return 1; return 0; } /* * all the tag types that linkylinky can produce are * defined by this structure. */ typedef struct linkytype { char *pat; int szpat; char *link_pfx; /* tag prefix and link pointer (eg: "" */ char *text_sfx; /* text suffix (eg: "" */ int flags; /* reparse flags */ int kind; /* tag is url or something else? */ #define IS_URL 0x01 } linkytype; static linkytype imaget = { 0, 0, "\"",", MKD_NOIMAGE|MKD_TAGTEXT, IS_URL }; static linkytype linkt = { 0, 0, "", "", MKD_NOLINKS, IS_URL }; /* * pseudo-protocols for [][]; * * id: generates tag * class: generates tag * raw: just dump the link without any processing */ static linkytype specials[] = { { "id:", 3, "", "", 0, 0 }, { "raw:", 4, 0, 0, 0, 0, 0, MKD_NOHTML, 0 }, { "lang:", 5, "", "", 0, 0 }, { "abbr:", 5, "", "", 0, 0 }, { "class:", 6, "", "", 0, 0 }, } ; #define NR(x) (sizeof x / sizeof x[0]) /* see if t contains one of our pseudo-protocols. */ static linkytype * pseudo(Cstring t) { int i; linkytype *r; for ( i=0, r=specials; i < NR(specials); i++,r++ ) { if ( (S(t) > r->szpat) && (strncasecmp(T(t), r->pat, r->szpat) == 0) ) return r; } return 0; } /* print out the start of an `img' or `a' tag, applying callbacks as needed. */ static void printlinkyref(MMIOT *f, linkytype *tag, char *link, int size) { char *edit; if ( f->flags & IS_LABEL ) return; Qstring(tag->link_pfx, f); if ( tag->kind & IS_URL ) { if ( f->cb && f->cb->e_url && (edit = (*f->cb->e_url)(link, size, f->cb->e_data)) ) { puturl(edit, strlen(edit), f, 0); if ( f->cb->e_free ) (*f->cb->e_free)(edit, f->cb->e_data); } else puturl(link + tag->szpat, size - tag->szpat, f, 0); } else ___mkd_reparse(link + tag->szpat, size - tag->szpat, MKD_TAGTEXT, f, 0); Qstring(tag->link_sfx, f); if ( f->cb && f->cb->e_flags && (edit = (*f->cb->e_flags)(link, size, f->cb->e_data)) ) { Qchar(' ', f); Qstring(edit, f); if ( f->cb->e_free ) (*f->cb->e_free)(edit, f->cb->e_data); } } /* printlinkyref */ /* helper function for php markdown extra footnotes; allow the user to * define a prefix tag instead of just `fn` */ static char * p_or_nothing(p) MMIOT *p; { return p->ref_prefix ? p->ref_prefix : "fn"; } /* php markdown extra/daring fireball style print footnotes */ static int extra_linky(MMIOT *f, Cstring text, Footnote *ref) { if ( ref->flags & REFERENCED ) return 0; if ( f->flags & IS_LABEL ) ___mkd_reparse(T(text), S(text), linkt.flags, f, 0); else { ref->flags |= REFERENCED; ref->refnumber = ++ f->footnotes->reference; Qprintf(f, "%d", p_or_nothing(f), ref->refnumber, p_or_nothing(f), ref->refnumber, ref->refnumber); } return 1; } /* extra_linky */ /* print out a linky (or fail if it's Not Allowed) */ static int linkyformat(MMIOT *f, Cstring text, int image, Footnote *ref) { linkytype *tag; if ( image ) tag = &imaget; else if ( tag = pseudo(ref->link) ) { if ( f->flags & (MKD_NO_EXT|MKD_SAFELINK) ) return 0; } else if ( (f->flags & MKD_SAFELINK) && T(ref->link) && (T(ref->link)[0] != '/') && !isautoprefix(T(ref->link), S(ref->link)) ) /* if MKD_SAFELINK, only accept links that are local or * a well-known protocol */ return 0; else tag = &linkt; if ( f->flags & tag->flags ) return 0; if ( f->flags & IS_LABEL ) ___mkd_reparse(T(text), S(text), tag->flags, f, 0); else if ( tag->link_pfx ) { printlinkyref(f, tag, T(ref->link), S(ref->link)); if ( tag->WxH ) { if ( ref->height ) Qprintf(f," height=\"%d\"", ref->height); if ( ref->width ) Qprintf(f, " width=\"%d\"", ref->width); } if ( S(ref->title) ) { Qstring(" title=\"", f); ___mkd_reparse(T(ref->title), S(ref->title), MKD_TAGTEXT, f, 0); Qchar('"', f); } Qstring(tag->text_pfx, f); ___mkd_reparse(T(text), S(text), tag->flags, f, 0); Qstring(tag->text_sfx, f); } else Qwrite(T(ref->link) + tag->szpat, S(ref->link) - tag->szpat, f); return 1; } /* linkyformat */ /* * process embedded links and images */ static int linkylinky(int image, MMIOT *f) { int start = mmiottell(f); Cstring name; Footnote key, *ref; int status = 0; int extra_footnote = 0; CREATE(name); memset(&key, 0, sizeof key); if ( linkylabel(f, &name) ) { if ( peek(f,1) == '(' ) { pull(f); if ( linkyurl(f, image, &key) ) status = linkyformat(f, name, image, &key); } else { int goodlink, implicit_mark = mmiottell(f); if ( isspace(peek(f,1)) ) pull(f); if ( peek(f,1) == '[' ) { pull(f); /* consume leading '[' */ goodlink = linkylabel(f, &key.tag); } else { /* new markdown implicit name syntax doesn't * require a second [] */ mmiotseek(f, implicit_mark); goodlink = !(f->flags & MKD_1_COMPAT); if ( (f->flags & MKD_EXTRA_FOOTNOTE) && (!image) && S(name) && T(name)[0] == '^' ) extra_footnote = 1; } if ( goodlink ) { if ( !S(key.tag) ) { DELETE(key.tag); T(key.tag) = T(name); S(key.tag) = S(name); } if ( ref = bsearch(&key, T(f->footnotes->note), S(f->footnotes->note), sizeof key, (stfu)__mkd_footsort) ) { if ( extra_footnote ) status = extra_linky(f,name,ref); else status = linkyformat(f, name, image, ref); } } } } DELETE(name); ___mkd_freefootnote(&key); if ( status == 0 ) mmiotseek(f, start); return status; } /* write a character to output, doing text escapes ( & -> &, * > -> > < -> < ) */ static void cputc(int c, MMIOT *f) { switch (c) { case '&': Qstring("&", f); break; case '>': Qstring(">", f); break; case '<': Qstring("<", f); break; default : Qchar(c, f); break; } } /* * convert an email address to a string of nonsense */ static void mangle(char *s, int len, MMIOT *f) { while ( len-- > 0 ) { #if DEBIAN_GLITCH Qprintf(f, "d;", *((unsigned char*)(s++)) ); #else Qstring("&#", f); Qprintf(f, COINTOSS() ? "x%02x;" : "%02d;", *((unsigned char*)(s++)) ); #endif } } /* nrticks() -- count up a row of tick marks */ static int nrticks(int offset, int tickchar, MMIOT *f) { int tick = 0; while ( peek(f, offset+tick) == tickchar ) tick++; return tick; } /* nrticks */ /* matchticks() -- match a certain # of ticks, and if that fails * match the largest subset of those ticks. * * if a subset was matched, return the # of ticks * that were matched. */ static int matchticks(MMIOT *f, int tickchar, int ticks, int *endticks) { int size, count, c; int subsize=0, subtick=0; *endticks = ticks; for (size = 0; (c=peek(f,size+ticks)) != EOF; size ++) { if ( (c == tickchar) && ( count = nrticks(size+ticks,tickchar,f)) ) { if ( count == ticks ) return size; else if ( count ) { if ( (count > subtick) && (count < ticks) ) { subsize = size; subtick = count; } size += count; } } } if ( subsize ) { *endticks = subtick; return subsize; } return 0; } /* matchticks */ /* code() -- write a string out as code. The only characters that have * special meaning in a code block are * `<' and `&' , which * are /always/ expanded to < and & */ static void code(MMIOT *f, char *s, int length) { int i,c; for ( i=0; i < length; i++ ) if ( (c = s[i]) == MKD_EOLN) /* ^C: expand back to 2 spaces */ Qstring(" ", f); else if ( c == '\\' && (i < length-1) && escaped(f, s[i+1]) ) cputc(s[++i], f); else cputc(c, f); } /* code */ /* delspan() -- write out a chunk of text, blocking with ... */ static void delspan(MMIOT *f, int size) { Qstring("", f); ___mkd_reparse(cursor(f)-1, size, 0, f, 0); Qstring("", f); } /* codespan() -- write out a chunk of text as code, trimming one * space off the front and/or back as appropriate. */ static void codespan(MMIOT *f, int size) { int i=0; if ( size > 1 && peek(f, size-1) == ' ' ) --size; if ( peek(f,i) == ' ' ) ++i, --size; Qstring("", f); code(f, cursor(f)+(i-1), size); Qstring("", f); } /* codespan */ /* before letting a tag through, validate against * MKD_NOLINKS and MKD_NOIMAGE */ static int forbidden_tag(MMIOT *f) { int c = toupper(peek(f, 1)); if ( f->flags & MKD_NOHTML ) return 1; if ( c == 'A' && (f->flags & MKD_NOLINKS) && !isthisalnum(f,2) ) return 1; if ( c == 'I' && (f->flags & MKD_NOIMAGE) && strncasecmp(cursor(f)+1, "MG", 2) == 0 && !isthisalnum(f,4) ) return 1; return 0; } /* Check a string to see if it looks like a mail address * "looks like a mail address" means alphanumeric + some * specials, then a `@`, then alphanumeric + some specials, * but with a `.` */ static int maybe_address(char *p, int size) { int ok = 0; for ( ;size && (isalnum(*p) || strchr("._-+*", *p)); ++p, --size) ; if ( ! (size && *p == '@') ) return 0; --size, ++p; if ( size && *p == '.' ) return 0; for ( ;size && (isalnum(*p) || strchr("._-+", *p)); ++p, --size ) if ( *p == '.' && size > 1 ) ok = 1; return size ? 0 : ok; } /* The size-length token at cursor(f) is either a mailto:, an * implicit mailto:, one of the approved url protocols, or just * plain old text. If it's a mailto: or an approved protocol, * linkify it, otherwise say "no" */ static int process_possible_link(MMIOT *f, int size) { int address= 0; int mailto = 0; char *text = cursor(f); if ( f->flags & MKD_NOLINKS ) return 0; if ( (size > 7) && strncasecmp(text, "mailto:", 7) == 0 ) { /* if it says it's a mailto, it's a mailto -- who am * I to second-guess the user? */ address = 1; mailto = 7; /* 7 is the length of "mailto:"; we need this */ } else address = maybe_address(text, size); if ( address ) { Qstring("", f); mangle(text+mailto, size-mailto, f); Qstring("", f); return 1; } else if ( isautoprefix(text, size) ) { printlinkyref(f, &linkt, text, size); Qchar('>', f); puturl(text,size,f, 1); Qstring("", f); return 1; } return 0; } /* process_possible_link */ /* a < may be just a regular character, the start of an embedded html * tag, or the start of an . If it's an automatic * link, we also need to know if it's an email address because if it * is we need to mangle it in our futile attempt to cut down on the * spaminess of the rendered page. */ static int maybe_tag_or_link(MMIOT *f) { int c, size; int maybetag = 1; if ( f->flags & MKD_TAGTEXT ) return 0; for ( size=0; (c = peek(f, size+1)) != '>'; size++) { if ( c == EOF ) return 0; else if ( c == '\\' ) { maybetag=0; if ( peek(f, size+2) != EOF ) size++; } else if ( isspace(c) ) break; #if WITH_GITHUB_TAGS else if ( ! (c == '/' || c == '-' || c == '_' || isalnum(c) ) ) #else else if ( ! (c == '/' || isalnum(c) ) ) #endif maybetag=0; } if ( size ) { if ( maybetag || (size >= 3 && strncmp(cursor(f), "!--", 3) == 0) ) { /* It is not a html tag unless we find the closing '>' in * the same block. */ while ( (c = peek(f, size+1)) != '>' ) if ( c == EOF ) return 0; else size++; if ( forbidden_tag(f) ) return 0; Qchar('<', f); while ( ((c = peek(f, 1)) != EOF) && (c != '>') ) Qchar(pull(f), f); return 1; } else if ( !isspace(c) && process_possible_link(f, size) ) { shift(f, size+1); return 1; } } return 0; } /* autolinking means that all inline html is . A * autolink url is alphanumerics, slashes, periods, underscores, * the at sign, colon, and the % character. */ static int maybe_autolink(MMIOT *f) { register int c; int size; /* greedily scan forward for the end of a legitimate link. */ for ( size=0; (c=peek(f, size+1)) != EOF; size++ ) { if ( c == '\\' ) { if ( peek(f, size+2) != EOF ) ++size; } else if ( isspace(c) || strchr("'\"()[]{}<>`", c) || c == MKD_EOLN ) break; } if ( (size > 1) && process_possible_link(f, size) ) { shift(f, size); return 1; } return 0; } /* smartyquote code that's common for single and double quotes */ static int smartyquote(int *flags, char typeofquote, MMIOT *f) { int bit = (typeofquote == 's') ? 0x01 : 0x02; if ( bit & (*flags) ) { if ( isthisnonword(f,1) ) { Qprintf(f, "&r%cquo;", typeofquote); (*flags) &= ~bit; return 1; } } else if ( isthisnonword(f,-1) && peek(f,1) != EOF ) { Qprintf(f, "&l%cquo;", typeofquote); (*flags) |= bit; return 1; } return 0; } static int islike(MMIOT *f, char *s) { int len; int i; if ( s[0] == '|' ) { if ( !isthisnonword(f, -1) ) return 0; ++s; } if ( !(len = strlen(s)) ) return 0; if ( s[len-1] == '|' ) { if ( !isthisnonword(f,len-1) ) return 0; len--; } for (i=1; i < len; i++) if (tolower(peek(f,i)) != s[i]) return 0; return 1; } static struct smarties { char c0; char *pat; char *entity; int shift; } smarties[] = { { '\'', "'s|", "rsquo", 0 }, { '\'', "'t|", "rsquo", 0 }, { '\'', "'re|", "rsquo", 0 }, { '\'', "'ll|", "rsquo", 0 }, { '\'', "'ve|", "rsquo", 0 }, { '\'', "'m|", "rsquo", 0 }, { '\'', "'d|", "rsquo", 0 }, { '-', "---", "mdash", 2 }, { '-', "--", "ndash", 1 }, { '.', "...", "hellip", 2 }, { '.', ". . .", "hellip", 4 }, { '(', "(c)", "copy", 2 }, { '(', "(r)", "reg", 2 }, { '(', "(tm)", "trade", 3 }, { '3', "|3/4|", "frac34", 2 }, { '3', "|3/4ths|", "frac34", 2 }, { '1', "|1/2|", "frac12", 2 }, { '1', "|1/4|", "frac14", 2 }, { '1', "|1/4th|", "frac14", 2 }, { '&', "�", 0, 3 }, } ; #define NRSMART ( sizeof smarties / sizeof smarties[0] ) /* Smarty-pants-style chrome for quotes, -, ellipses, and (r)(c)(tm) */ static int smartypants(int c, int *flags, MMIOT *f) { int i; if ( f->flags & (MKD_NOPANTS|MKD_TAGTEXT|IS_LABEL) ) return 0; for ( i=0; i < NRSMART; i++) if ( (c == smarties[i].c0) && islike(f, smarties[i].pat) ) { if ( smarties[i].entity ) Qprintf(f, "&%s;", smarties[i].entity); shift(f, smarties[i].shift); return 1; } switch (c) { case '<' : return 0; case '\'': if ( smartyquote(flags, 's', f) ) return 1; break; case '"': if ( smartyquote(flags, 'd', f) ) return 1; break; case '`': if ( peek(f, 1) == '`' ) { int j = 2; while ( (c=peek(f,j)) != EOF ) { if ( c == '\\' ) j += 2; else if ( c == '`' ) break; else if ( c == '\'' && peek(f, j+1) == '\'' ) { Qstring("“", f); ___mkd_reparse(cursor(f)+1, j-2, 0, f, 0); Qstring("”", f); shift(f,j+1); return 1; } else ++j; } } break; } return 0; } /* smartypants */ /* process a body of text encased in some sort of tick marks. If it * works, generate the output and return 1, otherwise just return 0 and * let the caller figure it out. */ static int tickhandler(MMIOT *f, int tickchar, int minticks, int allow_space, spanhandler spanner) { int endticks, size; int tick = nrticks(0, tickchar, f); if ( !allow_space && isspace(peek(f,tick)) ) return 0; if ( (tick >= minticks) && (size = matchticks(f,tickchar,tick,&endticks)) ) { if ( endticks < tick ) { size += (tick - endticks); tick = endticks; } shift(f, tick); (*spanner)(f,size); shift(f, size+tick-1); return 1; } return 0; } #define tag_text(f) (f->flags & MKD_TAGTEXT) static void text(MMIOT *f) { int c, j; int rep; int smartyflags = 0; while (1) { if ( (f->flags & MKD_AUTOLINK) && isalpha(peek(f,1)) && !tag_text(f) ) maybe_autolink(f); c = pull(f); if (c == EOF) break; if ( smartypants(c, &smartyflags, f) ) continue; switch (c) { case 0: break; case MKD_EOLN: Qstring(tag_text(f) ? " " : "
", f); break; case '>': if ( tag_text(f) ) Qstring(">", f); else Qchar(c, f); break; case '"': if ( tag_text(f) ) Qstring(""", f); else Qchar(c, f); break; case '!': if ( peek(f,1) == '[' ) { pull(f); if ( tag_text(f) || !linkylinky(1, f) ) Qstring("![", f); } else Qchar(c, f); break; case '[': if ( tag_text(f) || !linkylinky(0, f) ) Qchar(c, f); break; /* A^B -> AB */ case '^': if ( (f->flags & (MKD_NOSUPERSCRIPT|MKD_STRICT|MKD_TAGTEXT)) || (isthisnonword(f,-1) && peek(f,-1) != ')') || isthisspace(f,1) ) Qchar(c,f); else { char *sup = cursor(f); int len = 0; if ( peek(f,1) == '(' ) { int here = mmiottell(f); pull(f); if ( (len = parenthetical('(',')',f)) <= 0 ) { mmiotseek(f,here); Qchar(c, f); break; } sup++; } else { while ( isthisalnum(f,1+len) ) ++len; if ( !len ) { Qchar(c,f); break; } shift(f,len); } Qstring("",f); ___mkd_reparse(sup, len, 0, f, "()"); Qstring("", f); } break; case '_': /* Underscores don't count if they're in the middle of a word */ if ( !(f->flags & (MKD_NORELAXED|MKD_STRICT)) && isthisalnum(f,-1) && isthisalnum(f,1) ) { Qchar(c, f); break; } case '*': /* Underscores & stars don't count if they're out in the middle * of whitespace */ if ( isthisspace(f,-1) && isthisspace(f,1) ) { Qchar(c, f); break; } /* else fall into the regular old emphasis case */ if ( tag_text(f) ) Qchar(c, f); else { for (rep = 1; peek(f,1) == c; pull(f) ) ++rep; Qem(f,c,rep); } break; case '~': if ( (f->flags & (MKD_NOSTRIKETHROUGH|MKD_TAGTEXT|MKD_STRICT)) || ! tickhandler(f,c,2,0, delspan) ) Qchar(c, f); break; case '`': if ( tag_text(f) || !tickhandler(f,c,1,1,codespan) ) Qchar(c, f); break; case '\\': switch ( c = pull(f) ) { case '&': Qstring("&", f); break; case '<': c = peek(f,1); if ( (c == EOF) || isspace(c) ) Qstring("<", f); else { /* Markdown.pl does not escape <[nonwhite] * sequences */ Qchar('\\', f); shift(f, -1); } break; case '^': if ( f->flags & (MKD_STRICT|MKD_NOSUPERSCRIPT) ) { Qchar('\\', f); shift(f,-1); break; } Qchar(c, f); break; case ':': case '|': if ( f->flags & MKD_NOTABLES ) { Qchar('\\', f); shift(f,-1); break; } Qchar(c, f); break; case EOF: Qchar('\\', f); break; default: if ( escaped(f,c) || strchr(">#.-+{}]![*_\\()`", c) ) Qchar(c, f); else { Qchar('\\', f); shift(f, -1); } break; } break; case '<': if ( !maybe_tag_or_link(f) ) Qstring("<", f); break; case '&': j = (peek(f,1) == '#' ) ? 2 : 1; while ( isthisalnum(f,j) ) ++j; if ( peek(f,j) != ';' ) Qstring("&", f); else Qchar(c, f); break; default: Qchar(c, f); break; } } /* truncate the input string after we've finished processing it */ S(f->in) = f->isp = 0; } /* text */ /* print a header block */ static void printheader(Paragraph *pp, MMIOT *f) { #if WITH_ID_ANCHOR Qprintf(f, "hnumber); if ( f->flags & MKD_TOC ) { Qstring(" id=\"", f); mkd_string_to_anchor(T(pp->text->text), S(pp->text->text), (mkd_sta_function_t)Qchar, f, 1); Qchar('"', f); } Qchar('>', f); #else if ( f->flags & MKD_TOC ) { Qstring("
text->text), S(pp->text->text), (mkd_sta_function_t)Qchar, f, 1); Qstring("\">\n", f); } Qprintf(f, "", pp->hnumber); #endif push(T(pp->text->text), S(pp->text->text), f); text(f); Qprintf(f, "", pp->hnumber); } enum e_alignments { a_NONE, a_CENTER, a_LEFT, a_RIGHT }; static char* alignments[] = { "", " style=\"text-align:center;\"", " style=\"text-align:left;\"", " style=\"text-align:right;\"" }; typedef STRING(int) Istring; static int splat(Line *p, char *block, Istring align, int force, MMIOT *f) { int first, idx = p->dle, colno = 0; ___mkd_tidy(&p->text); if ( T(p->text)[S(p->text)-1] == '|' ) --S(p->text); Qstring("\n", f); while ( idx < S(p->text) ) { first = idx; if ( force && (colno >= S(align)-1) ) idx = S(p->text); else while ( (idx < S(p->text)) && (T(p->text)[idx] != '|') ) { if ( T(p->text)[idx] == '\\' ) ++idx; ++idx; } Qprintf(f, "<%s%s>", block, alignments[ (colno < S(align)) ? T(align)[colno] : a_NONE ]); ___mkd_reparse(T(p->text)+first, idx-first, 0, f, "|"); Qprintf(f, "\n", block); idx++; colno++; } if ( force ) while (colno < S(align) ) { Qprintf(f, "<%s>\n", block, block); ++colno; } Qstring("\n", f); return colno; } static int printtable(Paragraph *pp, MMIOT *f) { /* header, dashes, then lines of content */ Line *hdr, *dash, *body; Istring align; int hcols,start; char *p; enum e_alignments it; hdr = pp->text; dash= hdr->next; body= dash->next; if ( T(hdr->text)[hdr->dle] == '|' ) { /* trim leading pipe off all lines */ Line *r; for ( r = pp->text; r; r = r->next ) r->dle ++; } /* figure out cell alignments */ CREATE(align); for (p=T(dash->text), start=dash->dle; start < S(dash->text); ) { char first, last; int end; last=first=0; for (end=start ; (end < S(dash->text)) && p[end] != '|'; ++ end ) { if ( p[end] == '\\' ) ++ end; else if ( !isspace(p[end]) ) { if ( !first) first = p[end]; last = p[end]; } } it = ( first == ':' ) ? (( last == ':') ? a_CENTER : a_LEFT) : (( last == ':') ? a_RIGHT : a_NONE ); EXPAND(align) = it; start = 1+end; } Qstring("\n", f); Qstring("\n", f); hcols = splat(hdr, "th", align, 0, f); Qstring("\n", f); if ( hcols < S(align) ) S(align) = hcols; else while ( hcols > S(align) ) EXPAND(align) = a_NONE; Qstring("\n", f); for ( ; body; body = body->next) splat(body, "td", align, 1, f); Qstring("\n", f); Qstring("
\n", f); DELETE(align); return 1; } static int printblock(Paragraph *pp, MMIOT *f) { Line *t = pp->text; static char *Begin[] = { "", "

", "

" }; static char *End[] = { "", "

","

" }; while (t) { if ( S(t->text) ) { if ( t->next && S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' ' && T(t->text)[S(t->text)-1] == ' ' ) { push(T(t->text), S(t->text)-2, f); pushc(MKD_EOLN, f); pushc('\n', f); } else { ___mkd_tidy(&t->text); push(T(t->text), S(t->text), f); if ( t->next ) pushc('\n', f); } } t = t->next; } Qstring(Begin[pp->align], f); text(f); Qstring(End[pp->align], f); return 1; } static void printcode(Line *t, char *lang, MMIOT *f) { int blanks; Qstring("
", f);
    for ( blanks = 0; t ; t = t->next ) {
	if ( S(t->text) > t->dle ) {
	    while ( blanks ) {
		Qchar('\n', f);
		--blanks;
	    }
	    code(f, T(t->text), S(t->text));
	    Qchar('\n', f);
	}
	else blanks++;
    }
    Qstring("
", f); } static void printhtml(Line *t, MMIOT *f) { int blanks; for ( blanks=0; t ; t = t->next ) if ( S(t->text) ) { for ( ; blanks; --blanks ) Qchar('\n', f); Qwrite(T(t->text), S(t->text), f); Qchar('\n', f); } else blanks++; } static void htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f) { ___mkd_emblock(f); if ( block ) Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments); ___mkd_emblock(f); while (( p = display(p, f) )) { ___mkd_emblock(f); Qstring("\n\n", f); } if ( block ) Qprintf(f, "", block); ___mkd_emblock(f); } static void definitionlist(Paragraph *p, MMIOT *f) { Line *tag; if ( p ) { Qstring("
\n", f); for ( ; p ; p = p->next) { for ( tag = p->text; tag; tag = tag->next ) { Qstring("
", f); ___mkd_reparse(T(tag->text), S(tag->text), 0, f, 0); Qstring("
\n", f); } htmlify(p->down, "dd", p->ident, f); Qchar('\n', f); } Qstring("
", f); } } static void listdisplay(int typ, Paragraph *p, MMIOT* f) { if ( p ) { Qprintf(f, "<%cl", (typ==UL)?'u':'o'); if ( typ == AL ) Qprintf(f, " type=\"a\""); Qprintf(f, ">\n"); for ( ; p ; p = p->next ) { htmlify(p->down, "li", p->ident, f); Qchar('\n', f); } Qprintf(f, "\n", (typ==UL)?'u':'o'); } } /* dump out a Paragraph in the desired manner */ static Paragraph* display(Paragraph *p, MMIOT *f) { if ( !p ) return 0; switch ( p->typ ) { case STYLE: case WHITESPACE: break; case HTML: printhtml(p->text, f); break; case CODE: printcode(p->text, p->lang, f); break; case QUOTE: htmlify(p->down, p->ident ? "div" : "blockquote", p->ident, f); break; case UL: case OL: case AL: listdisplay(p->typ, p->down, f); break; case DL: definitionlist(p->down, f); break; case HR: Qstring("
", f); break; case HDR: printheader(p, f); break; case TABLE: printtable(p, f); break; case SOURCE: htmlify(p->down, 0, 0, f); break; default: printblock(p, f); break; } return p->next; } /* dump out a list of footnotes */ static void mkd_extra_footnotes(MMIOT *m) { int j, i; Footnote *t; if ( m->footnotes->reference == 0 ) return; Csprintf(&m->out, "\n
\n
\n
    \n"); for ( i=1; i <= m->footnotes->reference; i++ ) { for ( j=0; j < S(m->footnotes->note); j++ ) { t = &T(m->footnotes->note)[j]; if ( (t->refnumber == i) && (t->flags & REFERENCED) ) { Csprintf(&m->out, "
  1. \n

    ", p_or_nothing(m), t->refnumber); Csreparse(&m->out, T(t->title), S(t->title), 0); Csprintf(&m->out, "", p_or_nothing(m), t->refnumber); Csprintf(&m->out, "

  2. \n"); } } } Csprintf(&m->out, "
\n
\n"); } /* return a pointer to the compiled markdown * document. */ int mkd_document(Document *p, char **res) { int size; if ( p && p->compiled ) { if ( ! p->html ) { htmlify(p->code, 0, 0, p->ctx); if ( p->ctx->flags & MKD_EXTRA_FOOTNOTE ) mkd_extra_footnotes(p->ctx); p->html = 1; } size = S(p->ctx->out); if ( (size == 0) || T(p->ctx->out)[size-1] ) EXPAND(p->ctx->out) = 0; *res = T(p->ctx->out); return size; } return EOF; } rdiscount-2.1.8/ext/mkdio.h0000644000004100000410000000755012477017312015653 0ustar www-datawww-data#ifndef _MKDIO_D #define _MKDIO_D #include typedef void MMIOT; typedef unsigned int mkd_flag_t; /* line builder for markdown() */ MMIOT *mkd_in(FILE*,mkd_flag_t); /* assemble input from a file */ MMIOT *mkd_string(const char*,int,mkd_flag_t); /* assemble input from a buffer */ /* line builder for github flavoured markdown */ MMIOT *gfm_in(FILE*,mkd_flag_t); /* assemble input from a file */ MMIOT *gfm_string(const char*,int,mkd_flag_t); /* assemble input from a buffer */ void mkd_basename(MMIOT*,char*); void mkd_initialize(); void mkd_with_html5_tags(); void mkd_shlib_destructor(); /* compilation, debugging, cleanup */ int mkd_compile(MMIOT*, mkd_flag_t); void mkd_cleanup(MMIOT*); /* markup functions */ int mkd_dump(MMIOT*, FILE*, int, char*); int markdown(MMIOT*, FILE*, mkd_flag_t); int mkd_line(char *, int, char **, mkd_flag_t); typedef int (*mkd_sta_function_t)(const int,const void*); void mkd_string_to_anchor(char *, int, mkd_sta_function_t, void*, int); int mkd_xhtmlpage(MMIOT*,int,FILE*); /* header block access */ char* mkd_doc_title(MMIOT*); char* mkd_doc_author(MMIOT*); char* mkd_doc_date(MMIOT*); /* compiled data access */ int mkd_document(MMIOT*, char**); int mkd_toc(MMIOT*, char**); int mkd_css(MMIOT*, char **); int mkd_xml(char *, int, char **); /* write-to-file functions */ int mkd_generatehtml(MMIOT*,FILE*); int mkd_generatetoc(MMIOT*,FILE*); int mkd_generatexml(char *, int,FILE*); int mkd_generatecss(MMIOT*,FILE*); #define mkd_style mkd_generatecss int mkd_generateline(char *, int, FILE*, mkd_flag_t); #define mkd_text mkd_generateline /* url generator callbacks */ typedef char * (*mkd_callback_t)(const char*, const int, void*); typedef void (*mkd_free_t)(char*, void*); void mkd_e_url(void *, mkd_callback_t); void mkd_e_flags(void *, mkd_callback_t); void mkd_e_free(void *, mkd_free_t ); void mkd_e_data(void *, void *); /* version#. */ extern char markdown_version[]; void mkd_mmiot_flags(FILE *, MMIOT *, int); void mkd_flags_are(FILE*, mkd_flag_t, int); void mkd_ref_prefix(MMIOT*, char*); /* special flags for markdown() and mkd_text() */ #define MKD_NOLINKS 0x00000001 /* don't do link processing, block tags */ #define MKD_NOIMAGE 0x00000002 /* don't do image processing, block */ #define MKD_NOPANTS 0x00000004 /* don't run smartypants() */ #define MKD_NOHTML 0x00000008 /* don't allow raw html through AT ALL */ #define MKD_STRICT 0x00000010 /* disable SUPERSCRIPT, RELAXED_EMPHASIS */ #define MKD_TAGTEXT 0x00000020 /* process text inside an html tag; no * , no , no html or [] expansion */ #define MKD_NO_EXT 0x00000040 /* don't allow pseudo-protocols */ #define MKD_NOEXT MKD_NO_EXT /* ^^^ (aliased for user convenience) */ #define MKD_CDATA 0x00000080 /* generate code for xml ![CDATA[...]] */ #define MKD_NOSUPERSCRIPT 0x00000100 /* no A^B */ #define MKD_NORELAXED 0x00000200 /* emphasis happens /everywhere/ */ #define MKD_NOTABLES 0x00000400 /* disallow tables */ #define MKD_NOSTRIKETHROUGH 0x00000800 /* forbid ~~strikethrough~~ */ #define MKD_TOC 0x00001000 /* do table-of-contents processing */ #define MKD_1_COMPAT 0x00002000 /* compatibility with MarkdownTest_1.0 */ #define MKD_AUTOLINK 0x00004000 /* make http://foo.com link even without <>s */ #define MKD_SAFELINK 0x00008000 /* paranoid check for link protocol */ #define MKD_NOHEADER 0x00010000 /* don't process header blocks */ #define MKD_TABSTOP 0x00020000 /* expand tabs to 4 spaces */ #define MKD_NODIVQUOTE 0x00040000 /* forbid >%class% blocks */ #define MKD_NOALPHALIST 0x00080000 /* forbid alphabetic lists */ #define MKD_NODLIST 0x00100000 /* forbid definition lists */ #define MKD_EXTRA_FOOTNOTE 0x00200000 /* enable markdown extra-style footnotes */ #define MKD_NOSTYLE 0x00400000 /* don't extract