<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -115,12 +115,13 @@ module Tepee::Views
   def _markup body
     return '' if body.blank?
     body.gsub!(Tepee::Models::Page::PAGE_LINK) do
-      page = title = $1.underscore
+      page = title = $1
       title = $2 unless $2.empty?
+      page = page.gsub /\W/, '_'
       if Tepee::Models::Page.find(:all, :select =&gt; 'title').collect { |p| p.title }.include?(page)
-        %Q{&lt;a href=&quot;#{R Show, page}&quot;&gt;#{title}&lt;/a&gt;}
+        %Q{&lt;a href=&quot;#{self/R(Show, page)}&quot;&gt;#{title}&lt;/a&gt;}
       else
-        %Q{&lt;span&gt;#{title}&lt;a href=&quot;#{R Edit, page, 1}&quot;&gt;?&lt;/a&gt;&lt;/span&gt;}
+        %Q{&lt;span&gt;#{title}&lt;a href=&quot;#{self/R(Edit, page, 1)}&quot;&gt;?&lt;/a&gt;&lt;/span&gt;}
       end
     end
     RedCloth.new(body, [ :hard_breaks ]).to_html</diff>
      <filename>examples/camping/tepee.rb</filename>
    </modified>
    <modified>
      <diff>@@ -40,7 +40,16 @@ module Mongrel
               head[k] = vi
             end
           end
-          out &lt;&lt; controller.body
+          if controller.body.respond_to? :read
+            while chunk = controller.body.read(16384)
+              out &lt;&lt; chunk
+            end
+            if controller.body.respond_to? :close
+              controller.body.close
+            end
+          else
+            out &lt;&lt; controller.body
+          end
         end
       end
     end</diff>
      <filename>lib/mongrel/camping.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 #
 # setup.rb
 #
-# Copyright (c) 2000-2004 Minero Aoki
+# Copyright (c) 2000-2005 Minero Aoki
 #
 # This program is free software.
 # You can distribute/modify this program under the terms of
@@ -22,175 +22,67 @@ unless File.respond_to?(:read)   # Ruby 1.6
   end
 end
 
+unless Errno.const_defined?(:ENOTEMPTY)   # Windows?
+  module Errno
+    class ENOTEMPTY
+      # We do not raise this exception, implementation is not needed.
+    end
+  end
+end
+
 def File.binread(fname)
   open(fname, 'rb') {|f|
     return f.read
   }
 end
 
-# for corrupted windows stat(2)
+# for corrupted Windows' stat(2)
 def File.dir?(path)
   File.directory?((path[-1,1] == '/') ? path : path + '/')
 end
 
 
-class SetupError &lt; StandardError; end
-
-def setup_rb_error(msg)
-  raise SetupError, msg
-end
-
-#
-# Config
-#
-
-if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
-  ARGV.delete(arg)
-  require arg.split(/=/, 2)[1]
-  $&quot;.push 'rbconfig.rb'
-else
-  require 'rbconfig'
-end
-
-def multipackage_install?
-  FileTest.directory?(File.dirname($0) + '/packages')
-end
-
-
-class ConfigItem
-  def initialize(name, template, default, desc)
-    @name = name.freeze
-    @template = template
-    @value = default
-    @default = default.dup.freeze
-    @description = desc
-  end
-
-  attr_reader :name
-  attr_reader :description
-
-  attr_accessor :default
-  alias help_default default
-
-  def help_opt
-    &quot;--#{@name}=#{@template}&quot;
-  end
-
-  def value
-    @value
-  end
-
-  def eval(table)
-    @value.gsub(%r&lt;\$([^/]+)&gt;) { table[$1] }
-  end
-
-  def set(val)
-    @value = check(val)
-  end
-
-  private
-
-  def check(val)
-    setup_rb_error &quot;config: --#{name} requires argument&quot; unless val
-    val
-  end
-end
-
-class BoolItem &lt; ConfigItem
-  def config_type
-    'bool'
-  end
-
-  def help_opt
-    &quot;--#{@name}&quot;
-  end
-
-  private
+class ConfigTable
 
-  def check(val)
-    return 'yes' unless val
-    unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val
-      setup_rb_error &quot;config: --#{@name} accepts only yes/no for argument&quot;
-    end
-    (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
-  end
-end
+  include Enumerable
 
-class PathItem &lt; ConfigItem
-  def config_type
-    'path'
+  def initialize(rbconfig)
+    @rbconfig = rbconfig
+    @items = []
+    @table = {}
+    # options
+    @install_prefix = nil
+    @config_opt = nil
+    @verbose = true
+    @no_harm = false
   end
 
-  private
-
-  def check(path)
-    setup_rb_error &quot;config: --#{@name} requires argument&quot;  unless path
-    path[0,1] == '$' ? path : File.expand_path(path)
-  end
-end
+  attr_accessor :install_prefix
+  attr_accessor :config_opt
 
-class ProgramItem &lt; ConfigItem
-  def config_type
-    'program'
-  end
-end
+  attr_writer :verbose
 
-class SelectItem &lt; ConfigItem
-  def initialize(name, template, default, desc)
-    super
-    @ok = template.split('/')
-  end
-
-  def config_type
-    'select'
+  def verbose?
+    @verbose
   end
 
-  private
+  attr_writer :no_harm
 
-  def check(val)
-    unless @ok.include?(val.strip)
-      setup_rb_error &quot;config: use --#{@name}=#{@template} (#{val})&quot;
-    end
-    val.strip
+  def no_harm?
+    @no_harm
   end
-end
 
-class PackageSelectionItem &lt; ConfigItem
-  def initialize(name, template, default, help_default, desc)
-    super name, template, default, desc
-    @help_default = help_default
+  def [](key)
+    lookup(key).resolve(self)
   end
 
-  attr_reader :help_default
-
-  def config_type
-    'package'
+  def []=(key, val)
+    lookup(key).set val
   end
 
-  private
-
-  def check(val)
-    unless File.dir?(&quot;packages/#{val}&quot;)
-      setup_rb_error &quot;config: no such package: #{val}&quot;
-    end
-    val
+  def names
+    @items.map {|i| i.name }
   end
-end
-
-class ConfigTable_class
-
-  def initialize(items)
-    @items = items
-    @table = {}
-    items.each do |i|
-      @table[i.name] = i
-    end
-    ALIASES.each do |ali, name|
-      @table[ali] = @table[name]
-    end
-  end
-
-  include Enumerable
 
   def each(&amp;block)
     @items.each(&amp;block)
@@ -201,7 +93,7 @@ class ConfigTable_class
   end
 
   def lookup(name)
-    @table[name] or raise ArgumentError, &quot;no such config item: #{name}&quot;
+    @table[name] or setup_rb_error &quot;no such config item: #{name}&quot;
   end
 
   def add(item)
@@ -216,24 +108,24 @@ class ConfigTable_class
     item
   end
 
-  def new
-    dup()
+  def load_script(path, inst = nil)
+    if File.file?(path)
+      MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
+    end
   end
 
   def savefile
     '.config'
   end
 
-  def load
+  def load_savefile
     begin
-      t = dup()
       File.foreach(savefile()) do |line|
         k, v = *line.split(/=/, 2)
-        t[k] = v.strip
+        self[k] = v.strip
       end
-      t
     rescue Errno::ENOENT
-      setup_rb_error $!.message + &quot;#{File.basename($0)} config first&quot;
+      setup_rb_error $!.message + &quot;\n#{File.basename($0)} config first&quot;
     end
   end
 
@@ -241,117 +133,151 @@ class ConfigTable_class
     @items.each {|i| i.value }
     File.open(savefile(), 'w') {|f|
       @items.each do |i|
-        f.printf &quot;%s=%s\n&quot;, i.name, i.value if i.value
+        f.printf &quot;%s=%s\n&quot;, i.name, i.value if i.value? and i.value
       end
     }
   end
 
-  def [](key)
-    lookup(key).eval(self)
+  def load_standard_entries
+    standard_entries(@rbconfig).each do |ent|
+      add ent
+    end
   end
 
-  def []=(key, val)
-    lookup(key).set val
-  end
+  def standard_entries(rbconfig)
+    c = rbconfig
+
+    rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
+
+    major = c['MAJOR'].to_i
+    minor = c['MINOR'].to_i
+    teeny = c['TEENY'].to_i
+    version = &quot;#{major}.#{minor}&quot;
+
+    # ruby ver. &gt;= 1.4.4?
+    newpath_p = ((major &gt;= 2) or
+                 ((major == 1) and
+                  ((minor &gt;= 5) or
+                   ((minor == 4) and (teeny &gt;= 4)))))
+
+    if c['rubylibdir']
+      # V &gt; 1.6.3
+      libruby         = &quot;#{c['prefix']}/lib/ruby&quot;
+      librubyver      = c['rubylibdir']
+      librubyverarch  = c['archdir']
+      siteruby        = c['sitedir']
+      siterubyver     = c['sitelibdir']
+      siterubyverarch = c['sitearchdir']
+    elsif newpath_p
+      # 1.4.4 &lt;= V &lt;= 1.6.3
+      libruby         = &quot;#{c['prefix']}/lib/ruby&quot;
+      librubyver      = &quot;#{c['prefix']}/lib/ruby/#{version}&quot;
+      librubyverarch  = &quot;#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}&quot;
+      siteruby        = c['sitedir']
+      siterubyver     = &quot;$siteruby/#{version}&quot;
+      siterubyverarch = &quot;$siterubyver/#{c['arch']}&quot;
+    else
+      # V &lt; 1.4.4
+      libruby         = &quot;#{c['prefix']}/lib/ruby&quot;
+      librubyver      = &quot;#{c['prefix']}/lib/ruby/#{version}&quot;
+      librubyverarch  = &quot;#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}&quot;
+      siteruby        = &quot;#{c['prefix']}/lib/ruby/#{version}/site_ruby&quot;
+      siterubyver     = siteruby
+      siterubyverarch = &quot;$siterubyver/#{c['arch']}&quot;
+    end
+    parameterize = lambda {|path|
+      path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
+    }
 
-end
+    if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
+      makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
+    else
+      makeprog = 'make'
+    end
 
-c = ::Config::CONFIG
-
-rubypath = c['bindir'] + '/' + c['ruby_install_name']
-
-major = c['MAJOR'].to_i
-minor = c['MINOR'].to_i
-teeny = c['TEENY'].to_i
-version = &quot;#{major}.#{minor}&quot;
-
-# ruby ver. &gt;= 1.4.4?
-newpath_p = ((major &gt;= 2) or
-             ((major == 1) and
-              ((minor &gt;= 5) or
-               ((minor == 4) and (teeny &gt;= 4)))))
-
-if c['rubylibdir']
-  # V &lt; 1.6.3
-  _stdruby         = c['rubylibdir']
-  _siteruby        = c['sitedir']
-  _siterubyver     = c['sitelibdir']
-  _siterubyverarch = c['sitearchdir']
-elsif newpath_p
-  # 1.4.4 &lt;= V &lt;= 1.6.3
-  _stdruby         = &quot;$prefix/lib/ruby/#{version}&quot;
-  _siteruby        = c['sitedir']
-  _siterubyver     = &quot;$siteruby/#{version}&quot;
-  _siterubyverarch = &quot;$siterubyver/#{c['arch']}&quot;
-else
-  # V &lt; 1.4.4
-  _stdruby         = &quot;$prefix/lib/ruby/#{version}&quot;
-  _siteruby        = &quot;$prefix/lib/ruby/#{version}/site_ruby&quot;
-  _siterubyver     = _siteruby
-  _siterubyverarch = &quot;$siterubyver/#{c['arch']}&quot;
-end
-libdir = '-* dummy libdir *-'
-stdruby = '-* dummy rubylibdir *-'
-siteruby = '-* dummy site_ruby *-'
-siterubyver = '-* dummy site_ruby version *-'
-parameterize = lambda {|path|
-  path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\
-      .sub(/\A#{Regexp.quote(libdir)}/,      '$libdir')\
-      .sub(/\A#{Regexp.quote(stdruby)}/,     '$stdruby')\
-      .sub(/\A#{Regexp.quote(siteruby)}/,    '$siteruby')\
-      .sub(/\A#{Regexp.quote(siterubyver)}/, '$siterubyver')
-}
-libdir          = parameterize.call(c['libdir'])
-stdruby         = parameterize.call(_stdruby)
-siteruby        = parameterize.call(_siteruby)
-siterubyver     = parameterize.call(_siterubyver)
-siterubyverarch = parameterize.call(_siterubyverarch)
-
-if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
-  makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
-else
-  makeprog = 'make'
-end
+    [
+      ExecItem.new('installdirs', 'std/site/home',
+                   'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
+          {|val, table|
+            case val
+            when 'std'
+              table['rbdir'] = '$librubyver'
+              table['sodir'] = '$librubyverarch'
+            when 'site'
+              table['rbdir'] = '$siterubyver'
+              table['sodir'] = '$siterubyverarch'
+            when 'home'
+              setup_rb_error '$HOME was not set' unless ENV['HOME']
+              table['prefix'] = ENV['HOME']
+              table['rbdir'] = '$libdir/ruby'
+              table['sodir'] = '$libdir/ruby'
+            end
+          },
+      PathItem.new('prefix', 'path', c['prefix'],
+                   'path prefix of target environment'),
+      PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
+                   'the directory for commands'),
+      PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
+                   'the directory for libraries'),
+      PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
+                   'the directory for shared data'),
+      PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
+                   'the directory for man pages'),
+      PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
+                   'the directory for system configuration files'),
+      PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
+                   'the directory for local state data'),
+      PathItem.new('libruby', 'path', libruby,
+                   'the directory for ruby libraries'),
+      PathItem.new('librubyver', 'path', librubyver,
+                   'the directory for standard ruby libraries'),
+      PathItem.new('librubyverarch', 'path', librubyverarch,
+                   'the directory for standard ruby extensions'),
+      PathItem.new('siteruby', 'path', siteruby,
+          'the directory for version-independent aux ruby libraries'),
+      PathItem.new('siterubyver', 'path', siterubyver,
+                   'the directory for aux ruby libraries'),
+      PathItem.new('siterubyverarch', 'path', siterubyverarch,
+                   'the directory for aux ruby binaries'),
+      PathItem.new('rbdir', 'path', '$siterubyver',
+                   'the directory for ruby scripts'),
+      PathItem.new('sodir', 'path', '$siterubyverarch',
+                   'the directory for ruby extentions'),
+      PathItem.new('rubypath', 'path', rubypath,
+                   'the path to set to #! line'),
+      ProgramItem.new('rubyprog', 'name', rubypath,
+                      'the ruby program using for installation'),
+      ProgramItem.new('makeprog', 'name', makeprog,
+                      'the make program to compile ruby extentions'),
+      SelectItem.new('shebang', 'all/ruby/never', 'ruby',
+                     'shebang line (#!) editing mode'),
+      BoolItem.new('without-ext', 'yes/no', 'no',
+                   'does not compile/install ruby extentions')
+    ]
+  end
+  private :standard_entries
+
+  def load_multipackage_entries
+    multipackage_entries().each do |ent|
+      add ent
+    end
+  end
+
+  def multipackage_entries
+    [
+      PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
+                               'package names that you want to install'),
+      PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
+                               'package names that you do not want to install')
+    ]
+  end
+  private :multipackage_entries
 
-common_conf = [
-  PathItem.new('prefix', 'path', c['prefix'],
-               'path prefix of target environment'),
-  PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
-               'the directory for commands'),
-  PathItem.new('libdir', 'path', libdir,
-               'the directory for libraries'),
-  PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
-               'the directory for shared data'),
-  PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
-               'the directory for man pages'),
-  PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
-               'the directory for man pages'),
-  PathItem.new('stdruby', 'path', stdruby,
-               'the directory for standard ruby libraries'),
-  PathItem.new('siteruby', 'path', siteruby,
-      'the directory for version-independent aux ruby libraries'),
-  PathItem.new('siterubyver', 'path', siterubyver,
-               'the directory for aux ruby libraries'),
-  PathItem.new('siterubyverarch', 'path', siterubyverarch,
-               'the directory for aux ruby binaries'),
-  PathItem.new('rbdir', 'path', '$siterubyver',
-               'the directory for ruby scripts'),
-  PathItem.new('sodir', 'path', '$siterubyverarch',
-               'the directory for ruby extentions'),
-  PathItem.new('rubypath', 'path', rubypath,
-               'the path to set to #! line'),
-  ProgramItem.new('rubyprog', 'name', rubypath,
-                  'the ruby program using for installation'),
-  ProgramItem.new('makeprog', 'name', makeprog,
-                  'the make program to compile ruby extentions'),
-  SelectItem.new('shebang', 'all/ruby/never', 'ruby',
-                 'shebang line (#!) editing mode'),
-  BoolItem.new('without-ext', 'yes/no', 'no',
-               'does not compile/install ruby extentions')
-]
-class ConfigTable_class   # open again
   ALIASES = {
-    'std-ruby'         =&gt; 'stdruby',
+    'std-ruby'         =&gt; 'librubyver',
+    'stdruby'          =&gt; 'librubyver',
+    'rubylibdir'       =&gt; 'librubyver',
+    'archdir'          =&gt; 'librubyverarch',
     'site-ruby-common' =&gt; 'siteruby',     # For backward compatibility
     'site-ruby'        =&gt; 'siterubyver',  # For backward compatibility
     'bin-dir'          =&gt; 'bindir',
@@ -365,78 +291,248 @@ class ConfigTable_class   # open again
     'make-prog'        =&gt; 'makeprog',
     'make'             =&gt; 'makeprog'
   }
