Skip to content

Commit

Permalink
Child: force all IO objects and buffers to BINARY for now
Browse files Browse the repository at this point in the history
Otherwise, the out and err buffer strings are assumed to be whatever
the source file encoding is (US-ASCII currently), while the IO
objects will get their encoding from Encoding.default_external and
Encoding.default_internal which can vary by environment (LANG,
LC_ALL, etc.). This causes the out and err strings to be implicitly
converted to a different encoding when appending to them with
String#<< data we've read off the IO object.

Forcing everything to binary also lets us get rid of the need to
define String#bytesize since #size = #bytesize with BINARY encoding.

At some point we'll need to deal with this stuff in a way that's
consistent with Ruby's core IO methods. We should accept input in
any encoding and transcode it to the Encoding default or a
user-specified external_encoding when writing to the stdin fd. We
should also read data off of the stdout/stderr IO objects in the
default or user-specified external_encoding and transcode to the
internal_encoding. Unfortunately, that's going to require also
adding support for specifying encodings to Child::new so that you
can override Encoding.default_internal/default_external for binary
reads and whatnot.
  • Loading branch information
rtomayko committed Mar 6, 2011
1 parent d20b414 commit af16f2a
Showing 1 changed file with 19 additions and 13 deletions.
32 changes: 19 additions & 13 deletions lib/posix/spawn/child.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
require 'posix/spawn'

class String
alias bytesize size
end unless ''.respond_to?(:bytesize)

module POSIX
module Spawn
# POSIX::Spawn::Child includes logic for executing child processes and
Expand Down Expand Up @@ -145,22 +141,32 @@ def exec!
# Raises MaximumOutputExceeded when the total number of bytes output
# exceeds the amount specified by the max argument.
def read_and_write(input, stdin, stdout, stderr, timeout=nil, max=nil)
if input
input = input.dup.force_encoding('BINARY') if input.respond_to?(:force_encoding)
else
stdin.close
end

max = nil if max && max <= 0
out, err = '', ''
offset = 0

# force all string and IO encodings to BINARY under 1.9 for now
if out.respond_to?(:force_encoding)
[stdin, stdout, stderr].each do |fd|
fd.set_encoding('BINARY', 'BINARY')
end
out.force_encoding('BINARY')
err.force_encoding('BINARY')
input = input.dup.force_encoding('BINARY') if input
end

timeout = nil if timeout && timeout <= 0.0
@runtime = 0.0
start = Time.now

writers = input ? [stdin] : []
readers = [stdout, stderr]
writers =
if input
[stdin]
else
stdin.close
[]
end
t = timeout
while readers.any? || writers.any?
ready = IO.select(readers, writers, readers + writers, t)
Expand All @@ -171,11 +177,11 @@ def read_and_write(input, stdin, stdout, stderr, timeout=nil, max=nil)
begin
boom = nil
size = fd.write_nonblock(input)
input = input[size, input.bytesize]
input = input[size, input.size]
rescue Errno::EPIPE => boom
rescue Errno::EAGAIN, Errno::EINTR
end
if boom || input.bytesize == 0
if boom || input.size == 0
stdin.close
writers.delete(stdin)
end
Expand Down

0 comments on commit af16f2a

Please sign in to comment.