#!/usr/bin/ruby

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

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

require 'yaml'
#--begin of require 'erubis'
##
## $Rev: 21 $
## $Release: 2.0.1 $
## 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(input)
##   puts "--- source ---"
##   puts eruby.src
##   puts "--- result ---"
##   puts eruby.evaluate(:list=>list)
##   # or puts eruby.result(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: 24 $
## $Release: 2.0.1 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

#--begin of require 'abstract'
##
## $Rev: 1 $
## $Release$
## $Copyright$
##
##
## 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


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


  ##
  ## .[abstract] 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


  ##
  ## .[abstract] base engine class
  ##
  class Engine

    def self.supported_properties    # :nodoc:
      return [
              [:pattern,  '<% %>', "embed pattern"],
              #[:filename,  nil,    "filename"],
              [:trim,      true,   "trim spaces around <% ... %>"],
              [:preamble,  nil,    "preamble (no preamble when false)"],
              [:postamble, nil,    "postamble (no postamble when false)"],
              [:escape,    nil,     "escape function name"],
             ]
    end

    def initialize(input, properties={})
      #@input    = input
      @pattern   = properties[:pattern]
      @filename  = properties[:filename]
      @trim      = properties[:trim] != false
      @preamble  = properties[:preamble]
      @postamble = properties[:postamble]
      @escape    = properties[:escape]
      @src       = compile(input) if input
    end
    attr_reader :src
    attr_accessor :filename

    ## 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

    ## 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

    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
      end
    end
    protected :pattern_regexp

    ## compile input string into target language
    def compile(input)
      src = ""
      @preamble.nil? ? add_preamble(src) : (@preamble && (src << @preamble))
      regexp = pattern_regexp(@pattern)
      input.scan(regexp) do |text, lspace, indicator, code, rspace|
        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
          #flag_trim = @trim && lspace && rspace
          #add_text(src, lspace) if !flag_trim && lspace
          #n = code.count("\n") + (rspace ? 1 : 0)
          #add_stmt(src, "\n" * n)
          #add_text(src, rspace) if !flag_trim && rspace
        else                        # <%= %>
          add_text(src, lspace) if lspace
          add_expr(src, code, indicator)
          add_text(src, rspace) if rspace
        end
        #if indicator && indicator[0] == ?=
        #  flag_trim = false
        #else
        #  flag_trim = @trim && lspace && rspace
        #end
        ##flag_trim = @trim && !(indicator && indicator[0]==?=) && lspace && rspace
        #add_text(src, text)
        #add_text(src, lspace) if !flag_trim && lspace
        #if !indicator             # <% %>
        #  code = "#{lspace}#{code}#{rspace}" if flag_trim
        #  add_stmt(src, code)
        #elsif indicator[0] == ?\# # <%# %>
        #  n = code.count("\n") + (rspace ? 1 : 0)
        #  add_stmt(src, "\n" * n)
        #else                      # <%= %>
        #  add_expr(src, code, indicator)
        #end
        #add_text(src, rspace) if !flag_trim && rspace
      end
      rest = $' || input     # add input when no matched
      add_text(src, rest)
      @postamble.nil? ? add_postamble(src) : (@postamble && (src << @postamble))
      return src
    end

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

    protected

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

    ## .[abstract] return escaped expression code
    ##
    ## ex.
    ##   def escaped_expr(code)
    ##     @escape ||= 'escape'
    ##     return "#{@escape}(#{code.strip})"
    ##   end
    def escaped_expr(code)
      not_implemented
    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

    ## add expression code to src
    def add_expr(src, code, indicator)
      case indicator
      when '='
        add_expr_literal(src, code)
      when '=='
        add_expr_escaped(src, code)
      when '==='
        add_expr_debug(src, code)
      end
    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 of class Engine


end
#--end of require 'erubis/engine'
#--begin of require 'erubis/helper'
##
## $Rev: 21 $
## $Release: 2.0.1 $
## 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: 21 $
## $Release: 2.0.1 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##