-end
-multipackage_conf = [
-  PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
-                           'package names that you want to install'),
-  PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
-                           'package names that you do not want to install')
-]
-if multipackage_install?
-  ConfigTable = ConfigTable_class.new(common_conf + multipackage_conf)
-else
-  ConfigTable = ConfigTable_class.new(common_conf)
-end
-
 
-module MetaConfigAPI
-
-  def eval_file_ifexist(fname)
-    instance_eval File.read(fname), fname, 1 if File.file?(fname)
+  def fixup
+    ALIASES.each do |ali, name|
+      @table[ali] = @table[name]
+    end
+    @items.freeze
+    @table.freeze
+    @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
   end
 
-  def config_names
-    ConfigTable.map {|i| i.name }
+  def parse_opt(opt)
+    m = @options_re.match(opt) or setup_rb_error &quot;config: unknown option #{opt}&quot;
+    m.to_a[1,2]
   end
 
-  def config?(name)
-    ConfigTable.key?(name)
+  def dllext
+    @rbconfig['DLEXT']
   end
 
-  def bool_config?(name)
-    ConfigTable.lookup(name).config_type == 'bool'
+  def value_config?(name)
+    lookup(name).value?
   end
 
-  def path_config?(name)
-    ConfigTable.lookup(name).config_type == 'path'
-  end
+  class Item
+    def initialize(name, template, default, desc)
+      @name = name.freeze
+      @template = template
+      @value = default
+      @default = default
+      @description = desc
+    end
 
