summaryrefslogtreecommitdiff
path: root/ruby_1_8_5/lib/rdoc/markup/simple_markup
diff options
context:
space:
mode:
Diffstat (limited to 'ruby_1_8_5/lib/rdoc/markup/simple_markup')
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/fragments.rb328
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/inline.rb340
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/lines.rb151
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/preprocess.rb73
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/to_flow.rb188
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/to_html.rb289
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/to_latex.rb333
7 files changed, 1702 insertions, 0 deletions
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/fragments.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/fragments.rb
new file mode 100644
index 0000000000..6ca06382ab
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/fragments.rb
@@ -0,0 +1,328 @@
+require 'rdoc/markup/simple_markup/lines.rb'
+#require 'rdoc/markup/simple_markup/to_flow.rb'
+
+module SM
+
+ ##
+ # A Fragment is a chunk of text, subclassed as a paragraph, a list
+ # entry, or verbatim text
+
+ class Fragment
+ attr_reader :level, :param, :txt
+ attr_accessor :type
+
+ def initialize(level, param, type, txt)
+ @level = level
+ @param = param
+ @type = type
+ @txt = ""
+ add_text(txt) if txt
+ end
+
+ def add_text(txt)
+ @txt << " " if @txt.length > 0
+ @txt << txt.tr_s("\n ", " ").strip
+ end
+
+ def to_s
+ "L#@level: #{self.class.name.split('::')[-1]}\n#@txt"
+ end
+
+ ######
+ # This is a simple factory system that lets us associate fragement
+ # types (a string) with a subclass of fragment
+
+ TYPE_MAP = {}
+
+ def Fragment.type_name(name)
+ TYPE_MAP[name] = self
+ end
+
+ def Fragment.for(line)
+ klass = TYPE_MAP[line.type] ||
+ raise("Unknown line type: '#{line.type.inspect}:' '#{line.text}'")
+ return klass.new(line.level, line.param, line.flag, line.text)
+ end
+ end
+
+ ##
+ # A paragraph is a fragment which gets wrapped to fit. We remove all
+ # newlines when we're created, and have them put back on output
+
+ class Paragraph < Fragment
+ type_name Line::PARAGRAPH
+ end
+
+ class BlankLine < Paragraph
+ type_name Line::BLANK
+ end
+
+ class Heading < Paragraph
+ type_name Line::HEADING
+
+ def head_level
+ @param.to_i
+ end
+ end
+
+ ##
+ # A List is a fragment with some kind of label
+ #
+
+ class ListBase < Paragraph
+ # List types
+ BULLET = :BULLET
+ NUMBER = :NUMBER
+ UPPERALPHA = :UPPERALPHA
+ LOWERALPHA = :LOWERALPHA
+ LABELED = :LABELED
+ NOTE = :NOTE
+ end
+
+ class ListItem < ListBase
+ type_name Line::LIST
+
+ # def label
+ # am = AttributeManager.new(@param)
+ # am.flow
+ # end
+ end
+
+ class ListStart < ListBase
+ def initialize(level, param, type)
+ super(level, param, type, nil)
+ end
+ end
+
+ class ListEnd < ListBase
+ def initialize(level, type)
+ super(level, "", type, nil)
+ end
+ end
+
+ ##
+ # Verbatim code contains lines that don't get wrapped.
+
+ class Verbatim < Fragment
+ type_name Line::VERBATIM
+
+ def add_text(txt)
+ @txt << txt.chomp << "\n"
+ end
+
+ end
+
+ ##
+ # A horizontal rule
+ class Rule < Fragment
+ type_name Line::RULE
+ end
+
+
+ # Collect groups of lines together. Each group
+ # will end up containing a flow of text
+
+ class LineCollection
+
+ def initialize
+ @fragments = []
+ end
+
+ def add(fragment)
+ @fragments << fragment
+ end
+
+ def each(&b)
+ @fragments.each(&b)
+ end
+
+ # For testing
+ def to_a
+ @fragments.map {|fragment| fragment.to_s}
+ end
+
+ # Factory for different fragment types
+ def fragment_for(*args)
+ Fragment.for(*args)
+ end
+
+ # tidy up at the end
+ def normalize
+ change_verbatim_blank_lines
+ add_list_start_and_ends
+ add_list_breaks
+ tidy_blank_lines
+ end
+
+ def to_s
+ @fragments.join("\n----\n")
+ end
+
+ def accept(am, visitor)
+
+ visitor.start_accepting
+
+ @fragments.each do |fragment|
+ case fragment
+ when Verbatim
+ visitor.accept_verbatim(am, fragment)
+ when Rule
+ visitor.accept_rule(am, fragment)
+ when ListStart
+ visitor.accept_list_start(am, fragment)
+ when ListEnd
+ visitor.accept_list_end(am, fragment)
+ when ListItem
+ visitor.accept_list_item(am, fragment)
+ when BlankLine
+ visitor.accept_blank_line(am, fragment)
+ when Heading
+ visitor.accept_heading(am, fragment)
+ when Paragraph
+ visitor.accept_paragraph(am, fragment)
+ end
+ end
+
+ visitor.end_accepting
+ end
+ #######
+ private
+ #######
+
+ # If you have:
+ #
+ # normal paragraph text.
+ #
+ # this is code
+ #
+ # and more code
+ #
+ # You'll end up with the fragments Paragraph, BlankLine,
+ # Verbatim, BlankLine, Verbatim, BlankLine, etc
+ #
+ # The BlankLine in the middle of the verbatim chunk needs to
+ # be changed to a real verbatim newline, and the two
+ # verbatim blocks merged
+ #
+ #
+ def change_verbatim_blank_lines
+ frag_block = nil
+ blank_count = 0
+ @fragments.each_with_index do |frag, i|
+ if frag_block.nil?
+ frag_block = frag if Verbatim === frag
+ else
+ case frag
+ when Verbatim
+ blank_count.times { frag_block.add_text("\n") }
+ blank_count = 0
+ frag_block.add_text(frag.txt)
+ @fragments[i] = nil # remove out current fragment
+ when BlankLine
+ if frag_block
+ blank_count += 1
+ @fragments[i] = nil
+ end
+ else
+ frag_block = nil
+ blank_count = 0
+ end
+ end
+ end
+ @fragments.compact!
+ end
+
+ # List nesting is implicit given the level of
+ # Make it explicit, just to make life a tad
+ # easier for the output processors
+
+ def add_list_start_and_ends
+ level = 0
+ res = []
+ type_stack = []
+
+ @fragments.each do |fragment|
+ # $stderr.puts "#{level} : #{fragment.class.name} : #{fragment.level}"
+ new_level = fragment.level
+ while (level < new_level)
+ level += 1
+ type = fragment.type
+ res << ListStart.new(level, fragment.param, type) if type
+ type_stack.push type
+ # $stderr.puts "Start: #{level}"
+ end
+
+ while level > new_level
+ type = type_stack.pop
+ res << ListEnd.new(level, type) if type
+ level -= 1
+ # $stderr.puts "End: #{level}, #{type}"
+ end
+
+ res << fragment
+ level = fragment.level
+ end
+ level.downto(1) do |i|
+ type = type_stack.pop
+ res << ListEnd.new(i, type) if type
+ end
+
+ @fragments = res
+ end
+
+ # now insert start/ends between list entries at the
+ # same level that have different element types
+
+ def add_list_breaks
+ res = @fragments
+
+ @fragments = []
+ list_stack = []
+
+ res.each do |fragment|
+ case fragment
+ when ListStart
+ list_stack.push fragment
+ when ListEnd
+ start = list_stack.pop
+ fragment.type = start.type
+ when ListItem
+ l = list_stack.last
+ if fragment.type != l.type
+ @fragments << ListEnd.new(l.level, l.type)
+ start = ListStart.new(l.level, fragment.param, fragment.type)
+ @fragments << start
+ list_stack.pop
+ list_stack.push start
+ end
+ else
+ ;
+ end
+ @fragments << fragment
+ end
+ end
+
+ # Finally tidy up the blank lines:
+ # * change Blank/ListEnd into ListEnd/Blank
+ # * remove blank lines at the front
+
+ def tidy_blank_lines
+ (@fragments.size - 1).times do |i|
+ if @fragments[i].kind_of?(BlankLine) and
+ @fragments[i+1].kind_of?(ListEnd)
+ @fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i]
+ end
+ end
+
+ # remove leading blanks
+ @fragments.each_with_index do |f, i|
+ break unless f.kind_of? BlankLine
+ @fragments[i] = nil
+ end
+
+ @fragments.compact!
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/inline.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/inline.rb
new file mode 100644
index 0000000000..d54fe1e667
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/inline.rb
@@ -0,0 +1,340 @@
+module SM
+
+ # We manage a set of attributes. Each attribute has a symbol name
+ # and a bit value
+
+ class Attribute
+ SPECIAL = 1
+
+ @@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
+ @@next_bitmap = 2
+
+ def Attribute.bitmap_for(name)
+ bitmap = @@name_to_bitmap[name]
+ if !bitmap
+ bitmap = @@next_bitmap
+ @@next_bitmap <<= 1
+ @@name_to_bitmap[name] = bitmap
+ end
+ bitmap
+ end
+
+ def Attribute.as_string(bitmap)
+ return "none" if bitmap.zero?
+ res = []
+ @@name_to_bitmap.each do |name, bit|
+ res << name if (bitmap & bit) != 0
+ end
+ res.join(",")
+ end
+
+ def Attribute.each_name_of(bitmap)
+ @@name_to_bitmap.each do |name, bit|
+ next if bit == SPECIAL
+ yield name.to_s if (bitmap & bit) != 0
+ end
+ end
+ end
+
+
+ # An AttrChanger records a change in attributes. It contains
+ # a bitmap of the attributes to turn on, and a bitmap of those to
+ # turn off
+
+ AttrChanger = Struct.new(:turn_on, :turn_off)
+ class AttrChanger
+ def to_s
+ "Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}"
+ end
+ end
+
+ # An array of attributes which parallels the characters in a string
+ class AttrSpan
+ def initialize(length)
+ @attrs = Array.new(length, 0)
+ end
+
+ def set_attrs(start, length, bits)
+ for i in start ... (start+length)
+ @attrs[i] |= bits
+ end
+ end
+
+ def [](n)
+ @attrs[n]
+ end
+ end
+
+ ##
+ # Hold details of a special sequence
+
+ class Special
+ attr_reader :type
+ attr_accessor :text
+
+ def initialize(type, text)
+ @type, @text = type, text
+ end
+
+ def ==(o)
+ self.text == o.text && self.type == o.type
+ end
+
+ def to_s
+ "Special: type=#{type}, text=#{text.dump}"
+ end
+ end
+
+ class AttributeManager
+
+ NULL = "\000".freeze
+
+ ##
+ # We work by substituting non-printing characters in to the
+ # text. For now I'm assuming that I can substitute
+ # a character in the range 0..8 for a 7 bit character
+ # without damaging the encoded string, but this might
+ # be optimistic
+ #
+
+ A_PROTECT = 004
+ PROTECT_ATTR = A_PROTECT.chr
+
+ # This maps delimiters that occur around words (such as
+ # *bold* or +tt+) where the start and end delimiters
+ # and the same. This lets us optimize the regexp
+ MATCHING_WORD_PAIRS = {}
+
+ # And this is used when the delimiters aren't the same. In this
+ # case the hash maps a pattern to the attribute character
+ WORD_PAIR_MAP = {}
+
+ # This maps HTML tags to the corresponding attribute char
+ HTML_TAGS = {}
+
+ # And this maps _special_ sequences to a name. A special sequence
+ # is something like a WikiWord
+ SPECIAL = {}
+
+ # Return an attribute object with the given turn_on
+ # and turn_off bits set
+
+ def attribute(turn_on, turn_off)
+ AttrChanger.new(turn_on, turn_off)
+ end
+
+
+ def change_attribute(current, new)
+ diff = current ^ new
+ attribute(new & diff, current & diff)
+ end
+
+ def changed_attribute_by_name(current_set, new_set)
+ current = new = 0
+ current_set.each {|name| current |= Attribute.bitmap_for(name) }
+ new_set.each {|name| new |= Attribute.bitmap_for(name) }
+ change_attribute(current, new)
+ end
+
+ def copy_string(start_pos, end_pos)
+ res = @str[start_pos...end_pos]
+ res.gsub!(/\000/, '')
+ res
+ end
+
+ # Map attributes like <b>text</b>to the sequence \001\002<char>\001\003<char>,
+ # where <char> is a per-attribute specific character
+
+ def convert_attrs(str, attrs)
+ # first do matching ones
+ tags = MATCHING_WORD_PAIRS.keys.join("")
+ re = "(^|\\W)([#{tags}])([A-Za-z_]+?)\\2(\\W|\$)"
+# re = "(^|\\W)([#{tags}])(\\S+?)\\2(\\W|\$)"
+ 1 while str.gsub!(Regexp.new(re)) {
+ attr = MATCHING_WORD_PAIRS[$2];
+ attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
+ $1 + NULL*$2.length + $3 + NULL*$2.length + $4
+ }
+
+ # then non-matching
+ unless WORD_PAIR_MAP.empty?
+ WORD_PAIR_MAP.each do |regexp, attr|
+ str.gsub!(regexp) {
+ attrs.set_attrs($`.length + $1.length, $2.length, attr)
+ NULL*$1.length + $2 + NULL*$3.length
+ }
+ end
+ end
+ end
+
+ def convert_html(str, attrs)
+ tags = HTML_TAGS.keys.join("|")
+ re = "<(#{tags})>(.*?)</\\1>"
+ 1 while str.gsub!(Regexp.new(re, Regexp::IGNORECASE)) {
+ attr = HTML_TAGS[$1.downcase]
+ html_length = $1.length + 2
+ seq = NULL * html_length
+ attrs.set_attrs($`.length + html_length, $2.length, attr)
+ seq + $2 + seq + NULL
+ }
+ end
+
+ def convert_specials(str, attrs)
+ unless SPECIAL.empty?
+ SPECIAL.each do |regexp, attr|
+ str.scan(regexp) do
+ attrs.set_attrs($`.length, $&.length, attr | Attribute::SPECIAL)
+ end
+ end
+ end
+ end
+
+ # A \ in front of a character that would normally be
+ # processed turns off processing. We do this by turning
+ # \< into <#{PROTECT}
+
+ PROTECTABLE = [ "<" << "\\" ] #"
+
+
+ def mask_protected_sequences
+ protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])")
+ @str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}")
+ end
+
+ def unmask_protected_sequences
+ @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000")
+ end
+
+ def initialize
+ add_word_pair("*", "*", :BOLD)
+ add_word_pair("_", "_", :EM)
+ add_word_pair("+", "+", :TT)
+
+ add_html("em", :EM)
+ add_html("i", :EM)
+ add_html("b", :BOLD)
+ add_html("tt", :TT)
+ add_html("code", :TT)
+
+ add_special(/<!--(.*?)-->/, :COMMENT)
+ end
+
+ def add_word_pair(start, stop, name)
+ raise "Word flags may not start '<'" if start[0] == ?<
+ bitmap = Attribute.bitmap_for(name)
+ if start == stop
+ MATCHING_WORD_PAIRS[start] = bitmap
+ else
+ pattern = Regexp.new("(" + Regexp.escape(start) + ")" +
+# "([A-Za-z]+)" +
+ "(\\S+)" +
+ "(" + Regexp.escape(stop) +")")
+ WORD_PAIR_MAP[pattern] = bitmap
+ end
+ PROTECTABLE << start[0,1]
+ PROTECTABLE.uniq!
+ end
+
+ def add_html(tag, name)
+ HTML_TAGS[tag.downcase] = Attribute.bitmap_for(name)
+ end
+
+ def add_special(pattern, name)
+ SPECIAL[pattern] = Attribute.bitmap_for(name)
+ end
+
+ def flow(str)
+ @str = str
+
+ puts("Before flow, str='#{@str.dump}'") if $DEBUG
+ mask_protected_sequences
+
+ @attrs = AttrSpan.new(@str.length)
+
+ puts("After protecting, str='#{@str.dump}'") if $DEBUG
+ convert_attrs(@str, @attrs)
+ convert_html(@str, @attrs)
+ convert_specials(str, @attrs)
+ unmask_protected_sequences
+ puts("After flow, str='#{@str.dump}'") if $DEBUG
+ return split_into_flow
+ end
+
+ def display_attributes
+ puts
+ puts @str.tr(NULL, "!")
+ bit = 1
+ 16.times do |bno|
+ line = ""
+ @str.length.times do |i|
+ if (@attrs[i] & bit) == 0
+ line << " "
+ else
+ if bno.zero?
+ line << "S"
+ else
+ line << ("%d" % (bno+1))
+ end
+ end
+ end
+ puts(line) unless line =~ /^ *$/
+ bit <<= 1
+ end
+ end
+
+ def split_into_flow
+
+ display_attributes if $DEBUG
+
+ res = []
+ current_attr = 0
+ str = ""
+
+
+ str_len = @str.length
+
+ # skip leading invisible text
+ i = 0
+ i += 1 while i < str_len and @str[i].zero?
+ start_pos = i
+
+ # then scan the string, chunking it on attribute changes
+ while i < str_len
+ new_attr = @attrs[i]
+ if new_attr != current_attr
+ if i > start_pos
+ res << copy_string(start_pos, i)
+ start_pos = i
+ end
+
+ res << change_attribute(current_attr, new_attr)
+ current_attr = new_attr
+
+ if (current_attr & Attribute::SPECIAL) != 0
+ i += 1 while i < str_len and (@attrs[i] & Attribute::SPECIAL) != 0
+ res << Special.new(current_attr, copy_string(start_pos, i))
+ start_pos = i
+ next
+ end
+ end
+
+ # move on, skipping any invisible characters
+ begin
+ i += 1
+ end while i < str_len and @str[i].zero?
+ end
+
+ # tidy up trailing text
+ if start_pos < str_len
+ res << copy_string(start_pos, str_len)
+ end
+
+ # and reset to all attributes off
+ res << change_attribute(current_attr, 0) if current_attr != 0
+
+ return res
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/lines.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/lines.rb
new file mode 100644
index 0000000000..4e294f27dc
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/lines.rb
@@ -0,0 +1,151 @@
+##########################################################################
+#
+# We store the lines we're working on as objects of class Line.
+# These contain the text of the line, along with a flag indicating the
+# line type, and an indentation level
+
+module SM
+
+ class Line
+ INFINITY = 9999
+
+ BLANK = :BLANK
+ HEADING = :HEADING
+ LIST = :LIST
+ RULE = :RULE
+ PARAGRAPH = :PARAGRAPH
+ VERBATIM = :VERBATIM
+
+ # line type
+ attr_accessor :type
+
+ # The indentation nesting level
+ attr_accessor :level
+
+ # The contents
+ attr_accessor :text
+
+ # A prefix or parameter. For LIST lines, this is
+ # the text that introduced the list item (the label)
+ attr_accessor :param
+
+ # A flag. For list lines, this is the type of the list
+ attr_accessor :flag
+
+ # the number of leading spaces
+ attr_accessor :leading_spaces
+
+ # true if this line has been deleted from the list of lines
+ attr_accessor :deleted
+
+
+ def initialize(text)
+ @text = text.dup
+ @deleted = false
+
+ # expand tabs
+ 1 while @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)} && $~ #`
+
+ # Strip trailing whitespace
+ @text.sub!(/\s+$/, '')
+
+ # and look for leading whitespace
+ if @text.length > 0
+ @text =~ /^(\s*)/
+ @leading_spaces = $1.length
+ else
+ @leading_spaces = INFINITY
+ end
+ end
+
+ # Return true if this line is blank
+ def isBlank?
+ @text.length.zero?
+ end
+
+ # stamp a line with a type, a level, a prefix, and a flag
+ def stamp(type, level, param="", flag=nil)
+ @type, @level, @param, @flag = type, level, param, flag
+ end
+
+ ##
+ # Strip off the leading margin
+ #
+
+ def strip_leading(size)
+ if @text.size > size
+ @text[0,size] = ""
+ else
+ @text = ""
+ end
+ end
+
+ def to_s
+ "#@type#@level: #@text"
+ end
+ end
+
+ ###############################################################################
+ #
+ # A container for all the lines
+ #
+
+ class Lines
+ include Enumerable
+
+ attr_reader :lines # for debugging
+
+ def initialize(lines)
+ @lines = lines
+ rewind
+ end
+
+ def empty?
+ @lines.size.zero?
+ end
+
+ def each
+ @lines.each do |line|
+ yield line unless line.deleted
+ end
+ end
+
+# def [](index)
+# @lines[index]
+# end
+
+ def rewind
+ @nextline = 0
+ end
+
+ def next
+ begin
+ res = @lines[@nextline]
+ @nextline += 1 if @nextline < @lines.size
+ end while res and res.deleted and @nextline < @lines.size
+ res
+ end
+
+ def unget
+ @nextline -= 1
+ end
+
+ def delete(a_line)
+ a_line.deleted = true
+ end
+
+ def normalize
+ margin = @lines.collect{|l| l.leading_spaces}.min
+ margin = 0 if margin == Line::INFINITY
+ @lines.each {|line| line.strip_leading(margin) } if margin > 0
+ end
+
+ def as_text
+ @lines.map {|l| l.text}.join("\n")
+ end
+
+ def line_types
+ @lines.map {|l| l.type }
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/preprocess.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/preprocess.rb
new file mode 100644
index 0000000000..101c9bdeb1
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/preprocess.rb
@@ -0,0 +1,73 @@
+module SM
+
+ ##
+ # Handle common directives that can occur in a block of text:
+ #
+ # : include : filename
+ #
+
+ class PreProcess
+
+ def initialize(input_file_name, include_path)
+ @input_file_name = input_file_name
+ @include_path = include_path
+ end
+
+ # Look for common options in a chunk of text. Options that
+ # we don't handle are passed back to our caller
+ # as |directive, param|
+
+ def handle(text)
+ text.gsub!(/^([ \t#]*):(\w+):\s*(.+)?\n/) do
+ prefix = $1
+ directive = $2.downcase
+ param = $3
+
+ case directive
+ when "include"
+ filename = param.split[0]
+ include_file(filename, prefix)
+
+ else
+ yield(directive, param)
+ end
+ end
+ end
+
+ #######
+ private
+ #######
+
+ # Include a file, indenting it correctly
+
+ def include_file(name, indent)
+ if (full_name = find_include_file(name))
+ content = File.open(full_name) {|f| f.read}
+ # strip leading '#'s, but only if all lines start with them
+ if content =~ /^[^#]/
+ content.gsub(/^/, indent)
+ else
+ content.gsub(/^#?/, indent)
+ end
+ else
+ $stderr.puts "Couldn't find file to include: '#{name}'"
+ ''
+ end
+ end
+
+ # Look for the given file in the directory containing the current
+ # file, and then in each of the directories specified in the
+ # RDOC_INCLUDE path
+
+ def find_include_file(name)
+ to_search = [ File.dirname(@input_file_name) ].concat @include_path
+ to_search.each do |dir|
+ full_name = File.join(dir, name)
+ stat = File.stat(full_name) rescue next
+ return full_name if stat.readable?
+ end
+ nil
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_flow.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_flow.rb
new file mode 100644
index 0000000000..048e71abce
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_flow.rb
@@ -0,0 +1,188 @@
+require 'rdoc/markup/simple_markup/fragments'
+require 'rdoc/markup/simple_markup/inline'
+require 'cgi'
+
+module SM
+
+ module Flow
+ P = Struct.new(:body)
+ VERB = Struct.new(:body)
+ RULE = Struct.new(:width)
+ class LIST
+ attr_reader :type, :contents
+ def initialize(type)
+ @type = type
+ @contents = []
+ end
+ def <<(stuff)
+ @contents << stuff
+ end
+ end
+ LI = Struct.new(:label, :body)
+ H = Struct.new(:level, :text)
+ end
+
+ class ToFlow
+ LIST_TYPE_TO_HTML = {
+ SM::ListBase::BULLET => [ "<ul>", "</ul>" ],
+ SM::ListBase::NUMBER => [ "<ol>", "</ol>" ],
+ SM::ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
+ SM::ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
+ SM::ListBase::LABELED => [ "<dl>", "</dl>" ],
+ SM::ListBase::NOTE => [ "<table>", "</table>" ],
+ }
+
+ InlineTag = Struct.new(:bit, :on, :off)
+
+ def initialize
+ init_tags
+ end
+
+ ##
+ # Set up the standard mapping of attributes to HTML tags
+ #
+ def init_tags
+ @attr_tags = [
+ InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
+ InlineTag.new(SM::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
+ InlineTag.new(SM::Attribute.bitmap_for(:EM), "<em>", "</em>"),
+ ]
+ end
+
+ ##
+ # Add a new set of HTML tags for an attribute. We allow
+ # separate start and end tags for flexibility
+ #
+ def add_tag(name, start, stop)
+ @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
+ end
+
+ ##
+ # Given an HTML tag, decorate it with class information
+ # and the like if required. This is a no-op in the base
+ # class, but is overridden in HTML output classes that
+ # implement style sheets
+
+ def annotate(tag)
+ tag
+ end
+
+ ##
+ # Here's the client side of the visitor pattern
+
+ def start_accepting
+ @res = []
+ @list_stack = []
+ end
+
+ def end_accepting
+ @res
+ end
+
+ def accept_paragraph(am, fragment)
+ @res << Flow::P.new((convert_flow(am.flow(fragment.txt))))
+ end
+
+ def accept_verbatim(am, fragment)
+ @res << Flow::VERB.new((convert_flow(am.flow(fragment.txt))))
+ end
+
+ def accept_rule(am, fragment)
+ size = fragment.param
+ size = 10 if size > 10
+ @res << Flow::RULE.new(size)
+ end
+
+ def accept_list_start(am, fragment)
+ @list_stack.push(@res)
+ list = Flow::LIST.new(fragment.type)
+ @res << list
+ @res = list
+ end
+
+ def accept_list_end(am, fragment)
+ @res = @list_stack.pop
+ end
+
+ def accept_list_item(am, fragment)
+ @res << Flow::LI.new(fragment.param, convert_flow(am.flow(fragment.txt)))
+ end
+
+ def accept_blank_line(am, fragment)
+ # @res << annotate("<p />") << "\n"
+ end
+
+ def accept_heading(am, fragment)
+ @res << Flow::H.new(fragment.head_level, convert_flow(am.flow(fragment.txt)))
+ end
+
+
+ #######################################################################
+
+ private
+
+ #######################################################################
+
+ def on_tags(res, item)
+ attr_mask = item.turn_on
+ return if attr_mask.zero?
+
+ @attr_tags.each do |tag|
+ if attr_mask & tag.bit != 0
+ res << annotate(tag.on)
+ end
+ end
+ end
+
+ def off_tags(res, item)
+ attr_mask = item.turn_off
+ return if attr_mask.zero?
+
+ @attr_tags.reverse_each do |tag|
+ if attr_mask & tag.bit != 0
+ res << annotate(tag.off)
+ end
+ end
+ end
+
+ def convert_flow(flow)
+ res = ""
+ flow.each do |item|
+ case item
+ when String
+ res << convert_string(item)
+ when AttrChanger
+ off_tags(res, item)
+ on_tags(res, item)
+ when Special
+ res << convert_special(item)
+ else
+ raise "Unknown flow element: #{item.inspect}"
+ end
+ end
+ res
+ end
+
+ # some of these patterns are taken from SmartyPants...
+
+ def convert_string(item)
+ CGI.escapeHTML(item)
+ end
+
+ def convert_special(special)
+ handled = false
+ Attribute.each_name_of(special.type) do |name|
+ method_name = "handle_special_#{name}"
+ if self.respond_to? method_name
+ special.text = send(method_name, special)
+ handled = true
+ end
+ end
+ raise "Unhandled special: #{special}" unless handled
+ special.text
+ end
+
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_html.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_html.rb
new file mode 100644
index 0000000000..26b5f4ce70
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_html.rb
@@ -0,0 +1,289 @@
+require 'rdoc/markup/simple_markup/fragments'
+require 'rdoc/markup/simple_markup/inline'
+
+require 'cgi'
+
+module SM
+
+ class ToHtml
+
+ LIST_TYPE_TO_HTML = {
+ ListBase::BULLET => [ "<ul>", "</ul>" ],
+ ListBase::NUMBER => [ "<ol>", "</ol>" ],
+ ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
+ ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
+ ListBase::LABELED => [ "<dl>", "</dl>" ],
+ ListBase::NOTE => [ "<table>", "</table>" ],
+ }
+
+ InlineTag = Struct.new(:bit, :on, :off)
+
+ def initialize
+ init_tags
+ end
+
+ ##
+ # Set up the standard mapping of attributes to HTML tags
+ #
+ def init_tags
+ @attr_tags = [
+ InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
+ InlineTag.new(SM::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
+ InlineTag.new(SM::Attribute.bitmap_for(:EM), "<em>", "</em>"),
+ ]
+ end
+
+ ##
+ # Add a new set of HTML tags for an attribute. We allow
+ # separate start and end tags for flexibility
+ #
+ def add_tag(name, start, stop)
+ @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
+ end
+
+ ##
+ # Given an HTML tag, decorate it with class information
+ # and the like if required. This is a no-op in the base
+ # class, but is overridden in HTML output classes that
+ # implement style sheets
+
+ def annotate(tag)
+ tag
+ end
+
+ ##
+ # Here's the client side of the visitor pattern
+
+ def start_accepting
+ @res = ""
+ @in_list_entry = []
+ end
+
+ def end_accepting
+ @res
+ end
+
+ def accept_paragraph(am, fragment)
+ @res << annotate("<p>") + "\n"
+ @res << wrap(convert_flow(am.flow(fragment.txt)))
+ @res << annotate("</p>") + "\n"
+ end
+
+ def accept_verbatim(am, fragment)
+ @res << annotate("<pre>") + "\n"
+ @res << CGI.escapeHTML(fragment.txt)
+ @res << annotate("</pre>") << "\n"
+ end
+
+ def accept_rule(am, fragment)
+ size = fragment.param
+ size = 10 if size > 10
+ @res << "<hr size=\"#{size}\"></hr>"
+ end
+
+ def accept_list_start(am, fragment)
+ @res << html_list_name(fragment.type, true) <<"\n"
+ @in_list_entry.push false
+ end
+
+ def accept_list_end(am, fragment)
+ if tag = @in_list_entry.pop
+ @res << annotate(tag) << "\n"
+ end
+ @res << html_list_name(fragment.type, false) <<"\n"
+ end
+
+ def accept_list_item(am, fragment)
+ if tag = @in_list_entry.last
+ @res << annotate(tag) << "\n"
+ end
+ @res << list_item_start(am, fragment)
+ @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
+ @in_list_entry[-1] = list_end_for(fragment.type)
+ end
+
+ def accept_blank_line(am, fragment)
+ # @res << annotate("<p />") << "\n"
+ end
+
+ def accept_heading(am, fragment)
+ @res << convert_heading(fragment.head_level, am.flow(fragment.txt))
+ end
+
+ # This is a higher speed (if messier) version of wrap
+
+ def wrap(txt, line_len = 76)
+ res = ""
+ sp = 0
+ ep = txt.length
+ while sp < ep
+ # scan back for a space
+ p = sp + line_len - 1
+ if p >= ep
+ p = ep
+ else
+ while p > sp and txt[p] != ?\s
+ p -= 1
+ end
+ if p <= sp
+ p = sp + line_len
+ while p < ep and txt[p] != ?\s
+ p += 1
+ end
+ end
+ end
+ res << txt[sp...p] << "\n"
+ sp = p
+ sp += 1 while sp < ep and txt[sp] == ?\s
+ end
+ res
+ end
+
+ #######################################################################
+
+ private
+
+ #######################################################################
+
+ def on_tags(res, item)
+ attr_mask = item.turn_on
+ return if attr_mask.zero?
+
+ @attr_tags.each do |tag|
+ if attr_mask & tag.bit != 0
+ res << annotate(tag.on)
+ end
+ end
+ end
+
+ def off_tags(res, item)
+ attr_mask = item.turn_off
+ return if attr_mask.zero?
+
+ @attr_tags.reverse_each do |tag|
+ if attr_mask & tag.bit != 0
+ res << annotate(tag.off)
+ end
+ end
+ end
+
+ def convert_flow(flow)
+ res = ""
+ flow.each do |item|
+ case item
+ when String
+ res << convert_string(item)
+ when AttrChanger
+ off_tags(res, item)
+ on_tags(res, item)
+ when Special
+ res << convert_special(item)
+ else
+ raise "Unknown flow element: #{item.inspect}"
+ end
+ end
+ res
+ end
+
+ # some of these patterns are taken from SmartyPants...
+
+ def convert_string(item)
+ CGI.escapeHTML(item).
+
+
+ # convert -- to em-dash, (-- to en-dash)
+ gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
+
+ # convert ... to elipsis (and make sure .... becomes .<elipsis>)
+ gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
+
+ # convert single closing quote
+ gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1&#8217;" }.
+ gsub(%r{\'(?=\W|s\b)}) { "&#8217;" }.
+
+ # convert single opening quote
+ gsub(/'/, '&#8216;').
+
+ # convert double closing quote
+ gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}) { "#$1&#8221;" }.
+
+ # convert double opening quote
+ gsub(/'/, '&#8220;').
+
+ # convert copyright
+ gsub(/\(c\)/, '&#169;').
+
+ # convert and registered trademark
+ gsub(/\(r\)/, '&#174;')
+
+ end
+
+ def convert_special(special)
+ handled = false
+ Attribute.each_name_of(special.type) do |name|
+ method_name = "handle_special_#{name}"
+ if self.respond_to? method_name
+ special.text = send(method_name, special)
+ handled = true
+ end
+ end
+ raise "Unhandled special: #{special}" unless handled
+ special.text
+ end
+
+ def convert_heading(level, flow)
+ res =
+ annotate("<h#{level}>") +
+ convert_flow(flow) +
+ annotate("</h#{level}>\n")
+ end
+
+ def html_list_name(list_type, is_open_tag)
+ tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}")
+ annotate(tags[ is_open_tag ? 0 : 1])
+ end
+
+ def list_item_start(am, fragment)
+ case fragment.type
+ when ListBase::BULLET, ListBase::NUMBER
+ annotate("<li>")
+
+ when ListBase::UPPERALPHA
+ annotate("<li type=\"A\">")
+
+ when ListBase::LOWERALPHA
+ annotate("<li type=\"a\">")
+
+ when ListBase::LABELED
+ annotate("<dt>") +
+ convert_flow(am.flow(fragment.param)) +
+ annotate("</dt>") +
+ annotate("<dd>")
+
+ when ListBase::NOTE
+ annotate("<tr>") +
+ annotate("<td valign=\"top\">") +
+ convert_flow(am.flow(fragment.param)) +
+ annotate("</td>") +
+ annotate("<td>")
+ else
+ raise "Invalid list type"
+ end
+ end
+
+ def list_end_for(fragment_type)
+ case fragment_type
+ when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA
+ "</li>"
+ when ListBase::LABELED
+ "</dd>"
+ when ListBase::NOTE
+ "</td></tr>"
+ else
+ raise "Invalid list type"
+ end
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_latex.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_latex.rb
new file mode 100644
index 0000000000..6c16278652
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_latex.rb
@@ -0,0 +1,333 @@
+require 'rdoc/markup/simple_markup/fragments'
+require 'rdoc/markup/simple_markup/inline'
+
+require 'cgi'
+
+module SM
+
+ # Convert SimpleMarkup to basic LaTeX report format
+
+ class ToLaTeX
+
+ BS = "\020" # \
+ OB = "\021" # {
+ CB = "\022" # }
+ DL = "\023" # Dollar
+
+ BACKSLASH = "#{BS}symbol#{OB}92#{CB}"
+ HAT = "#{BS}symbol#{OB}94#{CB}"
+ BACKQUOTE = "#{BS}symbol#{OB}0#{CB}"
+ TILDE = "#{DL}#{BS}sim#{DL}"
+ LESSTHAN = "#{DL}<#{DL}"
+ GREATERTHAN = "#{DL}>#{DL}"
+
+ def self.l(str)
+ str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL)
+ end
+
+ def l(arg)
+ SM::ToLaTeX.l(arg)
+ end
+
+ LIST_TYPE_TO_LATEX = {
+ ListBase::BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ],
+ ListBase::NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ],
+ ListBase::UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ],
+ ListBase::LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ],
+ ListBase::LABELED => [ l("\\begin{description}"), l("\\end{description}") ],
+ ListBase::NOTE => [
+ l("\\begin{tabularx}{\\linewidth}{@{} l X @{}}"),
+ l("\\end{tabularx}") ],
+ }
+
+ InlineTag = Struct.new(:bit, :on, :off)
+
+ def initialize
+ init_tags
+ @list_depth = 0
+ @prev_list_types = []
+ end
+
+ ##
+ # Set up the standard mapping of attributes to LaTeX
+ #
+ def init_tags
+ @attr_tags = [
+ InlineTag.new(SM::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")),
+ InlineTag.new(SM::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")),
+ InlineTag.new(SM::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")),
+ ]
+ end
+
+ ##
+ # Escape a LaTeX string
+ def escape(str)
+# $stderr.print "FE: ", str
+ s = str.
+# sub(/\s+$/, '').
+ gsub(/([_\${}&%#])/, "#{BS}\\1").
+ gsub(/\\/, BACKSLASH).
+ gsub(/\^/, HAT).
+ gsub(/~/, TILDE).
+ gsub(/</, LESSTHAN).
+ gsub(/>/, GREATERTHAN).
+ gsub(/,,/, ",{},").
+ gsub(/\`/, BACKQUOTE)
+# $stderr.print "-> ", s, "\n"
+ s
+ end
+
+ ##
+ # Add a new set of LaTeX tags for an attribute. We allow
+ # separate start and end tags for flexibility
+ #
+ def add_tag(name, start, stop)
+ @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
+ end
+
+
+ ##
+ # Here's the client side of the visitor pattern
+
+ def start_accepting
+ @res = ""
+ @in_list_entry = []
+ end
+
+ def end_accepting
+ @res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$')
+ end
+
+ def accept_paragraph(am, fragment)
+ @res << wrap(convert_flow(am.flow(fragment.txt)))
+ @res << "\n"
+ end
+
+ def accept_verbatim(am, fragment)
+ @res << "\n\\begin{code}\n"
+ @res << fragment.txt.sub(/[\n\s]+\Z/, '')
+ @res << "\n\\end{code}\n\n"
+ end
+
+ def accept_rule(am, fragment)
+ size = fragment.param
+ size = 10 if size > 10
+ @res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n"
+ end
+
+ def accept_list_start(am, fragment)
+ @res << list_name(fragment.type, true) <<"\n"
+ @in_list_entry.push false
+ end
+
+ def accept_list_end(am, fragment)
+ if tag = @in_list_entry.pop
+ @res << tag << "\n"
+ end
+ @res << list_name(fragment.type, false) <<"\n"
+ end
+
+ def accept_list_item(am, fragment)
+ if tag = @in_list_entry.last
+ @res << tag << "\n"
+ end
+ @res << list_item_start(am, fragment)
+ @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
+ @in_list_entry[-1] = list_end_for(fragment.type)
+ end
+
+ def accept_blank_line(am, fragment)
+ # @res << "\n"
+ end
+
+ def accept_heading(am, fragment)
+ @res << convert_heading(fragment.head_level, am.flow(fragment.txt))
+ end
+
+ # This is a higher speed (if messier) version of wrap
+
+ def wrap(txt, line_len = 76)
+ res = ""
+ sp = 0
+ ep = txt.length
+ while sp < ep
+ # scan back for a space
+ p = sp + line_len - 1
+ if p >= ep
+ p = ep
+ else
+ while p > sp and txt[p] != ?\s
+ p -= 1
+ end
+ if p <= sp
+ p = sp + line_len
+ while p < ep and txt[p] != ?\s
+ p += 1
+ end
+ end
+ end
+ res << txt[sp...p] << "\n"
+ sp = p
+ sp += 1 while sp < ep and txt[sp] == ?\s
+ end
+ res
+ end
+
+ #######################################################################
+
+ private
+
+ #######################################################################
+
+ def on_tags(res, item)
+ attr_mask = item.turn_on
+ return if attr_mask.zero?
+
+ @attr_tags.each do |tag|
+ if attr_mask & tag.bit != 0
+ res << tag.on
+ end
+ end
+ end
+
+ def off_tags(res, item)
+ attr_mask = item.turn_off
+ return if attr_mask.zero?
+
+ @attr_tags.reverse_each do |tag|
+ if attr_mask & tag.bit != 0
+ res << tag.off
+ end
+ end
+ end
+
+ def convert_flow(flow)
+ res = ""
+ flow.each do |item|
+ case item
+ when String
+# $stderr.puts "Converting '#{item}'"
+ res << convert_string(item)
+ when AttrChanger
+ off_tags(res, item)
+ on_tags(res, item)
+ when Special
+ res << convert_special(item)
+ else
+ raise "Unknown flow element: #{item.inspect}"
+ end
+ end
+ res
+ end
+
+ # some of these patterns are taken from SmartyPants...
+
+ def convert_string(item)
+
+ escape(item).
+
+
+ # convert ... to elipsis (and make sure .... becomes .<elipsis>)
+ gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}').
+
+ # convert single closing quote
+ gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1'" }.
+ gsub(%r{\'(?=\W|s\b)}) { "'" }.
+
+ # convert single opening quote
+ gsub(/'/, '`').
+
+ # convert double closing quote
+ gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}) { "#$1''" }.
+
+ # convert double opening quote
+ gsub(/"/, "``").
+
+ # convert copyright
+ gsub(/\(c\)/, '\copyright{}')
+
+ end
+
+ def convert_special(special)
+ handled = false
+ Attribute.each_name_of(special.type) do |name|
+ method_name = "handle_special_#{name}"
+ if self.respond_to? method_name
+ special.text = send(method_name, special)
+ handled = true
+ end
+ end
+ raise "Unhandled special: #{special}" unless handled
+ special.text
+ end
+
+ def convert_heading(level, flow)
+ res =
+ case level
+ when 1 then "\\chapter{"
+ when 2 then "\\section{"
+ when 3 then "\\subsection{"
+ when 4 then "\\subsubsection{"
+ else "\\paragraph{"
+ end +
+ convert_flow(flow) +
+ "}\n"
+ end
+
+ def list_name(list_type, is_open_tag)
+ tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}")
+ if tags[2] # enumerate
+ if is_open_tag
+ @list_depth += 1
+ if @prev_list_types[@list_depth] != tags[2]
+ case @list_depth
+ when 1
+ roman = "i"
+ when 2
+ roman = "ii"
+ when 3
+ roman = "iii"
+ when 4
+ roman = "iv"
+ else
+ raise("Too deep list: level #{@list_depth}")
+ end
+ @prev_list_types[@list_depth] = tags[2]
+ return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0]
+ end
+ else
+ @list_depth -= 1
+ end
+ end
+ tags[ is_open_tag ? 0 : 1]
+ end
+
+ def list_item_start(am, fragment)
+ case fragment.type
+ when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA
+ "\\item "
+
+ when ListBase::LABELED
+ "\\item[" + convert_flow(am.flow(fragment.param)) + "] "
+
+ when ListBase::NOTE
+ convert_flow(am.flow(fragment.param)) + " & "
+ else
+ raise "Invalid list type"
+ end
+ end
+
+ def list_end_for(fragment_type)
+ case fragment_type
+ when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA, ListBase::LABELED
+ ""
+ when ListBase::NOTE
+ "\\\\\n"
+ else
+ raise "Invalid list type"
+ end
+ end
+
+ end
+
+end