plist4r 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,6 +10,8 @@ Current project status: in alpha
10
10
 
11
11
  == Quick Start
12
12
 
13
+ require 'plist4r'
14
+
13
15
  Plist4r::Config.default_dir "/Library/LaunchDaemons"
14
16
  filename = "com.github.myservice.plist"
15
17
  p = Plist4r.open(filename)
@@ -65,20 +67,20 @@ We believe thats allright for most uses, and decided to include `next_step` for
65
67
  module ::Plist4r::Backend::MyPlistReaderWriter
66
68
  # implement some plist4r api calls here
67
69
  end
68
-
70
+
69
71
  # append my backend to the end of the list
70
72
  Plist4r::Config[:backends] << :my_plist_reader_writer
71
73
 
72
74
  # or to the front of the list (executes first)
73
75
  Plist4r::Config[:backends].insert 0 :my_plist_reader_writer
74
-
76
+
75
77
  # The default directory to load / save files from
76
78
  Plist4r::Config.default_path "/Library/Cars"
77
79
 
78
80
  car = Plist4r.new("car.plist")
79
81
 
80
82
  car.load
81
-
83
+
82
84
  car.file_format :binary
83
85
  # car.plist_type :car # not implemented *yet*
84
86
 
@@ -121,8 +123,32 @@ Plist4r is currently alpha - quality software. Yet to be completed...
121
123
  * Fork the project, and create a topic branch as per {these instructions}[http://wiki.opscode.com/display/opscode/Working+with+Git].
122
124
  * Make your feature addition or bug fix.
123
125
  * Include documentation for it.
124
- * Include a regression test for it. So I don't break it in a future version unintentionally.
126
+ * Include a regression test for it. So I dont break it in a future version unintentionally.
127
+
128
+ == Contributors
129
+
130
+ Popen4
131
+ * Ara T Howard
132
+
133
+ ActiveSupport::OrderedHash
134
+ * Copyright (c) 2005 David Hansson,
135
+ * Copyright (c) 2007 Mauricio Fernandez, Sam Stephenson
136
+ * Copyright (c) 2008 Steve Purcell, Josh Peek
137
+ * Copyright (c) 2009 Christoffer Sawicki
138
+
139
+ Mixlib::Config
140
+ * Author:: Adam Jacob
141
+ * Author:: Nuo Yan
142
+ * Author:: Christopher Brown
143
+ * Copyright:: Copyright (c) 2008 Opscode, Inc.
144
+
145
+ Backends...
146
+
147
+ Haml, Libxml4r, RubyCocoa
148
+ * Dreamcat4
125
149
 
126
150
  == Copyright
127
151
 
128
152
  Copyright (c) 2010 Dreamcat4. See LICENSE for details.
153
+
154
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
@@ -1,23 +1,24 @@
1
1
 
2
- $:.unshift(File.dirname(__FILE__)) unless
3
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+ dir = File.dirname(__FILE__)
3
+ $LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
4
4
 
5
5
  require 'plist4r/plist'
6
6
 
7
7
  module Plist4r
8
8
  class << self
9
9
  def new *args, &blk
10
- puts args.inspect
10
+ # puts args.inspect
11
11
  return Plist.new *args, &blk
12
12
  end
13
13
 
14
14
  def open filename, *args, &blk
15
- puts args.inspect
16
- return Plist.new filename, *args, &blk
15
+ # puts args.inspect
16
+ p = Plist.new filename, *args, &blk
17
+ p.open
17
18
  end
18
19
 
19
20
  def string_detect_format string
20
- s.strip!
21
+ s = string.strip!
21
22
  case s[0,1]
22
23
  when "{","("
23
24
  :next_step
@@ -1,52 +1,63 @@
1
1
 
2
2
  require 'plist4r/config'
3
+ require 'plist4r/backend_base'
3
4
  require 'plist4r/mixin/ordered_hash'
4
5
 
5
- class Plist4r::Backend
6
- ApiMethods = %w[from_string to_xml to_binary to_next_step open save]
6
+ module Plist4r
7
+ class Backend
8
+ ApiMethods = %w[from_string to_xml to_binary to_next_step open save]
7
9
 
8
- def initialize plist, *args, &blk
9
- @plist = plist
10
- @backends = Config[:backends].collect do |b|
11
- case b
12
- when Module
13
- b
14
- when Symbol, String
15
- eval "::Plist4r::Backend::#{b.to_s.camelcase}"
16
- else
17
- raise "Backend #{b.inspect} is of unsupported type: #{b.class}"
10
+ def initialize plist, *args, &blk
11
+ @plist = plist
12
+ @backends = plist.backends.collect do |b|
13
+ case b
14
+ when Module
15
+ b
16
+ when Symbol, String
17
+ eval "::Plist4r::Backend::#{b.to_s.camelcase}"
18
+ else
19
+ raise "Backend #{b.inspect} is of unsupported type: #{b.class}"
20
+ end
18
21
  end
19
22
  end
20
- end
21
23
 
22
- def call method_sym, *args, &blk
23
- raise "Unsupported api call #{method_sym.inspect}" unless ApiMethods.include? method_sym.to_s
24
- exceptions = []
25
- @backends.each do |backend|
26
- if backend.respond_to? method_sym
27
- begin
28
- return backend.send(method_sym, @plist, *args, &blk)
29
- rescue LoadError
30
- exceptions << $!
31
- rescue
32
- exceptions << $!
24
+ # vv We also need a version of :call for matrix test harness vv
25
+
26
+ def call method_sym, *args, &blk
27
+ puts "in call"
28
+ puts "#{method_sym.inspect} #{args.inspect}"
29
+ raise "Unsupported api call #{method_sym.inspect}" unless ApiMethods.include? method_sym.to_s
30
+ exceptions = []
31
+ @backends.each do |backend|
32
+ if backend.respond_to? method_sym
33
+ begin
34
+ result = backend.send(method_sym, @plist, *args, &blk)
35
+ # puts "result = #{result.inspect}"
36
+ return result
37
+ # return backend.send(method_sym, @plist, *args, &blk)
38
+ rescue LoadError
39
+ exceptions << $!
40
+ rescue
41
+ exceptions << $!
42
+ end
43
+ end
44
+ if Config[:raise_any_failure] && exceptions.first
45
+ raise exceptions.first
33
46
  end
34
47
  end
35
- if Config[:raise_any_failure] && exceptions.first
36
- raise exceptions.first
37
- end
38
- end
39
- if exceptions.empty?
40
- raise "Plist4r: No backend found to handle method #{method_sym.inspect}. Could not execute method #{method_sym.inspect} on plist #{@plist.inspect}"
41
- else
42
- $stderr.puts "Couldn't execute method #{method_sym.inspect} on plist #{@plist.inspect}."
43
- exceptions.each do |e|
44
- $stderr.puts e.inspect
48
+ if exceptions.empty?
49
+ raise "Plist4r: No backend found to handle method #{method_sym.inspect}. Could not execute method #{method_sym.inspect} on plist #{@plist.inspect}"
50
+ else
51
+ # $stderr.puts "Failure(s) while executing method #{method_sym.inspect} on plist #{@plist}."
52
+ exceptions.each do |e|
53
+ $stderr.puts e.inspect
54
+ $stderr.puts e.backtrace.collect { |l| "\tfrom #{l}"}.join "\n"
55
+ end
56
+ # raise exceptions.first
57
+ raise "Failure(s) while executing method #{method_sym.inspect} on plist #{@plist}."
45
58
  end
46
- raise exceptions.first
47
59
  end
48
60
  end
49
61
  end
50
62
 
51
63
 
52
-
@@ -1,5 +1,5 @@
1
1
 
2
- require 'plist4r/backend'
2
+ require 'plist4r/backend_base'
3
3
 
4
4
  module Plist4r::Backend::Example
5
5
  class << self
@@ -1,7 +1,7 @@
1
1
 
2
- require 'plist4r/backend'
2
+ require 'plist4r/backend_base'
3
3
 
4
- module Plist4r::Backend::HamlXmlWriter
4
+ module Plist4r::Backend::Haml
5
5
  class << self
6
6
  def to_xml_haml
7
7
  @to_xml_haml ||= <<-'EOC'
@@ -66,7 +66,7 @@ EOC
66
66
  hash = plist.to_hash
67
67
  filename = plist.filename_path
68
68
  File.open(filename,'w') do |out|
69
- out << to_xml plist
69
+ out << to_xml(plist)
70
70
  end
71
71
  end
72
72
  end
@@ -1,7 +1,7 @@
1
1
 
2
- require 'plist4r/backend'
2
+ require 'plist4r/backend_base'
3
3
 
4
- module Plist4r::Backend::Libxml4rXmlReader
4
+ module Plist4r::Backend::Libxml4r
5
5
  class << self
6
6
  def tree_hash n
7
7
  hash = ::ActiveSupport::OrderedHash.new
@@ -49,6 +49,7 @@ module Plist4r::Backend::Libxml4rXmlReader
49
49
  end
50
50
 
51
51
  def parse_plist_xml string
52
+ require 'rubygems'
52
53
  require 'libxml4r'
53
54
  ::LibXML::XML.default_keep_blanks = false
54
55
  doc = string.to_xmldoc
@@ -60,10 +61,9 @@ module Plist4r::Backend::Libxml4rXmlReader
60
61
  def from_string plist, string
61
62
  plist_format = Plist4r.string_detect_format string
62
63
  raise "#{self} - cant convert string of format #{plist_format}" unless plist_format == :xml
63
-
64
64
  hash = parse_plist_xml string
65
65
  plist.import_hash hash
66
- plist.file_format = file_format
66
+ plist.file_format plist_format
67
67
  return plist
68
68
  end
69
69
 
@@ -1,4 +1,6 @@
1
1
 
2
+ require 'plist4r/backend_base'
3
+
2
4
  module Plist4r::Backend::Plutil
3
5
  # maybe this should be a helper, included by other backends
4
6
  class << self
@@ -1,5 +1,5 @@
1
1
 
2
- require 'plist4r/backend'
2
+ require 'plist4r/backend_base'
3
3
 
4
4
  module Plist4r::Backend::RubyCocoa
5
5
  class << self
@@ -103,7 +103,7 @@ EOC
103
103
  require 'tempfile'
104
104
  require 'plist4r/mixin/popen4'
105
105
 
106
- if @rb_script && File.exists?(@rb_script.path)
106
+ unless @rb_script && File.exists?(@rb_script.path)
107
107
  @rb_script ||= Tempfile.new("ruby_cocoa_wrapper.rb") do |o|
108
108
  o << ruby_cocoa_rb
109
109
  end
@@ -113,7 +113,7 @@ EOC
113
113
  cmd = @rb_script.path
114
114
  ordered_hash_rb = File.join(File.dirname(__FILE__), "..", "mixin", "ordered_hash.rb")
115
115
 
116
- pid, stdin, stdout, stderr = Popen4::popen4 [cmd, ordered_hash_rb]
116
+ pid, stdin, stdout, stderr = ::Plist4r::Popen4::popen4 cmd, ordered_hash_rb
117
117
 
118
118
  stdin.puts stdin_str
119
119
 
@@ -0,0 +1,3 @@
1
+
2
+ module Plist4r end
3
+ class Plist4r::Backend; end
@@ -1,18 +1,21 @@
1
1
 
2
2
  require 'plist4r/mixin/mixlib_config'
3
+ require 'plist4r/backend'
4
+ Dir.glob(File.dirname(__FILE__) + "/backend/**/*.rb").each {|b| require File.expand_path b}
3
5
 
4
- class Plist4r::Config
5
- extend Mixlib::Config
6
+ module Plist4r
7
+ class Config
8
+ extend Mixlib::Config
6
9
 
7
- backends [
8
- Plist4r::Backend::RubyCocoa,
9
- Plist4r::Backend::Haml,
10
- Plist4r::Backend::Libxml4r
11
- ]
10
+ backends [
11
+ ::Plist4r::Backend::RubyCocoa,
12
+ ::Plist4r::Backend::Haml,
13
+ ::Plist4r::Backend::Libxml4r
14
+ ]
12
15
 
13
- unsupported_keys true
14
- raise_any_failure false
15
- deafult_format :xml
16
- default_path nil
16
+ unsupported_keys true
17
+ raise_any_failure false
18
+ deafult_format :xml
19
+ default_path nil
20
+ end
17
21
  end
18
-
@@ -1,63 +1,65 @@
1
1
 
2
- require 'plist4r/mixins/ordered_hash'
2
+ require 'plist4r/mixin/ordered_hash'
3
3
 
4
- module Plst4r::DataMethods
4
+ module Plist4r
5
+ module DataMethods
5
6
 
6
- def classes_for_key_type
7
- {
8
- :string => [String],
9
- :bool => [TrueClass,FalseClass],
10
- :integer => [Fixnum],
11
- :array_of_strings => [Array],
12
- :hash_of_bools => [Hash],
13
- :hash => [Hash],
14
- :bool_or_string_or_array_of_strings => [TrueClass,FalseClass,String,Array]
15
- }
16
- end
7
+ def classes_for_key_type
8
+ {
9
+ :string => [String],
10
+ :bool => [TrueClass,FalseClass],
11
+ :integer => [Fixnum],
12
+ :array_of_strings => [Array],
13
+ :hash_of_bools => [Hash],
14
+ :hash => [Hash],
15
+ :bool_or_string_or_array_of_strings => [TrueClass,FalseClass,String,Array]
16
+ }
17
+ end
17
18
 
18
- def valid_keys
19
- {}
20
- end
19
+ def valid_keys
20
+ {}
21
+ end
21
22
 
22
- def method_missing method_symbol, *args, &blk
23
- puts "method_missing: #{method_symbol.inspect}, args: #{args.inspect}"
24
- valid_keys.each do |key_type, valid_keys_of_those_type|
25
- if valid_keys_of_those_type.include?(method_symbol.to_s.camelcase)
26
- puts "key_type = #{key_type}, method_symbol.to_s.camelcase = #{method_symbol.to_s.camelcase}, args = #{args.inspect}"
27
- return eval("set_or_return key_type, method_symbol.to_s.camelcase, *args, &blk")
23
+ def method_missing method_symbol, *args, &blk
24
+ puts "method_missing: #{method_symbol.inspect}, args: #{args.inspect}"
25
+ valid_keys.each do |key_type, valid_keys_of_those_type|
26
+ if valid_keys_of_those_type.include?(method_symbol.to_s.camelcase)
27
+ puts "key_type = #{key_type}, method_symbol.to_s.camelcase = #{method_symbol.to_s.camelcase}, args = #{args.inspect}"
28
+ return eval("set_or_return key_type, method_symbol.to_s.camelcase, *args, &blk")
29
+ end
28
30
  end
29
31
  end
30
- end
31
32
 
32
- def validate_value key_type, key, value
33
- unless classes_for_key_type[key_type].include? value.class
34
- raise "Key: #{key}, value: #{value.inspect} is of type #{value.class}. Should be: #{classes_for_key_type[key_type].join ", "}"
35
- end
36
- case key_type
37
- when :array_of_strings, :bool_or_string_or_array_of_strings
38
- if value.class == Array
39
- value.each_index do |i|
40
- unless value[i].class == String
41
- raise "Element: #{key}[#{i}], value: #{value[i].inspect} is of type #{value[i].class}. Should be: #{classes_for_key_type[:string].join ", "}"
33
+ def validate_value key_type, key, value
34
+ unless classes_for_key_type[key_type].include? value.class
35
+ raise "Key: #{key}, value: #{value.inspect} is of type #{value.class}. Should be: #{classes_for_key_type[key_type].join ", "}"
36
+ end
37
+ case key_type
38
+ when :array_of_strings, :bool_or_string_or_array_of_strings
39
+ if value.class == Array
40
+ value.each_index do |i|
41
+ unless value[i].class == String
42
+ raise "Element: #{key}[#{i}], value: #{value[i].inspect} is of type #{value[i].class}. Should be: #{classes_for_key_type[:string].join ", "}"
43
+ end
42
44
  end
43
45
  end
44
- end
45
- when :hash_of_bools
46
- value.each do |k,v|
47
- unless [TrueClass,FalseClass].include? v.class
48
- raise "Key: #{key}[#{k}], value: #{v.inspect} is of type #{v.class}. Should be: #{classes_for_key_type[:bool].join ", "}"
46
+ when :hash_of_bools
47
+ value.each do |k,v|
48
+ unless [TrueClass,FalseClass].include? v.class
49
+ raise "Key: #{key}[#{k}], value: #{v.inspect} is of type #{v.class}. Should be: #{classes_for_key_type[:bool].join ", "}"
50
+ end
49
51
  end
50
52
  end
51
53
  end
52
- end
53
54
 
54
- def set_or_return key_type, key, value=nil
55
- puts "#{method_name}, key_type: #{key_type.inspect}, value: #{value.inspect}"
56
- if value
57
- validate_value key_type, key, value unless key_type == nil
58
- @hash[key] = value
59
- else
60
- @orig[key]
55
+ def set_or_return key_type, key, value=nil
56
+ puts "#{method_name}, key_type: #{key_type.inspect}, value: #{value.inspect}"
57
+ if value
58
+ validate_value key_type, key, value unless key_type == nil
59
+ @hash[key] = value
60
+ else
61
+ @orig[key]
62
+ end
61
63
  end
62
64
  end
63
- end
65
+ end
@@ -1,193 +1,199 @@
1
1
 
2
-
3
- module Plist4r::Popen4
4
-
5
- # This is taken directly from Ara T Howard's Open4 library, and then
6
- # modified to suit the needs of Chef. Any bugs here are most likely
7
- # my own, and not Ara's.
8
- #
9
- # The original appears in external/open4.rb in its unmodified form.
10
- #
11
- # Thanks Ara!
12
- def popen4(cmd, args={}, &b)
2
+ require 'fcntl'
3
+ require 'etc'
4
+ require 'io/wait'
5
+
6
+ module Plist4r
7
+ module Popen4
8
+ class << self
9
+ # This is taken directly from Ara T Howard's Open4 library, and then
10
+ # modified to suit the needs of Chef. Any bugs here are most likely
11
+ # my own, and not Ara's.
12
+ #
13
+ # The original appears in external/open4.rb in its unmodified form.
14
+ #
15
+ # Thanks Ara!
16
+ def popen4(cmd, args={}, &b)
13
17
 
14
- # Waitlast - this is magic.
15
- #
16
- # Do we wait for the child process to die before we yield
17
- # to the block, or after? That is the magic of waitlast.
18
- #
19
- # By default, we are waiting before we yield the block.
20
- args[:waitlast] ||= false
18
+ # Waitlast - this is magic.
19
+ #
20
+ # Do we wait for the child process to die before we yield
21
+ # to the block, or after? That is the magic of waitlast.
22
+ #
23
+ # By default, we are waiting before we yield the block.
24
+ args[:waitlast] ||= false
21
25
 
22
- args[:user] ||= nil
23
- unless args[:user].kind_of?(Integer)
24
- args[:user] = Etc.getpwnam(args[:user]).uid if args[:user]
25
- end
26
- args[:group] ||= nil
27
- unless args[:group].kind_of?(Integer)
28
- args[:group] = Etc.getgrnam(args[:group]).gid if args[:group]
29
- end
30
- args[:environment] ||= {}
26
+ args[:user] ||= nil
27
+ unless args[:user].kind_of?(Integer)
28
+ args[:user] = Etc.getpwnam(args[:user]).uid if args[:user]
29
+ end
30
+ args[:group] ||= nil
31
+ unless args[:group].kind_of?(Integer)
32
+ args[:group] = Etc.getgrnam(args[:group]).gid if args[:group]
33
+ end
34
+ args[:environment] ||= {}
31
35
 
32
- # Default on C locale so parsing commands output can be done
33
- # independently of the node's default locale.
34
- # "LC_ALL" could be set to nil, in which case we also must ignore it.
35
- unless args[:environment].has_key?("LC_ALL")
36
- args[:environment]["LC_ALL"] = "C"
37
- end
36
+ # Default on C locale so parsing commands output can be done
37
+ # independently of the node's default locale.
38
+ # "LC_ALL" could be set to nil, in which case we also must ignore it.
39
+ unless args[:environment].has_key?("LC_ALL")
40
+ args[:environment]["LC_ALL"] = "C"
41
+ end
38
42
 
39
- pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
43
+ pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
40
44
 
41
- verbose = $VERBOSE
42
- begin
43
- $VERBOSE = nil
44
- ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
45
+ verbose = $VERBOSE
46
+ begin
47
+ $VERBOSE = nil
48
+ ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
45
49
 
46
- cid = fork {
47
- pw.last.close
48
- STDIN.reopen pw.first
49
- pw.first.close
50
+ cid = fork {
51
+ pw.last.close
52
+ STDIN.reopen pw.first
53
+ pw.first.close
50
54
 
51
- pr.first.close
52
- STDOUT.reopen pr.last
53
- pr.last.close
55
+ pr.first.close
56
+ STDOUT.reopen pr.last
57
+ pr.last.close
54
58
 
55
- pe.first.close
56
- STDERR.reopen pe.last
57
- pe.last.close
59
+ pe.first.close
60
+ STDERR.reopen pe.last
61
+ pe.last.close
58
62
 
59
- STDOUT.sync = STDERR.sync = true
63
+ STDOUT.sync = STDERR.sync = true
60
64
 
61
- if args[:group]
62
- Process.egid = args[:group]
63
- Process.gid = args[:group]
64
- end
65
+ if args[:group]
66
+ Process.egid = args[:group]
67
+ Process.gid = args[:group]
68
+ end
65
69
 
66
- if args[:user]
67
- Process.euid = args[:user]
68
- Process.uid = args[:user]
69
- end
70
+ if args[:user]
71
+ Process.euid = args[:user]
72
+ Process.uid = args[:user]
73
+ end
70
74
 
71
- args[:environment].each do |key,value|
72
- ENV[key] = value
73
- end
75
+ args[:environment].each do |key,value|
76
+ ENV[key] = value
77
+ end
74
78
 
75
- if args[:umask]
76
- umask = ((args[:umask].respond_to?(:oct) ? args[:umask].oct : args[:umask].to_i) & 007777)
77
- File.umask(umask)
78
- end
79
+ if args[:umask]
80
+ umask = ((args[:umask].respond_to?(:oct) ? args[:umask].oct : args[:umask].to_i) & 007777)
81
+ File.umask(umask)
82
+ end
79
83
 
80
- begin
81
- if cmd.kind_of?(Array)
82
- exec(*cmd)
83
- else
84
- exec(cmd)
85
- end
86
- raise 'forty-two'
87
- rescue Exception => e
88
- Marshal.dump(e, ps.last)
89
- ps.last.flush
84
+ begin
85
+ if cmd.kind_of?(Array)
86
+ exec(*cmd)
87
+ else
88
+ exec(cmd)
89
+ end
90
+ raise 'forty-two'
91
+ rescue Exception => e
92
+ Marshal.dump(e, ps.last)
93
+ ps.last.flush
94
+ end
95
+ ps.last.close unless (ps.last.closed?)
96
+ exit!
97
+ }
98
+ ensure
99
+ $VERBOSE = verbose
90
100
  end
91
- ps.last.close unless (ps.last.closed?)
92
- exit!
93
- }
94
- ensure
95
- $VERBOSE = verbose
96
- end
97
-
98
- [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close}
99
101
 
100
- begin
101
- e = Marshal.load ps.first
102
- raise(Exception === e ? e : "unknown failure!")
103
- rescue EOFError # If we get an EOF error, then the exec was successful
104
- 42
105
- ensure
106
- ps.first.close
107
- end
108
-
109
- pw.last.sync = true
102
+ [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close}
110
103
 
111
- pi = [pw.last, pr.first, pe.first]
104
+ begin
105
+ e = Marshal.load ps.first
106
+ raise(Exception === e ? e : "unknown failure!")
107
+ rescue EOFError # If we get an EOF error, then the exec was successful
108
+ 42
109
+ ensure
110
+ ps.first.close
111
+ end
112
112
 
113
- if b
114
- begin
115
- if args[:waitlast]
116
- b[cid, *pi]
117
- # send EOF so that if the child process is reading from STDIN
118
- # it will actually finish up and exit
119
- pi[0].close_write
120
- Process.waitpid2(cid).last
121
- else
122
- # This took some doing.
123
- # The trick here is to close STDIN
124
- # Then set our end of the childs pipes to be O_NONBLOCK
125
- # Then wait for the child to die, which means any IO it
126
- # wants to do must be done - it's dead. If it isn't,
127
- # it's because something totally skanky is happening,
128
- # and we don't care.
129
- o = StringIO.new
130
- e = StringIO.new
131
-
132
- pi[0].close
113
+ pw.last.sync = true
114
+
115
+ pi = [pw.last, pr.first, pe.first]
116
+
117
+ if b
118
+ begin
119
+ if args[:waitlast]
120
+ b[cid, *pi]
121
+ # send EOF so that if the child process is reading from STDIN
122
+ # it will actually finish up and exit
123
+ pi[0].close_write
124
+ Process.waitpid2(cid).last
125
+ else
126
+ # This took some doing.
127
+ # The trick here is to close STDIN
128
+ # Then set our end of the childs pipes to be O_NONBLOCK
129
+ # Then wait for the child to die, which means any IO it
130
+ # wants to do must be done - it's dead. If it isn't,
131
+ # it's because something totally skanky is happening,
132
+ # and we don't care.
133
+ o = StringIO.new
134
+ e = StringIO.new
135
+
136
+ pi[0].close
133
137
 
134
- stdout = pi[1]
135
- stderr = pi[2]
138
+ stdout = pi[1]
139
+ stderr = pi[2]
136
140
 
137
- stdout.sync = true
138
- stderr.sync = true
141
+ stdout.sync = true
142
+ stderr.sync = true
139
143
 
140
- stdout.fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
141
- stderr.fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
144
+ stdout.fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
145
+ stderr.fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
142
146
 
143
- stdout_finished = false
144
- stderr_finished = false
147
+ stdout_finished = false
148
+ stderr_finished = false
145
149
 
146
- results = nil
147
-
148
- while !stdout_finished || !stderr_finished
149
- begin
150
- channels_to_watch = []
151
- channels_to_watch << stdout if !stdout_finished
152
- channels_to_watch << stderr if !stderr_finished
153
- ready = IO.select(channels_to_watch, nil, nil, 1.0)
154
- rescue Errno::EAGAIN
155
- ensure
156
- results = Process.waitpid2(cid, Process::WNOHANG)
157
- if results
158
- stdout_finished = true
159
- stderr_finished = true
160
- end
161
- end
162
-
163
- if ready && ready.first.include?(stdout)
164
- line = results ? stdout.gets(nil) : stdout.gets
165
- if line
166
- o.write(line)
167
- else
168
- stdout_finished = true
169
- end
170
- end
171
- if ready && ready.first.include?(stderr)
172
- line = results ? stderr.gets(nil) : stderr.gets
173
- if line
174
- e.write(line)
175
- else
176
- stderr_finished = true
150
+ results = nil
151
+
152
+ while !stdout_finished || !stderr_finished
153
+ begin
154
+ channels_to_watch = []
155
+ channels_to_watch << stdout if !stdout_finished
156
+ channels_to_watch << stderr if !stderr_finished
157
+ ready = IO.select(channels_to_watch, nil, nil, 1.0)
158
+ rescue Errno::EAGAIN
159
+ ensure
160
+ results = Process.waitpid2(cid, Process::WNOHANG)
161
+ if results
162
+ stdout_finished = true
163
+ stderr_finished = true
164
+ end
165
+ end
166
+
167
+ if ready && ready.first.include?(stdout)
168
+ line = results ? stdout.gets(nil) : stdout.gets
169
+ if line
170
+ o.write(line)
171
+ else
172
+ stdout_finished = true
173
+ end
174
+ end
175
+ if ready && ready.first.include?(stderr)
176
+ line = results ? stderr.gets(nil) : stderr.gets
177
+ if line
178
+ e.write(line)
179
+ else
180
+ stderr_finished = true
181
+ end
182
+ end
177
183
  end
184
+ results = Process.waitpid2(cid) unless results
185
+ o.rewind
186
+ e.rewind
187
+ b[cid, pi[0], o, e]
188
+ results.last
178
189
  end
190
+ ensure
191
+ pi.each{|fd| fd.close unless fd.closed?}
179
192
  end
180
- results = Process.waitpid2(cid) unless results
181
- o.rewind
182
- e.rewind
183
- b[cid, pi[0], o, e]
184
- results.last
193
+ else
194
+ [cid, pw.last, pr.first, pe.first]
185
195
  end
186
- ensure
187
- pi.each{|fd| fd.close unless fd.closed?}
188
196
  end
189
- else
190
- [cid, pw.last, pr.first, pe.first]
191
197
  end
192
198
  end
193
199
  end