-  def value_config?(name)
-    case ConfigTable.lookup(name).config_type
-    when 'bool', 'path'
+    attr_reader :name
+    attr_reader :description
+
+    attr_accessor :default
+    alias help_default default
+
+    def help_opt
+      &quot;--#{@name}=#{@template}&quot;
+    end
+
+    def value?
       true
-    else
-      false
+    end
+
+    def value
+      @value
+    end
+
+    def resolve(table)
+      @value.gsub(%r&lt;\$([^/]+)&gt;) { table[$1] }
+    end
+
+    def set(val)
+      @value = check(val)
+    end
+
+    private
+
+    def check(val)
+      setup_rb_error &quot;config: --#{name} requires argument&quot; unless val
+      val
     end
   end
 
-  def add_config(item)
-    ConfigTable.add item
+  class BoolItem &lt; Item
+    def config_type
+      'bool'
+    end
+
+    def help_opt
+      &quot;--#{@name}&quot;
+    end
+
+    private
+
+    def check(val)
+      return 'yes' unless val
+      case val
+      when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
+      when /\An(o)?\z/i, /\Af(alse)\z/i  then 'no'
+      else
+        setup_rb_error &quot;config: --#{@name} accepts only yes/no for argument&quot;
+      end
+    end
   end
 
-  def add_bool_config(name, default, desc)
-    ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
+  class PathItem &lt; Item
+    def config_type
+      'path'
+    end
+
+    private
+
+    def check(path)
+      setup_rb_error &quot;config: --#{@name} requires argument&quot;  unless path
+      path[0,1] == '$' ? path : File.expand_path(path)
+    end
   end
 
-  def add_path_config(name, default, desc)
-    ConfigTable.add PathItem.new(name, 'path', default, desc)
+  class ProgramItem &lt; Item
+    def config_type
+      'program'
+    end
   end
 
-  def set_config_default(name, default)
-    ConfigTable.lookup(name).default = default
+  class SelectItem &lt; Item
+    def initialize(name, selection, default, desc)
+      super
+      @ok = selection.split('/')
+    end
+
+    def config_type
+      'select'
+    end
+
+    private
+
+    def check(val)
+      unless @ok.include?(val.strip)
+        setup_rb_error &quot;config: use --#{@name}=#{@template} (#{val})&quot;
+      end
+      val.strip
+    end
   end
 
-  def remove_config(name)
-    ConfigTable.remove(name)
+  class ExecItem &lt; Item
+    def initialize(name, selection, desc, &amp;block)
+      super name, selection, nil, desc
+      @ok = selection.split('/')
+      @action = block
+    end
+
+    def config_type
+      'exec'
+    end
+
+    def value?
+      false
+    end
+
+    def resolve(table)
+      setup_rb_error &quot;$#{name()} wrongly used as option value&quot;
+    end
+
+    undef set
+
+    def evaluate(val, table)
+      v = val.strip.downcase
+      unless @ok.include?(v)
+        setup_rb_error &quot;invalid option --#{@name}=#{val} (use #{@template})&quot;
+      end
+      @action.call v, table
+    end
   end
 