#--already included require 'erubis/engine'


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 '='
        add_expr_escaped(src, code)
        #add_expr_literal(src, code)
      when '=='
        add_expr_literal(src, code)
        #add_expr_escaped(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=Context.new)
      _src = @src
      if context.is_a?(Hash)
        context.each do |key, val| instance_variable_set("@#{key}", val) end
      else
        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


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

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

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

    def compile(input)
      src = ""
      add_preamble(src)
      #regexp = pattern_regexp(@pattern)
      input.scan(SIMPLE_REGEXP) do |text, indicator, code|
        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 compile(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: 21 $
## $Release: 2.0.1 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

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


module Erubis


  ##
  ## engine for Ruby
  ##
  class Eruby < Engine
    #include StringBufferEnhancer
    include ArrayBufferEnhancer

    def self.supported_properties()  # :nodoc:
      return super
    end

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

    def escaped_expr(code)
      @escape ||= "Erubis::XmlHelper.escape_xml"
      return "#{@escape}(#{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


  ##
  ## 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


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.0.1 $
## 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: 21 $
## $Release: 2.0.1 $
## 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)
      @src = compile(input)
    end
    attr_reader :src

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

    def compile(input)
      src = "_buf = [];"           # preamble
      input.scan(EMBEDDED_PATTERN) do |text, indicator, code|
        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

end
#--end of require 'erubis/tiny'
#--begin of require 'erubis/engine/enhanced'
##
## $Rev: 19 $
## $Release: 2.0.1 $
## 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 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: 21 $
## $Release: 2.0.1 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##


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


module Erubis


  ##
  ## Eruby class which generates optimized ruby code
  ##
  class OptimizedEruby < Engine    # Eruby

    def self.supported_properties()  # :nodoc:
      return super
    end

    def initialize(input, properties={})
      @initialized = false
      @prev_is_expr = false
      super
    end

    protected

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

    def escaped_expr(code)
      @escape ||= 'Erubis::XmlHelper.escape_xml'
      return "#{@escape}(#{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


  ##
  ## 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: 24 $
## $Release: 2.0.1 $
## copyright(c) 2006 kuwata-lab all rights reserved.
##

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


module Erubis


  ##
  ## engine for PHP
  ##
  class Ephp < Engine

    def self.supported_properties()  # :nodoc:
      return super
    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 escaped_expr(code)
      @escape ||= 'htmlspecialchars'
      return "#{@escape}(#{code.strip})"
    end

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

    def add_expr_escaped(src, code)
      src << "<?php echo #{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


  class EscapedEphp < Ephp
    include EscapeEnhancer
  end


  #class XmlEphp < Ephp
  #  include EscapeEnhancer
  #end


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

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


module Erubis


  ##
  ## engine for C
  ##
  class Ec < Engine

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

    def initialize(input, properties={})
      @indent = properties[:indent] || ''
      @out = properties[:out] || 'stdout'
      super
    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)
      @escape ||= "escape"
      code.strip!
      if code =~ /\A(\".*?\")\s*,\s*(.*)/
        return "#{$1}, #{@escape}(#{$2})"
      else
        return "#{@escape}(#{code})"
      end
    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 << " fprintf(#{@out}, " << 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


  class EscapedEc < Ec
    include EscapeEnhancer
  end


  #class XmlEc < Ec
  #  include EscapeEnhancer
  #end


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

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


module Erubis


  ##
  ## engine for Java
  ##
  class Ejava < Engine

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

    def initialize(input, properties={})
      @indent = properties[:indent] || ''
      @buf = properties[:buf] || '_buf'
      @bufclass = properties[:bufclass] || 'StringBuffer'
      super
    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 escaped_expr(code)
      @escape ||= 'escape'
      return "#{@escape}(#{code.strip})"
    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
      src << ' ' << @buf << '.append(' << code.strip << ');'
    end

    def add_expr_escaped(src, code)
      src << @indent if src.empty? || src[-1] == ?\n
      src << ' ' << @buf << '.append(' << 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"
    end

  end


  class EscapedEjava < Ejava
    include EscapeEnhancer
  end


  #class XmlEjava < Ejava
  #  include EscapeEnhancer
  #end


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

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


module Erubis


  ##
  ## engine for Scheme
  ##
  class Escheme < Engine

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

    def initialize(input, properties={})
      @func = properties[:func] || '_add'   # or 'display'
      super
    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)
      @escape ||= 'escape'
      return "(#{@escape} #{code.strip})"
    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)
      src << "(#{@func} " << code.strip << ')'
    end

    def add_expr_escaped(src, code)
      src << "(#{@func} " << 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


  class EscapedEscheme < Escheme
    include EscapeEnhancer
  end


  #class XmlEscheme < Escheme
  #  include EscapeEnhancer
  #end


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

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


