     end
   end
 
-  defp binary_to_list(l, _)        when is_list(l),   do: l
-  defp binary_to_list(b, :unicode) when is_binary(b), do: to_charlist(b)
-  defp binary_to_list(b, :latin1)  when is_binary(b), do: :binary.bin_to_list(b)
-  defp list_to_binary(b, _)        when is_binary(b), do: b
-  defp list_to_binary(l, :unicode) when is_list(l),   do: to_string(l)
-  defp list_to_binary(l, :latin1)  when is_list(l),   do: :binary.list_to_bin(l)
-
-  # From http://erlang.org/doc/apps/stdlib/io_protocol.html: Result can be any
+  defp binary_to_list(data, _) when is_list(data), do: data
+  defp binary_to_list(data, :unicode) when is_binary(data), do: String.to_charlist(data)
+  defp binary_to_list(data, :latin1) when is_binary(data), do: :erlang.binary_to_list(data)
+
+  defp list_to_binary(data, _) when is_binary(data), do: data
+  defp list_to_binary(data, :unicode) when is_list(data), do: List.to_string(data)
+  defp list_to_binary(data, :latin1) when is_list(data), do: :erlang.list_to_binary(data)
+
+  # From http://erlang.org/doc/apps/stdlib/io_protocol.html: result can be any
   # Erlang term, but if it is a list(), the I/O server can convert it to a binary().
-  defp get_until_result(l, encoding) when is_list(l), do: list_to_binary(l, encoding)
-  defp get_until_result(other, _), do: other
+  defp get_until_result(data, encoding) when is_list(data), do: list_to_binary(data, encoding)
+  defp get_until_result(data, _), do: data
 
   ## io_requests
 
-  defp io_requests([r | rs], {:ok, s}) do
-    io_requests(rs, io_request(r, s))
+  defp io_requests([req | rest], {:ok, state}) do
+    io_requests(rest, io_request(req, state))
   end
 
   defp io_requests(_, result) do

    {:ok, %{input: string, output: "", capture_prompt: capture_prompt}}
  end

  def handle_info({:io_request, from, reply_as, req}, s) do
    s = io_request(from, reply_as, req, s)
    {:noreply, s}
  end

  def handle_info(msg, s) do
    super(msg, s)
  end

  def handle_call(:contents, _from, %{input: input, output: output} = s) do
    {:reply, {input, output}, s}
  end

  def handle_call(:flush, _from, %{output: output} = s) do
    {:reply, output, %{s | output: ""}}
  end

  def handle_call(:close, _from, %{input: input, output: output} = s) do
    {:stop, :normal, {:ok, {input, output}}, s}
  end

  def handle_call(request, from, s) do
    super(request, from, s)
  end

  defp io_request(from, reply_as, req, s) do
    {reply, s} = io_request(req, s)
    io_reply(from, reply_as, to_reply(reply))
    s
  end

  defp io_request({:put_chars, chars} = req, s) do
    put_chars(:latin1, chars, req, s)
  end

  defp io_request({:put_chars, m, f, as} = req, s) do
    put_chars(:latin1, apply(m, f, as), req, s)
  end

  defp io_request({:put_chars, encoding, chars} = req, s) do
    put_chars(encoding, chars, req, s)
  end

  defp io_request({:put_chars, encoding, mod, func, args} = req, s) do
    put_chars(encoding, apply(mod, func, args), req, s)
  end

  defp io_request({:get_chars, prompt, n}, s) when n >= 0 do
    io_request({:get_chars, :latin1, prompt, n}, s)
  end

  defp io_request({:get_chars, encoding, prompt, n}, s) when n >= 0 do
    get_chars(encoding, prompt, n, s)
  end

  defp io_request({:get_line, prompt}, s) do
    io_request({:get_line, :latin1, prompt}, s)
  end

  defp io_request({:get_line, encoding, prompt}, s) do
    get_line(encoding, prompt, s)
  end

  defp io_request({:get_until, prompt, mod, fun, args}, s) do
    io_request({:get_until, :latin1, prompt, mod, fun, args}, s)
  end

  defp io_request({:get_until, encoding, prompt, mod, fun, args}, s) do
    get_until(encoding, prompt, mod, fun, args, s)
  end

  defp io_request({:get_password, encoding}, s) do
    get_line(encoding, "", s)
  end

  defp io_request({:setopts, _opts}, s) do
    {{:error, :enotsup}, s}
  end

  defp io_request(:getopts, s) do
    {{:ok, [binary: true, encoding: :unicode]}, s}
  end

  defp io_request({:get_geometry, :columns}, s) do
    {{:error, :enotsup}, s}
  end

  defp io_request({:get_geometry, :rows}, s) do
    {{:error, :enotsup}, s}
  end

  defp io_request({:requests, reqs}, s) do
    io_requests(reqs, {:ok, s})
  end

  defp io_request(_, s) do
    {{:error, :request}, s}
  end

  ## put_chars

  defp put_chars(encoding, chars, req, %{output: output} = s) do
    case :unicode.characters_to_binary(chars, encoding, :unicode) do
      string when is_binary(string) ->
        {:ok, %{s | output: output <> string}}
      {_, _, _} ->
        {{:error, req}, s}
    end
  end

  ## get_chars

  defp get_chars(encoding, prompt, n, %{input: input} = s) do
    case do_get_chars(input, encoding, n) do
      {:error, _} = error ->
        {error, s}
      {result, input} ->
        {result, state_after_read(s, input, prompt)}
    end
  end

  defp do_get_chars("", _encoding, _n) do
    {:eof, ""}
  end

  defp do_get_chars(input, :latin1, n) when byte_size(input) < n do
    {input, ""}
  end

  defp do_get_chars(input, :latin1, n) do
    <<chars::binary-size(n), rest::binary>> = input
    {chars, rest}
  end

  defp do_get_chars(input, encoding, n) do
    try do
      case :file_io_server.count_and_find(input, n, encoding) do
        {buf_count, split_pos} when buf_count < n or split_pos == :none ->
          {input, ""}
        {_buf_count, split_pos} ->
          <<chars::binary-size(split_pos), rest::binary>> = input

          {:done, result, extra} ->
            {result, extra ++ binary_to_list(rest, encoding), count + 1}
          {:more, next_continuation} ->
            get_until(rest, encoding, mod, fun, args, next_continuation, count + 1)
        end

      :error ->
