#!/usr/bin/ruby

###
### $Rev: 10 $
### $Release: 2.1.0 $
### copyright(c) 2006 kuwata-lab all rights reserved.
###

#--begin of require 'erubis/main'
###
### $Rev: 41 $
### $Release: 2.1.0 $
### copyright(c) 2006 kuwata-lab all rights reserved.
###

require 'yaml'
#--begin of require 'erubis'
##
## $Rev: 36 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

##
## an implementation of eRuby
##
## * class Eruby - normal eRuby class
## * class XmlEruby - eRuby class which escape '&<>"' into '&amp;&lt;&gt;&quot;'
## * module StdoutEnhancer - use $stdout instead of String as output
## * module PrintEnhancer - enable to write print statement in <% ... %>
## * class OptimizedEruby - optimized Eruby class faster than FastEruby
## * class OptimizedXmlEruby - optimized XmlEruby class faster than FastXmlEruby
##
## example:
##   input = <<'END'
##    <ul>
##     <% for item in @list %>
##      <li><%= item %>
##          <%== item %></li>
##     <% end %>
##    </ul>
##   END
##   list = ['<aaa>', 'b&b', '"ccc"']
##   eruby = Erubis::Eruby.new()
##   code = eruby.convert(input)
##   puts "--- code ---"
##   puts code
##   puts "--- result ---"
##   context = Object.new
##   context.instance_variable_set("@list", list)
##   puts context.instance_eval(code)
##   # or @list = list; puts eval(code, binding())
##
## result:
##   --- source ---
##   _buf = ""; _buf << " <ul>\n"
##      for item in list
##   _buf << "   <li>"; _buf << ( item ).to_s; _buf << "\n"
##   _buf << "       "; _buf << Erubis::XmlEruby.escape( item ); _buf << "</li>\n"
##      end
##   _buf << " </ul>\n"
##   _buf
##   --- result ---
##    <ul>
##      <li><aaa>
##          &lt;aaa&gt;</li>
##      <li>b&b
##          b&amp;b</li>
##      <li>"ccc"
##          &quot;ccc&quot;</li>
##    </ul>
##


#--begin of require 'erubis/engine'
##
## $Rev: 32 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##


#--begin of require 'erubis/generator'
##
## $Rev: 32 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--begin of require 'abstract'
##
## $Rev: 1 $
## $Release: 0.1.0 $
## copyright(c) 2006 kuwata-lab.com all rights reserved.
##
##
## helper to define abstract method in Ruby.
##
##
## example1. (shorter notation)
##
##   require 'abstract'
##   class Foo
##     abstract_method 'arg1, arg2=""', :method1, :method2, :method3
##   end
##
##
## example2. (RDoc friendly notation)
##
##   require 'abstract'
##   class Bar
##     # ... method1 description ...
##     def method1(arg1, arg2="")
##       not_implemented
##     end
##
##     # ... method2 description ...
##     def method2(arg1, arg2="")
##       not_implemented
##     end
##   end
##


##
class Module

  ##
  ## define abstract methods
  ##
  def abstract_method args_str, *method_names
    method_names.each do |name|
      module_eval <<-END
        def #{name}(#{args_str})
          mesg = "class \#{self.class.name} must implement abstract method `#{self.name}##{name}()'."
          #mesg = "\#{self.class.name}##{name}() is not implemented."
          err = NotImplementedError.new mesg
          err.set_backtrace caller()
          raise err
        end
      END
    end
  end

end