module Erubis


  ##
  ## engine for Perl
  ##
  class Eperl < Engine

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

    def initialize(input, properties={})
      @func = properties[:func] || 'print'
      super
    end

    def add_preamble(src)
      # empty
    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 escaped_expr(code)
      @escape ||= 'escape'
      return "escape(#{code.strip})"
    end

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

    def add_expr_escaped(src, code)
      src << @func << "(" << 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


  class EscapedEperl < Eperl
    include EscapeEnhancer
  end


  #class XmlEperl < Eperl
  #  include EscapeEnhancer
  #end


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

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


module Erubis


  ##
  ## engine for JavaScript
  ##
  class Ejavascript < Engine

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

    def initialize(input, properties={})
      @indent = properties[:indent] || ''
      @buf = properties[:out] || '_buf'
      #@bufclass = properties[:outclass] || 'StringBuffer'
      super
    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 escaped_expr(code)
      @escape ||= 'escape'
      return "#{@escape}(#{code.strip})"
    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)
      src << @buf << '.push(' << code.strip << ');'
    end

    def add_expr_escaped(src, code)
      add_indent(src, @indent)
      src << @buf << '.push(' << 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


  class EscapedEjavascript < Ejavascript
    include EscapeEnhancer
  end


  #class XmlEjavascript < Ejavascript
  #  include EscapeEnhancer
  #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 ||= 'compile' if opts.source

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

      ## class name of Eruby
      classname = opts.class
      klass = get_classobj(classname, lang)

      ## 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[:pattern]  = opts.pattern if opts.pattern
      properties[:trim]     = false        if opts.notrim
      properties[:preamble] = properties[:postamble] = false if opts.bodyonly

      ## 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

      ## compile 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.compile!(File.read(filename))
          print val if val = do_action(action, engine, context, opts)
        end
      else
        engine.filename = '(stdin)'
        engine.compile!($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 'compile'
        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 compiler for multi-language
Usage: #{command} [..options..] [file ...]
  -h, --help    : help
  -v            : version
  -x            : compiled 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       : compile but no execute (ruby/php/c/java/scheme/perl/js)
  -E enhancer,... : enhancer name (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)'

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

    def show_properties
      s = "supported properties:\n"
      %w[(common) ruby php c java scheme perl javascript].each do |lang|
        list = Erubis::Engine.supported_properties
        if lang != '(common)'
          klass = Erubis.const_get("E#{lang}")
          list = klass.supported_properties - list
        end
        s << "  * #{lang}\n"
        list.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.0.1 $' =~ /([.\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(text, width=8)
      sb = ''
      text.scan(/(.*?)\t/m) do |s, |
        len = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length
        sb << s << (" " * (width - len % width))
      end
      return $' ? (sb << $') : text
    end

    def get_classobj(classname, lang)
      unless classname
        classname = lang =~ /\Axml(.*)/ ? "EscapedE#{$1}" : "E#{lang}"
      end
      begin
        klass = Erubis.const_get(classname)
      rescue NameError
        klass = nil
      end
      unless klass
        if lang
          msg = "-l #{lang}: invalid language name (class Erubis::#{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
        convert_mapping_key_from_string_to_symbol(ydoc) if opts.intern
        hash.update(ydoc)
      end
      context = hash
      return context
    end

    def convert_mapping_key_from_string_to_symbol(ydoc)
      if ydoc.is_a?(Hash)
        ydoc.each do |key, val|
          ydoc[key.intern] = ydoc.delete(key) if key.is_a?(String)
          convert_mapping_key_from_string_to_symbol(val)
        end
      elsif ydoc.is_a?(Array)
        ydoc.each do |val|
          convert_mapping_key_from_string_to_symbol(val)
        end
      end
      return ydoc
    end

  end

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

Erubis::Main.main(ARGV)
