rdiscount-2.2.7.3/0000755000004100000410000000000014571373162013736 5ustar www-datawww-datardiscount-2.2.7.3/README.markdown0000644000004100000410000000440314571373162016440 0ustar www-datawww-dataDiscount Markdown Processor for Ruby ==================================== [![Build Status](https://github.com/davidfstr/rdiscount/actions/workflows/main.yml/badge.svg)](https://github.com/davidfstr/rdiscount/actions/workflows/main.yml) Discount is an implementation of John Gruber's Markdown markup language in C. It implements all of the language described in [the markdown syntax document][1] and passes the [Markdown 1.0 test suite][2]. CODE: `git clone git://github.com/davidfstr/rdiscount.git` HOME: DOCS: BUGS: Discount was developed by [David Loren Parsons][3]. The Ruby extension is maintained by [David Foster][4]. [1]: https://daringfireball.net/projects/markdown/syntax [2]: https://daringfireball.net/projects/downloads/MarkdownTest_1.0.zip [3]: https://www.pell.portland.or.us/~orc [4]: https://github.com/davidfstr INSTALL, HACKING ---------------- New releases of RDiscount are published to [RubyGems][]: $ [sudo] gem install rdiscount The RDiscount sources are available via Git: $ git clone git://github.com/davidfstr/rdiscount.git $ cd rdiscount $ rake --tasks See the file [BUILDING][] for hacking instructions. [RubyGems]: https://rubygems.org/gems/rdiscount [BUILDING]: https://github.com/davidfstr/rdiscount/blob/master/BUILDING USAGE ----- RDiscount implements the basic protocol popularized by RedCloth and adopted by BlueCloth: require 'rdiscount' markdown = RDiscount.new("Hello World!") puts markdown.to_html Additional processing options can be turned on when creating the RDiscount object: markdown = RDiscount.new("Hello World!", :smart, :filter_html) 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 COPYING ------- Discount is free software; it is released under a BSD-style license that allows you to do as you wish with it as long as you don't attempt to claim it as your own work. RDiscount adopts Discount's license verbatim. See the file `COPYING` for more information. rdiscount-2.2.7.3/bin/0000755000004100000410000000000014571373162014506 5ustar www-datawww-datardiscount-2.2.7.3/bin/rdiscount0000755000004100000410000000140414571373162016445 0ustar www-datawww-data#!/usr/bin/env ruby # # Usage: [env RDISCOUNT_EXTENSIONS=',...'] rdiscount [...] # no or when is '-', read Markdown source text from standard input. # # 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. Optionally, the RDISCOUNT_EXTENSIONS environment variable # can specify a comma-separated list of extensions to enable in RDiscount. # if ARGV.include?('--help') File.read(__FILE__).split("\n").grep(/^# /).each do |line| puts line[2..-1] end exit 0 end require 'rdiscount' extensions = ENV['RDISCOUNT_EXTENSIONS'].to_s.split(',').map{ |key| key.to_sym } STDOUT.write(RDiscount.new(ARGF.read, *extensions).to_html) rdiscount-2.2.7.3/rdiscount.gemspec0000644000004100000410000000310714571373162017316 0ustar www-datawww-dataGem::Specification.new do |s| s.name = 'rdiscount' s.version = '2.2.7.3' s.summary = "Fast Implementation of Gruber's Markdown in C" s.email = 'david@dafoster.net' s.homepage = 'http://dafoster.net/projects/rdiscount/' s.authors = ["Ryan Tomayko", "David Loren Parsons", "Andrew White", "David Foster", "l33tname"] s.license = "BSD-3-Clause" # = MANIFEST = s.files = %w[ BUILDING CHANGELOG.md COPYING README.markdown Rakefile bin/rdiscount discount ext/Csio.c ext/VERSION ext/amalloc.c ext/amalloc.h ext/basename.c ext/blocktags ext/config.h ext/css.c ext/cstring.h ext/docheader.c ext/dumptree.c ext/emmatch.c ext/extconf.rb ext/flags.c ext/generate.c ext/gethopt.c ext/gethopt.h ext/github_flavoured.c ext/h1title.c ext/html5.c ext/markdown.c ext/markdown.h ext/mkdio.c ext/mkdio.h ext/mktags.c ext/notspecial.c ext/pgm_options.c ext/pgm_options.h ext/rdiscount.c ext/resource.c ext/setup.c ext/tags.c ext/tags.h ext/toc.c ext/version.c ext/xml.c ext/xmlpage.c lib/markdown.rb lib/rdiscount.rb man/markdown.7 man/rdiscount.1 man/rdiscount.1.ronn rdiscount.gemspec test/benchmark.rb test/benchmark.txt test/markdown_test.rb test/rdiscount_test.rb ] # = MANIFEST = s.test_files = ["test/markdown_test.rb", "test/rdiscount_test.rb"] s.extra_rdoc_files = ["COPYING"] s.extensions = ["ext/extconf.rb"] s.executables = ["rdiscount"] s.require_paths = ["lib"] end rdiscount-2.2.7.3/lib/0000755000004100000410000000000014571373162014504 5ustar www-datawww-datardiscount-2.2.7.3/lib/rdiscount.rb0000644000004100000410000000767414571373162017061 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.2.7.3' # Original Markdown formatted text. attr_reader :text # Set true to have smarty-like quote translation performed. attr_accessor :smart # Do not output EOS assert_equal "

Hello

\n\n\n\n", rd.to_html end def test_that_style_tag_is_filtered_with_flag rd = RDiscount.new(< p { margin: 5px; } EOS assert_equal "

Hello

\n\n\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_latex_passtrough_dont_render_link rd = RDiscount.new("$$[(1+2)*3-4](1-2)$$", :latex) assert_equal "

$$[(1+2)*3-4](1-2)$$

\n", rd.to_html 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 def test_taht rd = RDiscount.new(<
  • Bullet
  • Bullet
    1. Numbered
    2. Numbered
    EOS end def test_that_extra_definition_lists_work rd = RDiscount.new(<
    tag1
    data
    EOS end end rdiscount-2.2.7.3/test/benchmark.txt0000644000004100000410000001760014571373162017414 0ustar www-datawww-dataMarkdown: Basics ================ Getting the Gist of Markdown's Formatting Syntax ------------------------------------------------ This page offers a brief overview of what it's like to use Markdown. The [syntax page] [s] provides complete, detailed documentation for every feature, but Markdown should be very easy to pick up simply by looking at a few examples of it in action. The examples on this page are written in a before/after style, showing example syntax and the HTML output produced by Markdown. It's also helpful to simply try Markdown out; the [Dingus] [d] is a web application that allows you type your own Markdown-formatted text and translate it to XHTML. **Note:** This document is itself written using Markdown; you can [see the source for it by adding '.text' to the URL] [src]. [s]: /projects/markdown/syntax "Markdown Syntax" [d]: /projects/markdown/dingus "Markdown Dingus" [src]: /projects/markdown/basics.text ## Paragraphs, Headers, Blockquotes ## 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 spaces or tabs is considered blank.) Normal paragraphs should not be intended with spaces or tabs. Markdown offers two styles of headers: *Setext* and *atx*. Setext-style headers for `

    ` and `

    ` are created by "underlining" with equal signs (`=`) and hyphens (`-`), respectively. To create an atx-style header, you put 1-6 hash marks (`#`) at the beginning of the line -- the number of hashes equals the resulting HTML header level. Blockquotes are indicated using email-style '`>`' angle brackets. Markdown: A First Level Header ==================== A Second Level Header --------------------- Now is the time for all good men to come to the aid of their country. This is just a regular paragraph. The quick brown fox jumped over the lazy dog's back. ### Header 3 > This is a blockquote. > > This is the second paragraph in the blockquote. > > ## This is an H2 in a blockquote Output:

    A First Level Header

    A Second Level Header

    Now is the time for all good men to come to the aid of their country. This is just a regular paragraph.

    The quick brown fox jumped over the lazy dog's back.

    Header 3

    This is a blockquote.

    This is the second paragraph in the blockquote.

    This is an H2 in a blockquote

    ### Phrase Emphasis ### Markdown uses asterisks and underscores to indicate spans of emphasis. Markdown: Some of these words *are emphasized*. Some of these words _are emphasized also_. Use two asterisks for **strong emphasis**. Or, if you prefer, __use two underscores instead__. Output:

    Some of these words are emphasized. Some of these words are emphasized also.

    Use two asterisks for strong emphasis. Or, if you prefer, use two underscores instead.

    ## Lists ## Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`, `+`, and `-`) as list markers. These three markers are interchangable; this: * Candy. * Gum. * Booze. this: + Candy. + Gum. + Booze. and this: - Candy. - Gum. - Booze. all produce the same output:
    • Candy.
    • Gum.
    • Booze.
    Ordered (numbered) lists use regular numbers, followed by periods, as list markers: 1. Red 2. Green 3. Blue Output:
    1. Red
    2. Green
    3. Blue
    If you put blank lines between items, you'll get `

    ` tags for the list item text. You can create multi-paragraph list items by indenting the paragraphs by 4 spaces or 1 tab: * A list item. With multiple paragraphs. * Another item in the list. Output:

    • A list item.

      With multiple paragraphs.

    • Another item in the list.

    ### Links ### Markdown supports two styles for creating links: *inline* and *reference*. With both styles, you use square brackets to delimit the text you want to turn into a link. Inline-style links use parentheses immediately after the link text. For example: This is an [example link](http://example.com/). Output:

    This is an example link.

    Optionally, you may include a title attribute in the parentheses: This is an [example link](http://example.com/ "With a Title"). Output:

    This is an example link.

    Reference-style links allow you to refer to your links by names, which you define elsewhere in your document: 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" Output:

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

    The title attribute is optional. Link names may contain letters, numbers and spaces, but are *not* case sensitive: I start my morning with a cup of coffee and [The New York Times][NY Times]. [ny times]: http://www.nytimes.com/ Output:

    I start my morning with a cup of coffee and The New York Times.

    ### Images ### Image syntax is very much like link syntax. Inline (titles are optional): ![alt text](/path/to/img.jpg "Title") Reference-style: ![alt text][id] [id]: /path/to/img.jpg "Title" Both of the above examples produce the same output: alt text ### Code ### In a regular paragraph, you can create code span by wrapping text in backtick quotes. Any ampersands (`&`) and angle brackets (`<` or `>`) will automatically be translated into HTML entities. This makes it easy to use Markdown to write about HTML example code: I strongly recommend against using any `` tags. I wish SmartyPants used named entities like `—` instead of decimal-encoded entites like `—`. Output:

    I strongly recommend against using any <blink> tags.

    I wish SmartyPants used named entities like &mdash; instead of decimal-encoded entites like &#8212;.

    To specify an entire block of pre-formatted code, indent every line of the block by 4 spaces or 1 tab. Just like with code spans, `&`, `<`, and `>` characters will be escaped automatically. Markdown: If you want your page to validate under XHTML 1.0 Strict, you've got to put paragraph tags in your blockquotes:

    For example.

    Output:

    If you want your page to validate under XHTML 1.0 Strict, you've got to put paragraph tags in your blockquotes:

    <blockquote>
            <p>For example.</p>
        </blockquote>
        
    rdiscount-2.2.7.3/test/benchmark.rb0000644000004100000410000000255314571373162017201 0ustar www-datawww-datarequire 'rubygems' iterations = 100 test_file = "#{File.dirname(__FILE__)}/benchmark.txt" implementations = %w[BlueCloth RDiscount Maruku PEGMarkdown] # Attempt to require each implementation and remove any that are not # installed. implementations.reject! do |class_name| begin module_path = if class_name == 'PEGMarkdown' 'peg_markdown' else class_name.downcase end require module_path false rescue LoadError => boom module_path.tr! '_', '-' puts "#{class_name} excluded. Try: gem install #{module_path}" true end end # Grab actual class objects. implementations.map! { |class_name| Object.const_get(class_name) } # The actual benchmark. def benchmark(implementation, text, iterations) start = Time.now iterations.times do |i| implementation.new(text).to_html end Time.now - start end # Read test file test_data = File.read(test_file) # Prime the pump puts "Spinning up ..." implementations.each { |impl| benchmark(impl, test_data, 1) } # Run benchmarks; gather results. puts "Running benchmarks ..." results = implementations.inject([]) do |r,impl| GC.start r << [ impl, benchmark(impl, test_data, iterations) ] end puts "Results for #{iterations} iterations:" results.each do |impl,time| printf " %10s %09.06fs total time, %09.06fs average\n", "#{impl}:", time, time / iterations end rdiscount-2.2.7.3/test/markdown_test.rb0000644000004100000410000001331614571373162020127 0ustar www-datawww-datarootdir = File.dirname(File.dirname(__FILE__)) $LOAD_PATH.unshift "#{rootdir}/lib" require 'test/unit' require 'markdown' MARKDOWN_TEST_DIR = "#{rootdir}/test/MarkdownTest_1.0.3" class MarkdownTest < Test::Unit::TestCase def test_that_extension_methods_are_present_on_markdown_class assert Markdown.instance_methods.map{|m| m.to_s }.include?('to_html'), "Markdown class should respond to #to_html" end def test_that_simple_one_liner_goes_to_html markdown = Markdown.new('Hello World.') assert_respond_to markdown, :to_html assert_equal "

    Hello World.

    ", markdown.to_html.strip end def test_that_inline_markdown_goes_to_html markdown = Markdown.new('_Hello World_!') assert_equal "

    Hello World!

    ", markdown.to_html.strip end def test_that_inline_markdown_starts_and_ends_correctly markdown = Markdown.new('_start _ foo_bar bar_baz _ end_ *italic* **bold** _blah_') assert_respond_to markdown, :to_html assert_equal "

    start _ foo_bar bar_baz _ end italic bold blah

    ", markdown.to_html.strip markdown = Markdown.new("Run 'rake radiant:extensions:rbac_base:migrate'") assert_equal "

    Run 'rake radiant:extensions:rbac_base:migrate'

    ", markdown.to_html.strip end def test_that_filter_html_works markdown = Markdown.new('Through NO ', :filter_html) assert_equal "

    Through <em>NO</em> <script>DOUBLE NO</script>

    ", markdown.to_html.strip end def test_that_bluecloth_restrictions_are_supported markdown = Markdown.new('Hello World.') [:filter_html, :filter_styles].each do |restriction| assert_respond_to markdown, restriction assert_respond_to markdown, "#{restriction}=" end assert_not_equal true, markdown.filter_html assert_not_equal true, markdown.filter_styles markdown = Markdown.new('Hello World.', :filter_html, :filter_styles) assert_equal true, markdown.filter_html assert_equal true, markdown.filter_styles end def test_that_redcloth_attributes_are_supported markdown = Markdown.new('Hello World.') assert_respond_to markdown, :fold_lines assert_respond_to markdown, :fold_lines= assert_not_equal true, markdown.fold_lines markdown = Markdown.new('Hello World.', :fold_lines) assert_equal true, markdown.fold_lines end def test_that_redcloth_to_html_with_single_arg_is_supported markdown = Markdown.new('Hello World.') assert_nothing_raised(ArgumentError) { markdown.to_html(true) } end def test_that_smart_converts_single_quotes_in_words_that_end_in_re markdown = Markdown.new("They're not for sale.", :smart) assert_equal "

    They’re not for sale.

    \n", markdown.to_html end def test_that_smart_converts_single_quotes_in_words_that_end_in_ll markdown = Markdown.new("Well that'll be the day", :smart) assert_equal "

    Well that’ll be the day

    \n", markdown.to_html end def test_that_urls_are_not_doubly_escaped markdown = Markdown.new('[Page 2](/search?query=Markdown+Test&page=2)') assert_equal "

    Page 2

    \n", markdown.to_html end def test_simple_inline_html markdown = Markdown.new("before\n\n
    \n foo\n
    \nafter") assert_equal "

    before

    \n\n
    \n foo\n
    \n\n\n

    after

    \n", markdown.to_html end def test_that_html_blocks_do_not_require_their_own_end_tag_line markdown = Markdown.new("Para 1\n\n
    HTML block\n
    \n\nPara 2 [Link](#anchor)") assert_equal "

    Para 1

    \n\n
    HTML block\n
    \n\n\n

    Para 2 Link

    \n", markdown.to_html end def test_filter_html_doesnt_break_two_space_hard_break markdown = Markdown.new("Lorem, \nipsum\n", :filter_html) assert_equal "

    Lorem,
    \nipsum

    \n", markdown.to_html end # This isn't in the spec but is Markdown.pl behavior. def test_block_quotes_preceded_by_spaces markdown = Markdown.new( "A wise man once said:\n\n" + " > Isn't it wonderful just to be alive.\n" ) assert_equal "

    A wise man once said:

    \n\n" + "

    Isn't it wonderful just to be alive.

    \n", markdown.to_html end def test_ul_with_zero_space_indent markdown = Markdown.new("- foo\n\n- bar\n\n baz\n") assert_equal "
    • foo

    • bar

      baz

    ", markdown.to_html.gsub("\n", "") end def test_ul_with_single_space_indent markdown = Markdown.new(" - foo\n\n - bar\n\n baz\n") assert_equal "
    • foo

    • bar

      baz

    ", markdown.to_html.gsub("\n", "") end # http://github.com/davidfstr/rdiscount/issues/#issue/13 def test_headings_with_trailing_space text = "The Ant-Sugar Tales \n" + "=================== \n\n" + "By Candice Yellowflower \n" markdown = Markdown.new(text) assert_equal "

    The Ant-Sugar Tales

    \n\n

    By Candice Yellowflower

    \n", markdown.to_html end # Build tests for each file in the MarkdownTest test suite Dir["#{MARKDOWN_TEST_DIR}/Tests/*.text"].each do |text_file| basename = File.basename(text_file).sub(/\.text$/, '') method_name = basename.gsub(/[-,()]/, '').gsub(/\s+/, '_').downcase define_method "test_#{method_name}" do markdown = Markdown.new(File.read(text_file)) actual_html = markdown.to_html assert_not_nil actual_html end define_method "test_#{method_name}_smart" do markdown = Markdown.new(File.read(text_file), :smart) actual_html = markdown.to_html assert_not_nil actual_html end end end rdiscount-2.2.7.3/Rakefile0000644000004100000410000001573714571373162015420 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 += ['-r 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 # Allow to run this rake tasks multiple times # https://medium.com/@shaneilske/invoke-a-rake-task-multiple-times-1bcb01dee9d9 ENV.delete("MARKDOWN_TEST_VER") ENV.delete("RDISCOUNT_EXTENSIONS") Rake::Task["test:conformance"].reenable end desc 'Run version 1.0 conformance suite' task 'test:conformance:1.0' => [:build] do |t| ENV['MARKDOWN_TEST_VER'] = '1.0' # see https://github.com/Orc/discount/issues/261 # requires flags -f1.0,tabstop,nopants ENV['RDISCOUNT_EXTENSIONS'] = "md1compat" 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:1.0 test:conformance:1.0.3] 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/' # Apply patches system('cat ext.diff | patch -p1 -d ext') 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.2.7.3/COPYING0000644000004100000410000000311714571373162014773 0ustar www-datawww-dataThe core Discount C sources are Copyright (C) 2007 David Loren Parsons. The Discount Ruby extension sources are Copyright (C) 2008 Ryan Tomayko. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of works must retain the original copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the original copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither my name (David L Parsons) nor the names of contributors to this code may be used to endorse or promote products derived from this work 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. rdiscount-2.2.7.3/ext/0000755000004100000410000000000014571373162014536 5ustar www-datawww-datardiscount-2.2.7.3/ext/dumptree.c0000644000004100000410000000574214571373162016537 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 "markdown.h" #include "cstring.h" #include "amalloc.h" struct frame { int indent; char c; }; typedef STRING(struct frame) Stack; static char * Pptype(int typ) { switch (typ) { case WHITESPACE: return "whitespace"; case CODE : return "code"; case QUOTE : return "quote"; case MARKUP : return "markup"; case HTML : return "html"; case DL : return "dl"; case UL : return "ul"; case OL : return "ol"; case LISTITEM : return "item"; case HDR : return "header"; case HR : return "hr"; case TABLE : return "table"; case SOURCE : return "source"; case STYLE : return "style"; default : return "mystery node!"; } } static void pushpfx(int indent, char c, Stack *sp) { struct frame *q = &EXPAND(*sp); q->indent = indent; q->c = c; } static void poppfx(Stack *sp) { S(*sp)--; } static void changepfx(Stack *sp, char c) { char ch; if ( !S(*sp) ) return; ch = T(*sp)[S(*sp)-1].c; if ( ch == '+' || ch == '|' ) T(*sp)[S(*sp)-1].c = c; } static void printpfx(Stack *sp, FILE *f) { int i; char c; if ( !S(*sp) ) return; c = T(*sp)[S(*sp)-1].c; if ( c == '+' || c == '-' ) { fprintf(f, "--%c", c); T(*sp)[S(*sp)-1].c = (c == '-') ? ' ' : '|'; } else for ( i=0; i < S(*sp); i++ ) { if ( i ) fprintf(f, " "); fprintf(f, "%*s%c", T(*sp)[i].indent + 2, " ", T(*sp)[i].c); if ( T(*sp)[i].c == '`' ) T(*sp)[i].c = ' '; } fprintf(f, "--"); } static void dumptree(Paragraph *pp, Stack *sp, FILE *f) { int count; Line *p; int d; static char *Begin[] = { 0, "P", "center" }; while ( pp ) { if ( !pp->next ) changepfx(sp, '`'); printpfx(sp, f); if ( pp->typ == HDR ) d += fprintf(f, "[h%d", pp->hnumber); else d = fprintf(f, "[%s", Pptype(pp->typ)); if ( pp->ident ) d += fprintf(f, " %s", pp->ident); #ifdef GITHUB_CHECKBOX if ( pp->flags ) d += fprintf(f, " %x", pp->flags); #endif if ( pp->align > 1 ) d += fprintf(f, ", <%s>", Begin[pp->align]); for (count=0, p=pp->text; p; ++count, (p = p->next) ) ; if ( count ) d += fprintf(f, ", %d line%s", count, (count==1)?"":"s"); d += fprintf(f, "]"); if ( pp->down ) { pushpfx(d, pp->down->next ? '+' : '-', sp); dumptree(pp->down, sp, f); poppfx(sp); } else fputc('\n', f); pp = pp->next; } } int mkd_dump(Document *doc, FILE *out, mkd_flag_t flags, char *title) { Stack stack; if (mkd_compile(doc, flags) ) { CREATE(stack); pushpfx(fprintf(out, "%s", title), doc->code->next ? '+' : '-', &stack); dumptree(doc->code, &stack, out); DELETE(stack); return 0; } return -1; } rdiscount-2.2.7.3/ext/flags.c0000644000004100000410000000444514571373162016005 0ustar www-datawww-data#include #include "markdown.h" struct flagnames { mkd_flag_t flag; char *name; }; static struct flagnames flagnames[] = { { MKD_NOLINKS, "!LINKS" }, { MKD_NOIMAGE, "!IMAGE" }, { MKD_NOPANTS, "!PANTS" }, { MKD_NOHTML, "!HTML" }, { MKD_STRICT, "STRICT" }, { MKD_TAGTEXT, "TAGTEXT" }, { MKD_NO_EXT, "!EXT" }, { MKD_CDATA, "CDATA" }, { MKD_NOSUPERSCRIPT, "!SUPERSCRIPT" }, { MKD_NORELAXED, "!RELAXED" }, { MKD_NOTABLES, "!TABLES" }, { MKD_NOSTRIKETHROUGH,"!STRIKETHROUGH" }, { MKD_TOC, "TOC" }, { MKD_1_COMPAT, "MKD_1_COMPAT" }, { MKD_AUTOLINK, "AUTOLINK" }, { MKD_SAFELINK, "SAFELINK" }, { MKD_NOHEADER, "!HEADER" }, { MKD_TABSTOP, "TABSTOP" }, { MKD_NODIVQUOTE, "!DIVQUOTE" }, { MKD_NOALPHALIST, "!ALPHALIST" }, { MKD_NODLIST, "!DLIST" }, { MKD_EXTRA_FOOTNOTE, "FOOTNOTE" }, { MKD_NOSTYLE, "!STYLE" }, { MKD_NODLDISCOUNT, "!DLDISCOUNT" }, { MKD_DLEXTRA, "DLEXTRA" }, { MKD_FENCEDCODE, "FENCEDCODE" }, { MKD_IDANCHOR, "IDANCHOR" }, { MKD_GITHUBTAGS, "GITHUBTAGS" }, { MKD_URLENCODEDANCHOR, "URLENCODEDANCHOR" }, { MKD_LATEX, "LATEX" }, { MKD_EXPLICITLIST, "EXPLICITLIST" }, }; #define NR(x) (sizeof x/sizeof x[0]) void mkd_flags_are(FILE *f, mkd_flag_t flags, int htmlplease) { int i; int not, set, even=1; char *name; if ( htmlplease ) fprintf(f, "\n"); for (i=0; i < NR(flagnames); i++) { set = flags & flagnames[i].flag; name = flagnames[i].name; if ( not = (*name == '!') ) { ++name; set = !set; } if ( htmlplease ) { if ( even ) fprintf(f, " "); fprintf(f, ""); if ( !even ) fprintf(f, "\n"); } even = !even; } if ( htmlplease ) { if ( even ) fprintf(f, "\n"); fprintf(f, "
    "); } else fputc(' ', f); if ( !set ) fprintf(f, htmlplease ? "" : "!"); fprintf(f, "%s", name); if ( htmlplease ) { if ( !set ) fprintf(f, ""); fprintf(f, "
    \n"); } } void mkd_mmiot_flags(FILE *f, MMIOT *m, int htmlplease) { if ( m ) mkd_flags_are(f, m->flags, htmlplease); } rdiscount-2.2.7.3/ext/rdiscount.c0000644000004100000410000001116414571373162016717 0ustar www-datawww-data#include #include #include "ruby.h" #include "mkdio.h" typedef struct { char *accessor_name; int flag; } AccessorFlagPair; /* * Maps accessor names on the RDiscount object to Discount flags. * * The following flags are handled specially: * - MKD_TABSTOP: Always set. * - MKD_NOHEADER: Always set. * - MKD_DLEXTRA: Always set. (For compatibility with RDiscount 2.1.8 and earlier.) * - MKD_FENCEDCODE: Always set. (For compatibility with RDiscount 2.1.8 and earlier.) * - MKD_GITHUBTAGS: Always set. (For compatibility with RDiscount 2.1.8 and earlier.) * - MKD_NOPANTS: Set unless the "smart" accessor returns true. * - MKD_NOSTYLE: Set unless the "filter_styles" accessor returns true. * * See rb_rdiscount__get_flags() for the detailed implementation. */ static AccessorFlagPair ACCESSOR_2_FLAG[] = { { "filter_html", MKD_NOHTML }, { "footnotes", MKD_EXTRA_FOOTNOTE }, { "generate_toc", MKD_TOC }, { "no_image", MKD_NOIMAGE }, { "no_links", MKD_NOLINKS }, { "no_tables", MKD_NOTABLES }, { "strict", MKD_STRICT }, { "autolink", MKD_AUTOLINK }, { "safelink", MKD_SAFELINK }, { "no_pseudo_protocols", MKD_NO_EXT }, { "no_superscript", MKD_NOSUPERSCRIPT }, { "no_strikethrough", MKD_NOSTRIKETHROUGH }, { "latex", MKD_LATEX }, { "explicitlist", MKD_EXPLICITLIST }, { "md1compat", MKD_1_COMPAT }, { NULL, 0 } /* sentinel */ }; static VALUE rb_cRDiscount; int rb_rdiscount__get_flags(VALUE ruby_obj) { AccessorFlagPair *entry; /* compile flags */ int flags = MKD_TABSTOP | MKD_NOHEADER | MKD_DLEXTRA | MKD_FENCEDCODE | MKD_GITHUBTAGS; /* The "smart" accessor turns OFF the MKD_NOPANTS flag. */ if ( rb_funcall(ruby_obj, rb_intern("smart"), 0) != Qtrue ) { flags = flags | MKD_NOPANTS; } /* The "filter_styles" accessor turns OFF the MKD_NOSTYLE flag. */ if ( rb_funcall(ruby_obj, rb_intern("filter_styles"), 0) != Qtrue ) { flags = flags | MKD_NOSTYLE; } /* Handle standard flags declared in ACCESSOR_2_FLAG */ for ( entry = ACCESSOR_2_FLAG; entry->accessor_name; entry++ ) { if ( rb_funcall(ruby_obj, rb_intern(entry->accessor_name), 0) == Qtrue ) { flags = flags | entry->flag; } } return flags; } static VALUE rb_rdiscount_to_html(int argc, VALUE *argv, VALUE self) { /* grab char pointer to markdown input text */ char *res; int szres; VALUE encoding; VALUE text = rb_funcall(self, rb_intern("text"), 0); VALUE buf = rb_str_buf_new(1024); Check_Type(text, T_STRING); int flags = rb_rdiscount__get_flags(self); /* * Force Discount to use ASCII character encoding for isalnum(), isalpha(), * and similar functions. * * Ruby tends to use UTF-8 encoding, which is ill-defined for these * functions since they expect 8-bit codepoints (and UTF-8 has codepoints * of at least 21 bits). */ char *old_locale = strdup(setlocale(LC_CTYPE, NULL)); setlocale(LC_CTYPE, "C"); /* ASCII (and passthru characters > 127) */ MMIOT *doc = mkd_string(RSTRING_PTR(text), RSTRING_LEN(text), flags); if ( mkd_compile(doc, flags) ) { szres = mkd_document(doc, &res); if ( szres != EOF ) { rb_str_cat(buf, res, szres); rb_str_cat(buf, "\n", 1); } } mkd_cleanup(doc); setlocale(LC_CTYPE, old_locale); free(old_locale); /* force the input encoding */ if ( rb_respond_to(text, rb_intern("encoding")) ) { encoding = rb_funcall(text, rb_intern("encoding"), 0); rb_funcall(buf, rb_intern("force_encoding"), 1, encoding); } return buf; } static VALUE rb_rdiscount_toc_content(int argc, VALUE *argv, VALUE self) { char *res; int szres; int flags = rb_rdiscount__get_flags(self); /* grab char pointer to markdown input text */ VALUE text = rb_funcall(self, rb_intern("text"), 0); Check_Type(text, T_STRING); /* allocate a ruby string buffer and wrap it in a stream */ VALUE buf = rb_str_buf_new(4096); MMIOT *doc = mkd_string(RSTRING_PTR(text), RSTRING_LEN(text), flags); if ( mkd_compile(doc, flags) ) { szres = mkd_toc(doc, &res); if ( szres != EOF ) { rb_str_cat(buf, res, szres); rb_str_cat(buf, "\n", 1); } } mkd_cleanup(doc); return buf; } void Init_rdiscount() { rb_cRDiscount = rb_define_class("RDiscount", rb_cObject); rb_define_method(rb_cRDiscount, "to_html", rb_rdiscount_to_html, -1); rb_define_method(rb_cRDiscount, "toc_content", rb_rdiscount_toc_content, -1); } /* vim: set ts=4 sw=4: */ rdiscount-2.2.7.3/ext/gethopt.h0000644000004100000410000000162614571373162016366 0ustar www-datawww-data/* * gethopt; options processing with both single-character and whole-work * options both introduced with - */ #ifndef __GETHOPT_D #define __GETHOPT_D #include #include struct h_opt { int option; char *optword; char optchar; char *opthasarg; char *optdesc; } ; #define HOPTERR ((struct h_opt*)-1) struct h_context { char **argv; int argc; int optchar; int optind; char *optarg; char optopt; int opterr:1; int optend:1; } ; extern char *hoptarg(struct h_context *); extern int hoptind(struct h_context *); extern char hoptopt(struct h_context *); extern void hoptset(struct h_context *, int, char **); extern int hopterr(struct h_context *, int); extern struct h_opt *gethopt(struct h_context *, struct h_opt*, int); extern void hoptusage(char *, struct h_opt*, int, char *); #endif/*__GETHOPT_D*/ rdiscount-2.2.7.3/ext/mktags.c0000644000004100000410000000341514571373162016173 0ustar www-datawww-data/* block-level tags for passing html blocks through the blender */ #include #define __WITHOUT_AMALLOC 1 #include "config.h" #include "cstring.h" #include "tags.h" STRING(struct kw) blocktags; /* define a html block tag */ static void define_one_tag(char *id, int selfclose) { struct kw *p = &EXPAND(blocktags); p->id = id; p->size = strlen(id); p->selfclose = selfclose; } /* case insensitive string sort (for qsort() and bsearch() of block tags) */ static int casort(struct kw *a, struct kw *b) { if ( a->size != b->size ) return a->size - b->size; return strncasecmp(a->id, b->id, b->size); } /* stupid cast to make gcc shut up about the function types being * passed into qsort() and bsearch() */ typedef int (*stfu)(const void*,const void*); /* load in the standard collection of html tags that markdown supports */ int main() { int i; #define KW(x) define_one_tag(x, 0) #define SC(x) define_one_tag(x, 1) KW("STYLE"); KW("SCRIPT"); KW("ADDRESS"); KW("BDO"); KW("BLOCKQUOTE"); KW("CENTER"); KW("DFN"); KW("DIV"); KW("OBJECT"); KW("H1"); KW("H2"); KW("H3"); KW("H4"); KW("H5"); KW("H6"); KW("LISTING"); KW("NOBR"); KW("FORM"); KW("UL"); KW("P"); KW("OL"); KW("DL"); KW("PLAINTEXT"); KW("PRE"); KW("TABLE"); KW("WBR"); KW("XMP"); SC("HR"); KW("IFRAME"); KW("MAP"); qsort(T(blocktags), S(blocktags), sizeof(struct kw), (stfu)casort); printf("static struct kw blocktags[] = {\n"); for (i=0; i < S(blocktags); i++) printf(" { \"%s\", %d, %d },\n", T(blocktags)[i].id, T(blocktags)[i].size, T(blocktags)[i].selfclose ); printf("};\n\n"); printf("#define NR_blocktags %d\n", S(blocktags)); exit(0); } rdiscount-2.2.7.3/ext/markdown.c0000644000004100000410000007332714571373162016540 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 "config.h" #include #include #include #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" #include "tags.h" typedef int (*stfu)(const void*,const void*); typedef ANCHOR(Paragraph) ParagraphRoot; static Paragraph *Pp(ParagraphRoot *, Line *, int); static Paragraph *compile(Line *, int, MMIOT *); /* case insensitive string sort for Footnote tags. */ int __mkd_footsort(Footnote *a, Footnote *b) { int i; char ac, bc; if ( S(a->tag) != S(b->tag) ) return S(a->tag) - S(b->tag); for ( i=0; i < S(a->tag); i++) { ac = tolower(T(a->tag)[i]); bc = tolower(T(b->tag)[i]); if ( isspace(ac) && isspace(bc) ) continue; if ( ac != bc ) return ac - bc; } return 0; } /* find the first blank character after position */ static int nextblank(Line *t, int i) { while ( (i < S(t->text)) && !isspace(T(t->text)[i]) ) ++i; return i; } /* find the next nonblank character after position */ static int nextnonblank(Line *t, int i) { while ( (i < S(t->text)) && isspace(T(t->text)[i]) ) ++i; return i; } /* find the first nonblank character on the Line. */ int mkd_firstnonblank(Line *p) { return nextnonblank(p,0); } static inline int blankline(Line *p) { return ! (p && (S(p->text) > p->dle) ); } static Line * skipempty(Line *p) { while ( p && (p->dle == S(p->text)) ) p = p->next; return p; } void ___mkd_tidy(Cstring *t) { while ( S(*t) && isspace(T(*t)[S(*t)-1]) ) --S(*t); } static struct kw comment = { "!--", 3, 0 }; static struct kw * isopentag(Line *p) { int i=0, len; char *line; if ( !p ) return 0; line = T(p->text); len = S(p->text); if ( len < 3 || line[0] != '<' ) return 0; if ( line[1] == '!' && line[2] == '-' && line[3] == '-' ) /* comments need special case handling, because * the !-- doesn't need to end in a whitespace */ return &comment; /* find how long the tag is so we can check to see if * it's a block-level tag */ for ( i=1; i < len && T(p->text)[i] != '>' && T(p->text)[i] != '/' && !isspace(T(p->text)[i]); ++i ) ; return mkd_search_tags(T(p->text)+1, i-1); } typedef struct _flo { Line *t; int i; } FLO; #define floindex(x) (x.i) static unsigned int flogetc(FLO *f) { if ( f && f->t ) { if ( f->i < S(f->t->text) ) return (unsigned char)T(f->t->text)[f->i++]; f->t = f->t->next; f->i = 0; return flogetc(f); } return EOF; } static void splitline(Line *t, int cutpoint) { if ( t && (cutpoint < S(t->text)) ) { Line *tmp = calloc(1, sizeof *tmp); tmp->next = t->next; t->next = tmp; SUFFIX(tmp->text, T(t->text)+cutpoint, S(t->text)-cutpoint); EXPAND(tmp->text) = 0; S(tmp->text)--; S(t->text) = cutpoint; } } #define UNCHECK(l) ((l)->flags &= ~CHECKED) #define UNLESS_FENCED(t) if (fenced) { \ other = 1; l->count += (c == ' ' ? 0 : -1); \ } else { t; } /* * walk a line, seeing if it's any of half a dozen interesting regular * types. */ static void checkline(Line *l, mkd_flag_t flags) { int eol, i; int dashes = 0, spaces = 0, equals = 0, underscores = 0, stars = 0, tildes = 0, other = 0, backticks = 0, fenced = 0; l->flags |= CHECKED; l->kind = chk_text; l->count = 0; if (l->dle >= 4) { l->kind=chk_code; return; } for ( eol = S(l->text); eol > l->dle && isspace(T(l->text)[eol-1]); --eol ) ; for (i=l->dle; itext)[i]; int is_fence_char = 0; if ( c != ' ' ) l->count++; switch (c) { case '-': UNLESS_FENCED(dashes = 1); break; case ' ': UNLESS_FENCED(spaces = 1); break; case '=': UNLESS_FENCED(equals = 1); break; case '_': UNLESS_FENCED(underscores = 1); break; case '*': stars = 1; break; default: if ( is_flag_set(flags, MKD_FENCEDCODE) ) { switch (c) { case '~': if (other) return; is_fence_char = 1; tildes = 1; break; case '`': if (other) return; is_fence_char = 1; backticks = 1; break; } if (is_fence_char) { fenced = 1; break; } } other = 1; l->count--; if (!fenced) return; } } if ( dashes + equals + underscores + stars + tildes + backticks > 1 ) return; if ( spaces ) { if ( (underscores || stars || dashes) ) l->kind = chk_hr; return; } if ( stars || underscores ) { l->kind = chk_hr; } else if ( dashes ) { l->kind = chk_dash; } else if ( equals ) { l->kind = chk_equal; } else if ( tildes ) { l->kind = chk_tilde; } else if ( backticks ) { l->kind = chk_backtick; } } /* markdown only does special handling of comments if the comment end * is at the end of a line */ static Line * commentblock(Paragraph *p, int *unclosed) { Line *t, *ret; char *end; for ( t = p->text; t ; t = t->next) { if ( end = strstr(T(t->text), "-->") ) { if ( nextnonblank(t, 3 + (end - T(t->text))) < S(t->text) ) continue; /*splitline(t, 3 + (end - T(t->text)) );*/ ret = t->next; t->next = 0; return ret; } } *unclosed = 1; return t; } static Line * htmlblock(Paragraph *p, struct kw *tag, int *unclosed) { Line *ret; FLO f = { p->text, 0 }; int c; int i, closing, depth=0; *unclosed = 0; if ( tag == &comment ) return commentblock(p, unclosed); if ( tag->selfclose ) { ret = f.t->next; f.t->next = 0; return ret; } while ( (c = flogetc(&f)) != EOF ) { if ( c == '<' ) { /* tag? */ c = flogetc(&f); if ( c == '!' ) { /* comment? */ if ( flogetc(&f) == '-' && flogetc(&f) == '-' ) { /* yes */ while ( (c = flogetc(&f)) != EOF ) { if ( c == '-' && flogetc(&f) == '-' && flogetc(&f) == '>') /* consumed whole comment */ break; } } } else { if ( closing = (c == '/') ) c = flogetc(&f); for ( i=0; i < tag->size; i++, c=flogetc(&f) ) { if ( tag->id[i] != toupper(c) ) break; } if ( (i == tag->size) && !isalnum(c) ) { depth = depth + (closing ? -1 : 1); if ( depth == 0 ) { while ( c != EOF && c != '>' ) { /* consume trailing gunk in close tag */ c = flogetc(&f); } if ( c == EOF ) break; if ( !f.t ) return 0; splitline(f.t, floindex(f)); ret = f.t->next; f.t->next = 0; return ret; } } } } } *unclosed = 1; return 0; } /* footnotes look like ^{0,3}[stuff]: $ */ static int isfootnote(Line *t) { int i; if ( ( (i = t->dle) > 3) || (T(t->text)[i] != '[') ) return 0; for ( ++i; i < S(t->text) ; ++i ) { if ( T(t->text)[i] == '[' ) return 0; else if ( T(t->text)[i] == ']' ) return ( T(t->text)[i+1] == ':' ) ; } return 0; } static inline int isquote(Line *t) { return (t->dle < 4 && T(t->text)[t->dle] == '>'); } static inline int iscode(Line *t) { return (t->dle >= 4); } static inline int ishr(Line *t, mkd_flag_t flags) { if ( ! (t->flags & CHECKED) ) checkline(t, flags); if ( t->count > 2 ) return t->kind == chk_hr || t->kind == chk_dash || t->kind == chk_equal; return 0; } static int issetext(Line *t, int *htyp, mkd_flag_t flags) { Line *n; /* check for setext-style HEADER * ====== */ if ( (n = t->next) ) { if ( !(n->flags & CHECKED) ) checkline(n, flags); if ( n->kind == chk_dash || n->kind == chk_equal ) { *htyp = SETEXT; return 1; } } return 0; } static int ishdr(Line *t, int *htyp, mkd_flag_t flags) { /* ANY leading `#`'s make this into an ETX header */ if ( (t->dle == 0) && (S(t->text) > 1) && (T(t->text)[0] == '#') ) { *htyp = ETX; return 1; } /* And if not, maybe it's a SETEXT header instead */ return issetext(t, htyp, flags); } static inline int end_of_block(Line *t, mkd_flag_t flags) { int dummy; if ( !t ) return 0; return ( (S(t->text) <= t->dle) || ishr(t, flags) || ishdr(t, &dummy, flags) ); } static Line* is_discount_dt(Line *t, int *clip, mkd_flag_t flags) { if ( !is_flag_set(flags, MKD_NODLDISCOUNT) && t && t->next && (S(t->text) > 2) && (t->dle == 0) && (T(t->text)[0] == '=') && (T(t->text)[S(t->text)-1] == '=') ) { if ( t->next->dle >= 4 ) { *clip = 4; return t; } else return is_discount_dt(t->next, clip, flags); } return 0; } static int is_extra_dd(Line *t) { return (t->dle < 4) && (T(t->text)[t->dle] == ':') && isspace(T(t->text)[t->dle+1]); } static Line* is_extra_dt(Line *t, int *clip, mkd_flag_t flags) { if ( is_flag_set(flags, MKD_DLEXTRA) && t && t->next && S(t->text) && T(t->text)[0] != '=' && T(t->text)[S(t->text)-1] != '=') { Line *x; if ( iscode(t) || end_of_block(t, flags) ) return 0; if ( (x = skipempty(t->next)) && is_extra_dd(x) ) { *clip = x->dle+2; return t; } if ( x=is_extra_dt(t->next, clip, flags) ) return x; } return 0; } static Line* isdefinition(Line *t, int *clip, int *kind, mkd_flag_t flags) { Line *ret; *kind = 1; if ( ret = is_discount_dt(t,clip,flags) ) return ret; *kind=2; return is_extra_dt(t,clip,flags); } static int islist(Line *t, int *clip, mkd_flag_t flags, int *list_type) { int i, j; char *q; if ( end_of_block(t, flags) ) return 0; if ( !(is_flag_set(flags, MKD_NODLIST) || is_flag_set(flags, MKD_STRICT)) && isdefinition(t,clip,list_type,flags) ) return DL; if ( strchr("*-+", T(t->text)[t->dle]) && isspace(T(t->text)[t->dle+1]) ) { i = nextnonblank(t, t->dle+1); *clip = (i > 4) ? 4 : i; *list_type = UL; return is_flag_set(flags, MKD_EXPLICITLIST) ? UL : AL; } if ( (j = nextblank(t,t->dle)) > t->dle ) { if ( T(t->text)[j-1] == '.' ) { if ( !(is_flag_set(flags, MKD_NOALPHALIST) || is_flag_set(flags, MKD_STRICT)) && (j == t->dle + 2) && isalpha(T(t->text)[t->dle]) ) { j = nextnonblank(t,j); *clip = (j > 4) ? 4 : j; *list_type = AL; return AL; } strtoul(T(t->text)+t->dle, &q, 10); if ( (q > T(t->text)+t->dle) && (q == T(t->text) + (j-1)) ) { j = nextnonblank(t,j); *clip = j; *list_type = OL; return AL; } } } return 0; } static Line * headerblock(Paragraph *pp, int htyp) { Line *ret = 0; Line *p = pp->text; int i, j; switch (htyp) { case SETEXT: /* p->text is header, p->next->text is -'s or ='s */ pp->hnumber = (T(p->next->text)[0] == '=') ? 1 : 2; ret = p->next->next; ___mkd_freeLine(p->next); p->next = 0; break; case ETX: /* p->text is ###header###, so we need to trim off * the leading and trailing `#`'s */ for (i=0; (T(p->text)[i] == T(p->text)[0]) && (i < S(p->text)-1) && (i < 6); i++) ; pp->hnumber = i; while ( (i < S(p->text)) && isspace(T(p->text)[i]) ) ++i; CLIP(p->text, 0, i); UNCHECK(p); for (j=S(p->text); (j > 1) && (T(p->text)[j-1] == '#'); --j) ; while ( j && isspace(T(p->text)[j-1]) ) --j; S(p->text) = j; ret = p->next; p->next = 0; break; } return ret; } static Line * codeblock(Paragraph *p) { Line *t = p->text, *r; for ( ; t; t = r ) { __mkd_trim_line(t,4); if ( !( (r = skipempty(t->next)) && iscode(r)) ) { ___mkd_freeLineRange(t,r); t->next = 0; return r; } } return t; } static int iscodefence(Line *r, int size, line_type kind, mkd_flag_t flags) { if ( !is_flag_set(flags, MKD_FENCEDCODE) ) return 0; if ( !(r->flags & CHECKED) ) checkline(r, flags); if ( kind ) return (r->kind == kind) && (r->count >= size); else return (r->kind == chk_tilde || r->kind == chk_backtick) && (r->count >= size); } static Paragraph * fencedcodeblock(ParagraphRoot *d, Line **ptr, mkd_flag_t flags) { Line *first, *r; Paragraph *ret; first = (*ptr); /* don't allow zero-length code fences */ if ( (first->next == 0) || iscodefence(first->next, first->count, 0, flags) ) return 0; /* find the closing fence, discard the fences, * return a Paragraph with the contents */ for ( r = first; r && r->next; r = r->next ) if ( iscodefence(r->next, first->count, first->kind, flags) ) { (*ptr) = r->next->next; ret = Pp(d, first->next, CODE); if (S(first->text) - first->count > 0) { char *lang_attr = T(first->text) + first->count; while ( *lang_attr != 0 && *lang_attr == ' ' ) lang_attr++; ret->lang = strdup(lang_attr); } else { ret->lang = 0; } ___mkd_freeLine(first); ___mkd_freeLine(r->next); r->next = 0; return ret; } return 0; } static int centered(Line *first, Line *last) { if ( first&&last ) { int len = S(last->text); if ( (len > 2) && (strncmp(T(first->text), "->", 2) == 0) && (strncmp(T(last->text)+len-2, "<-", 2) == 0) ) { CLIP(first->text, 0, 2); S(last->text) -= 2; return CENTER; } } return 0; } static int endoftextblock(Line *t, int toplevelblock, mkd_flag_t flags) { int z; if ( end_of_block(t, flags) || isquote(t) ) return 1; /* HORRIBLE STANDARDS KLUDGES: * 1. non-toplevel paragraphs absorb adjacent code blocks * 2. Toplevel paragraphs eat absorb adjacent list items, * but sublevel blocks behave properly. * (What this means is that we only need to check for code * blocks at toplevel, and only check for list items at * nested levels.) */ return toplevelblock ? 0 : islist(t,&z,flags,&z); } static Line * textblock(Paragraph *p, int toplevel, mkd_flag_t flags) { Line *t, *next; for ( t = p->text; t ; t = next ) { if ( ((next = t->next) == 0) || endoftextblock(next, toplevel, flags) ) { p->align = centered(p->text, t); t->next = 0; return next; } } return t; } /* length of the id: or class: kind in a special div-not-quote block */ static int szmarkerclass(char *p) { if ( strncasecmp(p, "id:", 3) == 0 ) return 3; if ( strncasecmp(p, "class:", 6) == 0 ) return 6; return 0; } /* * check if the first line of a quoted block is the special div-not-quote * marker %[kind:]name% */ #define iscsschar(c) (isalpha(c) || (c == '-') || (c == '_') ) static int isdivmarker(Line *p, int start, mkd_flag_t flags) { char *s; int last, i; if ( is_flag_set(flags, MKD_NODIVQUOTE) || is_flag_set(flags, MKD_STRICT) ) return 0; start = nextnonblank(p, start); last= S(p->text) - (1 + start); s = T(p->text) + start; if ( (last <= 0) || (*s != '%') || (s[last] != '%') ) return 0; i = szmarkerclass(s+1); if ( !iscsschar(s[i+1]) ) return 0; while ( ++i < last ) if ( !(isdigit(s[i]) || iscsschar(s[i])) ) return 0; return 1; } /* * accumulate a blockquote. * * one sick horrible thing about blockquotes is that even though * it just takes ^> to start a quote, following lines, if quoted, * assume that the prefix is ``> ''. This means that code needs * to be indented *5* spaces from the leading '>', but *4* spaces * from the start of the line. This does not appear to be * documented in the reference implementation, but it's the * way the markdown sample web form at Daring Fireball works. */ static Line * quoteblock(Paragraph *p, mkd_flag_t flags) { Line *t, *q; int qp; for ( t = p->text; t ; t = q ) { if ( isquote(t) ) { /* clip leading spaces */ for (qp = 0; T(t->text)[qp] != '>'; qp ++) /* assert: the first nonblank character on this line * will be a > */; /* clip '>' */ qp++; /* clip next space, if any */ if ( T(t->text)[qp] == ' ' ) qp++; __mkd_trim_line(t,qp); UNCHECK(t); } q = skipempty(t->next); if ( (q == 0) || ((q != t->next) && (!isquote(q) || isdivmarker(q,1,flags))) ) { ___mkd_freeLineRange(t, q); t = q; break; } } if ( isdivmarker(p->text,0,flags) ) { char *prefix = "class"; int i; q = p->text; p->text = p->text->next; if ( (i = szmarkerclass(1+T(q->text))) == 3 ) /* and this would be an "%id:" prefix */ prefix="id"; if ( p->ident = malloc(4+strlen(prefix)+S(q->text)) ) sprintf(p->ident, "%s=\"%.*s\"", prefix, S(q->text)-(i+2), T(q->text)+(i+1) ); ___mkd_freeLine(q); } return t; } typedef int (*linefn)(Line *); /* * pull in a list block. A list block starts with a list marker and * runs until the next list marker, the next non-indented paragraph, * or EOF. You do not have to indent nonblank lines after the list * marker, but multiple paragraphs need to start with a 4-space indent. */ static Line * listitem(Paragraph *p, int indent, mkd_flag_t flags, linefn check) { Line *t, *q; int clip = indent; int z; #ifdef GITHUB_CHECKBOX int firstpara = 1; int ischeck; #define CHECK_NOT 0 #define CHECK_NO 1 #define CHECK_YES 2 #endif for ( t = p->text; t ; t = q) { UNCHECK(t); __mkd_trim_line(t, clip); #ifdef GITHUB_CHECKBOX if ( firstpara ) { ischeck = CHECK_NOT; if ( strncmp(T(t->text)+t->dle, "[ ]", 3) == 0 ) ischeck = CHECK_NO; else if ( strncasecmp(T(t->text)+t->dle, "[x]", 3) == 0 ) ischeck = CHECK_YES; if ( ischeck != CHECK_NOT ) { __mkd_trim_line(t, 3); p->flags |= GITHUB_CHECK; if ( ischeck == CHECK_YES ) p->flags |= IS_CHECKED; } firstpara = 0; } #endif /* even though we had to trim a long leader off this item, * the indent for trailing paragraphs is still 4... */ if (indent > 4) { indent = 4; } if ( (q = skipempty(t->next)) == 0 ) { ___mkd_freeLineRange(t,q); return 0; } /* after a blank line, the next block needs to start with a line * that's indented 4(? -- reference implementation allows a 1 * character indent, but that has unfortunate side effects here) * spaces, but after that the line doesn't need any indentation */ if ( q != t->next ) { if (q->dle < indent) { q = t->next; t->next = 0; return q; } /* indent at least 2, and at most as * as far as the initial line was indented. */ indent = clip ? clip : 2; } if ( (q->dle < indent) && (ishr(q,flags) || islist(q,&z,flags,&z) || (check && (*check)(q))) && !issetext(q,&z,flags) ) { q = t->next; t->next = 0; return q; } clip = (q->dle > indent) ? indent : q->dle; } return t; } static Line * definition_block(Paragraph *top, int clip, MMIOT *f, int kind) { ParagraphRoot d = { 0, 0 }; Paragraph *p; Line *q = top->text, *text = 0, *labels; int z, para; while (( labels = q )) { if ( (q = isdefinition(labels, &z, &kind, f->flags)) == 0 ) break; if ( (text = skipempty(q->next)) == 0 ) break; if ( para = (text != q->next) ) ___mkd_freeLineRange(q, text); q->next = 0; if ( kind == 1 /* discount dl */ ) for ( q = labels; q; q = q->next ) { CLIP(q->text, 0, 1); UNCHECK(q); S(q->text)--; } dd_block: p = Pp(&d, text, LISTITEM); text = listitem(p, clip, f->flags, (kind==2) ? is_extra_dd : 0); p->down = compile(p->text, 0, f); p->text = labels; labels = 0; if ( para && p->down ) p->down->align = PARA; if ( (q = skipempty(text)) == 0 ) break; if ( para = (q != text) ) { Line anchor; anchor.next = text; ___mkd_freeLineRange(&anchor,q); text = q; } if ( kind == 2 && is_extra_dd(q) ) goto dd_block; } top->text = 0; top->down = T(d); return text; } static Line * enumerated_block(Paragraph *top, int clip, MMIOT *f, int list_class) { ParagraphRoot d = { 0, 0 }; Paragraph *p; Line *q = top->text, *text; int para = 0, z; while (( text = q )) { p = Pp(&d, text, LISTITEM); text = listitem(p, clip, f->flags, 0); p->down = compile(p->text, 0, f); p->text = 0; if ( para && p->down ) p->down->align = PARA; if ( (q = skipempty(text)) == 0 || islist(q, &clip, f->flags, &z) != list_class ) break; if ( para = (q != text) ) { Line anchor; anchor.next = text; ___mkd_freeLineRange(&anchor, q); if ( p->down ) p->down->align = PARA; } } top->text = 0; top->down = T(d); return text; } static int tgood(char c) { switch (c) { case '\'': case '"': return c; case '(': return ')'; } return 0; } /* * eat lines for a markdown extra footnote */ static Line * extrablock(Line *p) { Line *np; while ( p && p->next ) { np = p->next; if ( np->dle < 4 && np->dle < S(np->text) ) { p->next = 0; return np; } __mkd_trim_line(np,4); p = np; } return 0; } /* * add a new (image or link) footnote to the footnote table */ static Line* addfootnote(Line *p, MMIOT* f) { int j, i; int c; Line *np = p->next; Footnote *foot = &EXPAND(f->footnotes->note); CREATE(foot->tag); CREATE(foot->link); CREATE(foot->title); foot->text = 0; foot->flags = foot->height = foot->width = 0; /* keep the footnote label */ for (j=i=p->dle+1; T(p->text)[j] != ']'; j++) EXPAND(foot->tag) = T(p->text)[j]; EXPAND(foot->tag) = 0; S(foot->tag)--; /* consume the closing ]: */ j = nextnonblank(p, j+2); if ( is_flag_set(f->flags, MKD_EXTRA_FOOTNOTE) && (T(foot->tag)[0] == '^') ) { /* markdown extra footnote: All indented lines past this point; * the first line includes the footnote reference, so we need to * snip that out as we go. */ foot->flags |= EXTRA_FOOTNOTE; __mkd_trim_line(p,j); np = extrablock(p); foot->text = compile(p, 0, f); return np; } while ( (j < S(p->text)) && !isspace(T(p->text)[j]) ) EXPAND(foot->link) = T(p->text)[j++]; EXPAND(foot->link) = 0; S(foot->link)--; j = nextnonblank(p,j); if ( T(p->text)[j] == '=' ) { sscanf(T(p->text)+j, "=%dx%d", &foot->width, &foot->height); j = nextblank(p, j); j = nextnonblank(p,j); } if ( (j >= S(p->text)) && np && np->dle && tgood(T(np->text)[np->dle]) ) { ___mkd_freeLine(p); p = np; np = p->next; j = p->dle; } if ( (c = tgood(T(p->text)[j])) ) { /* Try to take the rest of the line as a comment; read to * EOL, then shrink the string back to before the final * quote. */ ++j; /* skip leading quote */ while ( j < S(p->text) ) EXPAND(foot->title) = T(p->text)[j++]; while ( S(foot->title) && T(foot->title)[S(foot->title)-1] != c ) --S(foot->title); if ( S(foot->title) ) /* skip trailing quote */ --S(foot->title); EXPAND(foot->title) = 0; --S(foot->title); } ___mkd_freeLine(p); return np; } /* * allocate a paragraph header, link it to the * tail of the current document */ static Paragraph * Pp(ParagraphRoot *d, Line *ptr, int typ) { Paragraph *ret = calloc(sizeof *ret, 1); ret->text = ptr; ret->typ = typ; return ATTACH(*d, ret); } static Line* consume(Line *ptr, int *eaten) { Line *next; int blanks=0; for (; ptr && blankline(ptr); ptr = next, blanks++ ) { next = ptr->next; ___mkd_freeLine(ptr); } if ( ptr ) *eaten = blanks; return ptr; } typedef ANCHOR(Line) Cache; static void uncache(Cache *cache, ParagraphRoot *d, MMIOT *f) { Paragraph *p; if ( T(*cache) ) { E(*cache)->next = 0; p = Pp(d, 0, SOURCE); p->down = compile(T(*cache), 1, f); T(*cache) = E(*cache) = 0; } } /* * top-level compilation; break the document into * style, html, and source blocks with footnote links * weeded out. */ static Paragraph * compile_document(Line *ptr, MMIOT *f) { ParagraphRoot d = { 0, 0 }; Cache source = { 0, 0 }; Paragraph *p = 0; struct kw *tag; int eaten, unclosed; int previous_was_break = 1; while ( ptr ) { if ( !is_flag_set(f->flags, MKD_NOHTML) && (tag = isopentag(ptr)) ) { int blocktype; /* If we encounter a html/style block, compile and save all * of the cached source BEFORE processing the html/style. */ uncache(&source, &d, f); if (is_flag_set(f->flags, MKD_NOSTYLE) ) blocktype = HTML; else blocktype = strcmp(tag->id, "STYLE") == 0 ? STYLE : HTML; p = Pp(&d, ptr, blocktype); ptr = htmlblock(p, tag, &unclosed); if ( unclosed ) { p->typ = SOURCE; p->down = compile(p->text, 1, f); p->text = 0; } previous_was_break = 1; } else if ( isfootnote(ptr) ) { /* footnotes, like cats, sleep anywhere; pull them * out of the input stream and file them away for * later processing */ ptr = consume(addfootnote(ptr, f), &eaten); previous_was_break = 1; } else if ( previous_was_break && iscodefence(ptr,3,0,f->flags)) { uncache(&source, &d, f); if ( !fencedcodeblock(&d, &ptr, f->flags) ) /* just source */ goto attach; } else { attach: /* source; cache it up to wait for eof or the * next html/style block */ ATTACH(source,ptr); previous_was_break = blankline(ptr); ptr = ptr->next; } } /* if there's any cached source at EOF, compile * it now. */ uncache(&source, &d, f); return T(d); } static int first_nonblank_before(Line *j, int dle) { return (j->dle < dle) ? j->dle : dle; } static int actually_a_table(MMIOT *f, Line *pp) { Line *r; int j; int c; /* tables need to be turned on */ if ( is_flag_set(f->flags, MKD_STRICT) || is_flag_set(f->flags, MKD_NOTABLES) ) return 0; /* tables need three lines */ if ( !(pp && pp->next && pp->next->next) ) { return 0; } /* all lines must contain |'s */ for (r = pp; r; r = r->next ) if ( !(r->flags & PIPECHAR) ) { return 0; } /* if the header has a leading |, all lines must have leading |'s */ if ( T(pp->text)[pp->dle] == '|' ) { for ( r = pp; r; r = r->next ) if ( T(r->text)[first_nonblank_before(r,pp->dle)] != '|' ) { return 0; } } /* second line must be only whitespace, -, |, or : */ r = pp->next; for ( j=r->dle; j < S(r->text); ++j ) { c = T(r->text)[j]; if ( !(isspace(c)||(c=='-')||(c==':')||(c=='|')) ) { return 0; } } return 1; } /* * break a collection of markdown input into * blocks of lists, code, html, and text to * be marked up. */ static Paragraph * compile(Line *ptr, int toplevel, MMIOT *f) { ParagraphRoot d = { 0, 0 }; Paragraph *p = 0; Line *r; int para = toplevel; int blocks = 0; int hdr_type, list_type, list_class, indent; ptr = consume(ptr, ¶); while ( ptr ) { if ( iscode(ptr) ) { p = Pp(&d, ptr, CODE); if ( is_flag_set(f->flags, MKD_1_COMPAT) ) { /* HORRIBLE STANDARDS KLUDGE: the first line of every block * has trailing whitespace trimmed off. */ ___mkd_tidy(&p->text->text); } ptr = codeblock(p); } else if ( iscodefence(ptr,3,0,f->flags) && (p=fencedcodeblock(&d, &ptr, f->flags)) ) /* yay, it's already done */ ; else if ( ishr(ptr, f->flags) ) { p = Pp(&d, 0, HR); r = ptr; ptr = ptr->next; ___mkd_freeLine(r); } else if ( list_class = islist(ptr, &indent, f->flags, &list_type) ) { if ( list_class == DL ) { p = Pp(&d, ptr, DL); ptr = definition_block(p, indent, f, list_type); } else { p = Pp(&d, ptr, list_type); ptr = enumerated_block(p, indent, f, list_class); } } else if ( isquote(ptr) ) { p = Pp(&d, ptr, QUOTE); ptr = quoteblock(p, f->flags); p->down = compile(p->text, 1, f); p->text = 0; } else if ( ishdr(ptr, &hdr_type, f->flags) ) { p = Pp(&d, ptr, HDR); ptr = headerblock(p, hdr_type); } else { /* either markup or an html block element */ struct kw *tag; int unclosed = 1; p = Pp(&d, ptr, MARKUP); /* default to regular markup, * then check if it's an html * block. If it IS an html * block, htmlblock() will * populate this paragraph & * all we need to do is reset * the paragraph type to HTML, * otherwise the paragraph * remains empty and ready for * processing with textblock() */ if ( !is_flag_set(f->flags, MKD_NOHTML) && (tag = isopentag(ptr)) ) { /* possibly an html block */ ptr = htmlblock(p, tag, &unclosed); if ( ! unclosed ) { p->typ = HTML; } } if ( unclosed ) { ptr = textblock(p, toplevel, f->flags); /* tables are a special kind of paragraph */ if ( actually_a_table(f, p->text) ) p->typ = TABLE; } } if ( (para||toplevel) && !p->align ) p->align = PARA; blocks++; para = toplevel || (blocks > 1); ptr = consume(ptr, ¶); if ( para && !p->align ) p->align = PARA; } return T(d); } /* * the guts of the markdown() function, ripped out so I can do * debugging. */ /* * prepare and compile `text`, returning a Paragraph tree. */ int mkd_compile(Document *doc, mkd_flag_t flags) { if ( !doc ) return 0; flags &= USER_FLAGS; if ( doc->compiled ) { if ( doc->ctx->flags == flags && !doc->dirty) return 1; else { doc->compiled = doc->dirty = 0; if ( doc->code) ___mkd_freeParagraph(doc->code); if ( doc->ctx->footnotes ) ___mkd_freefootnotes(doc->ctx); } } doc->compiled = 1; memset(doc->ctx, 0, sizeof(MMIOT) ); doc->ctx->ref_prefix= doc->ref_prefix; doc->ctx->cb = &(doc->cb); doc->ctx->flags = flags; CREATE(doc->ctx->in); doc->ctx->footnotes = malloc(sizeof doc->ctx->footnotes[0]); doc->ctx->footnotes->reference = 0; CREATE(doc->ctx->footnotes->note); mkd_initialize(); doc->code = compile_document(T(doc->content), doc->ctx); qsort(T(doc->ctx->footnotes->note), S(doc->ctx->footnotes->note), sizeof T(doc->ctx->footnotes->note)[0], (stfu)__mkd_footsort); memset(&doc->content, 0, sizeof doc->content); return 1; } rdiscount-2.2.7.3/ext/basename.c0000644000004100000410000000157314571373162016463 0ustar www-datawww-data/* * mkdio -- markdown front end input functions * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include "config.h" #include #include #include #include "mkdio.h" #include "cstring.h" #include "amalloc.h" static char * e_basename(const char *string, const int size, void *context) { char *ret; char *base = (char*)context; if ( base && string && (*string == '/') && (ret=malloc(strlen(base)+size+2)) ) { strcpy(ret, base); strncat(ret, string, size); return ret; } return 0; } static void e_free(char *string, void *context) { if ( string ) free(string); } void mkd_basename(MMIOT *document, char *base) { mkd_e_url(document, e_basename); mkd_e_data(document, base); mkd_e_free(document, e_free); } rdiscount-2.2.7.3/ext/cstring.h0000644000004100000410000000455114571373162016365 0ustar www-datawww-data/* two template types: STRING(t) which defines a pascal-style string * of element (t) [STRING(char) is the closest to the pascal string], * and ANCHOR(t) which defines a baseplate that a linked list can be * built up from. [The linked list /must/ contain a ->next pointer * for linking the list together with.] */ #ifndef _CSTRING_D #define _CSTRING_D #include #include #ifndef __WITHOUT_AMALLOC # include "amalloc.h" #endif /* expandable Pascal-style string. */ #define STRING(type) struct { type *text; int size, alloc; } #define CREATE(x) ( (T(x) = (void*)0), (S(x) = (x).alloc = 0) ) #define EXPAND(x) (S(x)++)[(S(x) < (x).alloc) \ ? (T(x)) \ : (T(x) = T(x) ? realloc(T(x), sizeof T(x)[0] * ((x).alloc += 100)) \ : malloc(sizeof T(x)[0] * ((x).alloc += 100)) )] #define DELETE(x) ALLOCATED(x) ? (free(T(x)), S(x) = (x).alloc = 0) \ : ( S(x) = 0 ) #define CLIP(t,i,sz) \ S(t) -= ( ((i) >= 0) && ((sz) > 0) && (((i)+(sz)) <= S(t)) ) ? \ (memmove(&T(t)[i], &T(t)[i+sz], (S(t)-(i+sz)+1)*sizeof(T(t)[0])), \ (sz)) : 0 #define RESERVE(x, sz) T(x) = ((x).alloc > S(x) + (sz) \ ? T(x) \ : T(x) \ ? realloc(T(x), sizeof T(x)[0] * ((x).alloc = 100+(sz)+S(x))) \ : malloc(sizeof T(x)[0] * ((x).alloc = 100+(sz)+S(x)))) #define SUFFIX(t,p,sz) \ memcpy(((S(t) += (sz)) - (sz)) + \ (T(t) = T(t) ? realloc(T(t), sizeof T(t)[0] * ((t).alloc += sz)) \ : malloc(sizeof T(t)[0] * ((t).alloc += sz))), \ (p), sizeof(T(t)[0])*(sz)) #define PREFIX(t,p,sz) \ RESERVE( (t), (sz) ); \ if ( S(t) ) { memmove(T(t)+(sz), T(t), S(t)); } \ memcpy( T(t), (p), (sz) ); \ S(t) += (sz) /* reference-style links (and images) are stored in an array */ #define T(x) (x).text #define S(x) (x).size #define ALLOCATED(x) (x).alloc /* abstract anchor type that defines a list base * with a function that attaches an element to * the end of the list. * * the list base field is named .text so that the T() * macro will work with it. */ #define ANCHOR(t) struct { t *text, *end; } #define E(t) ((t).end) #define ATTACH(t, p) ( T(t) ? ( (E(t)->next = (p)), (E(t) = (p)) ) \ : ( (T(t) = E(t) = (p)) ) ) typedef STRING(char) Cstring; extern void Csputc(int, Cstring *); extern int Csprintf(Cstring *, char *, ...); extern int Cswrite(Cstring *, char *, int); #endif/*_CSTRING_D*/ rdiscount-2.2.7.3/ext/blocktags0000644000004100000410000000126114571373162016432 0ustar www-datawww-datastatic struct kw blocktags[] = { { "P", 1, 0 }, { "DL", 2, 0 }, { "H1", 2, 0 }, { "H2", 2, 0 }, { "H3", 2, 0 }, { "H4", 2, 0 }, { "H5", 2, 0 }, { "H6", 2, 0 }, { "HR", 2, 1 }, { "OL", 2, 0 }, { "UL", 2, 0 }, { "BDO", 3, 0 }, { "DFN", 3, 0 }, { "DIV", 3, 0 }, { "MAP", 3, 0 }, { "PRE", 3, 0 }, { "WBR", 3, 0 }, { "XMP", 3, 0 }, { "FORM", 4, 0 }, { "NOBR", 4, 0 }, { "STYLE", 5, 0 }, { "TABLE", 5, 0 }, { "CENTER", 6, 0 }, { "IFRAME", 6, 0 }, { "OBJECT", 6, 0 }, { "SCRIPT", 6, 0 }, { "ADDRESS", 7, 0 }, { "LISTING", 7, 0 }, { "PLAINTEXT", 9, 0 }, { "BLOCKQUOTE", 10, 0 }, }; #define NR_blocktags 30 rdiscount-2.2.7.3/ext/version.c0000644000004100000410000000026114571373162016366 0ustar www-datawww-data#include "config.h" char markdown_version[] = BRANCH VERSION #if 4 != 4 " TAB=4" #endif #if USE_AMALLOC " DEBUG" #endif #if GITHUB_CHECKBOX " GITHUB_CHECKBOX" #endif ; rdiscount-2.2.7.3/ext/notspecial.c0000644000004100000410000000133414571373162017044 0ustar www-datawww-data/* * check a filename to see if it's a (fifo|character special|socket) object * (if a stat() function doesn't exist, we can't stat so we'll just return * true no matter what.) */ #include "config.h" #if HAVE_STAT && HAS_ISCHR && HAS_ISFIFO && HAS_ISSOCK #include int notspecial(char *file) { struct stat info; if ( stat(file, &info) != 0 ) return 1; return !( S_ISCHR(info.st_mode) || S_ISFIFO(info.st_mode) || S_ISSOCK(info.st_mode) ); } #else int notspecial(char *file) { return 1; } #endif #if DEBUG #include int main(argc, argv) char **argv; { int i; for ( i=1; i < argc; i++ ) printf("%s is %sspecial\n", argv[i], notspecial(argv[i]) ? "not " : ""); } #endif rdiscount-2.2.7.3/ext/tags.h0000644000004100000410000000050614571373162015646 0ustar www-datawww-data/* block-level tags for passing html blocks through the blender */ #ifndef _TAGS_D #define _TAGS_D struct kw { char *id; int size; int selfclose; } ; struct kw* mkd_search_tags(char *, int); void mkd_prepare_tags(); void mkd_deallocate_tags(); void mkd_sort_tags(); void mkd_define_tag(char *, int); #endif rdiscount-2.2.7.3/ext/markdown.h0000644000004100000410000001751114571373162016536 0ustar www-datawww-data#ifndef _MARKDOWN_D #define _MARKDOWN_D #include "config.h" #include "cstring.h" #ifdef HAVE_INTTYPES_H # include #elif HAVE_STDINT_H # include #endif /* flags, captured into a named type */ typedef DWORD mkd_flag_t; #define is_flag_set(flags, item) ((flags) & (item)) #define set_flag(flags, item) ((flags) |= (item)) #define clear_flag(flags, item) ((flags) &= ~(item)) /* each input line is read into a Line, which contains the line, * the offset of the first non-space character [this assumes * that all tabs will be expanded to spaces!], and a pointer to * the next line. */ typedef enum { chk_text, chk_code, chk_hr, chk_dash, chk_tilde, chk_backtick, chk_equal } line_type; typedef struct line { Cstring text; struct line *next; int dle; /* leading indent on the line */ int flags; /* special attributes for this line */ #define PIPECHAR 0x01 /* line contains a | */ #define CHECKED 0x02 line_type kind; int count; } Line; /* a paragraph is a collection of Lines, with links to the next paragraph * and (if it's a QUOTE, UL, or OL) to the reparsed contents of this * paragraph. */ typedef struct paragraph { struct paragraph *next; /* next paragraph */ struct paragraph *down; /* recompiled contents of this paragraph */ struct line *text; /* all the text in this paragraph */ char *ident; /* %id% tag for QUOTE */ char *lang; /* lang attribute for CODE */ enum { WHITESPACE=0, CODE, QUOTE, MARKUP, HTML, STYLE, DL, UL, OL, AL, LISTITEM, HDR, HR, TABLE, SOURCE } typ; enum { IMPLICIT=0, PARA, CENTER} align; int hnumber; /* for typ == HDR */ #if GITHUB_CHECKBOX int flags; #define GITHUB_CHECK 0x01 #define IS_CHECKED 0x02 #endif } Paragraph; enum { ETX, SETEXT }; /* header types */ /* reference-style links (and images) are stored in an array * of footnotes. */ typedef struct footnote { Cstring tag; /* the tag for the reference link */ Cstring link; /* what this footnote points to */ Cstring title; /* what it's called (TITLE= attribute) */ Paragraph *text; /* EXTRA_FOOTNOTE content */ int height, width; /* dimensions (for image link) */ int dealloc; /* deallocation needed? */ int refnumber; int flags; #define EXTRA_FOOTNOTE 0x01 #define REFERENCED 0x02 } Footnote; typedef struct block { enum { bTEXT, bSTAR, bUNDER } b_type; int b_count; char b_char; Cstring b_text; Cstring b_post; } block; typedef STRING(block) Qblock; typedef char* (*mkd_callback_t)(const char*, const int, void*); typedef void (*mkd_free_t)(char*, void*); typedef struct callback_data { void *e_data; /* private data for callbacks */ mkd_callback_t e_url; /* url edit callback */ mkd_callback_t e_flags; /* extra href flags callback */ mkd_callback_t e_anchor; /* callback for anchor types */ mkd_free_t e_free; /* edit/flags callback memory deallocator */ mkd_callback_t e_codefmt; /* codeblock formatter (for highlighting) */ } Callback_data; struct escaped { char *text; struct escaped *up; } ; struct footnote_list { int reference; STRING(Footnote) note; } ; /* a magic markdown io thing holds all the data structures needed to * do the backend processing of a markdown document */ typedef struct mmiot { Cstring out; Cstring in; Qblock Q; char last; /* last text character added to out */ int isp; struct escaped *esc; char *ref_prefix; struct footnote_list *footnotes; mkd_flag_t flags; #define MKD_NOLINKS 0x00000001 #define MKD_NOIMAGE 0x00000002 #define MKD_NOPANTS 0x00000004 #define MKD_NOHTML 0x00000008 #define MKD_STRICT 0x00000010 #define MKD_TAGTEXT 0x00000020 #define MKD_NO_EXT 0x00000040 #define MKD_CDATA 0x00000080 #define MKD_NOSUPERSCRIPT 0x00000100 #define MKD_NORELAXED 0x00000200 #define MKD_NOTABLES 0x00000400 #define MKD_NOSTRIKETHROUGH 0x00000800 #define MKD_TOC 0x00001000 #define MKD_1_COMPAT 0x00002000 #define MKD_AUTOLINK 0x00004000 #define MKD_SAFELINK 0x00008000 #define MKD_NOHEADER 0x00010000 #define MKD_TABSTOP 0x00020000 #define MKD_NODIVQUOTE 0x00040000 #define MKD_NOALPHALIST 0x00080000 #define MKD_NODLIST 0x00100000 #define MKD_EXTRA_FOOTNOTE 0x00200000 #define MKD_NOSTYLE 0x00400000 #define MKD_NODLDISCOUNT 0x00800000 #define MKD_DLEXTRA 0x01000000 #define MKD_FENCEDCODE 0x02000000 #define MKD_IDANCHOR 0x04000000 #define MKD_GITHUBTAGS 0x08000000 #define MKD_URLENCODEDANCHOR 0x10000000 #define IS_LABEL 0x20000000 #define MKD_LATEX 0x40000000 #define MKD_EXPLICITLIST 0x80000000 #define USER_FLAGS 0xFFFFFFFF #define INPUT_MASK (MKD_NOHEADER|MKD_TABSTOP) Callback_data *cb; } MMIOT; #define MKD_EOLN '\r' /* * the mkdio text input functions return a document structure, * which contains a header (retrieved from the document if * markdown was configured * with the * --enable-pandoc-header * and the document begins with a pandoc-style header) and the * root of the linked list of Lines. */ typedef struct document { int magic; /* "I AM VALID" magic number */ #define VALID_DOCUMENT 0x19600731 Line *title; Line *author; Line *date; ANCHOR(Line) content; /* uncompiled text, not valid after compile() */ Paragraph *code; /* intermediate code generated by compile() */ int compiled; /* set after mkd_compile() */ int dirty; /* flags or callbacks changed */ int html; /* set after (internal) htmlify() */ int tabstop; /* for properly expanding tabs (ick) */ char *ref_prefix; MMIOT *ctx; /* backend buffers, flags, and structures */ Callback_data cb; /* callback functions & private data */ } Document; /* * economy FILE-type structure for pulling characters out of a * fixed-length string. */ struct string_stream { const char *data; /* the unread data */ int size; /* and how much is there? */ } ; extern int mkd_firstnonblank(Line *); extern int mkd_compile(Document *, mkd_flag_t); extern int mkd_document(Document *, char **); extern int mkd_generatehtml(Document *, FILE *); extern int mkd_css(Document *, char **); extern int mkd_generatecss(Document *, FILE *); #define mkd_style mkd_generatecss extern int mkd_xml(char *, int , char **); extern int mkd_generatexml(char *, int, FILE *); extern void mkd_cleanup(Document *); extern int mkd_line(char *, int, char **, mkd_flag_t); extern int mkd_generateline(char *, int, FILE*, mkd_flag_t); #define mkd_text mkd_generateline extern void mkd_basename(Document*, char *); typedef int (*mkd_sta_function_t)(const int,const void*); extern void mkd_string_to_anchor(char*,int, mkd_sta_function_t, void*, int, MMIOT *); extern Document *mkd_in(FILE *, mkd_flag_t); extern Document *mkd_string(const char*, int, mkd_flag_t); extern Document *gfm_in(FILE *, mkd_flag_t); extern Document *gfm_string(const char*,int, mkd_flag_t); extern void mkd_initialize(); extern void mkd_shlib_destructor(); extern void mkd_ref_prefix(Document*, char*); /* internal resource handling functions. */ extern void ___mkd_freeLine(Line *); extern void ___mkd_freeLines(Line *); extern void ___mkd_freeParagraph(Paragraph *); extern void ___mkd_freefootnote(Footnote *); extern void ___mkd_freefootnotes(MMIOT *); extern void ___mkd_initmmiot(MMIOT *, void *); extern void ___mkd_freemmiot(MMIOT *, void *); extern void ___mkd_freeLineRange(Line *, Line *); extern void ___mkd_xml(char *, int, FILE *); extern void ___mkd_reparse(char *, int, mkd_flag_t, MMIOT*, char*); extern void ___mkd_emblock(MMIOT*); extern void ___mkd_tidy(Cstring *); extern Document *__mkd_new_Document(); extern void __mkd_enqueue(Document*, Cstring *); extern void __mkd_trim_line(Line *, int); extern int __mkd_io_strget(struct string_stream *); /* utility function to do some operation and exit the current function * if it fails */ #define DO_OR_DIE(op) if ( (op) == EOF ) return EOF; else 1 #endif/*_MARKDOWN_D*/ rdiscount-2.2.7.3/ext/pgm_options.h0000644000004100000410000000027314571373162017247 0ustar www-datawww-data#ifndef PGM_OPTIONS_D #define PGM_OPTIONS_D #include char *set_flag(mkd_flag_t *flags, char *optionstring); void show_flags(int byname, int verbose); #endif/*PGM_OPTIONS_D*/ rdiscount-2.2.7.3/ext/html5.c0000644000004100000410000000064114571373162015734 0ustar www-datawww-data/* block-level tags for passing html5 blocks through the blender */ #include "tags.h" void mkd_with_html5_tags() { static int populated = 0; if ( populated ) return; populated = 1; mkd_define_tag("ASIDE", 0); mkd_define_tag("FOOTER", 0); mkd_define_tag("HEADER", 0); mkd_define_tag("NAV", 0); mkd_define_tag("SECTION", 0); mkd_define_tag("ARTICLE", 0); mkd_sort_tags(); } rdiscount-2.2.7.3/ext/github_flavoured.c0000644000004100000410000000431414571373162020235 0ustar www-datawww-data /* * github_flavoured -- implement the obnoxious "returns are hard newlines" * feature in github flavoured markdown. * * Copyright (C) 2012 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include "config.h" #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" /* build a Document from any old input. */ typedef int (*getc_func)(void*); Document * gfm_populate(getc_func getc, void* ctx, int flags) { Cstring line; Document *a = __mkd_new_Document(); int c; int pandoc = 0; if ( !a ) return 0; a->tabstop = is_flag_set(flags, MKD_TABSTOP) ? 4 : TABSTOP; CREATE(line); while ( (c = (*getc)(ctx)) != EOF ) { if ( c == '\n' ) { if ( pandoc != EOF && pandoc < 3 ) { if ( S(line) && (T(line)[0] == '%') ) pandoc++; else pandoc = EOF; } if (pandoc == EOF) { EXPAND(line) = ' '; EXPAND(line) = ' '; } __mkd_enqueue(a, &line); S(line) = 0; } else if ( isprint(c) || isspace(c) || (c & 0x80) ) EXPAND(line) = c; } if ( S(line) ) __mkd_enqueue(a, &line); DELETE(line); if ( (pandoc == 3) && !(is_flag_set(flags, MKD_NOHEADER) || is_flag_set(flags, MKD_STRICT)) ) { /* the first three lines started with %, so we have a header. * clip the first three lines out of content and hang them * off header. */ Line *headers = T(a->content); a->title = headers; __mkd_trim_line(a->title, 1); a->author= headers->next; __mkd_trim_line(a->author, 1); a->date = headers->next->next; __mkd_trim_line(a->date, 1); T(a->content) = headers->next->next->next; } return a; } /* convert a block of text into a linked list */ Document * gfm_string(const char *buf, int len, mkd_flag_t flags) { struct string_stream about; about.data = buf; about.size = len; return gfm_populate((getc_func)__mkd_io_strget, &about, flags & INPUT_MASK); } /* convert a file into a linked list */ Document * gfm_in(FILE *f, mkd_flag_t flags) { return gfm_populate((getc_func)fgetc, f, flags & INPUT_MASK); } rdiscount-2.2.7.3/ext/amalloc.h0000644000004100000410000000075414571373162016325 0ustar www-datawww-data/* * debugging malloc()/realloc()/calloc()/free() that attempts * to keep track of just what's been allocated today. */ #ifndef AMALLOC_D #define AMALLOC_D #include "config.h" #ifdef USE_AMALLOC extern void *amalloc(int); extern void *acalloc(int,int); extern void *arealloc(void*,int); extern void afree(void*); extern void adump(); #define malloc amalloc #define calloc acalloc #define realloc arealloc #define free afree #else #define adump() (void)1 #endif #endif/*AMALLOC_D*/ rdiscount-2.2.7.3/ext/resource.c0000644000004100000410000000540714571373162016537 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" /* free a (single) line */ void ___mkd_freeLine(Line *ptr) { DELETE(ptr->text); free(ptr); } /* free a list of lines */ void ___mkd_freeLines(Line *p) { if (p->next) ___mkd_freeLines(p->next); ___mkd_freeLine(p); } /* bye bye paragraph. */ void ___mkd_freeParagraph(Paragraph *p) { if (p->next) ___mkd_freeParagraph(p->next); if (p->down) ___mkd_freeParagraph(p->down); if (p->text) ___mkd_freeLines(p->text); if (p->ident) free(p->ident); if (p->lang) free(p->lang); free(p); } /* bye bye footnote. */ void ___mkd_freefootnote(Footnote *f) { DELETE(f->tag); DELETE(f->link); DELETE(f->title); if ( f->text) ___mkd_freeParagraph(f->text); } /* bye bye footnotes. */ void ___mkd_freefootnotes(MMIOT *f) { int i; if ( f->footnotes ) { for (i=0; i < S(f->footnotes->note); i++) ___mkd_freefootnote( &T(f->footnotes->note)[i] ); DELETE(f->footnotes->note); free(f->footnotes); } } /* initialize a new MMIOT */ void ___mkd_initmmiot(MMIOT *f, void *footnotes) { if ( f ) { memset(f, 0, sizeof *f); CREATE(f->in); CREATE(f->out); CREATE(f->Q); if ( footnotes ) f->footnotes = footnotes; else { f->footnotes = malloc(sizeof f->footnotes[0]); CREATE(f->footnotes->note); } } } /* free the contents of a MMIOT, but leave the object alone. */ void ___mkd_freemmiot(MMIOT *f, void *footnotes) { if ( f ) { DELETE(f->in); DELETE(f->out); DELETE(f->Q); if ( f->footnotes != footnotes ) ___mkd_freefootnotes(f); memset(f, 0, sizeof *f); } } /* free lines up to an barrier. */ void ___mkd_freeLineRange(Line *anchor, Line *stop) { Line *r = anchor->next; if ( r != stop ) { while ( r && (r->next != stop) ) r = r->next; if ( r ) r->next = 0; ___mkd_freeLines(anchor->next); } anchor->next = 0; } /* clean up everything allocated in __mkd_compile() */ void mkd_cleanup(Document *doc) { if ( doc && (doc->magic == VALID_DOCUMENT) ) { if ( doc->ctx ) { ___mkd_freemmiot(doc->ctx, 0); free(doc->ctx); } if ( doc->code) ___mkd_freeParagraph(doc->code); if ( doc->title) ___mkd_freeLine(doc->title); if ( doc->author) ___mkd_freeLine(doc->author); if ( doc->date) ___mkd_freeLine(doc->date); if ( T(doc->content) ) ___mkd_freeLines(T(doc->content)); memset(doc, 0, sizeof doc[0]); free(doc); } } rdiscount-2.2.7.3/ext/toc.c0000644000004100000410000000520014571373162015464 0ustar www-datawww-data/* * toc -- spit out a table of contents based on header blocks * * Copyright (C) 2008 Jjgod Jiang, David L Parsons * portions Copyright (C) 2011 Stefano D'Angelo * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include "config.h" #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" static int Csputc_mkd_sta_function_t(const int n, const void* iot) { Csputc(n, iot); } /* write an header index */ int mkd_toc(Document *p, char **doc) { Paragraph *tp, *srcp; int last_hnumber = 0; Cstring res; int size; int first = 1; extern void Csreparse(Cstring *, char *, int, mkd_flag_t); if ( !(doc && p && p->ctx) ) return -1; *doc = 0; if ( ! is_flag_set(p->ctx->flags, MKD_TOC) ) return 0; CREATE(res); RESERVE(res, 100); for ( tp = p->code; tp ; tp = tp->next ) { if ( tp->typ == SOURCE ) { for ( srcp = tp->down; srcp; srcp = srcp->next ) { if ( (srcp->typ == HDR) && srcp->text ) { while ( last_hnumber > srcp->hnumber ) { if ( (last_hnumber - srcp->hnumber) > 1 ) Csprintf(&res, "\n"); Csprintf(&res, "\n%*s\n%*s", last_hnumber-1, "", last_hnumber-1, ""); --last_hnumber; } if ( last_hnumber == srcp->hnumber ) Csprintf(&res, "\n"); else if ( (srcp->hnumber > last_hnumber) && !first ) Csprintf(&res, "\n"); while ( srcp->hnumber > last_hnumber ) { Csprintf(&res, "%*s\n%*s", last_hnumber, "", last_hnumber, ""); } if ( (size = S(res)) > 0 ) { /* null-terminate & strdup into a free()able memory chunk */ EXPAND(res) = 0; *doc = strdup(T(res)); } DELETE(res); return size; } /* write an header index */ int mkd_generatetoc(Document *p, FILE *out) { char *buf = 0; int sz = mkd_toc(p, &buf); int ret = EOF; if ( sz > 0 ) ret = fwrite(buf, 1, sz, out); if ( buf ) free(buf); return (ret == sz) ? ret : EOF; } rdiscount-2.2.7.3/ext/generate.c0000644000004100000410000012477214571373162016511 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)) ? (unsigned char)T(f->in)[i] : EOF; } /* pull a byte from the input buffer */ static inline unsigned int pull(MMIOT *f) { return ( f->isp < S(f->in) ) ? (unsigned char)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 * (when setting the current cursor position we also need to flush the * last character written cache) */ #define mmiotseek(f,x) ((f->isp = x), (f->last = 0)) #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); } static int Qchar_mkd_sta_function_t(const int n, const void* iot) { Qchar(n, iot); } /* Qanchor() prints out a suitable-for-id-tag version of a string */ static void Qanchor(struct line *p, MMIOT *f) { mkd_string_to_anchor(T(p->text), S(p->text), Qchar_mkd_sta_function_t, f, 1, 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, mkd_flag_t 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); /* inherit the last character printed from the reparsed * text; this way superscripts can work when they're * applied to something embedded in a link */ f->last = sub.last; ___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 ( !is_flag_set(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 ( is_flag_set(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 */ /* check a url (or url fragment to see that it begins with a known good * protocol (or no protocol at all) */ static int safelink(Cstring link) { char *p, *colon; if ( T(link) == 0 ) /* no link; safe */ return 1; p = T(link); if ( (colon = memchr(p, ':', S(link))) == 0 ) return 1; /* no protocol specified: safe */ if ( !isalpha(*p) ) /* protocol/method is [alpha][alnum or '+.-'] */ return 1; while ( ++p < colon ) if ( !(isalnum(*p) || *p == '.' || *p == '+' || *p == '-') ) return 1; return isautoprefix(T(link), S(link)); } /* 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 ( is_flag_set(f->flags, MKD_NO_EXT) || is_flag_set(f->flags, MKD_SAFELINK) ) return 0; } else if ( is_flag_set(f->flags, MKD_SAFELINK) && !safelink(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 ( is_flag_set(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 ( is_flag_set(f->flags, MKD_EXTRA_FOOTNOTE) && !is_flag_set(f->flags, MKD_STRICT) && (!image) && S(name) && T(name)[0] == '^' ) { extra_footnote = 1; goodlink = 1; } else { 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 = !is_flag_set(f->flags, MKD_1_COMPAT); } } 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, "&#%02d;", *((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) /* 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); } #ifdef TYPORA /* subspan() -- write out a chunk of text, blocking with ... */ static void subspan(MMIOT *f, int size) { Qstring("", f); ___mkd_reparse(cursor(f)-1, size, 0, f, 0); Qstring("", f); } /* supspan() -- write out a chunk of text, blocking with ... */ static void supspan(MMIOT *f, int size) { Qstring("", f); ___mkd_reparse(cursor(f)-1, size, 0, f, 0); Qstring("", f); } /* highlightspan() -- write out a chunk of text, blocking with ... */ static void highlightspan(MMIOT *f, int size) { Qstring("", f); ___mkd_reparse(cursor(f)-1, size, 0, f, 0); Qstring("", f); } #endif /* 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 ( is_flag_set(f->flags, MKD_NOHTML) ) return 1; if ( c == 'A' && is_flag_set(f->flags, MKD_NOLINKS) && !isthisalnum(f,2) ) return 1; if ( c == 'I' && is_flag_set(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 ( is_flag_set(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 */ /* * check if a character is one of the things the reference implementation considers valid for starting * a html(ish) tag */ static inline int is_a_strict_tag_prefix(int c) { return isalpha(c) || (c == '/') || (c == '!') || (c == '$') || (c == '?'); } /* 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=0; if ( is_flag_set(f->flags, MKD_TAGTEXT) ) return 0; c = peek(f, 1); if ( is_a_strict_tag_prefix(c) ) { /* By decree of Markdown.pl *this is a tag* and we want to absorb everything up * to the next '>', unless interrupted by another '<' OR a '`', at which point * we kick it back to the caller as plain old text. */ size=1; while ( (c=peek(f,size+1)) != '>' ) { if ( c == EOF || c == '<' ) return 0; if ( is_flag_set(f->flags, MKD_STRICT) ) { if ( c == '`' ) return 0; } size++; } } if ( size > 0 ) { if ( process_possible_link(f, size) ) { shift(f, size+1); return 1; } else { int i; if ( forbidden_tag(f) ) return 0; for ( i=0; i <= size+1; i++ ) { c = peek(f,i); if ( (c == '&') && (i > 0) ) Qstring("&", f); else Qchar(c, f); } 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 ( c & 0x80 ) /* HACK: ignore utf-8 extended characters */ continue; 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 ( is_flag_set(f->flags, MKD_NOPANTS) || is_flag_set(f->flags, MKD_TAGTEXT) || is_flag_set(f->flags, 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 latex with arbitrary 2-character ( $$ .. $$, \[ .. \], \( .. \) * delimiters */ static int mathhandler(MMIOT *f, int e1, int e2) { int i = 0; while(peek(f, ++i) != EOF) { if (peek(f, i) == e1 && peek(f, i+1) == e2) { cputc(peek(f,-1), f); cputc(peek(f, 0), f); while ( i-- > -1 ) cputc(pull(f), f); return 1; } } return 0; } /* 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) is_flag_set(f->flags, MKD_TAGTEXT) static void text(MMIOT *f) { int c, j; int rep; int smartyflags = 0; while (1) { if ( is_flag_set(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; #ifdef TYPORA case '=': if ( is_flag_set(f->flags, MKD_NOSUPERSCRIPT) || is_flag_set(f->flags, MKD_STRICT) || is_flag_set(f->flags, MKD_TAGTEXT) || ! tickhandler(f,c,2,0, highlightspan)) Qchar(c, f); break; /* A^B^ -> AB */ case '^': if ( is_flag_set(f->flags, MKD_NOSUPERSCRIPT) || is_flag_set(f->flags, MKD_STRICT) || is_flag_set(f->flags, MKD_TAGTEXT) || ! tickhandler(f,c,1,0, supspan)) Qchar(c, f); break; #else /* !TYPORA */ /* A^B -> AB */ case '^': if ( is_flag_set(f->flags, MKD_NOSUPERSCRIPT) || is_flag_set(f->flags, MKD_STRICT) || is_flag_set(f->flags, MKD_TAGTEXT) || (f->last == 0) || ((ispunct(f->last) || isspace(f->last)) && f->last != ')') || 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; #endif /* TYPORA */ case '_': /* Underscores don't count if they're in the middle of a word */ if ( !(is_flag_set(f->flags, MKD_NORELAXED) || is_flag_set(f->flags, 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; #ifdef TYPORA #define ticktick(f,c) (tickhandler(f,c,2,0,delspan) || tickhandler(f,c,1,0,subspan)) #else #define ticktick(f,c) tickhandler(f,c,2,0,delspan) #endif case '~': if ( is_flag_set(f->flags, MKD_NOSTRIKETHROUGH) || is_flag_set(f->flags, MKD_STRICT) || is_flag_set(f->flags, MKD_TAGTEXT) || !ticktick(f,c) ) 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 ( is_flag_set(f->flags, MKD_STRICT) || is_flag_set(f->flags, MKD_NOSUPERSCRIPT) ) { Qchar('\\', f); shift(f,-1); break; } Qchar(c, f); break; case ':': case '|': if ( is_flag_set(f->flags, MKD_NOTABLES) ) { Qchar('\\', f); shift(f,-1); break; } Qchar(c, f); break; case EOF: Qchar('\\', f); break; case '[': case '(': if ( is_flag_set(f->flags, MKD_LATEX) && mathhandler(f, '\\', (c =='(')?')':']') ) break; /* else fall through to default */ 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) ) { if ( is_flag_set(f->flags, MKD_STRICT) && is_a_strict_tag_prefix(peek(f,1)) ) Qchar(c, f); else 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; case '$': if ( is_flag_set(f->flags, MKD_LATEX) && (peek(f, 1) == '$') ) { pull(f); if ( mathhandler(f, '$', '$') ) break; Qchar('$', f); } /* fall through to default */ default: f->last = c; 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 ( is_flag_set(f->flags, MKD_IDANCHOR) ) { Qprintf(f, "hnumber); if ( is_flag_set(f->flags, MKD_TOC) ) { Qstring(" id=\"", f); Qanchor(pp->text, f); Qchar('"', f); } Qchar('>', f); } else { if ( is_flag_set(f->flags, MKD_TOC) ) { Qstring("
    text, f); Qstring("\">\n", f); } Qprintf(f, "", pp->hnumber); } 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) { static char *Begin[] = { "", "

    ", "

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

    ","

    " }; Line *t = pp->text; int align = pp->align; 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[align], f); text(f); Qstring(End[align], f); return 1; } static void printcode(Line *t, char *lang, MMIOT *f) { int blanks; if ( f->cb->e_codefmt ) { /* external code block formatter; copy the text into a buffer, * call the formatter to style it, then dump that styled text * directly to the queue */ char *text; char *fmt; int size, copy_p; Line *p; for (size=0, p = t; p; p = p->next ) size += 1+S(p->text); text = malloc(1+size); for ( copy_p = 0; t ; t = t->next ) { memcpy(text+copy_p, T(t->text), S(t->text)); copy_p += S(t->text); text[copy_p++] = '\n'; } text[copy_p] = 0; fmt = (*(f->cb->e_codefmt))(text, copy_p, (lang && lang[0]) ? lang : 0); free(text); if ( fmt ) { Qwrite(fmt, strlen(fmt), f); if ( f->cb->e_free ) (*(f->cb->e_free))(fmt, f->cb->e_data); return; } /* otherwise the external formatter failed and we need to * fall back to the traditional codeblock format */ } 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_paragraphs(Paragraph *p, MMIOT *f) { ___mkd_emblock(f); while (( p = display(p, f) )) { ___mkd_emblock(f); Qstring("\n\n", f); } } #ifdef GITHUB_CHECKBOX static void li_htmlify(Paragraph *p, char *arguments, mkd_flag_t flags, MMIOT *f) { ___mkd_emblock(f); Qprintf(f, ""); #if CHECKBOX_AS_INPUT if ( flags & GITHUB_CHECK ) { Qprintf(f, ""); } #else if ( flags & GITHUB_CHECK ) Qprintf(f, flags & IS_CHECKED ? "☑" : "☐"); #endif htmlify_paragraphs(p, f); Qprintf(f, ""); ___mkd_emblock(f); } #endif static void htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f) { ___mkd_emblock(f); if ( block ) Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments); htmlify_paragraphs(p, 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 ) { #ifdef GITHUB_CHECKBOX li_htmlify(p->down, p->ident, p->flags, f); #else htmlify(p->down, "li", p->ident, f); #endif 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); htmlify(t->text, 0, 0, m); 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 ( is_flag_set(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] ) { /* Add a null byte at the end of the generated html, * but pretend it doesn't exist. */ EXPAND(p->ctx->out) = 0; --S(p->ctx->out); } } *res = T(p->ctx->out); return S(p->ctx->out); } return EOF; } rdiscount-2.2.7.3/ext/Csio.c0000644000004100000410000000177314571373162015607 0ustar www-datawww-data#include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" /* putc() into a cstring */ void Csputc(int c, Cstring *iot) { EXPAND(*iot) = c; } /* printf() into a cstring */ int Csprintf(Cstring *iot, char *fmt, ...) { va_list ptr; int siz=100; do { RESERVE(*iot, siz); va_start(ptr, fmt); siz = vsnprintf(T(*iot)+S(*iot), ALLOCATED(*iot)-S(*iot), fmt, ptr); va_end(ptr); } while ( siz > (ALLOCATED(*iot)-S(*iot)) ); S(*iot) += siz; return siz; } /* write() into a cstring */ int Cswrite(Cstring *iot, char *bfr, int size) { RESERVE(*iot, size); memcpy(T(*iot)+S(*iot), bfr, size); S(*iot) += size; return size; } /* reparse() into a cstring */ void Csreparse(Cstring *iot, char *buf, int size, mkd_flag_t flags) { MMIOT f; ___mkd_initmmiot(&f, 0); ___mkd_reparse(buf, size, flags, &f, 0); ___mkd_emblock(&f); SUFFIX(*iot, T(f.out), S(f.out)); ___mkd_freemmiot(&f, 0); } rdiscount-2.2.7.3/ext/css.c0000644000004100000410000000272614571373162015501 0ustar www-datawww-data/* markdown: a C implementation of John Gruber's Markdown markup language. * * Copyright (C) 2009 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" /* * dump out stylesheet sections. */ static void stylesheets(Paragraph *p, Cstring *f) { Line* q; for ( ; p ; p = p->next ) { if ( p->typ == STYLE ) { for ( q = p->text; q ; q = q->next ) { Cswrite(f, T(q->text), S(q->text)); Csputc('\n', f); } } if ( p->down ) stylesheets(p->down, f); } } /* dump any embedded styles to a string */ int mkd_css(Document *d, char **res) { Cstring f; int size; if ( res && d && d->compiled ) { *res = 0; CREATE(f); RESERVE(f, 100); stylesheets(d->code, &f); if ( (size = S(f)) > 0 ) { /* null-terminate, then strdup() into a free()able memory * chunk */ EXPAND(f) = 0; *res = strdup(T(f)); } DELETE(f); return size; } return EOF; } /* dump any embedded styles to a file */ int mkd_generatecss(Document *d, FILE *f) { char *res; int written; int size = mkd_css(d, &res); written = (size > 0) ? fwrite(res,1,size,f) : 0; if ( res ) free(res); return (written == size) ? size : EOF; } rdiscount-2.2.7.3/ext/xmlpage.c0000644000004100000410000000221114571373162016333 0ustar www-datawww-data/* * xmlpage -- write a skeletal xhtml page * * 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 int mkd_xhtmlpage(Document *p, mkd_flag_t flags, FILE *out) { char *title; extern char *mkd_doc_title(Document *); if ( mkd_compile(p, flags) ) { DO_OR_DIE( fprintf(out, "\n" "\n" "\n") ); DO_OR_DIE( fprintf(out, "\n") ); DO_OR_DIE( fprintf(out, "") ); if ( title = mkd_doc_title(p) ) { DO_OR_DIE( fprintf(out, "%s", title) ); } DO_OR_DIE( fprintf(out, "\n") ); DO_OR_DIE( mkd_generatecss(p, out) ); DO_OR_DIE( fprintf(out, "\n" "\n") ); DO_OR_DIE( mkd_generatehtml(p, out) ); DO_OR_DIE( fprintf(out, "\n" "\n") ); return 0; } return EOF; } rdiscount-2.2.7.3/ext/mkdio.c0000644000004100000410000002214614571373162016012 0ustar www-datawww-data/* * mkdio -- markdown front end input functions * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include "config.h" #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" typedef ANCHOR(Line) LineAnchor; /* create a new blank Document */ Document* __mkd_new_Document() { Document *ret = calloc(sizeof(Document), 1); if ( ret ) { if ( ret->ctx = calloc(sizeof(MMIOT), 1) ) { ret->magic = VALID_DOCUMENT; return ret; } free(ret); } return 0; } /* add a line to the markdown input chain, expanding tabs and * noting the presence of special characters as we go. */ void __mkd_enqueue(Document* a, Cstring *line) { Line *p = calloc(sizeof *p, 1); unsigned char c; int xp = 0; int size = S(*line); unsigned char *str = (unsigned char*)T(*line); CREATE(p->text); ATTACH(a->content, p); while ( size-- ) { if ( (c = *str++) == '\t' ) { /* expand tabs into ->tabstop spaces. We use ->tabstop * because the ENTIRE FREAKING COMPUTER WORLD uses editors * that don't do ^T/^D, but instead use tabs for indentation, * and, of course, set their tabs down to 4 spaces */ do { EXPAND(p->text) = ' '; } while ( ++xp % a->tabstop ); } else if ( c >= ' ' ) { if ( c == '|' ) p->flags |= PIPECHAR; EXPAND(p->text) = c; ++xp; } } EXPAND(p->text) = 0; S(p->text)--; p->dle = mkd_firstnonblank(p); } /* trim leading characters from a line, then adjust the dle. */ void __mkd_trim_line(Line *p, int clip) { if ( clip >= S(p->text) ) { S(p->text) = p->dle = 0; T(p->text)[0] = 0; } else if ( clip > 0 ) { CLIP(p->text, 0, clip); p->dle = mkd_firstnonblank(p); } } /* build a Document from any old input. */ typedef int (*getc_func)(void*); Document * populate(getc_func getc, void* ctx, mkd_flag_t flags) { Cstring line; Document *a = __mkd_new_Document(); int c; int pandoc = 0; if ( !a ) return 0; a->tabstop = is_flag_set(flags, MKD_TABSTOP) ? 4 : TABSTOP; CREATE(line); while ( (c = (*getc)(ctx)) != EOF ) { if ( c == '\n' ) { if ( pandoc != EOF && pandoc < 3 ) { if ( S(line) && (T(line)[0] == '%') ) pandoc++; else pandoc = EOF; } __mkd_enqueue(a, &line); S(line) = 0; } else if ( isprint(c) || isspace(c) || (c & 0x80) ) EXPAND(line) = c; } if ( S(line) ) __mkd_enqueue(a, &line); DELETE(line); if ( (pandoc == 3) && !(is_flag_set(flags, MKD_NOHEADER) || is_flag_set(flags, MKD_STRICT)) ) { /* the first three lines started with %, so we have a header. * clip the first three lines out of content and hang them * off header. */ Line *headers = T(a->content); a->title = headers; __mkd_trim_line(a->title, 1); a->author= headers->next; __mkd_trim_line(a->author, 1); a->date = headers->next->next; __mkd_trim_line(a->date, 1); T(a->content) = headers->next->next->next; } return a; } /* convert a file into a linked list */ Document * mkd_in(FILE *f, mkd_flag_t flags) { return populate((getc_func)fgetc, f, flags & INPUT_MASK); } /* return a single character out of a buffer */ int __mkd_io_strget(struct string_stream *in) { if ( !in->size ) return EOF; --(in->size); return *(in->data)++; } /* convert a block of text into a linked list */ Document * mkd_string(const char *buf, int len, mkd_flag_t flags) { struct string_stream about; about.data = buf; about.size = len; return populate((getc_func)__mkd_io_strget, &about, flags & INPUT_MASK); } /* write the html to a file (xmlified if necessary) */ int mkd_generatehtml(Document *p, FILE *output) { char *doc; int szdoc; DO_OR_DIE( szdoc = mkd_document(p,&doc) ); if ( is_flag_set(p->ctx->flags, MKD_CDATA) ) DO_OR_DIE( mkd_generatexml(doc, szdoc, output) ); else if ( fwrite(doc, szdoc, 1, output) != 1 ) return EOF; DO_OR_DIE( putc('\n', output) ); return 0; } /* convert some markdown text to html */ int markdown(Document *document, FILE *out, mkd_flag_t flags) { if ( mkd_compile(document, flags) ) { mkd_generatehtml(document, out); mkd_cleanup(document); return 0; } return -1; } /* anchor_format a string, returning the formatted string in malloc()ed space * MKD_URLENCODEDANCHOR is now perverted to being a html5 anchor * * !labelformat: print all characters * labelformat && h4anchor: prefix nonalpha label with L, * expand all nonalnum, _, ':', '.' to hex * except space which maps to - * labelformat && !h4anchor:expand space to -, other isspace() & '%' to hex */ static char * mkd_anchor_format(char *s, int len, int labelformat, mkd_flag_t flags) { char *res; unsigned char c; int i, needed, out = 0; int h4anchor = !is_flag_set(flags, MKD_URLENCODEDANCHOR); static const unsigned char hexchars[] = "0123456789abcdef"; needed = (labelformat ? (4*len) : len) + 2 /* 'L', trailing null */; if ( (res = malloc(needed)) == NULL ) return NULL; if ( h4anchor && labelformat && !isalpha(s[0]) ) res[out++] = 'L'; for ( i=0; i < len ; i++ ) { c = s[i]; if ( labelformat ) { if ( h4anchor ? (isalnum(c) || (c == '_') || (c == ':') || (c == '.' ) ) : !(isspace(c) || c == '%') ) res[out++] = c; else if ( c == ' ' ) res[out++] = '-'; else { res[out++] = h4anchor ? '-' : '%'; res[out++] = hexchars[c >> 4 & 0xf]; res[out++] = hexchars[c & 0xf]; if ( h4anchor ) res[out++] = '-'; } } else res[out++] = c; } res[out++] = 0; return res; } /* mkd_anchor_format */ /* write out a Cstring, mangled into a form suitable for `cb->e_anchor ) res = (*(f->cb->e_anchor))(line, size, f->cb->e_data); else res = mkd_anchor_format(line, size, labelformat, f->flags); free(line); if ( !res ) return; for ( i=0; res[i]; i++ ) (*outchar)(res[i], out); if ( f->cb->e_anchor ) { if ( f->cb->e_free ) (*(f->cb->e_free))(res, f->cb->e_data); } else free(res); } /* ___mkd_reparse() a line */ static void mkd_parse_line(char *bfr, int size, MMIOT *f, mkd_flag_t flags) { ___mkd_initmmiot(f, 0); f->flags = flags & USER_FLAGS; ___mkd_reparse(bfr, size, 0, f, 0); ___mkd_emblock(f); } /* ___mkd_reparse() a line, returning it in malloc()ed memory */ int mkd_line(char *bfr, int size, char **res, mkd_flag_t flags) { MMIOT f; int len; mkd_parse_line(bfr, size, &f, flags); if ( len = S(f.out) ) { EXPAND(f.out) = 0; /* strdup() doesn't use amalloc(), so in an amalloc()ed * build this copies the string safely out of our memory * paranoia arena. In a non-amalloc world, it's a spurious * memory allocation, but it avoids unintentional hilarity * with amalloc() */ *res = strdup(T(f.out)); } else { *res = 0; len = EOF; } ___mkd_freemmiot(&f, 0); return len; } /* ___mkd_reparse() a line, writing it to a FILE */ int mkd_generateline(char *bfr, int size, FILE *output, mkd_flag_t flags) { MMIOT f; int status; mkd_parse_line(bfr, size, &f, flags); if ( is_flag_set(flags, MKD_CDATA) ) status = mkd_generatexml(T(f.out), S(f.out), output) != EOF; else status = fwrite(T(f.out), S(f.out), 1, output) == S(f.out); ___mkd_freemmiot(&f, 0); return status ? 0 : EOF; } /* set the url display callback */ void mkd_e_url(Document *f, mkd_callback_t edit) { if ( f ) { if ( f->cb.e_url != edit ) f->dirty = 1; f->cb.e_url = edit; } } /* set the url options callback */ void mkd_e_flags(Document *f, mkd_callback_t edit) { if ( f ) { if ( f->cb.e_flags != edit ) f->dirty = 1; f->cb.e_flags = edit; } } /* set the anchor formatter */ void mkd_e_anchor(Document *f, mkd_callback_t format) { if ( f ) { if ( f->cb.e_anchor != format ) f->dirty = 1; f->cb.e_anchor = format; } } /* set the url display/options deallocator */ void mkd_e_free(Document *f, mkd_free_t dealloc) { if ( f ) { if ( f->cb.e_free != dealloc ) f->dirty = 1; f->cb.e_free = dealloc; } } /* set the url display/options context data field */ void mkd_e_data(Document *f, void *data) { if ( f ) { if ( f->cb.e_data != data ) f->dirty = 1; f->cb.e_data = data; } } /* set the code block display callback */ void mkd_e_code_format(Document *f, mkd_callback_t codefmt) { if ( f && (f->cb.e_codefmt != codefmt) ) { f->dirty = 1; f->cb.e_codefmt = codefmt; } } /* set the href prefix for markdown extra style footnotes */ void mkd_ref_prefix(Document *f, char *data) { if ( f ) { if ( f->ref_prefix != data ) f->dirty = 1; f->ref_prefix = data; } } rdiscount-2.2.7.3/ext/docheader.c0000644000004100000410000000155614571373162016627 0ustar www-datawww-data/* * docheader -- get values from the document header * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include "config.h" #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" static char * onlyifset(Line *l) { char *ret; if ( l->dle < 0 || l->dle >= S(l->text) ) return 0; ret = T(l->text) + l->dle; return ret[0] ? ret : 0; } char * mkd_doc_title(Document *doc) { if ( doc && doc->title ) return onlyifset(doc->title); return 0; } char * mkd_doc_author(Document *doc) { if ( doc && doc->author ) return onlyifset(doc->author); return 0; } char * mkd_doc_date(Document *doc) { if ( doc && doc->date ) return onlyifset(doc->date); return 0; } rdiscount-2.2.7.3/ext/emmatch.c0000644000004100000410000001012014571373162016312 0ustar www-datawww-data/* markdown: a C implementation of John Gruber's Markdown markup language. * * Copyright (C) 2010 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" /* emmatch: the emphasis mangler that's run after a block * of html has been generated. * * It should create MarkdownTest_1.0 (and _1.0.3) * compatible emphasis for non-pathological cases * and it should fail in a standards-compliant way * when someone attempts to feed it junk. * * Emmatching is done after the input has been * processed into a STRING (f->Q) of text and * emphasis blocks. After ___mkd_emblock() finishes, * it truncates f->Q and leaves the rendered paragraph * if f->out. */ /* empair() -- find the NEAREST matching emphasis token (or * subtoken of a 3+ long emphasis token. */ static int empair(MMIOT *f, int first, int last, int match) { int i; block *begin, *p; begin = &T(f->Q)[first]; for (i=first+1; i <= last; i++) { p = &T(f->Q)[i]; if ( (p->b_type != bTEXT) && (p->b_count <= 0) ) continue; /* break? */ if ( p->b_type == begin->b_type ) { if ( p->b_count == match ) /* exact match */ return i; if ( p->b_count > 2 ) /* fuzzy match */ return i; } } return 0; } /* empair */ /* emfill() -- if an emphasis token has leftover stars or underscores, * convert them back into character and append them to b_text. */ static void emfill(block *p) { int j; if ( p->b_type == bTEXT ) return; for (j=0; j < p->b_count; j++) EXPAND(p->b_text) = p->b_char; p->b_count = 0; } /* emfill */ static void emclose(MMIOT *f, int first, int last) { int j; for (j=first+1; jQ)[j]); } static struct emtags { char open[10]; char close[10]; int size; } emtags[] = { { "" , "", 5 }, { "", "", 9 } }; static void emblock(MMIOT*,int,int); /* emmatch() -- match emphasis for a single emphasis token. */ static void emmatch(MMIOT *f, int first, int last) { block *start = &T(f->Q)[first]; int e, e2, match; switch (start->b_count) { case 2: if ( e = empair(f,first,last,match=2) ) break; case 1: e = empair(f,first,last,match=1); break; case 0: return; default: e = empair(f,first,last,1); e2= empair(f,first,last,2); if ( e2 >= e ) { e = e2; match = 2; } else match = 1; break; } if ( e ) { /* if we found emphasis to match, match it, recursively call * emblock to match emphasis inside the new html block, add * the emphasis markers for the block, then (tail) recursively * call ourself to match any remaining emphasis on this token. */ block *end = &T(f->Q)[e]; end->b_count -= match; start->b_count -= match; emblock(f, first, e); PREFIX(start->b_text, emtags[match-1].open, emtags[match-1].size-1); SUFFIX(end->b_post, emtags[match-1].close, emtags[match-1].size); emmatch(f, first, last); } } /* emmatch */ /* emblock() -- walk a blocklist, attempting to match emphasis */ static void emblock(MMIOT *f, int first, int last) { int i; for ( i = first; i <= last; i++ ) if ( T(f->Q)[i].b_type != bTEXT ) emmatch(f, i, last); emclose(f, first, last); } /* emblock */ /* ___mkd_emblock() -- emblock a string of blocks, then concatenate the * resulting text onto f->out. */ void ___mkd_emblock(MMIOT *f) { int i; block *p; emblock(f, 0, S(f->Q)-1); for (i=0; i < S(f->Q); i++) { p = &T(f->Q)[i]; emfill(p); if ( S(p->b_post) ) { SUFFIX(f->out, T(p->b_post), S(p->b_post)); DELETE(p->b_post); } if ( S(p->b_text) ) { SUFFIX(f->out, T(p->b_text), S(p->b_text)); DELETE(p->b_text); } } S(f->Q) = 0; } /* ___mkd_emblock */ rdiscount-2.2.7.3/ext/VERSION0000644000004100000410000000000714571373162015603 0ustar www-datawww-data2.2.7d rdiscount-2.2.7.3/ext/gethopt.c0000644000004100000410000001326214571373162016360 0ustar www-datawww-data/* * gehopt; options processing with both single-character and whole-word * options both introduced with - */ #include #include #include "gethopt.h" void hoptset(ctx, argc, argv) struct h_context *ctx; int argc; char **argv; { memset(ctx, 0, sizeof *ctx); ctx->argc = argc; ctx->argv = argv; ctx->optind = 1; } char * hoptarg(ctx) struct h_context *ctx; { return ctx->optarg; } int hoptind(ctx) struct h_context *ctx; { return ctx->optind; } char hoptopt(ctx) struct h_context *ctx; { return ctx->optopt; } int hopterr(struct h_context *ctx, int val) { int old = ctx->opterr; ctx->opterr = !!val; return old; } struct h_opt * gethopt(ctx, opts, nropts) struct h_context *ctx; struct h_opt *opts; int nropts; { int i; int dashes; if ( (ctx == 0) || ctx->optend || (ctx->optind >= ctx->argc) ) return 0; ctx->optarg = 0; ctx->optopt = 0; if ( ctx->optchar == 0) { /* check for leading - */ if ( ctx->argv[ctx->optind][0] != '-' ) { /* out of arguments */ ctx->optend = 1; return 0; } if ( ctx->argv[ctx->optind][1] == 0 || strcmp(ctx->argv[ctx->optind], "--") == 0 ) { /* option list finishes with - or -- token */ ctx->optend = 1; ctx->optind++; return 0; } dashes = 1; if ( ctx->argv[ctx->optind][dashes] == '-' ) { /* support GNU-style long option double-dash prefix * (if gethopt is passed an unknown option with a double-dash * prefix, it won't match a word and then the second dash * will be scanned as if it was a regular old single-character * option.) */ dashes = 2; } for ( i=0; i < nropts; i++ ) { if ( ! opts[i].optword ) continue; if (strcmp(opts[i].optword, dashes+(ctx->argv[ctx->optind]) ) == 0 ) { if ( opts[i].opthasarg ) { if ( ctx->argc > ctx->optind ) { ctx->optarg = ctx->argv[ctx->optind+1]; ctx->optind += 2; } else { /* word argument with required arg at end of *command line */ if ( ctx->opterr ) fprintf(stderr, "%s: option requires an argument -- %s\n", ctx->argv[0], opts[i].optword); ctx->optind ++; return HOPTERR; } } else { ctx->optind ++; } return &opts[i]; } } ctx->optchar = 1; } ctx->optopt = ctx->argv[ctx->optind][ctx->optchar++]; if ( !ctx->optopt ) { /* fell off the end of this argument */ ctx->optind ++; ctx->optchar = 0; return gethopt(ctx, opts, nropts); } for ( i=0; ioptopt ) { /* found a single-char option! */ if ( opts[i].opthasarg ) { if ( ctx->argv[ctx->optind][ctx->optchar] ) { /* argument immediately follows this options (-Oc) */ ctx->optarg = &ctx->argv[ctx->optind][ctx->optchar]; ctx->optind ++; ctx->optchar = 0; } else if ( ctx->optind < ctx->argc-1 ) { /* argument is next arg (-O c) */ ctx->optarg = &ctx->argv[ctx->optind+1][0]; ctx->optind += 2; ctx->optchar = 0; } else { /* end of arg string (-O); set optarg to null, return * (should it opterr on me?) */ ctx->optarg = 0; ctx->optind ++; ctx->optchar = 0; if ( ctx->opterr ) fprintf(stderr, "%s: option requires an argument -- %c\n", ctx->argv[0], opts[i].optchar); return HOPTERR; } } else { if ( !ctx->argv[ctx->optind][ctx->optchar] ) { ctx->optind ++; ctx->optchar = 0; } } return &opts[i]; } } if ( ctx->opterr ) fprintf(stderr, "%s: illegal option -- %c\n", ctx->argv[0], ctx->optopt); return HOPTERR; } void hoptusage(char *pgm, struct h_opt opts[], int nropts, char *arguments) { int i; int optcount; fprintf(stderr, "usage: %s", pgm); /* print out the options that don't have flags first */ for ( optcount=i=0; i < nropts; i++ ) { if ( opts[i].optchar && !opts[i].opthasarg) { if (optcount == 0 ) fputs(" [-", stderr); fputc(opts[i].optchar, stderr); optcount++; } } if ( optcount ) fputc(']', stderr); /* print out the options WITH flags */ for ( i = 0; i < nropts; i++ ) if ( opts[i].optchar && opts[i].opthasarg) fprintf(stderr, " [-%c %s]", opts[i].optchar, opts[i].opthasarg); /* print out the long options */ for ( i = 0; i < nropts; i++ ) if ( opts[i].optword ) { fprintf(stderr, " [-%s", opts[i].optword); if ( opts[i].opthasarg ) fprintf(stderr, " %s", opts[i].opthasarg); fputc(']', stderr); } /* print out the arguments string, if any */ if ( arguments ) fprintf(stderr, " %s", arguments); /* and we're done */ fputc('\n', stderr); } #if DEBUG struct h_opt opts[] = { { 0, "css", 0, 1, "css file" }, { 1, "header", 0, 1, "header file" }, { 2, 0, 'a', 0, "option a (no arg)" }, { 3, 0, 'b', 1, "option B (with arg)" }, { 4, "help", '?', 0, "help message" }, } ; #define NROPT (sizeof opts/sizeof opts[0]) int main(argc, argv) char **argv; { struct h_opt *ret; struct h_context ctx; int i; hoptset(&ctx, argc, argv); hopterr(&ctx, 1); while (( ret = gethopt(&ctx, opts, NROPT) )) { if ( ret != HOPTERR ) { if ( ret->optword ) printf("%s", ret->optword); else printf("%c", ret->optchar); if ( ret->opthasarg ) { if ( hoptarg(&ctx) ) printf(" with argument \"%s\"", hoptarg(&ctx)); else printf(" with no argument?"); } printf(" (%s)\n", ret->optdesc); } } argc -= hoptind(&ctx); argv += hoptind(&ctx); for ( i=0; i < argc; i++ ) printf("%d: %s\n", i, argv[i]); return 0; } #endif /*DEBUG*/ rdiscount-2.2.7.3/ext/config.h0000644000004100000410000000100214571373162016145 0ustar www-datawww-data/* * rdiscount extension discount configuration */ #ifndef __MARKDOWN_D #define __MARKDOWN_D 1 /* tabs are four spaces */ #define TABSTOP 4 #define DESTRUCTOR __attribute__((__destructor__)) /* these are setup by extconf.rb */ #if HAVE_RANDOM #define COINTOSS() (random()&1) #elif HAVE_RAND #define COINTOSS() (rand()&1) #endif #if HAVE_SRANDOM #define INITRNG(x) srandom((unsigned int)x) #elif HAVE_SRAND #define INITRNG(x) srand((unsigned int)x) #endif #include "ruby-config.h" #endif/* __MARKDOWN_D */ rdiscount-2.2.7.3/ext/setup.c0000644000004100000410000000123514571373162016043 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 "config.h" #include #include #include #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" #include "tags.h" static int need_to_initrng = 1; void mkd_initialize() { if ( need_to_initrng ) { need_to_initrng = 0; INITRNG(time(0)); } } void DESTRUCTOR mkd_shlib_destructor() { mkd_deallocate_tags(); } rdiscount-2.2.7.3/ext/h1title.c0000644000004100000410000000126114571373162016254 0ustar www-datawww-data#include #include "markdown.h" static Paragraph * mkd_h1(Paragraph *p) { Paragraph *found; while ( p ) { if ( p->typ == HDR && p->hnumber == 1 ) return p; if ( p->down && (found = mkd_h1(p->down)) ) return found; p = p->next; } return 0; } char * mkd_h1_title(Document *doc, int flags) { Paragraph *title; if (doc && (title = mkd_h1(doc->code)) ) { char *generated; int size; /* assert that a H1 header is one line long, so that's * the only thing needed */ size = mkd_line(T(title->text->text), S(title->text->text), &generated, flags|MKD_TAGTEXT); if ( size ) return generated; } return 0; } rdiscount-2.2.7.3/ext/amalloc.c0000644000004100000410000000516314571373162016317 0ustar www-datawww-data/* * debugging malloc()/realloc()/calloc()/free() that attempts * to keep track of just what's been allocated today. */ #include #include #include "config.h" #define MAGIC 0x1f2e3d4c struct alist { int magic, size, index; int *end; struct alist *next, *last; }; static struct alist list = { 0, 0, 0, 0 }; static int mallocs=0; static int reallocs=0; static int frees=0; static int index = 0; static void die(char *msg, int index) { fprintf(stderr, msg, index); abort(); } void * acalloc(int count, int size) { struct alist *ret; if ( size > 1 ) { count *= size; size = 1; } if ( ret = calloc(count + sizeof(struct alist) + sizeof(int), size) ) { ret->magic = MAGIC; ret->size = size * count; ret->index = index ++; ret->end = (int*)(count + (char*) (ret + 1)); *(ret->end) = ~MAGIC; if ( list.next ) { ret->next = list.next; ret->last = &list; ret->next->last = ret; list.next = ret; } else { ret->last = ret->next = &list; list.next = list.last = ret; } ++mallocs; return ret+1; } return 0; } void* amalloc(int size) { return acalloc(size,1); } void afree(void *ptr) { struct alist *p2 = ((struct alist*)ptr)-1; if ( p2->magic == MAGIC ) { if ( ! (p2->end && *(p2->end) == ~MAGIC) ) die("goddam: corrupted memory block %d in free()!\n", p2->index); p2->last->next = p2->next; p2->next->last = p2->last; ++frees; free(p2); } else free(ptr); } void * arealloc(void *ptr, int size) { struct alist *p2 = ((struct alist*)ptr)-1; struct alist save; if ( p2->magic == MAGIC ) { if ( ! (p2->end && *(p2->end) == ~MAGIC) ) die("goddam: corrupted memory block %d in realloc()!\n", p2->index); save.next = p2->next; save.last = p2->last; p2 = realloc(p2, sizeof(int) + sizeof(*p2) + size); if ( p2 ) { p2->size = size; p2->end = (int*)(size + (char*) (p2 + 1)); *(p2->end) = ~MAGIC; p2->next->last = p2; p2->last->next = p2; ++reallocs; return p2+1; } else { save.next->last = save.last; save.last->next = save.next; return 0; } } return realloc(ptr, size); } void adump() { struct alist *p; for ( p = list.next; p && (p != &list); p = p->next ) { fprintf(stderr, "allocated: %d byte%s\n", p->size, (p->size==1) ? "" : "s"); fprintf(stderr, " [%.*s]\n", p->size, (char*)(p+1)); } if ( getenv("AMALLOC_STATISTICS") ) { fprintf(stderr, "%d malloc%s\n", mallocs, (mallocs==1)?"":"s"); fprintf(stderr, "%d realloc%s\n", reallocs, (reallocs==1)?"":"s"); fprintf(stderr, "%d free%s\n", frees, (frees==1)?"":"s"); } } rdiscount-2.2.7.3/ext/tags.c0000644000004100000410000000372014571373162015642 0ustar www-datawww-data/* block-level tags for passing html blocks through the blender */ #include "config.h" #define __WITHOUT_AMALLOC 1 #include "cstring.h" #include "tags.h" STRING(struct kw) extratags; /* the standard collection of tags are built and sorted when * discount is configured, so all we need to do is pull them * in and use them. * * Additional tags still need to be allocated, sorted, and deallocated. */ #include "blocktags" /* define an additional html block tag */ void mkd_define_tag(char *id, int selfclose) { struct kw *p; /* only add the new tag if it doesn't exist in * either the standard or extra tag tables. */ if ( !(p = mkd_search_tags(id, strlen(id))) ) { /* extratags could be deallocated */ if ( S(extratags) == 0 ) CREATE(extratags); p = &EXPAND(extratags); p->id = id; p->size = strlen(id); p->selfclose = selfclose; } } /* case insensitive string sort (for qsort() and bsearch() of block tags) */ static int casort(struct kw *a, struct kw *b) { if ( a->size != b->size ) return a->size - b->size; return strncasecmp(a->id, b->id, b->size); } /* stupid cast to make gcc shut up about the function types being * passed into qsort() and bsearch() */ typedef int (*stfu)(const void*,const void*); /* sort the list of extra html block tags for later searching */ void mkd_sort_tags() { qsort(T(extratags), S(extratags), sizeof(struct kw), (stfu)casort); } /* look for a token in the html block tag list */ struct kw* mkd_search_tags(char *pat, int len) { struct kw key; struct kw *ret; key.id = pat; key.size = len; if ( (ret=bsearch(&key,blocktags,NR_blocktags,sizeof key,(stfu)casort)) ) return ret; if ( S(extratags) ) return bsearch(&key,T(extratags),S(extratags),sizeof key,(stfu)casort); return 0; } /* destroy the extratags list (for shared libraries) */ void mkd_deallocate_tags() { if ( S(extratags) > 0 ) DELETE(extratags); } /* mkd_deallocate_tags */ rdiscount-2.2.7.3/ext/mkdio.h0000644000004100000410000001072514571373162016017 0ustar www-datawww-data#ifndef _MKDIO_D #define _MKDIO_D #include #include typedef void MMIOT; typedef uint32_t 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*, mkd_flag_t, char*); int markdown(MMIOT*, FILE*, mkd_flag_t); int mkd_line(char *, int, char **, mkd_flag_t); int mkd_xhtmlpage(MMIOT*,mkd_flag_t,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_anchor(void *, mkd_callback_t); void mkd_e_code_format(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