# File lib/cgikit/utilities.rb, line 59
  def query_from_multipart( headers, input, boundary, content_length )
    params   = Hash.new([])
    boundary = "--" + boundary
    buf      = ""
    bufsize  = 10 * 1024

    # start multipart/form-data
    input.binmode
    boundary_size   = boundary.size + EOL.size
    content_length -= boundary_size
    status          = input.read boundary_size
    if status == nil then
      raise EOFError, "no content body"
    elsif boundary + EOL != status
      raise EOFError, "bad content body"
    end

    until content_length == -1
      head = nil
      tmp = nil
      if content_length > MEM_CONTENT_LENGTH
        require "tempfile"
        tmp = Tempfile.new("CGIKit")
        tmp.binmode
        data = TempfileByteData.new(tmp)
      else
        data = ByteData.new
      end

      until head and /#{boundary}(?:#{EOL}|--)/n.match(buf)
        if (not head) and /#{EOL}#{EOL}/n.match(buf)
          buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
            head = $1.dup
            ""
          end
          next
        end

        if head and ( (EOL + boundary + EOL).size < buf.size )
          data << buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
          buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
        end

        if bufsize < content_length then
          c = input.read(bufsize) or ''
        else
          c = input.read(content_length) or ''
        end
        buf += c
        content_length -= c.size
      end

      buf = buf.sub(/\A((?:.|\n)*?)(?:#{EOL})?#{boundary}(#{EOL}|--)/n) do
        data << $1
        if "--" == $2
          content_length = -1
        end
        ""
      end

      /Content-Disposition:.* filename="?([^\";]*)"?/ni.match(head)
      filename = ($1 || "").dup
      if /Mac/ni.match(headers['HTTP_USER_AGENT']) and
          /Mozilla/ni.match(headers['HTTP_USER_AGENT']) and
          (not /MSIE/ni.match(headers['HTTP_USER_AGENT']))
        filename = Utilities.unescape_url filename
      end
      data.path = filename

      /Content-Type: ([^\r\n]*)/ni.match(head)
      if $1 then
        data.content_type = $1.dup
      else
        data = data.to_s
      end

      /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
      name = $1.dup

      if params.has_key? name then
        params[name].push data
      else
        params[name] = [data]
      end
    end

    params
  end