-end
+  class PackageSelectionItem &lt; Item
+    def initialize(name, template, default, help_default, desc)
+      super name, template, default, desc
+      @help_default = help_default
+    end
 
+    attr_reader :help_default
 
-#
-# File Operations
-#
+    def config_type
+      'package'
+    end
+
+    private
+
+    def check(val)
+      unless File.dir?(&quot;packages/#{val}&quot;)
+        setup_rb_error &quot;config: no such package: #{val}&quot;
+      end
+      val
+    end
+  end
+
+  class MetaConfigEnvironment
+    def initialize(config, installer)
+      @config = config
+      @installer = installer
+    end
+
+    def config_names
+      @config.names
+    end
+
+    def config?(name)
+      @config.key?(name)
+    end
+
+    def bool_config?(name)
+      @config.lookup(name).config_type == 'bool'
+    end
+
+    def path_config?(name)
+      @config.lookup(name).config_type == 'path'
+    end
+
+    def value_config?(name)
+      @config.lookup(name).config_type != 'exec'
+    end
+
+    def add_config(item)
+      @config.add item
+    end
+
+    def add_bool_config(name, default, desc)
+      @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
+    end
 
+    def add_path_config(name, default, desc)
+      @config.add PathItem.new(name, 'path', default, desc)
+    end
+
+    def set_config_default(name, default)
+      @config.lookup(name).default = default
+    end
+
+    def remove_config(name)
+      @config.remove(name)
+    end
+
+    # For only multipackage
+    def packages
+      raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
+      @installer.packages
+    end
+
+    # For only multipackage
+    def declare_packages(list)
+      raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
+      @installer.packages = list
+    end
+  end
+
+end   # class ConfigTable
+
+
+# This module requires: #verbose?, #no_harm?
 module FileOperations
 
   def mkdir_p(dirname, prefix = nil)
@@ -444,7 +540,7 @@ module FileOperations
     $stderr.puts &quot;mkdir -p #{dirname}&quot; if verbose?
     return if no_harm?
 
-    # does not check '/'... it's too abnormal case
+    # Does not check '/', it's too abnormal.
     dirs = File.expand_path(dirname).split(%r&lt;(?=/)&gt;)
     if /\A[a-z]:\z/i =~ dirs[0]
       disk = dirs.shift
@@ -456,49 +552,73 @@ module FileOperations
     end
   end
 
-  def rm_f(fname)
-    $stderr.puts &quot;rm -f #{fname}&quot; if verbose?
+  def rm_f(path)
+    $stderr.puts &quot;rm -f #{path}&quot; if verbose?
     return if no_harm?
-
-    if File.exist?(fname) or File.symlink?(fname)
-      File.chmod 0777, fname
-      File.unlink fname
-    end
+    force_remove_file path
   end
 
-  def rm_rf(dn)
-    $stderr.puts &quot;rm -rf #{dn}&quot; if verbose?
+  def rm_rf(path)
+    $stderr.puts &quot;rm -rf #{path}&quot; if verbose?
     return if no_harm?
+    remove_tree path
+  end
+
+  def remove_tree(path)
+    if File.symlink?(path)
+      remove_file path
+    elsif File.dir?(path)
+      remove_tree0 path
+    else
+      force_remove_file path
+    end
+  end
 
-    Dir.chdir dn
-    Dir.foreach('.') do |fn|
-      next if fn == '.'
-      next if fn == '..'
-      if File.dir?(fn)
-        verbose_off {
-          rm_rf fn
-        }
+  def remove_tree0(path)
+    Dir.foreach(path) do |ent|
+      next if ent == '.'
+      next if ent == '..'
+      entpath = &quot;#{path}/#{ent}&quot;
+      if File.symlink?(entpath)
+        remove_file entpath
+      elsif File.dir?(entpath)
+        remove_tree0 entpath
       else
-        verbose_off {
-          rm_f fn
-        }
+        force_remove_file entpath
       end
     end
-    Dir.chdir '..'
-    Dir.rmdir dn
+    begin
+      Dir.rmdir path
+    rescue Errno::ENOTEMPTY
+      # directory may not be empty
+    end
   end
 
   def move_file(src, dest)
-    File.unlink dest if File.exist?(dest)
+    force_remove_file dest
     begin
       File.rename src, dest
     rescue
-      File.open(dest, 'wb') {|f| f.write File.binread(src) }
+      File.open(dest, 'wb') {|f|
+        f.write File.binread(src)
+      }
       File.chmod File.stat(src).mode, dest
       File.unlink src
     end
   end
 
+  def force_remove_file(path)
+    begin
+      remove_file path
+    rescue
+    end
+  end
+
+  def remove_file(path)
+    File.chmod 0777, path
+    File.unlink path
+  end
+
   def install(from, dest, mode, prefix = nil)
     $stderr.puts &quot;install #{from} #{dest}&quot; if verbose?
     return if no_harm?
@@ -530,66 +650,42 @@ module FileOperations
     new_content != File.binread(path)
   end
 
-  def command(str)
-    $stderr.puts str if verbose?
-    system str or raise RuntimeError, &quot;'system #{str}' failed&quot;
+  def command(*args)
+    $stderr.puts args.join(' ') if verbose?
+    system(*args) or raise RuntimeError,
+        &quot;system(#{args.map{|a| a.inspect }.join(' ')}) failed&quot;
   end
 
-  def ruby(str)
-    command config('rubyprog') + ' ' + str
+  def ruby(*args)
+    command config('rubyprog'), *args
   end
   
-  def make(task = '')
-    command config('makeprog') + ' ' + task
+  def make(task = nil)
+    command(*[config('makeprog'), task].compact)
   end
 
   def extdir?(dir)
-    File.exist?(dir + '/MANIFEST')
+    File.exist?(&quot;#{dir}/MANIFEST&quot;) or File.exist?(&quot;#{dir}/extconf.rb&quot;)
   end
 
-  def all_files_in(dirname)
-    Dir.open(dirname) {|d|
-      return d.select {|ent| File.file?(&quot;#{dirname}/#{ent}&quot;) }
+  def files_of(dir)
+    Dir.open(dir) {|d|
+      return d.select {|ent| File.file?(&quot;#{dir}/#{ent}&quot;) }
     }
   end
 
-  REJECT_DIRS = %w(
-    CVS SCCS RCS CVS.adm .svn
-  )
+  DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
 
-  def all_dirs_in(dirname)
-    Dir.open(dirname) {|d|
-      return d.select {|n| File.dir?(&quot;#{dirname}/#{n}&quot;) } - %w(. ..) - REJECT_DIRS
+  def directories_of(dir)
+    Dir.open(dir) {|d|
+      return d.select {|ent| File.dir?(&quot;#{dir}/#{ent}&quot;) } - DIR_REJECT
     }
   end
 
 end
 
 
-#
-# Main Installer
-#
-
-module HookUtils
-
-  def run_hook(name)
-    try_run_hook &quot;#{curr_srcdir()}/#{name}&quot; or
-    try_run_hook &quot;#{curr_srcdir()}/#{name}.rb&quot;
-  end
-
-  def try_run_hook(fname)
-    return false unless File.file?(fname)
-    begin
-      instance_eval File.read(fname), fname, 1
-    rescue
-      setup_rb_error &quot;hook #{fname} failed:\n&quot; + $!.message
-    end
-    true
-  end
-
-end
-
-
+# This module requires: #srcdir_root, #objdir_root, #relpath
 module HookScriptAPI
 
   def get_config(key)
@@ -598,6 +694,7 @@ module HookScriptAPI
 
   alias config get_config
 
+  # obsolete: use metaconfig to change configuration
   def set_config(key, val)
     @config[key] = val
   end
@@ -606,10 +703,6 @@ module HookScriptAPI
   # srcdir/objdir (works only in the package directory)
   #
 
-  #abstract srcdir_root
-  #abstract objdir_root
-  #abstract relpath
-
   def curr_srcdir
     &quot;#{srcdir_root()}/#{relpath()}&quot;
   end
@@ -631,7 +724,7 @@ module HookScriptAPI
   end
   
   def srcfile?(path)
-    File.file? srcfile(path)
+    File.file?(srcfile(path))
   end
 
   def srcentries(path = '.')
@@ -657,8 +750,8 @@ end
 
 class ToplevelInstaller
 
-  Version   = '3.3.1'
-  Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
+  Version   = '3.4.1'
+  Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
 
   TASKS = [
     [ 'all',      'do config, setup, then install' ],
@@ -666,27 +759,44 @@ class ToplevelInstaller
     [ 'show',     'shows current configuration' ],
     [ 'setup',    'compiles ruby extentions and others' ],
     [ 'install',  'installs files' ],
+    [ 'test',     'run all tests in test/' ],
     [ 'clean',    &quot;does `make clean' for each extention&quot; ],
     [ 'distclean',&quot;does `make distclean' for each extention&quot; ]
   ]
 
   def ToplevelInstaller.invoke
-    instance().invoke
+    config = ConfigTable.new(load_rbconfig())
+    config.load_standard_entries
+    config.load_multipackage_entries if multipackage?
+    config.fixup
+    klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
+    klass.new(File.dirname($0), config).invoke
   end
 
-  @singleton = nil
-
-  def ToplevelInstaller.instance
-    @singleton ||= new(File.dirname($0))
-    @singleton
+  def ToplevelInstaller.multipackage?
+    File.dir?(File.dirname($0) + '/packages')
   end
 
-  include MetaConfigAPI
+  def ToplevelInstaller.load_rbconfig
+    if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
+      ARGV.delete(arg)
+      load File.expand_path(arg.split(/=/, 2)[1])
+      $&quot;.push 'rbconfig.rb'
+    else
+      require 'rbconfig'
+    end
+    ::Config::CONFIG
+  end
 
-  def initialize(ardir_root)
-    @config = nil
-    @options = { 'verbose' =&gt; true }
+  def initialize(ardir_root, config)
     @ardir = File.expand_path(ardir_root)
+    @config = config
+    # cache
+    @valid_task_re = nil
+  end
+
+  def config(key)
+    @config[key]
   end
 
   def inspect
@@ -697,14 +807,20 @@ class ToplevelInstaller
     run_metaconfigs
     case task = parsearg_global()
     when nil, 'all'
-      @config = load_config('config')
       parsearg_config
       init_installers
       exec_config
       exec_setup
       exec_install
     else
-      @config = load_config(task)
+      case task
+      when 'config', 'test'
+        ;
+      when 'clean', 'distclean'
+        @config.load_savefile if File.exist?(@config.savefile)
+      else
+        @config.load_savefile
+      end
       __send__ &quot;parsearg_#{task}&quot;
       init_installers
       __send__ &quot;exec_#{task}&quot;
@@ -712,25 +828,11 @@ class ToplevelInstaller
   end
   
   def run_metaconfigs
-    eval_file_ifexist &quot;#{@ardir}/metaconfig&quot;
-  end
-
-  def load_config(task)
-    case task
-    when 'config'
-      ConfigTable.new
-    when 'clean', 'distclean'
-      if File.exist?(ConfigTable.savefile)
-      then ConfigTable.load
-      else ConfigTable.new
-      end
-    else
-      ConfigTable.load
-    end
+    @config.load_script &quot;#{@ardir}/metaconfig&quot;
   end
 
   def init_installers
-    @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
+    @installer = Installer.new(@config, @ardir, File.expand_path('.'))
   end
 
   #
@@ -754,78 +856,89 @@ class ToplevelInstaller
   #
 
   def parsearg_global
-    valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
-
     while arg = ARGV.shift
       case arg
       when /\A\w+\z/
-        setup_rb_error &quot;invalid task: #{arg}&quot; unless valid_task =~ arg
+        setup_rb_error &quot;invalid task: #{arg}&quot; unless valid_task?(arg)
         return arg
-
       when '-q', '--quiet'
-        @options['verbose'] = false
-
-      when       '--verbose'
-        @options['verbose'] = true
-
-      when '-h', '--help'
+        @config.verbose = false
+      when '--verbose'
+        @config.verbose = true
+      when '--help'
         print_usage $stdout
         exit 0
-
-      when '-v', '--version'
+      when '--version'
         puts &quot;#{File.basename($0)} version #{Version}&quot;
         exit 0
-      
       when '--copyright'
         puts Copyright
         exit 0
-
       else
         setup_rb_error &quot;unknown global option '#{arg}'&quot;
       end
     end
-
     nil
   end
 
+  def valid_task?(t)
+    valid_task_re() =~ t
+  end
+
+  def valid_task_re
+    @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
+  end
 
   def parsearg_no_options
     unless ARGV.empty?
-      setup_rb_error &quot;#{task}:  unknown options: #{ARGV.join ' '}&quot;
+      task = caller(0).first.slice(%r&lt;`parsearg_(\w+)'&gt;, 1)
+      setup_rb_error &quot;#{task}: unknown options: #{ARGV.join(' ')}&quot;
     end
   end
 
   alias parsearg_show       parsearg_no_options
   alias parsearg_setup      parsearg_no_options
+  alias parsearg_test       parsearg_no_options
   alias parsearg_clean      parsearg_no_options
   alias parsearg_distclean  parsearg_no_options
 
   def parsearg_config
-    re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/
-    @options['config-opt'] = []
-
+    evalopt = []
+    set = []
+    @config.config_opt = []
     while i = ARGV.shift
       if /\A--?\z/ =~ i
-        @options['config-opt'] = ARGV.dup
+        @config.config_opt = ARGV.dup
         break
       end
-      m = re.match(i)  or setup_rb_error &quot;config: unknown option #{i}&quot;
-      name, value = *m.to_a[1,2]
-      @config[name] = value
+      name, value = *@config.parse_opt(i)
+      if @config.value_config?(name)
+        @config[name] = value
+      else
+        evalopt.push [name, value]
+      end
+      set.push name
+    end
+    evalopt.each do |name, value|
+      @config.lookup(name).evaluate value, @config
+    end
+    # Check if configuration is valid
+    set.each do |n|
+      @config[n] if @config.value_config?(n)
     end
   end
 
   def parsearg_install
-    @options['no-harm'] = false
-    @options['install-prefix'] = ''
+    @config.no_harm = false
+    @config.install_prefix = ''
     while a = ARGV.shift
       case a
-      when /\A--no-harm\z/
-        @options['no-harm'] = true
-      when /\A--prefix=(.*)\z/
-        path = $1
+      when '--no-harm'
+        @config.no_harm = true
+      when /\A--prefix=/
+        path = a.split(/=/, 2)[1]
         path = File.expand_path(path) unless path[0,1] == '/'
-        @options['install-prefix'] = path
+        @config.install_prefix = path
       else
         setup_rb_error &quot;install: unknown option #{a}&quot;
       end
@@ -847,8 +960,8 @@ class ToplevelInstaller
     out.puts 'Global options:'
     out.printf fmt, '-q,--quiet',   'suppress message outputs'
     out.printf fmt, '   --verbose', 'output messages verbosely'
-    out.printf fmt, '-h,--help',    'print this message'
-    out.printf fmt, '-v,--version', 'print version and quit'
+    out.printf fmt, '   --help',    'print this message'
+    out.printf fmt, '   --version', 'print version and quit'
     out.printf fmt, '   --copyright',  'print copyright and quit'
     out.puts
     out.puts 'Tasks:'
@@ -859,14 +972,14 @@ class ToplevelInstaller
     fmt = &quot;  %-24s %s [%s]\n&quot;
     out.puts
     out.puts 'Options for CONFIG or ALL:'
-    ConfigTable.each do |item|
+    @config.each do |item|
       out.printf fmt, item.help_opt, item.description, item.help_default
     end
     out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',&quot;running ruby's&quot;
     out.puts
     out.puts 'Options for INSTALL:'
     out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
-    out.printf fmt, '--prefix=path',  'install path prefix', '$prefix'
+    out.printf fmt, '--prefix=path',  'install path prefix', ''
     out.puts
   end
 
@@ -887,9 +1000,13 @@ class ToplevelInstaller
     @installer.exec_install
   end
 
+  def exec_test
+    @installer.exec_test
+  end
+
   def exec_show
-    ConfigTable.each do |i|
-      printf &quot;%-20s %s\n&quot;, i.name, i.value
+    @config.each do |i|
+      printf &quot;%-20s %s\n&quot;, i.name, i.value if i.value?
     end
   end
 
@@ -901,36 +1018,45 @@ class ToplevelInstaller
     @installer.exec_distclean
   end
 
-end
+end   # class ToplevelInstaller
 
 
 class ToplevelInstallerMulti &lt; ToplevelInstaller
 
-  include HookUtils
-  include HookScriptAPI
   include FileOperations
 
-  def initialize(ardir)
+  def initialize(ardir_root, config)
     super
-    @packages = all_dirs_in(&quot;#{@ardir}/packages&quot;)
+    @packages = directories_of(&quot;#{@ardir}/packages&quot;)
     raise 'no package exists' if @packages.empty?
+    @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
   end
 
   def run_metaconfigs
-    eval_file_ifexist &quot;#{@ardir}/metaconfig&quot;
+    @config.load_script &quot;#{@ardir}/metaconfig&quot;, self
     @packages.each do |name|
-      eval_file_ifexist &quot;#{@ardir}/packages/#{name}/metaconfig&quot;
+      @config.load_script &quot;#{@ardir}/packages/#{name}/metaconfig&quot;
     end
   end
 
+  attr_reader :packages
+
+  def packages=(list)
+    raise 'package list is empty' if list.empty?
+    list.each do |name|
+      raise &quot;directory packages/#{name} does not exist&quot;\
+              unless File.dir?(&quot;#{@ardir}/packages/#{name}&quot;)
+    end
+    @packages = list
+  end
+
   def init_installers
     @installers = {}
     @packages.each do |pack|
-      @installers[pack] = Installer.new(@config, @options,
+      @installers[pack] = Installer.new(@config,
                                        &quot;#{@ardir}/packages/#{pack}&quot;,
                                        &quot;packages/#{pack}&quot;)
     end
-
     with    = extract_selection(config('with'))
     without = extract_selection(config('without'))
     @selected = @installers.keys.select {|name|
@@ -955,21 +1081,6 @@ class ToplevelInstallerMulti &lt; ToplevelInstaller
   end
 
   #
-  # multi-package metaconfig API
-  #
-
-  attr_reader :packages
-
-  def declare_packages(list)
-    raise 'package list is empty' if list.empty?
-    list.each do |name|
-      raise &quot;directory packages/#{name} does not exist&quot;\
-              unless File.dir?(&quot;#{@ardir}/packages/#{name}&quot;)
-    end
-    @packages = list
-  end
-
-  #
   # Task Handlers
   #
 
@@ -992,15 +1103,21 @@ class ToplevelInstallerMulti &lt; ToplevelInstaller
     run_hook 'post-install'
   end
 
+  def exec_test
+    run_hook 'pre-test'
+    each_selected_installers {|inst| inst.exec_test }
+    run_hook 'post-test'
+  end
+
   def exec_clean
-    rm_f ConfigTable.savefile
+    rm_f @config.savefile
     run_hook 'pre-clean'
     each_selected_installers {|inst| inst.exec_clean }
     run_hook 'post-clean'
   end
 
   def exec_distclean
-    rm_f ConfigTable.savefile
+    rm_f @config.savefile
     run_hook 'pre-distclean'
     each_selected_installers {|inst| inst.exec_distclean }
     run_hook 'post-distclean'
@@ -1013,7 +1130,7 @@ class ToplevelInstallerMulti &lt; ToplevelInstaller
   def each_selected_installers
     Dir.mkdir 'packages' unless File.dir?('packages')
     @selected.each do |pack|
-      $stderr.puts &quot;Processing the package `#{pack}' ...&quot; if @options['verbose']
+      $stderr.puts &quot;Processing the package `#{pack}' ...&quot; if verbose?
       Dir.mkdir &quot;packages/#{pack}&quot; unless File.dir?(&quot;packages/#{pack}&quot;)
       Dir.chdir &quot;packages/#{pack}&quot;
       yield @installers[pack]
@@ -1021,28 +1138,32 @@ class ToplevelInstallerMulti &lt; ToplevelInstaller
     end
   end
 
+  def run_hook(id)
+    @root_installer.run_hook id
+  end
+
+  # module FileOperations requires this
   def verbose?
-    @options['verbose']
+    @config.verbose?
   end
 
+  # module FileOperations requires this
   def no_harm?
-    @options['no-harm']
+    @config.no_harm?
   end
 
-end
+end   # class ToplevelInstallerMulti
 
 
 class Installer
 
-  FILETYPES = %w( bin lib ext data )
+  FILETYPES = %w( bin lib ext data conf man )
 
-  include HookScriptAPI
-  include HookUtils
   include FileOperations
+  include HookScriptAPI
 
-  def initialize(config, opt, srcroot, objroot)
+  def initialize(config, srcroot, objroot)
     @config = config
-    @options = opt
     @srcdir = File.expand_path(srcroot)
     @objdir = File.expand_path(objroot)
     @currdir = '.'
@@ -1052,6 +1173,9 @@ class Installer
     &quot;#&lt;#{self.class} #{File.basename(@srcdir)}&gt;&quot;
   end
 
+  def noop(rel)
+  end
+
   #
   # Hook Script API base methods
   #
@@ -1069,23 +1193,25 @@ class Installer
   end
 
   #
-  # configs/options
+  # Config Access
   #
 
-  def no_harm?
-    @options['no-harm']
+  # module FileOperations requires this
+  def verbose?
+    @config.verbose?
   end
 
-  def verbose?
-    @options['verbose']
+  # module FileOperations requires this
+  def no_harm?
+    @config.no_harm?
   end
 
   def verbose_off
     begin
-      save, @options['verbose'] = @options['verbose'], false
+      save, @config.verbose = @config.verbose?, false
       yield
     ensure
-      @options['verbose'] = save
+      @config.verbose = save
     end
   end
 
@@ -1097,22 +1223,19 @@ class Installer
     exec_task_traverse 'config'
   end
 
-  def config_dir_bin(rel)
-  end
-
-  def config_dir_lib(rel)
-  end
+  alias config_dir_bin noop
+  alias config_dir_lib noop
 
   def config_dir_ext(rel)
     extconf if extdir?(curr_srcdir())
   end
 
-  def extconf
-    opt = @options['config-opt'].join(' ')
-    command &quot;#{config('rubyprog')} #{curr_srcdir()}/extconf.rb #{opt}&quot;
-  end
+  alias config_dir_data noop
+  alias config_dir_conf noop
+  alias config_dir_man noop
 
-  def config_dir_data(rel)
+  def extconf
+    ruby &quot;#{curr_srcdir()}/extconf.rb&quot;, *@config.config_opt
   end
 
   #
@@ -1124,39 +1247,90 @@ class Installer
   end
 
   def setup_dir_bin(rel)
-    all_files_in(curr_srcdir()).each do |fname|
-      adjust_shebang &quot;#{curr_srcdir()}/#{fname}&quot;
+    files_of(curr_srcdir()).each do |fname|
+      update_shebang_line &quot;#{curr_srcdir()}/#{fname}&quot;
     end
   end
 
-  def adjust_shebang(path)
+  alias setup_dir_lib noop
+
+  def setup_dir_ext(rel)
+    make if extdir?(curr_srcdir())
+  end
+
+  alias setup_dir_data noop
+  alias setup_dir_conf noop
+  alias setup_dir_man noop
+
+  def update_shebang_line(path)
     return if no_harm?
+    return if config('shebang') == 'never'
+    old = Shebang.load(path)
+    if old
+      $stderr.puts &quot;warning: #{path}: Shebang line includes too many args.  It is not portable and your program may not work.&quot; if old.args.size &gt; 1
+      new = new_shebang(old)
+      return if new.to_s == old.to_s
+    else
+      return unless config('shebang') == 'all'
+      new = Shebang.new(config('rubypath'))
+    end
+    $stderr.puts &quot;updating shebang: #{File.basename(path)}&quot; if verbose?
+    open_atomic_writer(path) {|output|
+      File.open(path, 'rb') {|f|
+        f.gets if old   # discard
+        output.puts new.to_s
+        output.print f.read
+      }
+    }
+  end
+
+  def new_shebang(old)
+    if /\Aruby/ =~ File.basename(old.cmd)
+      Shebang.new(config('rubypath'), old.args)
+    elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
+      Shebang.new(config('rubypath'), old.args[1..-1])
+    else
+      return old unless config('shebang') == 'all'
+      Shebang.new(config('rubypath'))
+    end
+  end
+
+  def open_atomic_writer(path, &amp;block)
     tmpfile = File.basename(path) + '.tmp'
     begin
-      File.open(path, 'rb') {|r|
-        first = r.gets
-        return unless File.basename(config('rubypath')) == 'ruby'
-        return unless File.basename(first.sub(/\A\#!/, '').split[0]) == 'ruby'
-        $stderr.puts &quot;adjusting shebang: #{File.basename(path)}&quot; if verbose?
-        File.open(tmpfile, 'wb') {|w|
-          w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath'))
-          w.write r.read
-        }
-        move_file tmpfile, File.basename(path)
-      }
+      File.open(tmpfile, 'wb', &amp;block)
+      File.rename tmpfile, File.basename(path)
     ensure
       File.unlink tmpfile if File.exist?(tmpfile)
     end
   end
 
-  def setup_dir_lib(rel)
-  end
+  class Shebang
+    def Shebang.load(path)
+      line = nil
+      File.open(path) {|f|
+        line = f.gets
+      }
+      return nil unless /\A#!/ =~ line
+      parse(line)
+    end
 
-  def setup_dir_ext(rel)
-    make if extdir?(curr_srcdir())
-  end
+    def Shebang.parse(line)
+      cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
+      new(cmd, args)
+    end
+
+    def initialize(cmd, args = [])
+      @cmd = cmd
+      @args = args
+    end
 
-  def setup_dir_data(rel)
+    attr_reader :cmd
+    attr_reader :args
+
+    def to_s
+      &quot;#! #{@cmd}&quot; + (@args.empty? ? '' : &quot; #{@args.join(' ')}&quot;)
+    end
   end
 
   #
@@ -1169,63 +1343,77 @@ class Installer
   end
 
   def install_dir_bin(rel)
-    install_files collect_filenames_auto(), &quot;#{config('bindir')}/#{rel}&quot;, 0755
+    install_files targetfiles(), &quot;#{config('bindir')}/#{rel}&quot;, 0755
   end
 
   def install_dir_lib(rel)
-    install_files ruby_scripts(), &quot;#{config('rbdir')}/#{rel}&quot;, 0644
+    install_files libfiles(), &quot;#{config('rbdir')}/#{rel}&quot;, 0644
   end
 
   def install_dir_ext(rel)
     return unless extdir?(curr_srcdir())
-    install_files ruby_extentions('.'),
+    install_files rubyextentions('.'),
                   &quot;#{config('sodir')}/#{File.dirname(rel)}&quot;,
                   0555
   end
 
   def install_dir_data(rel)
-    install_files collect_filenames_auto(), &quot;#{config('datadir')}/#{rel}&quot;, 0644
+    install_files targetfiles(), &quot;#{config('datadir')}/#{rel}&quot;, 0644
+  end
+
+  def install_dir_conf(rel)
+    # FIXME: should not remove current config files
+    # (rename previous file to .old/.org)
+    install_files targetfiles(), &quot;#{config('sysconfdir')}/#{rel}&quot;, 0644
+  end
+
+  def install_dir_man(rel)
+    install_files targetfiles(), &quot;#{config('mandir')}/#{rel}&quot;, 0644
   end
 
   def install_files(list, dest, mode)
-    mkdir_p dest, @options['install-prefix']
+    mkdir_p dest, @config.install_prefix
     list.each do |fname|
-      install fname, dest, mode, @options['install-prefix']
+      install fname, dest, mode, @config.install_prefix
     end
   end
 
-  def ruby_scripts
-    collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
+  def libfiles
+    glob_reject(%w(*.y *.output), targetfiles())
   end
-  
+
+  def rubyextentions(dir)
+    ents = glob_select(&quot;*.#{@config.dllext}&quot;, targetfiles())
+    if ents.empty?
+      setup_rb_error &quot;no ruby extention exists: 'ruby #{$0} setup' first&quot;
+    end
+    ents
+  end
+
+  def targetfiles
+    mapdir(existfiles() - hookfiles())
+  end
+
+  def mapdir(ents)
+    ents.map {|ent|
+      if File.exist?(ent)
+      then ent                         # objdir
+      else &quot;#{curr_srcdir()}/#{ent}&quot;   # srcdir
+      end
+    }
+  end
+
   # picked up many entries from cvs-1.11.1/src/ignore.c
-  reject_patterns = %w( 
+  JUNK_FILES = %w( 
     core RCSLOG tags TAGS .make.state
     .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
     *~ *.old *.bak *.BAK *.orig *.rej _$* *$
 
     *.org *.in .*
   )
-  mapping = {
-    '.' =&gt; '\.',
-    '$' =&gt; '\$',
-    '#' =&gt; '\#',
-    '*' =&gt; '.*'
-  }
-  REJECT_PATTERNS = Regexp.new('\A(?:' +
-                               reject_patterns.map {|pat|
-                                 pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
-                               }.join('|') +
-                               ')\z')
-
-  def collect_filenames_auto
-    mapdir((existfiles() - hookfiles()).reject {|fname|
-             REJECT_PATTERNS =~ fname
-           })
-  end
 
   def existfiles
-    all_files_in(curr_srcdir()) | all_files_in('.')
+    glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
   end
 
   def hookfiles
@@ -1234,24 +1422,49 @@ class Installer
     }.flatten
   end
 
-  def mapdir(filelist)
-    filelist.map {|fname|
-      if File.exist?(fname)   # objdir
-        fname
-      else                    # srcdir
-        File.join(curr_srcdir(), fname)
-      end
-    }
+  def glob_select(pat, ents)
+    re = globs2re([pat])
+    ents.select {|ent| re =~ ent }
   end
 
-  def ruby_extentions(dir)
-    Dir.open(dir) {|d|
-      ents = d.select {|fname| /\.#{::Config::CONFIG['DLEXT']}\z/ =~ fname }
-      if ents.empty?
-        setup_rb_error &quot;no ruby extention exists: 'ruby #{$0} setup' first&quot;
-      end
-      return ents
-    }
+  def glob_reject(pats, ents)
+    re = globs2re(pats)
+    ents.reject {|ent| re =~ ent }
+  end
+
+  GLOB2REGEX = {
+    '.' =&gt; '\.',
+    '$' =&gt; '\$',
+    '#' =&gt; '\#',
+    '*' =&gt; '.*'
+  }
+
+  def globs2re(pats)
+    /\A(?:#{
+      pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
+    })\z/
+  end
+
+  #
+  # TASK test
+  #
+
+  TESTDIR = 'test'
+
+  def exec_test
+    unless File.directory?('test')
+      $stderr.puts 'no test in this package' if verbose?
+      return
+    end
+    $stderr.puts 'Running tests...' if verbose?
+    begin
+      require 'test/unit'
+    rescue LoadError
+      setup_rb_error 'test/unit cannot loaded.  You need Ruby 1.8 or later to invoke this task.'
+    end
+    runner = Test::Unit::AutoRunner.new(true)
+    runner.to_run &lt;&lt; TESTDIR
+    runner.run
   end
 
   #
@@ -1260,53 +1473,51 @@ class Installer
 
   def exec_clean
     exec_task_traverse 'clean'
-    rm_f ConfigTable.savefile
+    rm_f @config.savefile
     rm_f 'InstalledFiles'
   end
 
-  def clean_dir_bin(rel)
-  end
-
-  def clean_dir_lib(rel)
-  end
+  alias clean_dir_bin noop
+  alias clean_dir_lib noop
+  alias clean_dir_data noop
+  alias clean_dir_conf noop
+  alias clean_dir_man noop
 
   def clean_dir_ext(rel)
     return unless extdir?(curr_srcdir())
     make 'clean' if File.file?('Makefile')
   end
 
-  def clean_dir_data(rel)
-  end
-
   #
   # TASK distclean
   #
 
   def exec_distclean
     exec_task_traverse 'distclean'
-    rm_f ConfigTable.savefile
+    rm_f @config.savefile
     rm_f 'InstalledFiles'
   end
 
-  def distclean_dir_bin(rel)
-  end
-
-  def distclean_dir_lib(rel)
-  end
+  alias distclean_dir_bin noop
+  alias distclean_dir_lib noop
 
   def distclean_dir_ext(rel)
     return unless extdir?(curr_srcdir())
     make 'distclean' if File.file?('Makefile')
   end
 
+  alias distclean_dir_data noop
+  alias distclean_dir_conf noop
+  alias distclean_dir_man noop
+
   #
-  # lib
+  # Traversing
   #
 
   def exec_task_traverse(task)
     run_hook &quot;pre-#{task}&quot;
     FILETYPES.each do |type|
-      if config('without-ext') == 'yes' and type == 'ext'
+      if type == 'ext' and config('without-ext') == 'yes'
         $stderr.puts 'skipping ext/* by user option' if verbose?
         next
       end
@@ -1319,7 +1530,7 @@ class Installer
     dive_into(rel) {
       run_hook &quot;pre-#{task}&quot;
       __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
-      all_dirs_in(curr_srcdir()).each do |d|
+      directories_of(curr_srcdir()).each do |d|
         traverse task, &quot;#{rel}/#{d}&quot;, mid
       end
       run_hook &quot;post-#{task}&quot;
@@ -1341,16 +1552,30 @@ class Installer
     @currdir = File.dirname(rel)
   end
 
-end
+  def run_hook(id)
+    path = [ &quot;#{curr_srcdir()}/#{id}&quot;,
+             &quot;#{curr_srcdir()}/#{id}.rb&quot; ].detect {|cand| File.file?(cand) }
+    return unless path
+    begin
+      instance_eval File.read(path), path, 1
+    rescue
+      raise if $DEBUG
+      setup_rb_error &quot;hook #{path} failed:\n&quot; + $!.message
+    end
+  end
+
+end   # class Installer
+
+
+class SetupError &lt; StandardError; end
 
+def setup_rb_error(msg)
+  raise SetupError, msg
+end
 
 if $0 == __FILE__
   begin
-    if multipackage_install?
-      ToplevelInstallerMulti.invoke
-    else
-      ToplevelInstaller.invoke
-    end
+    ToplevelInstaller.invoke
   rescue SetupError
     raise if $DEBUG
     $stderr.puts $!.message</diff>
      <filename>setup.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>c21a82a699ce967d279644ab453466417dd21b29</id>
    </parent>
  </parents>
  <author>
    <name>why</name>
    <email>why@19e92222-5c0b-0410-8929-a290d50e31e9</email>
  </author>
  <url>http://github.com/pivotal/mongrel/commit/8c52783d14d025f5c86be1a580182cc625808524</url>
  <id>8c52783d14d025f5c86be1a580182cc625808524</id>
  <committed-date>2006-04-10T11:56:16-07:00</committed-date>
  <authored-date>2006-04-10T11:56:16-07:00</authored-date>
  <message>Update setup.rb to silence errors on shebang replacement.  Camping handler now allows passing back of an IO object, to allow large file streaming.  Simpler underscore replacement on Tepee wiki words.


git-svn-id: svn://rubyforge.org/var/svn/mongrel/trunk@154 19e92222-5c0b-0410-8929-a290d50e31e9</message>
  <tree>5f19328bc441a3aa22e70f4390fdc69e98f0d66a</tree>
  <committer>
    <name>why</name>
    <email>why@19e92222-5c0b-0410-8929-a290d50e31e9</email>
  </committer>
</commit>