##
module Kernel

  ##
  ## raise NotImplementedError
  ##
  def not_implemented     #:doc:
    backtrace = caller()
    method_name = (backtrace.shift =~ /`(\w+)'$/) && $1
    mesg = "class #{self.class.name} must implement abstract method '#{method_name}()'."
    #mesg = "#{self.class.name}##{method_name}() is not implemented."
    err = NotImplementedError.new mesg
    err.set_backtrace backtrace
    raise err
  end
  private :not_implemented

end
#--end of require 'abstract'

module Erubis


  module Generator

    def self.supported_properties()  # :nodoc:
      return [
              [:escapefunc,    nil,    "escape function name"],
            ]
    end

    attr_accessor :escapefunc

    def init_generator(properties={})
      @escapefunc = properties[:escapefunc]
    end


    ## (abstract) escape text string
    ##
    ## ex.
    ##   def escape_text(text)
    ##     return text.dump
    ##     # or return "'" + text.gsub(/['\\]/, '\\\\\&') + "'"
    ##   end
    def escape_text(text)
      not_implemented
    end

    ## return escaped expression code (ex. 'h(...)' or 'htmlspecialchars(...)')
    def escaped_expr(code)
      code.strip!
      return "#{@escapefunc}(#{code})"
    end

    ## (abstract) add @preamble to src
    def add_preamble(src)
      not_implemented
    end

    ## (abstract) add text string to src
    def add_text(src, text)
      not_implemented
    end

    ## (abstract) add statement code to src
    def add_stmt(src, code)
      not_implemented
    end

    ## (abstract) add expression literal code to src. this is called by add_expr().
    def add_expr_literal(src, code)
      not_implemented
    end

    ## (abstract) add escaped expression code to src. this is called by add_expr().
    def add_expr_escaped(src, code)
      not_implemented
    end

    ## (abstract) add expression code to src for debug. this is called by add_expr().
    def add_expr_debug(src, code)
      not_implemented
    end

    ## (abstract) add @postamble to src
    def add_postamble(src)
      not_implemented
    end


  end


end
#--end of require 'erubis/generator'
#--begin of require 'erubis/converter'
##
## $Rev: 37 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--already included require 'abstract'

module Erubis


  ##
  ## convert
  ##
  module Converter

    attr_accessor :preamble, :postamble, :escape

    def self.supported_properties    # :nodoc:
      return [
              [:preamble,  nil,    "preamble (no preamble when false)"],
              [:postamble, nil,    "postamble (no postamble when false)"],
              [:escape,    nil,    "escape expression or not in default"],
             ]
    end

    def init_converter(properties={})
      @preamble  = properties[:preamble]
      @postamble = properties[:postamble]
      @escape    = properties[:escape]
    end

    ## convert input string into target language
    def convert(input)
      codebuf = ""    # or []
      @preamble.nil? ? add_preamble(codebuf) : (@preamble && (codebuf << @preamble))
      convert_input(codebuf, input)
      @postamble.nil? ? add_postamble(codebuf) : (@postamble && (codebuf << @postamble))
      return codebuf  # or codebuf.join()
    end

    protected

    ##
    ## (abstract) convert input to code
    ##
    def convert_input(codebuf, input)
      not_implemented
    end

  end


  module Basic
  end


  ##
  ## basic converter which supports '<% ... %>' notation.
  ##
  module Basic::Converter
    include Erubis::Converter

    def self.supported_properties    # :nodoc:
      return [
              [:pattern,  '<% %>', "embed pattern"],
              [:trim,      true,   "trim spaces around <% ... %>"],
             ]
    end

    attr_accessor :pattern, :trim

    def init_converter(properties={})
      super(properties)
      @pattern   = properties[:pattern]
      @trim      = properties[:trim] != false
    end

    #DEFAULT_REGEXP = /(.*?)(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
    DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m

    ## return regexp of pattern to parse eRuby script
    def pattern_regexp(pattern=@pattern)
      if pattern.nil? || pattern == '<% %>'
        return DEFAULT_REGEXP
      else
        prefix, postfix = pattern.split()
        #return /(.*?)(^[ \t]*)?#{prefix}(=+|\#)?(.*?)-?#{postfix}([ \t]*\r?\n)?/m
        return /(^[ \t]*)?#{prefix}(=+|\#)?(.*?)-?#{postfix}([ \t]*\r?\n)?/m
      end
    end
    protected :pattern_regexp

    def convert_input(src, input)
      regexp = pattern_regexp(@pattern)
      pos = 0
      input.scan(regexp) do |lspace, indicator, code, rspace|
        match = Regexp.last_match()
        index = match.begin(0)
        text  = input[pos, index - pos]
        pos   = match.end(0)
        add_text(src, text)
        ## * when '<%= %>', do nothing
        ## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
        if !indicator               # <% %>
          if @trim && lspace && rspace
            add_stmt(src, "#{lspace}#{code}#{rspace}")
          else
            add_text(src, lspace) if lspace
            add_stmt(src, code)
            add_text(src, rspace) if rspace
          end
        elsif indicator[0] == ?\#   # <%# %>
          n = code.count("\n") + (rspace ? 1 : 0)
          if @trim && lspace && rspace
            add_stmt(src, "\n" * n)
          else
            add_text(src, lspace) if lspace
            add_stmt(src, "\n" * n)
            add_text(src, rspace) if rspace
          end
        else                        # <%= %>
          add_text(src, lspace) if lspace
          add_expr(src, code, indicator)
          add_text(src, rspace) if rspace
        end
      end
      rest = $' || input     # add input when no matched
      add_text(src, rest)
    end

    ## add expression code to src
    def add_expr(src, code, indicator)
      case indicator
      when '='
        @escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
      when '=='
        @escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
      when '==='
        add_expr_debug(src, code)
      end
    end

  end


  module PI
  end

  ##
  ## Processing Instructions (PI) converter for XML.
  ## this class converts '<?rb ... ?>' and '${...}' notation.
  ##
  module PI::Converter
    include Erubis::Converter

    def self.desc   # :nodoc:
      "use processing instructions (PI) instead of '<% %>'"
    end

    def self.supported_properties    # :nodoc:
      return [
              [:trim,      true,   "trim spaces around <% ... %>"],
              [:pi,        'rb',   "PI (Processing Instrunctions) name"],
              [:embchar,   '@',    "char for embedded expression pattern('@{...}@')"],
              [:pattern,  '<% %>', "embed pattern"],
             ]
    end

    attr_accessor :pi, :prefix

    def init_converter(properties={})
      super(properties)
      @trim    = !(properties[:trim] == false)
      @pi      = properties[:pi] if properties[:pi]
      @embchar = properties[:embchar]  || '@'
      @pattern = properties[:pattern]
      @pattern = '<% %>' if @pattern.nil?  #|| @pattern == true
    end

    def convert(input)
      code = super(input)
      return @header || @footer ? "#{@header}#{code}#{@footer}" : code
    end

    protected

    def convert_input(codebuf, input)
      parse_stmts(codebuf, input)
      #parse_stmts2(codebuf, input)
    end

    def parse_stmts(codebuf, input)
      #regexp = pattern_regexp(@pattern)
      @pi ||= 'e'
      @stmt_pattern ||= /(^[ \t]*)?<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?/m
      pos = 0
      input.scan(@stmt_pattern) do |lspace, pi_arg, code, rspace|
        match = Regexp.last_match
        index = match.begin(0)
        text = input[pos, index - pos]
        pos = match.end(0)
        parse_exprs(codebuf, text) # unless text.empty?
        if @trim && lspace && rspace
          add_pi_stmt(codebuf, "#{lspace}#{code}#{rspace}", pi_arg)
        else
          add_text(codebuf, lspace)
          add_pi_stmt(codebuf, code, pi_arg)
          add_text(codebuf, rspace)
        end
      end
      rest = $' || input
      parse_exprs(codebuf, rest)
    end

    def parse_exprs(codebuf, input)
      unless @expr_pattern
        ch = Regexp.escape(@embchar)
        if @pattern
          left, right = @pattern.split(' ')
          @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/
        else
          @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}/
        end
      end
      pos = 0
      input.scan(@expr_pattern) do |indicator1, code1, indicator2, code2|
        indicator = indicator1 || indicator2
        code = code1 || code2
        match = Regexp.last_match
        index = match.begin(0)
        text = input[pos, index - pos]
        pos = match.end(0)
        add_text(codebuf, text) # unless text.empty?
        add_pi_expr(codebuf, code, indicator)
      end
      rest = $' || input
      add_text(codebuf, rest)
    end

    def add_pi_stmt(codebuf, code, pi_arg)  # :nodoc:
      case pi_arg
      when 'header' ;  @header = code
      when 'footer' ;  @footer = code
      when 'comment';  add_stmt(codebuf, "\n" * code.count("\n"))
      when 'value'  ;  add_expr_literal(codebuf, code)
      else          ;  add_stmt(codebuf, code)
      end
    end

    def add_pi_expr(codebuf, code, indicator)  # :nodoc:
      case indicator
      when  nil, '', '=='    # @{...}@ or <%== ... %>
        @escape == false ? add_expr_literal(codebuf, code) : add_expr_escaped(codebuf, code)
      when  '!', '='         # @!{...}@ or <%= ... %>
        @escape == false ? add_expr_escaped(codebuf, code) : add_expr_literal(codebuf, code)
      when  '!!', '==='      # @!!{...}@ or <%=== ... %>
        add_expr_debug(codebuf, code)
      else
        # ignore
      end
    end

    ## (obsolete) equivarent to parse_stmts(), but a little slower than it.
    def parse_stmts2(codebuf, input)   # :nodoc:
      #regexp = pattern_regexp(@pattern)
      @pi ||= 'e'
      unless @embedded_pattern
        ch = Regexp.escape(@embchar)
        if @pattern
          left, right = @pattern.split(' ')
          @embedded_pattern = /(^[ \t]*)?<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/m
        else
          @embedded_pattern = /(^[ \t]*)?<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}/m
        end
      end
      pos = 0
      input.scan(@embedded_pattern) do |lspace, pi_arg, stmt, rspace,
                                    indicator1, expr1, indicator2, expr2|
        match = Regexp.last_match
        index = match.begin(0)
        text = input[pos, index - pos]
        pos = match.end(0)
        add_text(codebuf, text)  # unless text.empty?
        if stmt
          code = stmt
          if @trim && lspace && rspace
            add_pi_stmt(codebuf, "#{lspace}#{code}#{rspace}", pi_arg)
          else
            add_text(codebuf, lspace)
            add_pi_stmt(codebuf, code, pi_arg)
            add_text(codebuf, rspace)
          end
        else
          code = expr1 || expr2
          indicator = indicator1 || indicator2
          add_pi_expr(codebuf, code, indicator)
        end
      end
      rest = $' || input
      add_text(codebuf, rest)
    end

  end


end
#--end of require 'erubis/converter'
#--begin of require 'erubis/evaluator'
##
## $Rev: 32 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--begin of require 'erubis/error'
##
## $Rev: 32 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

module Erubis


  ##
  ## base error class
  ##
  class ErubisError < StandardError
  end


  ##
  ## raised when method or function is not supported
  ##
  class NotSupportedError < ErubisError
  end


end
#--end of require 'erubis/error'
#--begin of require 'erubis/context'
##
## $Rev: 32 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##


module Erubis


  ##
  ## context object for Engine#evaluate
  ##
  ## ex.
  ##   template = <<'END'
  ##   Hello <%= @user %>!
  ##   <% for item in @list %>
  ##    - <%= item %>
  ##   <% end %>
  ##   END
  ##
  ##   context = Erubis::Context.new(:user=>'World', :list=>['a','b','c'])
  ##   # or
  ##   # context = Erubis::Context.new
  ##   # context[:user] = 'World'
  ##   # context[:list] = ['a', 'b', 'c']
  ##
  ##   eruby = Erubis::Eruby.new(template)
  ##   print eruby.evaluate(context)
  ##
  class Context

    def initialize(hash=nil)
      hash.each do |name, value|
        self[name] = value
      end if hash
    end

    def [](key)
      return instance_variable_get("@#{key}")
    end

    def []=(key, value)
      return instance_variable_set("@#{key}", value)
    end

    def keys
      return instance_variables.collect { |name| name[1,name.length-1] }
    end

  end


end
#--end of require 'erubis/context'


module Erubis


  ##
  ## evaluate code
  ##
  module Evaluator

    def self.supported_properties    # :nodoc:
      return []
    end

    attr_accessor :src, :filename

    def init_evaluator(properties)
      @filename = properties[:filename]
    end

    def result(*args)
      raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
    end

    def evaluate(*args)
      raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
    end

  end


  ##
  ## evaluator for Ruby
  ##
  module RubyEvaluator
    include Evaluator

    def self.supported_properties    # :nodoc:
      list = Evaluator.supported_properties
      return list
    end

    ## eval(@src) with binding object
    def result(_binding_or_hash=TOPLEVEL_BINDING)
      _arg = _binding_or_hash
      if _arg.is_a?(Hash)
        ## load _context data as local variables by eval
        eval _arg.keys.inject("") { |s, k| s << "#{k.to_s} = _arg[#{k.inspect}];" }
        _arg = binding()
      end
      return eval(@src, _arg, (@filename || '(erubis)'))
    end

    ## invoke context.instance_eval(@src)
    def evaluate(context=Context.new)
      context = Context.new(context) if context.is_a?(Hash)
      return context.instance_eval(@src, (@filename || '(erubis)'))
    end

  end


end
#--end of require 'erubis/evaluator'
#--already included require 'erubis/context'


module Erubis


  ##
  ## (abstract) abstract engine class.
  ## subclass must include evaluator and converter module.
  ##
  class Engine
    #include Evaluator
    #include Converter
    #include Generator

    # convert input string and set it to @src
    def convert!(input)
      @src = convert(input)
    end

    def initialize(input=nil, properties={})
      #@input = input
      init_generator(properties)
      init_converter(properties)
      init_evaluator(properties)
      @src    = convert(input) if input
    end

    ## load file and create engine object
    def self.load_file(filename, properties={})
      input = File.open(filename, 'rb') { |f| f.read }
      input.untaint   # is it ok?
      properties[:filename] = filename
      engine = self.new(input, properties)
      return engine
    end


    ##
    ## helper method to convert and evaluate input text with context object.
    ## context may be Binding, Hash, or Object.
    ##
    def process(input, context=nil, filename=nil)
      code = convert(input)
      filename ||= '(erubis)'
      if context.is_a?(Binding)
        return eval(code, context, filename)
      else
        context = Context.new(context) if context.is_a?(Hash)
        return context.instance_eval(code, filename)
      end
    end


    ##
    ## helper method evaluate Proc object iwth contect object.
    ## context may be Binding, Hash, or Object.
    ##
    def process_proc(proc_obj, context=nil, filename=nil)
      if context.is_a?(Binding)
        filename ||= '(erubis)'
        return eval(proc_obj, context, filename)
      else
        context = Context.new(context) if context.is_a?(Hash)
        return context.instance_eval(&proc_obj)
      end
    end


  end  # end of class Engine


  ##
  ## (abstract) base engine class for Eruby, Eperl, Ejava, and so on.
  ## subclass must include generator.
  ##
  class Basic::Engine < Engine
    include Evaluator
    include Basic::Converter
    include Generator
  end


  class PI::Engine < Engine
    include Evaluator
    include PI::Converter
    include Generator
  end


end
#--end of require 'erubis/engine'
#require 'erubis/generator'
#require 'erubis/converter'
#require 'erubis/evaluator'
#require 'erubis/error'
#require 'erubis/context'
#--begin of require 'erubis/helper'
##
## $Rev: 21 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##


module Erubis

  ##
  ## helper for xml
  ##
  module XmlHelper

    module_function

    ESCAPE_TABLE = {
      '&' => '&amp;',
      '<' => '&lt;',
      '>' => '&gt;',
      '"' => '&quot;',
      "'" => '&#039;',
    }

    def escape_xml(obj)
      #table = ESCAPE_TABLE
      #obj.to_s.gsub(/[&<>"]/) { |s| table[s] }    # or /[&<>"']/
      obj.to_s.gsub(/[&<>"]/) { |s| ESCAPE_TABLE[s] }   # or /[&<>"']/
      #obj.to_s.gsub(SCAN_REGEXP) { |s| ESCAPE_TABLE[s] }
      #obj.to_s.gsub(/[&<>"]/) { ESCAPE_TABLE[$&] }
    end

    #--
    #def escape_xml(obj)
    #  str = obj.to_s.dup
    #  #str = obj.to_s
    #  #str = str.dup if obj.__id__ == str.__id__
    #  str.gsub!(/&/, '&amp;')
    #  str.gsub!(/</, '&lt;')
    #  str.gsub!(/>/, '&gt;')
    #  str.gsub!(/"/, '&quot;')
    #  str.gsub!(/'/, '&#039;')
    #  return str
    #end
    #++

    alias h escape_xml
    alias html_escape escape_xml

  end


end
#--end of require 'erubis/helper'
#--begin of require 'erubis/enhancer'
##
## $Rev: 32 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##


module Erubis


  ##
  ## switch '<%= ... %>' to escaped and '<%== ... %>' to unescaped
  ##
  ## ex.
  ##   class XmlEruby < Eruby
  ##     include EscapeEnhancer
  ##   end
  ##
  ## this is language-indenedent.
  ##
  module EscapeEnhancer

    def self.desc   # :nodoc:
      "switch '<%= %>' to escaped and '<%== %>' to unescaped"
    end

    #--
    #def self.included(klass)
    #  klass.class_eval <<-END
    #    alias _add_expr_literal add_expr_literal
    #    alias _add_expr_escaped add_expr_escaped
    #    alias add_expr_literal _add_expr_escaped
    #    alias add_expr_escaped _add_expr_literal
    #  END
    #end
    #++

    def add_expr(src, code, indicator)
      case indicator
      when '='
        @escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
      when '=='
        @escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
      when '==='
        add_expr_debug(src, code)
      end
    end

  end


  #--
  ## (obsolete)
  #module FastEnhancer
  #end
  #++


  ##
  ## use $stdout instead of string
  ##
  ## this is only for Eruby.
  ##
  module StdoutEnhancer

    def self.desc   # :nodoc:
      "use $stdout instead of array buffer or string buffer"
    end

    def add_preamble(src)
      src << "_buf = $stdout;"
    end

    def add_postamble(src)
      src << "\n''\n"
    end

  end


  ##
  ## use print statement instead of '_buf << ...'
  ##
  ## this is only for Eruby.
  ##
  module PrintOutEnhancer

    def self.desc   # :nodoc:
      "use print statement instead of '_buf << ...'"
    end

    def add_preamble(src)
    end

    def add_text(src, text)
      src << " print '" << escape_text(text) << "';" unless text.empty?
    end

    def add_expr_literal(src, code)
      src << ' print((' << code << ').to_s);'
    end

    def add_expr_escaped(src, code)
      src << ' print ' << escaped_expr(code) << ';'
    end

    def add_postamble(src)
      src << "\n" unless src[-1] == ?\n
    end

  end


  ##
  ## enable print function
  ##
  ## Notice: use Eruby#evaluate() and don't use Eruby#result()
  ## to be enable print function.
  ##
  ## this is only for Eruby.
  ##
  module PrintEnabledEnhancer

    def self.desc   # :nodoc:
      "enable to use print function in '<% %>'"
    end

    def add_preamble(src)
      src << "@_buf = "
      super
    end

    def print(*args)
      args.each do |arg|
        @_buf << arg.to_s
      end
    end

    def evaluate(context=nil)
      _src = @src
      if context.is_a?(Hash)
        context.each do |key, val| instance_variable_set("@#{key}", val) end
      elsif context
        context.instance_variables.each do |name|
          instance_variable_set(name, context.instance_variable_get(name))
        end
      end
      return instance_eval(_src, (@filename || '(erubis)'))
    end

  end


  ##
  ## return array instead of string
  ##
  ## this is only for Eruby.
  ##
  module ArrayEnhancer

    def self.desc   # :nodoc:
      "return array instead of string"
    end

    def add_preamble(src)
      src << "_buf = [];"
    end

    def add_postamble(src)
      src << "\n" unless src[-1] == ?\n
      src << "_buf\n"
    end

  end


  ##
  ## use an Array object as buffer (included in Eruby by default)
  ##
  ## this is only for Eruby.
  ##
  module ArrayBufferEnhancer

    def self.desc   # :nodoc:
      "use an Array object for buffering (included in Eruby class)"
    end

    def add_preamble(src)
      src << "_buf = [];"
    end

    def add_postamble(src)
      src << "\n" unless src[-1] == ?\n
      src << "_buf.join\n"
    end

  end


  ##
  ## use String class for buffering
  ##
  ## this is only for Eruby.
  ##
  module StringBufferEnhancer

    def self.desc   # :nodoc:
      "use a String object for buffering"
    end

    def add_preamble(src)
      src << "_buf = '';"
    end

    def add_postamble(src)
      src << "\n" unless src[-1] == ?\n
      src << "_buf\n"
    end

  end


  ##
  ## use StringIO class for buffering
  ##
  ## this is only for Eruby.
  ##
  module StringIOEnhancer  # :nodoc:

    def self.desc   # :nodoc:
      "use a StringIO object for buffering"
    end

    def add_preamble(src)
      src << "_buf = StringIO.new;"
    end

    def add_postamble(src)
      src << "\n" unless src[-1] == ?\n
      src << "_buf.string\n"
    end

  end


  ##
  ## remove text and leave code, especially useful when debugging.
  ##
  ## ex.
  ##   $ erubis -s -E NoText file.eruby | more
  ##
  ## this is language independent.
  ##
  module NoTextEnhancer

    def self.desc   # :nodoc:
      "remove text and leave code (useful when debugging)"
    end

    def add_text(src, text)
      src << ("\n" * text.count("\n"))
      if text[-1] != ?\n
        text =~ /^(.*?)\z/
        src << (' ' * $1.length)
      end
    end

  end


  ##
  ## remove code and leave text, especially useful when validating HTML tags.
  ##
  ## ex.
  ##   $ erubis -s -E NoCode file.eruby | tidy -errors
  ##
  ## this is language independent.
  ##
  module NoCodeEnhancer

    def self.desc   # :nodoc:
      "remove code and leave text (useful when validating HTML)"
    end

    def add_preamble(src)
    end

    def add_postamble(src)
    end

    def add_text(src, text)
      src << text
    end

    def add_expr(src, code, indicator)
      src << "\n" * code.count("\n")
    end

    def add_stmt(src, code)
      src << "\n" * code.count("\n")
    end

  end


  ##
  ## get convert faster, but spaces around '<%...%>' are not trimmed.
  ##
  ## this is language-independent.
  ##
  module SimplifyEnhancer

    def self.desc   # :nodoc:
      "get convert faster but leave spaces around '<% %>'"
    end

    #DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
    SIMPLE_REGEXP = /<%(=+|\#)?(.*?)-?%>/m

    def convert(input)
      src = ""
      add_preamble(src)
      #regexp = pattern_regexp(@pattern)
      pos = 0
      input.scan(SIMPLE_REGEXP) do |indicator, code|
        index = Regexp.last_match.begin(0)
        text = input[pos, index - pos]
        pos = index + $&.length()
        add_text(src, text)
        if !indicator              # <% %>
          add_stmt(src, code)
        elsif indicator[0] == ?\#  # <%# %>
          n = code.count("\n")
          add_stmt(src, "\n" * n)
        else                       # <%= %>
          add_expr(src, code, indicator)
        end
      end
      rest = $' || input
      add_text(src, rest)
      add_postamble(src)
      return src
    end

  end


  ##
  ## enable to use other embedded expression pattern (default is '\[= =\]').
  ##
  ## notice! this is an experimental. spec may change in the future.
  ##
  ## ex.
  ##   input = <<END
  ##   <% for item in list %>
  ##     <%= item %> : <%== item %>
  ##     [= item =] : [== item =]
  ##   <% end %>
  ##   END
  ##
  ##   class BiPatternEruby
  ##     include BiPatternEnhancer
  ##   end
  ##   eruby = BiPatternEruby.new(input, :bipattern=>'\[= =\]')
  ##   list = ['<a>', 'b&b', '"c"']
  ##   print eruby.result(binding())
  ##
  ##   ## output
  ##     <a> : &lt;a&gt;
  ##     <a> : &lt;a&gt;
  ##     b&b : b&amp;b
  ##     b&b : b&amp;b
  ##     "c" : &quot;c&quot;
  ##     "c" : &quot;c&quot;
  ##
  ## this is language independent.
  ##
  module BiPatternEnhancer

    def self.desc   # :nodoc:
      "another embedded expression pattern (default '\[= =\]')."
    end

    def initialize(input, properties={})
      self.bipattern = properties[:bipattern]    # or '\$\{ \}'
      super
    end

    ## when pat is nil then '\[= =\]' is used
    def bipattern=(pat)   # :nodoc:
      @bipattern = pat || '\[= =\]'
      pre, post = @bipattern.split()
      @bipattern_regexp = /(.*?)#{pre}(=*)(.*?)#{post}/m
    end

    def add_text(src, text)
      return unless text
      text.scan(@bipattern_regexp) do |txt, indicator, code|
        super(src, txt)
        add_expr(src, code, '=' + indicator)
      end
      rest = $' || text
      super(src, rest)
    end

  end


  ##
  ## regards lines starting with '%' as program code
  ##
  ## this is for compatibility to eruby and ERB.
  ##
  ## this is language-independent.
  ##
  module PercentLineEnhancer

    def self.desc   # :nodoc:
      "regard lines starting with '%' as program code"
    end

    PERCENT_LINE_PATTERN = /(.*?)^\%(.*?\r?\n)/m

    def add_text(src, text)
      text.scan(PERCENT_LINE_PATTERN) do |txt, line|
        super(src, txt)
        if line[0] == ?%
          super(src, line)
        else
          add_stmt(src, line)
        end
      end
      rest = $' || text
      super(src, rest)
    end

  end


  ##
  ## [experimental] allow header and footer in eRuby script
  ##
  ## ex.
  ##   ====================
  ##   ## without header and footer
  ##   $ cat ex1.eruby
  ##   <% def list_items(list) %>
  ##   <%   for item in list %>
  ##   <li><%= item %></li>
  ##   <%   end %>
  ##   <% end %>
  ##
  ##   $ erubis -s ex1.eruby
  ##   _buf = []; def list_items(list)
  ##   ;   for item in list
  ##   ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
  ##   ';   end
  ##   ; end
  ##   ;
  ##   _buf.join
  ##
  ##   ## with header and footer
  ##   $ cat ex2.eruby
  ##   <!--#header:
  ##   def list_items(list)
  ##    #-->
  ##   <%  for item in list %>
  ##   <li><%= item %></li>
  ##   <%  end %>
  ##   <!--#footer:
  ##   end
  ##    #-->
  ##
  ##   $ erubis -s -c HeaderFooterEruby ex4.eruby
  ##
  ##   def list_items(list)
  ##    _buf = []; _buf << '
  ##   ';  for item in list
  ##   ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
  ##   ';  end
  ##   ; _buf << '
  ##   ';
  ##   _buf.join
  ##   end
  ##
  ##   ====================
  ##
  ## this is language-independent.
  ##
  module HeaderFooterEnhancer

    def self.desc   # :nodoc:
      "allow header/footer in document (ex. '<!--#header: #-->')"
    end

    HEADER_FOOTER_PATTERN = /(.*?)(^[ \t]*)?<!--\#(\w+):(.*?)\#-->([ \t]*\r?\n)?/m

    def add_text(src, text)
      text.scan(HEADER_FOOTER_PATTERN) do |txt, lspace, word, content, rspace|
        flag_trim = @trim && lspace && rspace
        super(src, txt)
        content = "#{lspace}#{content}#{rspace}" if flag_trim
        super(src, lspace) if !flag_trim && lspace
        instance_variable_set("@#{word}", content)
        super(src, rspace) if !flag_trim && rspace
      end
      rest = $' || text
      super(src, rest)
    end

    attr_accessor :header, :footer

    def convert(input)
      source = super
      return @src = "#{@header}#{source}#{@footer}"
    end

  end


end
#--end of require 'erubis/enhancer'
#require 'erubis/tiny'
#--begin of require 'erubis/engine/eruby'
##
## $Rev: 33 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--already included require 'erubis/engine'
#--already included require 'erubis/enhancer'
#--already included require 'abstract'


module Erubis


  ##
  ## code generator for Ruby
  ##
  module RubyGenerator
    include Generator
    #include StringBufferEnhancer
    include ArrayBufferEnhancer

    def init_generator(properties={})
      super
      @escapefunc ||= "Erubis::XmlHelper.escape_xml"
    end

    def self.supported_properties()  # :nodoc:
      return []
    end

    def escape_text(text)
      text.gsub(/['\\]/, '\\\\\&')   # "'" => "\\'",  '\\' => '\\\\'
    end

    def escaped_expr(code)
      return "#{@escapefunc}(#{code})"
    end

    #--
    #def add_preamble(src)
    #  src << "_buf = [];"
    #end
    #++

    def add_text(src, text)
      src << " _buf << '" << escape_text(text) << "';" unless text.empty?
    end

    def add_stmt(src, code)
      #src << code << ';'
      src << code
      src << ';' unless code[-1] == ?\n
    end

    def add_expr_literal(src, code)
      src << ' _buf << (' << code << ').to_s;'
    end

    def add_expr_escaped(src, code)
      src << ' _buf << ' << escaped_expr(code) << ';'
    end

    def add_expr_debug(src, code)
      code.strip!
      s = (code.dump =~ /\A"(.*)"\z/) && $1
      src << ' $stderr.puts("*** debug: ' << s << '=#{(' << code << ').inspect}");'
    end

    #--
    #def add_postamble(src)
    #  src << "\n_buf.join\n"
    #end
    #++

  end


  ##
  ## engine for Ruby
  ##
  class Eruby < Basic::Engine
    include RubyEvaluator
    include RubyGenerator
  end


  ##
  ## swtich '<%= %>' to escaped and '<%== %>' to not escaped
  ##
  class EscapedEruby < Eruby
    include EscapeEnhancer
  end


  ##
  ## sanitize expression (<%= ... %>) by default
  ##
  ## this is equivalent to EscapedEruby and is prepared only for compatibility.
  ##
  class XmlEruby < Eruby
    include EscapeEnhancer
  end


  class PI::Eruby < PI::Engine
    include RubyEvaluator
    include RubyGenerator

    def init_converter(properties={})
      @pi = 'rb'
      super(properties)
    end

  end


end
#--end of require 'erubis/engine/eruby'
#require 'erubis/engine/enhanced'    # enhanced eruby engines
#require 'erubis/engine/optimized'   # generates optimized ruby code
#require 'erubis/engine/ephp'
#require 'erubis/engine/ec'
#require 'erubis/engine/ejava'
#require 'erubis/engine/escheme'
#require 'erubis/engine/eperl'
#require 'erubis/engine/ejavascript'

#--begin of require 'erubis/local-setting'
##
## $Rev: 13 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

##
## you can add site-local settings here.
## this files is 'require'd by erubis.rb
##
#--end of require 'erubis/local-setting'
#--end of require 'erubis'
#--begin of require 'erubis/tiny'
##
## $Rev: 35 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

module Erubis

  ##
  ## tiny and the simplest implementation of eRuby
  ##
  ## ex.
  ##   eruby = TinyEruby.new(File.read('example.rhtml'))
  ##   print eruby.src                 # print ruby code
  ##   print eruby.result(binding())   # eval ruby code with Binding object
  ##   print eruby.evalute(context)    # eval ruby code with context object
  ##
  class TinyEruby

    def initialize(input=nil)
      @src = convert(input) if input
    end
    attr_reader :src

    EMBEDDED_PATTERN = /<%(=+|\#)?(.*?)-?%>/m

    def convert(input)
      src = "_buf = [];"           # preamble
      pos = 0
      input.scan(EMBEDDED_PATTERN) do |indicator, code|
        match = Regexp.last_match
        index = match.begin(0)
        text  = input[pos, index - pos]
        pos   = match.end(0)
        src << " _buf << '" << escape_text(text) << "';"
        if !indicator              # <% %>
          src << code << ";"
        elsif indicator[0] == ?\#  # <%# %>
          n = code.count("\n")
          add_stmt(src, "\n" * n)
        else                       # <%= %>
          src << " _buf << (" << code << ").to_s;"
        end
      end
      rest = $' || input
      src << " _buf << '" << escape_text(rest) << "';"
      src << "\n_buf.join\n"       # postamble
      return src
    end

    def escape_text(text)
      return text.gsub!(/['\\]/, '\\\\\&') || text
    end

    def result(binding=TOPLEVEL_BINDING)
      eval @src, binding
    end

    def evaluate(context=Object.new)
      if context.is_a?(Hash)
        obj = Object.new
        context.each do |k, v| obj.instance_variable_set("@#{k}", v) end
        context = obj
      end
      context.instance_eval @src
    end

  end



  module PI
  end

  class PI::TinyEruby

    def initialize(input=nil, options={})
      @escape  = options[:escape] || 'Erubis::XmlHelper.escape_xml'
      @src = convert(input) if input
    end

    attr_reader :src

    EMBEDDED_PATTERN = /(^[ \t]*)?<\?rb(\s.*?)\?>([ \t]*\r?\n)?|\$(!*)?\{(.*?)\}/

    def convert(input)
      src = "_buf = [];"           # preamble
      pos = 0
      input.scan(EMBEDDED_PATTERN) do |lspace, stmtcode, rspace, indicator, exprcode|
        match = Regexp.last_match
        index = match.begin(0)
        text  = input[pos, index - pos]
        pos   = match.end(0)
        if stmtcode                # <?rb ... ?>
          code = stmtcode
          src << " _buf << '" << escape_text(text) << "';"
          if lspace && rspace
            src << "#{lspace}#{code}#{rspace}"
          else
            src << " _buf << '#{lspace}';" if lspace
            src << code << ";"
            src << " _buf << '#{rspace}';" if rspace
          end
        else                       # ${...}, $!{...}
          code = exprcode
          if indicator.nil? || indicator.empty?
            src << " _buf << #{@escape}(" << code << ");"
          elsif indicator == '!'
            src << " _buf << (" << code << ").to_s;"
          end
        end
      end
      rest = $' || input
      src << " _buf << '" << escape_text(rest) << "';"
      src << "\n_buf.join\n"       # postamble
      return src
    end

    def escape_text(text)
      return text.gsub!(/['\\]/, '\\\\\&') || text
    end

    def result(binding=TOPLEVEL_BINDING)
      eval @src, binding
    end

    def evaluate(context=Object.new)
      if context.is_a?(Hash)
        obj = Object.new
        context.each do |k, v| obj.instance_variable_set("@#{k}", v) end
        context = obj
      end
      context.instance_eval @src
    end

  end


end
#--end of require 'erubis/tiny'
#--begin of require 'erubis/engine/enhanced'
##
## $Rev: 27 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--already included require 'erubis/enhancer'
#--already included require 'erubis/engine/eruby'


module Erubis


  #--
  ## moved to engine/ruby.rb
  #class EscapedEruby < Eruby
  #  include EscapeEnhancer
  #end
  #++


  #--
  ### (obsolete)
  #class FastEruby < Eruby
  #  include FastEnhancer
  #end
  #++


  class StdoutEruby < Eruby
    include StdoutEnhancer
  end


  class PrintOutEruby < Eruby
    include PrintOutEnhancer
  end


  class PrintEnabledEruby < Eruby
    include PrintEnabledEnhancer
  end


  class ArrayEruby < Eruby
    include ArrayEnhancer
  end


  class ArrayBufferEruby < Eruby
    include ArrayBufferEnhancer
  end


  class StringBufferEruby < Eruby
    include StringBufferEnhancer
  end


  class StringIOEruby < Eruby
    include StringIOEnhancer
  end


  class NoTextEruby < Eruby
    include NoTextEnhancer
  end


  class NoCodeEruby < Eruby
    include NoCodeEnhancer
  end


  class SimplifiedEruby < Eruby
    include SimplifyEnhancer
  end


  class StdoutSimplifiedEruby < Eruby
    include StdoutEnhancer
    include SimplifyEnhancer
  end


  class PrintOutSimplifiedEruby < Eruby
    include PrintOutEnhancer
    include SimplifyEnhancer
  end


  class BiPatternEruby < Eruby
    include BiPatternEnhancer
  end


  class PercentLineEruby < Eruby
    include PercentLineEnhancer
  end


  class HeaderFooterEruby < Eruby
    include HeaderFooterEnhancer
  end


end
#--end of require 'erubis/engine/enhanced'
#--begin of require 'erubis/engine/optimized'
##
## $Rev: 31 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##


#--already included require 'erubis/engine/eruby'


module Erubis


  module OptimizedGenerator
    include Generator

    def self.supported_properties()  # :nodoc:
      return []
    end

    def init_generator(properties={})
      super
      @escapefunc ||= "Erubis::XmlHelper.escape_xml"
      @initialized = false
      @prev_is_expr = false
    end

    protected

    def escape_text(text)
      text.gsub(/['\\]/, '\\\\\&')   # "'" => "\\'",  '\\' => '\\\\'
    end

    def escaped_expr(code)
      @escapefunc ||= 'Erubis::XmlHelper.escape_xml'
      return "#{@escapefunc}(#{code})"
    end

    def switch_to_expr(src)
      return if @prev_is_expr
      @prev_is_expr = true
      src << ' _buf'
    end

    def switch_to_stmt(src)
      return unless @prev_is_expr
      @prev_is_expr = false
      src << ';'
    end

    def add_preamble(src)
      #@initialized = false
      #@prev_is_expr = false
    end

    def add_text(src, text)
      return if text.empty?
      if @initialized
        switch_to_expr(src)
        src << " << '" << escape_text(text) << "'"
      else
        src << "_buf = '" << escape_text(text) << "';"
        @initialized = true
      end
    end

    def add_stmt(src, code)
      switch_to_stmt(src) if @initialized
      #super
      src << code
      src << ';' unless code[-1] == ?\n
    end

    def add_expr_literal(src, code)
      unless @initialized; src << "_buf = ''"; @initialized = true; end
      switch_to_expr(src)
      src << " << (" << code << ").to_s"
    end

    def add_expr_escaped(src, code)
      unless @initialized; src << "_buf = ''"; @initialized = true; end
      switch_to_expr(src)
      src << " << " << escaped_expr(code)
    end

    def add_expr_debug(src, code)
      code.strip!
      s = (code.dump =~ /\A"(.*)"\z/) && $1
      src << ' $stderr.puts("*** debug: ' << s << '=#{(' << code << ').inspect}");'
    end

    def add_postamble(src)
      #super if @initialized
      src << "\n_buf\n" if @initialized
    end

  end  # end of class OptimizedEruby


  ##
  ## Eruby class which generates optimized ruby code
  ##
  class OptimizedEruby < Basic::Engine    # Eruby
    include RubyEvaluator
    include OptimizedGenerator

    def init_converter(properties={})
      @pi = 'rb'
      super(properties)
    end

  end


  ##
  ## XmlEruby class which generates optimized ruby code
  ##
  class OptimizedXmlEruby < OptimizedEruby
    include EscapeEnhancer

    def add_expr_debug(src, code)
      switch_to_stmt(src) if indicator == '===' && !@initialized
      super
    end

  end  # end of class OptimizedXmlEruby

end
#--end of require 'erubis/engine/optimized'
#--already included require 'erubis/engine/eruby'
#--begin of require 'erubis/engine/ephp'
##
## $Rev: 31 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--already included require 'erubis/engine'
#--already included require 'erubis/enhancer'


module Erubis


  module PhpGenerator
    include Generator

    def self.supported_properties()  # :nodoc:
      return []
    end

    def init_generator(properties={})
      super
      @escapefunc ||= 'htmlspecialchars'
    end

    def add_preamble(src)
      # empty
    end

    def escape_text(text)
      return text.gsub!(/<\?xml\b/, '<<?php ?>?xml') || text
    end

    def add_text(src, text)
      src << escape_text(text)
    end

    def add_expr_literal(src, code)
      code.strip!
      src << "<?php echo #{code}; ?>"
    end

    def add_expr_escaped(src, code)
      add_expr_literal(src, escaped_expr(code))
    end

    def add_expr_debug(src, code)
      code.strip!
      s = code.gsub(/\'/, "\\'")
      src << "<?php error_log('*** debug: #{s}='.(#{code}), 0); ?>"
    end

    def add_stmt(src, code)
      src << "<?php"
      src << " " if code[0] != ?\ #
      if code[-1] == ?\n
        code.chomp!
        src << code << "?>\n"
      else
        src << code << "?>"
      end
    end

    def add_postamble(src)
      # empty
    end

  end


  ##
  ## engine for PHP
  ##
  class Ephp < Basic::Engine
    include PhpGenerator
  end


  class EscapedEphp < Ephp
    include EscapeEnhancer
  end


  #class XmlEphp < Ephp
  #  include EscapeEnhancer
  #end


  class PI::Ephp < PI::Engine
    include PhpGenerator

    def init_converter(properties={})
      @pi = 'php'
      super(properties)
    end

  end


end
#--end of require 'erubis/engine/ephp'
#--begin of require 'erubis/engine/ec'
##
## $Rev: 33 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--already included require 'erubis/engine'
#--already included require 'erubis/enhancer'


module Erubis


  module CGenerator
    include Generator

    def self.supported_properties()  # :nodoc:
      return [
              [:indent, '',       "indent spaces (ex. '  ')"],
              [:out,    'stdout', "output file pointer name"],
            ]
    end

    def init_generator(properties={})
      super
      @escapefunc ||= "escape"
      @indent = properties[:indent] || ''
      @out = properties[:out] || 'stdout'
    end

    def add_preamble(src)
      src << "#line 1 \"#{self.filename}\"\n" if self.filename
    end

    def escape_text(text)
      @@table_ ||= { "\r"=>"\\r", "\n"=>"\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" }
      text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] }
      return text
    end

    def escaped_expr(code)
      return "#{@escapefunc}(#{code.strip}, #{@out})"
    end

    def add_text(src, text)
      return if text.empty?
      src << (src.empty? || src[-1] == ?\n ? @indent : ' ')
      src << "fputs("
      i = 0
      text.each_line do |line|
        src << "\n" << @indent << '      ' if i > 0
        i += 1
        src << '"' << escape_text(line) << '"'
      end
      src << ", #{@out});"   #<< (text[-1] == ?\n ? "\n" : "")
      src << "\n" if text[-1] == ?\n
    end

    def add_stmt(src, code)
      src << code
    end

    def add_expr_literal(src, code)
      src << @indent if src.empty? || src[-1] == ?\n
      src << " fprintf(#{@out}, " << code.strip << ');'
    end

    def add_expr_escaped(src, code)
      src << @indent if src.empty? || src[-1] == ?\n
      src << ' ' << escaped_expr(code) << ';'
    end

    def add_expr_debug(src, code)
      code.strip!
      s = nil
      if code =~ /\A\".*?\"\s*,\s*(.*)/
        s = $1.gsub(/[%"]/, '\\\1') + '='
      end
      src << @indent if src.empty? || src[-1] == ?\n
      src << " fprintf(stderr, \"*** debug: #{s}\" #{code});"
    end

    def add_postamble(src)
      # empty
    end

  end


  ##
  ## engine for C
  ##
  class Ec < Basic::Engine
    include CGenerator
  end


  class EscapedEc < Ec
    include EscapeEnhancer
  end


  #class XmlEc < Ec
  #  include EscapeEnhancer
  #end

  class PI::Ec < PI::Engine
    include CGenerator

    def init_converter(properties={})
      @pi = 'c'
      super(properties)
    end

  end


end
#--end of require 'erubis/engine/ec'
#--begin of require 'erubis/engine/ejava'
##
## $Rev: 31 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--already included require 'erubis/engine'
#--already included require 'erubis/enhancer'


module Erubis


  module JavaGenerator
    include Generator

    def self.supported_properties()   # :nodoc:
      return [
              [:indent,   '',       "indent spaces (ex. '  ')"],
              [:buf,      '_buf',   "output buffer name"],
              [:bufclass, 'StringBuffer', "output buffer class (ex. 'StringBuilder')"],
            ]
    end

    def init_generator(properties={})
      super
      @escapefunc ||= 'escape'
      @indent = properties[:indent] || ''
      @buf = properties[:buf] || '_buf'
      @bufclass = properties[:bufclass] || 'StringBuffer'
    end

    def add_preamble(src)
      src << "#{@indent}#{@bufclass} #{@buf} = new #{@bufclass}();"
    end

    def escape_text(text)
      @@table_ ||= { "\r"=>"\\r", "\n"=>"\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" }
      return text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] } || text
    end

    def add_text(src, text)
      return if text.empty?
      src << (src.empty? || src[-1] == ?\n ? @indent : ' ')
      src << @buf << ".append("
      i = 0
      text.each_line do |line|
        src << "\n" << @indent << '          + ' if i > 0
        i += 1
        src << '"' << escape_text(line) << '"'
      end
      src << ");" << (text[-1] == ?\n ? "\n" : "")
    end

    def add_stmt(src, code)
      src << code
    end

    def add_expr_literal(src, code)
      src << @indent if src.empty? || src[-1] == ?\n
      code.strip!
      src << " #{@buf}.append(#{code});"
    end

    def add_expr_escaped(src, code)
      add_expr_literal(src, escaped_expr(code))
    end

    def add_expr_debug(src, code)
      code.strip!
      src << @indent if src.empty? || src[-1] == ?\n
      src << " System.err.println(\"*** debug: #{code}=\"+(#{code}));"
    end

    def add_postamble(src)
      src << "\n" if src[-1] == ?;
      src << @indent << "return " << @buf << ".toString();\n"
      #src << @indent << "System.out.print(" << @buf << ".toString());\n"
    end

  end


  ##
  ## engine for Java
  ##
  class Ejava < Basic::Engine
    include JavaGenerator
  end


  class EscapedEjava < Ejava
    include EscapeEnhancer
  end


  #class XmlEjava < Ejava
  #  include EscapeEnhancer
  #end

  class PI::Ejava < PI::Engine
    include JavaGenerator

    def init_converter(properties={})
      @pi = 'java'
      super(properties)
    end

  end

end
#--end of require 'erubis/engine/ejava'
#--begin of require 'erubis/engine/escheme'
##
## $Rev: 33 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--already included require 'erubis/engine'
#--already included require 'erubis/enhancer'


module Erubis


  module SchemeGenerator
    include Generator

    def self.supported_properties()  # :nodoc:
      return [
              [:func,  '_add',   "function name (ex. 'display')"],
              ]
    end

    def init_generator(properties={})
      super
      @escapefunc ||= 'escape'
      @func = properties[:func] || '_add'   # or 'display'
    end

    def add_preamble(src)
      return unless @func == '_add'
      src << "(let ((_buf '())) " + \
               "(define (_add x) (set! _buf (cons x _buf))) "
      #src << "(let* ((_buf '())" + \
      #             " (_add (lambda (x) (set! _buf (cons x _buf))))) "
    end

    def escape_text(text)
      @table_ ||= { '"'=>'\\"', '\\'=>'\\\\' }
      text.gsub!(/["\\]/) { |m| @table_[m] }
      return text
    end

    def escaped_expr(code)
      code.strip!
      return "(#{@escapefunc} #{code})"
    end

    def add_text(src, text)
      return if text.empty?
      t = escape_text(text)
      if t[-1] == ?\n
        t[-1, 1] = ''
        src << "(#{@func} \"" << t << "\\n\")\n"
      else
        src << "(#{@func} \"" << t << '")'
      end
    end

    def add_stmt(src, code)
      src << code
    end

    def add_expr_literal(src, code)
      code.strip!
      src << "(#{@func} #{code})"
    end

    def add_expr_escaped(src, code)
      add_expr_literal(src, escaped_expr(code))
    end

    def add_expr_debug(src, code)
      s = (code.strip! || code).gsub(/\"/, '\\"')
      src << "(display \"*** debug: #{s}=\")(display #{code.strip})(display \"\\n\")"
    end

    def add_postamble(src)
      return unless @func == '_add'
      src << "\n" unless src[-1] == ?\n
      src << "  (reverse _buf))\n"
    end

  end


  ##
  ## engine for Scheme
  ##
  class Escheme < Basic::Engine
    include SchemeGenerator
  end


  class EscapedEscheme < Escheme
    include EscapeEnhancer
  end


  #class XmlEscheme < Escheme
  #  include EscapeEnhancer
  #end


  class PI::Escheme < PI::Engine
    include SchemeGenerator

    def init_converter(properties={})
      @pi = 'scheme'
      super(properties)
    end

  end


end
#--end of require 'erubis/engine/escheme'
#--begin of require 'erubis/engine/eperl'
##
## $Rev: 40 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--already included require 'erubis/engine'
#--already included require 'erubis/enhancer'


module Erubis


  module PerlGenerator
    include Generator

    def self.supported_properties()  # :nodoc:
      return [
              [:func, 'print', "function name"],
              ]
    end

    def init_generator(properties={})
      super
      @escapefunc ||= 'encode_entities'
      @func = properties[:func] || 'print'
    end

    def add_preamble(src)
      src << "use HTML::Entities; ";
    end

    def escape_text(text)
      return text.gsub!(/['\\]/, '\\\\\&') || text
    end

    def add_text(src, text)
      src << @func << "('" << escape_text(text) << "'); " unless text.empty?
    end

    def add_expr_literal(src, code)
      code.strip!
      src << @func << "(" << code << "); "
    end

    def add_expr_escaped(src, code)
      add_expr_literal(src, escaped_expr(code))
    end

    def add_expr_debug(src, code)
      code.strip!
      s = code.gsub(/\'/, "\\'")
      src << @func << "('*** debug: #{code}=', #{code}, \"\\n\");"
    end

    def add_stmt(src, code)
      src << code
    end

    def add_postamble(src)
      src << "\n" unless src[-1] == ?\n
    end

  end


  ##
  ## engine for Perl
  ##
  class Eperl < Basic::Engine
    include PerlGenerator
  end


  class EscapedEperl < Eperl
    include EscapeEnhancer
  end


  #class XmlEperl < Eperl
  #  include EscapeEnhancer
  #end


  class PI::Eperl < PI::Engine
    include PerlGenerator

    def init_converter(properties={})
      @pi = 'perl'
      super(properties)
    end

  end


end
#--end of require 'erubis/engine/eperl'
#--begin of require 'erubis/engine/ejavascript'
##
## $Rev: 31 $
## $Release: 2.1.0 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--already included require 'erubis/engine'
#--already included require 'erubis/enhancer'


module Erubis


  module JavascriptGenerator
    include Generator

    def self.supported_properties()   # :nodoc:
      list = []
      #list << [:indent,   '',       "indent spaces (ex. '  ')"]
      #list << [:buf,      '_buf',   "output buffer name"]
      return list
    end

    def init_generator(properties={})
      super
      @escapefunc ||= 'escape'
      @indent = properties[:indent] || ''
      @buf = properties[:out] || '_buf'
    end

    def add_preamble(src)
      src << "#{@indent}var #{@buf} = [];"
    end

    def escape_text(text)
      @@table_ ||= { "\r"=>"\\r", "\n"=>"\\n\\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" }
      return text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] } || text
    end

    def add_indent(src, indent)
      src << (src.empty? || src[-1] == ?\n ? indent : ' ')
    end

    def add_text(src, text)
      return if text.empty?
      add_indent(src, @indent)
      src << @buf << '.push("'
      s = escape_text(text)
      if s[-1] == ?\n
        s[-2, 2] = ''
        src << s << "\");\n"
      else
        src << s << "\");"
      end
    end

    def add_stmt(src, code)
      src << code
    end

    def add_expr_literal(src, code)
      add_indent(src, @indent)
      code.strip!
      src << "#{@buf}.push(#{code});"
    end

    def add_expr_escaped(src, code)
      add_expr_literal(src, escaped_expr(code))
    end

    def add_expr_debug(src, code)
      add_indent(src, @indent)
      code.strip!
      src << "alert(\"*** debug: #{code}=\"+(#{code}));"
    end

    def add_postamble(src)
      src << "\n" if src[-1] == ?;
      src << @indent << 'document.write(' << @buf << ".join(\"\"));\n"
    end

  end


  ##
  ## engine for JavaScript
  ##
  class Ejavascript < Basic::Engine
    include JavascriptGenerator
  end


  class EscapedEjavascript < Ejavascript
    include EscapeEnhancer
  end


  #class XmlEjavascript < Ejavascript
  #  include EscapeEnhancer
  #end


  class PI::Ejavascript < PI::Engine
    include JavascriptGenerator

    def init_converter(properties={})
      @pi = 'js'
      super(properties)
    end

  end


end
#--end of require 'erubis/engine/ejavascript'


module Erubis


  Ejs = Ejavascript
  EscapedEjs = EscapedEjavascript


  class CommandOptionError < ErubisError
  end


  ##
  ## main class of command
  ##
  ## ex.
  ##   Main.main(ARGV)
  ##
  class Main

    def self.main(argv=ARGV)
      status = 0
      begin
        Main.new.execute(ARGV)
      rescue CommandOptionError => ex
        $stderr.puts ex.message
        status = 1
      end
      exit(status)
    end

    def initialize
      @single_options = "hvxTtSbeB"
      @arg_options    = "pcrfKIlaE"
      @option_names   = {
        ?h => :help,
        ?v => :version,
        ?x => :source,
        ?T => :notrim,
        ?t => :untabify,
        ?S => :intern,
        ?b => :bodyonly,
        ?B => :binding,
        ?p => :pattern,
        ?c => :class,
        ?e => :escape,
        ?r => :requires,
        ?f => :yamlfiles,
        ?K => :kanji,
        ?I => :includes,
        ?l => :lang,
        ?a => :action,
        ?E => :enhancers,
      }
      assert unless @single_options.length + @arg_options.length == @option_names.length
      (@single_options + @arg_options).each_byte do |ch|
        assert unless @option_names.key?(ch)
      end
    end


    def execute(argv=ARGV)
      ## parse command-line options
      options, properties = parse_argv(argv, @single_options, @arg_options)
      filenames = argv
      options[?h] = true if properties[:help]
      opts = Object.new
      arr = @option_names.collect { |ch, name| "def #{name}; @#{name}; end\n" }
      opts.instance_eval arr.join
      options.each do |ch, val|
        name = @option_names[ch]
        opts.instance_variable_set("@#{name}", val)
      end

      ## help, version, enhancer list
      if opts.help || opts.version
        puts version()         if opts.version
        puts usage()           if opts.help
        puts show_properties() if opts.help
        puts show_enhancers()  if opts.help
        return
      end

      ## include path
      opts.includes.split(/,/).each do |path|
        $: << path
      end if opts.includes

      ## require library
      opts.requires.split(/,/).each do |library|
        require library
      end if opts.requires

      ## action
      action = opts.action
      action ||= 'convert' if opts.source

      ## lang
      lang = opts.lang || 'ruby'
      action ||= 'convert' if opts.lang

      ## class name of Eruby
      classname = opts.class
      klass = get_classobj(classname, lang, properties[:pi])

      ## kanji code
      $KCODE = opts.kanji if opts.kanji

      ## read context values from yaml file
      yamlfiles = opts.yamlfiles
      context = load_yamlfiles(yamlfiles, opts)

      ## properties for engine
      properties[:escape]   = true         if opts.escape && !properties.key?(:escape)
      properties[:pattern]  = opts.pattern if opts.pattern
      properties[:trim]     = false        if opts.notrim
      properties[:preamble] = properties[:postamble] = false if opts.bodyonly
      properties[:pi]       = nil          if properties[:pi] == true

      ## create engine and extend enhancers
      engine = klass.new(nil, properties)
      enhancers = get_enhancers(opts.enhancers)
      #enhancers.push(Erubis::EscapeEnhancer) if opts.escape
      enhancers.each do |enhancer|
        engine.extend(enhancer)
        engine.bipattern = properties[:bipattern] if enhancer == Erubis::BiPatternEnhancer
      end

      ## convert and execute
      val = nil
      if filenames && !filenames.empty?
        filenames.each do |filename|
          test(?f, filename)  or raise CommandOptionError.new("#{filename}: file not found.")
          engine.filename = filename
          engine.convert!(File.read(filename))
          print val if val = do_action(action, engine, context, opts)
        end
      else
        engine.filename = '(stdin)'
        engine.convert!($stdin.read())
        print val if val = do_action(action, engine, context, opts)
      end

    end

    private

    def do_action(action, engine, context, opts)
      case action
      when 'convert'
        s = engine.src
      when nil, 'exec', 'execute'
        s = opts.binding ? engine.result(context) : engine.evaluate(context)
      else
        raise "*** internal error"
      end
      return s
    end

    def usage
      command = File.basename($0)
      s = <<END
erubis - embedded program converter for multi-language
Usage: #{command} [..options..] [file ...]
  -h, --help    : help
  -v            : version
  -x            : converted code
  -T            : don't trim spaces around '<% %>'
  -b            : body only (no preamble nor postamble)
  -e            : escape (equal to '--E Escape')
  -p pattern    : embedded pattern (default '<% %>')
  -l lang       : convert but no execute (ruby/php/c/java/scheme/perl/js)
  -E e1,e2,...  : enhancer names (Escape, PercentLine, BiPattern, ...)
  -I path       : library include path
  -K kanji      : kanji code (euc/sjis/utf8) (default none)
  -f file.yaml  : YAML file for context values (read stdin if filename is '-')
  -t            : expand tab character in YAML file
  -S            : convert mapping key from string to symbol in YAML file
  -B            : invoke 'result(binding)' instead of 'evaluate(context)'
  --pi=name     : parse '<?name ... ?>' instead of '<% ... %>'

END
      #'
      #  -c class      : class name (XmlEruby/PercentLineEruby/...) (default Eruby)
      #  -r library    : require library
      #  -a            : action (convert/execute)
      return s
    end


    def collect_supported_properties(erubis_klass)
      list = []
      erubis_klass.ancestors.each do |klass|
        if klass.respond_to?(:supported_properties)
          list.concat(klass.supported_properties)
        end
      end
      return list
    end

    def show_properties
      s = "supported properties:\n"
      basic_props = collect_supported_properties(Erubis::Basic::Engine)
      pi_props    = collect_supported_properties(Erubis::PI::Engine)
      list = []
      common_props = basic_props & pi_props
      list << ['(common)', common_props]
      list << ['(basic)',  basic_props - common_props]
      list << ['(pi)',     pi_props    - common_props]
      %w[ruby php c java scheme perl javascript].each do |lang|
        klass = Erubis.const_get("E#{lang}")
        list << [lang, collect_supported_properties(klass) - basic_props]
      end
      list.each do |lang, props|
        s << "  * #{lang}\n"
        props.each do |name, default_val, desc|
          s << ("     --%-23s : %s\n" % ["#{name}=#{default_val.inspect}", desc])
        end
      end
      s << "\n"
      return s
    end

    def show_enhancers
      s = "enhancers:\n"
      list = []
      ObjectSpace.each_object(Module) do |m| list << m end
      list.sort_by { |m| m.name }.each do |m|
        next unless m.name =~ /\AErubis::(.*)Enhancer\z/
        name = $1
        desc = m.desc
        s << ("  %-13s : %s\n" % [name, desc])
      end
      return s
    end

    def version
      release = ('$Release: 2.1.0 $' =~ /([.\d]+)/) && $1
      return release
    end

    def parse_argv(argv, arg_none='', arg_required='', arg_optional='')
      options = {}
      context = {}
      while argv[0] && argv[0][0] == ?-
        optstr = argv.shift
        optstr = optstr[1, optstr.length-1]
        #
        if optstr[0] == ?-    # context
          unless optstr =~ /\A\-([-\w]+)(?:=(.*))?/
            raise CommandOptionError.new("-#{optstr}: invalid context value.")
          end
          name = $1;  value = $2
          name  = name.gsub(/-/, '_').intern
          #value = value.nil? ? true : YAML.load(value)   # error, why?
          value = value.nil? ? true : YAML.load("---\n#{value}\n")
          context[name] = value
          #
        else                  # options
          while optstr && !optstr.empty?
            optchar = optstr[0]
            optstr[0,1] = ""
            if arg_none.include?(optchar)
              options[optchar] = true
            elsif arg_required.include?(optchar)
              arg = optstr.empty? ? argv.shift : optstr
              unless arg
                mesg = "-#{optchar.chr}: #{@option_args[optchar]} required."
                raise CommandOptionError.new(mesg)
              end
              options[optchar] = arg
              optstr = nil
            elsif arg_optional.include?(optchar)
              arg = optstr.empty? ? true : optstr
              options[optchar] = arg
              optstr = nil
            else
              raise CommandOptionError.new("-#{optchar.chr}: unknown option.")
            end
          end
        end
        #
      end  # end of while

      return options, context
    end


    def untabify(str, width=8)
      list = str.split(/\t/)
      last = list.pop
      sb = ''
      list.each do |s|
        column = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length
        n = width - (column % width)
        sb << s << (' ' * n)
      end
      sb << last
      return sb
    end
    #--
    #def untabify(str, width=8)
    #  sb = ''
    #  str.scan(/(.*?)\t/m) do |s, |
    #    len = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length
    #    sb << s << (" " * (width - len % width))
    #  end
    #  return $' ? (sb << $') : str
    #end
    #++


    def get_classobj(classname, lang, pi)
      classname ||= "E#{lang}"
      base_module = pi ? Erubis::PI : Erubis
      begin
        klass = base_module.const_get(classname)
      rescue NameError
        klass = nil
      end
      unless klass
        if lang
          msg = "-l #{lang}: invalid language name (class #{base_module.name}::#{classname} not found)."
        else
          msg = "-c #{classname}: invalid class name."
        end
        raise CommandOptionError.new(msg)
      end
      return klass
    end

    def get_enhancers(enhancer_names)
      return [] unless enhancer_names
      enhancers = []
      shortname = nil
      begin
        enhancer_names.split(/,/).each do |shortname|
          enhancers << Erubis.const_get("#{shortname}Enhancer")
        end
      rescue NameError
        raise CommandOptionError.new("#{shortname}: no such Enhancer (try '-E' to show all enhancers).")
      end
      return enhancers
    end

    def load_yamlfiles(yamlfiles, opts)
      hash = {}
      return hash unless yamlfiles
      yamlfiles.split(/,/).each do |yamlfile|
        if yamlfile == '-'
          str = $stdin.read()
        else
          test(?f, yamlfile)  or raise CommandOptionError.new("#{yamlfile}: file not found.")
          str = File.read(yamlfile)
        end
        str = yamlfile == '-' ? $stdin.read() : File.read(yamlfile)
        str = untabify(str) if opts.untabify
        ydoc = YAML.load(str)
        unless ydoc.is_a?(Hash)
          raise CommandOptionError.new("#{yamlfile}: root object is not a mapping.")
        end
        intern_hash_keys(ydoc) if opts.intern
        hash.update(ydoc)
      end
      context = hash
      return context
    end

    def intern_hash_keys(obj, done={})
      return if done.key?(obj.__id__)
      case obj
      when Hash
        done[obj.__id__] = obj
        obj.keys.each do |key|
          obj[key.intern] = obj.delete(key) if key.is_a?(String)
        end
        obj.values.each do |val|
          intern_hash_keys(val, done) if val.is_a?(Hash) || val.is_a?(Array)
        end
      when Array
        done[obj.__id__] = obj
        obj.each do |val|
          intern_hash_keys(val, done) if val.is_a?(Hash) || val.is_a?(Array)
        end
      end
    end

  end

end
#--end of require 'erubis/main'

Erubis::Main.main(ARGV)
