virtfs 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +154 -0
- data/Rakefile +5 -0
- data/lib/virtfs-nativefs-thick.rb +1 -0
- data/lib/virtfs-nativefs-thin.rb +1 -0
- data/lib/virtfs.rb +38 -0
- data/lib/virtfs/activation.rb +97 -0
- data/lib/virtfs/block_io.rb +140 -0
- data/lib/virtfs/byte_range.rb +71 -0
- data/lib/virtfs/context.rb +300 -0
- data/lib/virtfs/context_manager.rb +175 -0
- data/lib/virtfs/context_switch_class_methods.rb +96 -0
- data/lib/virtfs/delegate_module.rb +40 -0
- data/lib/virtfs/dir_instance_delegate.rb +3 -0
- data/lib/virtfs/exception.rb +13 -0
- data/lib/virtfs/file_instance_delegate.rb +3 -0
- data/lib/virtfs/file_modes_and_options.rb +293 -0
- data/lib/virtfs/find_class_methods.rb +106 -0
- data/lib/virtfs/io_buffer.rb +133 -0
- data/lib/virtfs/io_instance_delegate.rb +3 -0
- data/lib/virtfs/kernel.rb +146 -0
- data/lib/virtfs/nativefs/thick.rb +30 -0
- data/lib/virtfs/nativefs/thick/dir_class_methods.rb +38 -0
- data/lib/virtfs/nativefs/thick/file_class_methods.rb +178 -0
- data/lib/virtfs/nativefs/thin.rb +32 -0
- data/lib/virtfs/nativefs/thin/dir.rb +30 -0
- data/lib/virtfs/nativefs/thin/dir_class_methods.rb +41 -0
- data/lib/virtfs/nativefs/thin/file.rb +112 -0
- data/lib/virtfs/nativefs/thin/file_class_methods.rb +181 -0
- data/lib/virtfs/protofs/protofs.rb +7 -0
- data/lib/virtfs/protofs/protofs_base.rb +12 -0
- data/lib/virtfs/protofs/protofs_dir.rb +13 -0
- data/lib/virtfs/protofs/protofs_dir_class.rb +31 -0
- data/lib/virtfs/protofs/protofs_file.rb +27 -0
- data/lib/virtfs/protofs/protofs_file_class.rb +136 -0
- data/lib/virtfs/stat.rb +100 -0
- data/lib/virtfs/thin_dir_delegator.rb +79 -0
- data/lib/virtfs/thin_file_delegator.rb +77 -0
- data/lib/virtfs/thin_io_delegator_methods.rb +301 -0
- data/lib/virtfs/thin_io_delegator_methods_bufferio.rb +337 -0
- data/lib/virtfs/v_dir.rb +238 -0
- data/lib/virtfs/v_file.rb +480 -0
- data/lib/virtfs/v_io.rb +243 -0
- data/lib/virtfs/v_pathname.rb +128 -0
- data/lib/virtfs/version.rb +3 -0
- data/spec/activate_spec.rb +202 -0
- data/spec/chroot_spec.rb +120 -0
- data/spec/context_manager_class_spec.rb +246 -0
- data/spec/context_manager_instance_spec.rb +255 -0
- data/spec/context_spec.rb +335 -0
- data/spec/data/UTF-16LE-data.txt +0 -0
- data/spec/data/UTF-8-data.txt +212 -0
- data/spec/dir_class_spec.rb +506 -0
- data/spec/dir_instance_spec.rb +208 -0
- data/spec/file_class_spec.rb +2106 -0
- data/spec/file_instance_spec.rb +154 -0
- data/spec/file_modes_and_options_spec.rb +1556 -0
- data/spec/find_spec.rb +142 -0
- data/spec/io_bufferio_size_shared_examples.rb +371 -0
- data/spec/io_bufferio_size_spec.rb +861 -0
- data/spec/io_bufferio_spec.rb +801 -0
- data/spec/io_class_spec.rb +145 -0
- data/spec/io_instance_spec.rb +516 -0
- data/spec/kernel_spec.rb +285 -0
- data/spec/mount_spec.rb +186 -0
- data/spec/nativefs_local_root_spec.rb +132 -0
- data/spec/path_spec.rb +39 -0
- data/spec/spec_helper.rb +126 -0
- data/tasks/rspec.rake +3 -0
- data/tasks/yard.rake +7 -0
- data/test/UTF-8-demo.txt +212 -0
- data/test/bench.rb +18 -0
- data/test/bio_internal_test.rb +45 -0
- data/test/delegate_io.rb +31 -0
- data/test/delegate_module.rb +62 -0
- data/test/encode_test.rb +42 -0
- data/test/enoent_test.rb +30 -0
- data/test/namespace_test.rb +42 -0
- data/test/read_block_valid_encoding.rb +44 -0
- data/test/read_test.rb +78 -0
- data/test/stream_readers.rb +46 -0
- data/test/utf-16-demo.txt +0 -0
- data/test/utf8_to_utf16.rb +77 -0
- data/test/wrapper_test.rb +34 -0
- data/virtfs.gemspec +29 -0
- metadata +230 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
module VirtFS
|
2
|
+
module ContextSwitchClassMethods
|
3
|
+
def context_manager
|
4
|
+
ContextManager
|
5
|
+
end
|
6
|
+
|
7
|
+
def context
|
8
|
+
context_manager.current.current_context
|
9
|
+
end
|
10
|
+
|
11
|
+
def context!
|
12
|
+
context_manager.current!.current_context
|
13
|
+
end
|
14
|
+
|
15
|
+
def mount(fs_instance, mount_point)
|
16
|
+
context.mount(fs_instance, mount_point)
|
17
|
+
end
|
18
|
+
|
19
|
+
def umount(mount_point)
|
20
|
+
context.umount(mount_point)
|
21
|
+
end
|
22
|
+
|
23
|
+
def mounted?(mount_point)
|
24
|
+
context.mounted?(mount_point)
|
25
|
+
end
|
26
|
+
|
27
|
+
def cwd=(dir)
|
28
|
+
context.restore_cwd_root(dir, nil)
|
29
|
+
end
|
30
|
+
|
31
|
+
def root
|
32
|
+
_cwd, root = context.cwd_root
|
33
|
+
root
|
34
|
+
end
|
35
|
+
|
36
|
+
def dir_chroot(dir)
|
37
|
+
context.chroot(dir)
|
38
|
+
end
|
39
|
+
|
40
|
+
def dir_chdir(dir)
|
41
|
+
context.chdir(dir)
|
42
|
+
end
|
43
|
+
|
44
|
+
def dir_getwd
|
45
|
+
context.getwd
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Expand symbolic links and perform mount indirection look up.
|
50
|
+
#
|
51
|
+
def path_lookup(path, raise_full_path = false, include_last = true)
|
52
|
+
context.path_lookup(path, raise_full_path, include_last)
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Expand symbolic links in the path.
|
57
|
+
# This must be done here, because a symlink in one file system
|
58
|
+
# can point to a file in another filesystem.
|
59
|
+
#
|
60
|
+
def expand_links(p, include_last = true)
|
61
|
+
context.expand_links(p, include_last)
|
62
|
+
end
|
63
|
+
|
64
|
+
def normalize_path(p, relative_to = nil)
|
65
|
+
# When running on windows, File.expand_path will add a drive letter.
|
66
|
+
# Remove it if it's there.
|
67
|
+
VfsRealFile.expand_path(p, relative_to || context.getwd).sub(/^[a-zA-Z]:/, "") # XXX
|
68
|
+
end
|
69
|
+
|
70
|
+
# Invoke block withing the given filesystem context
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
# @param fs [VirtFS::FS] filesystem intstance through which to invoke block
|
74
|
+
# @param path [String] path to specify to block
|
75
|
+
#
|
76
|
+
# @raise [VirtFS::NotImplementedError] if filesystem method does not exist
|
77
|
+
#
|
78
|
+
def fs_call(fs, path = nil, &block)
|
79
|
+
block.arity < 1 ? fs.instance_eval(&block) : fs.instance_exec(path, &block)
|
80
|
+
rescue NoMethodError => err
|
81
|
+
STDOUT.puts err.to_s
|
82
|
+
STDOUT.puts err.backtrace.join("\n")
|
83
|
+
raise VirtFS::NotImplementedError.new(fs, err.name)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Invoke block using fully resolved filesystem path
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
# @see .fs_call
|
90
|
+
# @see Context#path_lookup
|
91
|
+
def fs_lookup_call(path, raise_full_path = false, include_last = true, &block)
|
92
|
+
fs, p = path_lookup(path, raise_full_path, include_last)
|
93
|
+
fs_call(fs, p, &block)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
3
|
+
module VirtFS
|
4
|
+
module DelegateModule
|
5
|
+
def delegate_module(superclass)
|
6
|
+
mod = Module.new
|
7
|
+
methods = superclass.instance_methods
|
8
|
+
methods -= ::Delegator.public_api
|
9
|
+
methods -= [:to_s, :inspect, :=~, :!~, :===]
|
10
|
+
|
11
|
+
mod.module_eval do
|
12
|
+
def __getobj__ # :nodoc:
|
13
|
+
unless defined?(@delegate_dc_obj)
|
14
|
+
return yield if block_given?
|
15
|
+
__raise__ ::ArgumentError, "not delegated"
|
16
|
+
end
|
17
|
+
@delegate_dc_obj
|
18
|
+
end
|
19
|
+
|
20
|
+
def __setobj__(obj) # :nodoc:
|
21
|
+
__raise__ ::ArgumentError, "cannot delegate to self" if self.equal?(obj)
|
22
|
+
@delegate_dc_obj = obj
|
23
|
+
end
|
24
|
+
|
25
|
+
methods.each do |method|
|
26
|
+
define_method(method, Delegator.delegating_block(method))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
mod.define_singleton_method :public_instance_methods do |all = true|
|
31
|
+
super(all) - superclass.protected_instance_methods
|
32
|
+
end
|
33
|
+
|
34
|
+
mod.define_singleton_method :protected_instance_methods do |all = true|
|
35
|
+
super(all) | superclass.protected_instance_methods
|
36
|
+
end
|
37
|
+
mod
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module VirtFS
|
2
|
+
class NotImplementedError < StandardError
|
3
|
+
def initialize(fs, method)
|
4
|
+
super "Feature: #{method} - not implemented in #{fs.name} filesystem"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class NoContextError < StandardError
|
9
|
+
def initialize
|
10
|
+
super "No filesystem context defined for thread group: #{Thread.current.group.inspect}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,293 @@
|
|
1
|
+
module VirtFS
|
2
|
+
|
3
|
+
# Utilty class to convert String File Modes and Options to their
|
4
|
+
# corresponding Ruby Constants.
|
5
|
+
#
|
6
|
+
# *args --> mode="r" <,permission> <,options>
|
7
|
+
# mode --> String or Integer
|
8
|
+
# permission --> Integer
|
9
|
+
# options --> Hash
|
10
|
+
#
|
11
|
+
# 1 arg: <mode | options>
|
12
|
+
# 2 args: mode, <permissions | options>
|
13
|
+
# 3 args: mode, permissions, options
|
14
|
+
#
|
15
|
+
# mode string --> file-mode[:external-encoding[:internal-encoding]]
|
16
|
+
#
|
17
|
+
# file-mode mapped to binary:
|
18
|
+
# "r" --> File::RDONLY
|
19
|
+
# "r+" --> File::RDWR
|
20
|
+
# "w" --> File::WRONLY | File::TRUNC | File::CREAT
|
21
|
+
# "w+" --> File::RDWR | File::TRUNC | File::CREAT
|
22
|
+
# "a" --> File::WRONLY | File::APPEND | File::CREAT
|
23
|
+
# "a+" --> File::RDWR | File::APPEND | File::CREAT
|
24
|
+
#
|
25
|
+
# Options:
|
26
|
+
# :autoclose => If false, the underlying file will not be closed
|
27
|
+
# when this I/O object is finalized.
|
28
|
+
#
|
29
|
+
# :binmode => Opens the IO object in binary mode if true (same as mode: "b").
|
30
|
+
#
|
31
|
+
# :encoding => Specifies both external and internal encodings
|
32
|
+
# as "external:internal" (same format used in mode parameter).
|
33
|
+
#
|
34
|
+
# :external_encoding => Specifies the external encoding.
|
35
|
+
#
|
36
|
+
# :internal_encoding => Specifies the internal encoding.
|
37
|
+
#
|
38
|
+
# :mode => Specifies what would have been the mode parameter.
|
39
|
+
#
|
40
|
+
# :textmode => Open the file in text mode (the default).
|
41
|
+
#
|
42
|
+
class FileModesAndOptions # rubocop:disable ClassLength
|
43
|
+
attr_reader :external_encoding, :external_encoding_str
|
44
|
+
attr_reader :internal_encoding, :internal_encoding_str
|
45
|
+
attr_reader :permissions, :mode_bits, :options
|
46
|
+
attr_reader :args
|
47
|
+
|
48
|
+
BINARY_ENCODING = "ASCII-8BIT"
|
49
|
+
|
50
|
+
def initialize(*args) # rubocop:disable AbcSize, PerceivedComplexity
|
51
|
+
@args = args
|
52
|
+
@options = {}
|
53
|
+
@mode_bits = 0
|
54
|
+
@external_encoding_str = ""
|
55
|
+
@internal_encoding_str = ""
|
56
|
+
@binmode = false
|
57
|
+
@autoclose = true
|
58
|
+
@permissions = nil
|
59
|
+
@closed = false
|
60
|
+
|
61
|
+
process_args(args)
|
62
|
+
|
63
|
+
@mode_bits = VFile::RDONLY if @mode_bits == 0
|
64
|
+
|
65
|
+
if @external_encoding_str.empty? || @external_encoding_str.empty? == "-"
|
66
|
+
@external_encoding = Encoding.default_external
|
67
|
+
else
|
68
|
+
@external_encoding = Encoding.find(@external_encoding_str)
|
69
|
+
end
|
70
|
+
|
71
|
+
if @internal_encoding_str.empty? || @internal_encoding_str == "-"
|
72
|
+
@internal_encoding = Encoding.default_internal
|
73
|
+
else
|
74
|
+
@internal_encoding = Encoding.find(@internal_encoding_str)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def [](key)
|
79
|
+
@options[key]
|
80
|
+
end
|
81
|
+
|
82
|
+
def append?
|
83
|
+
@mode_bits & VFile::APPEND != 0
|
84
|
+
end
|
85
|
+
|
86
|
+
def autoclose?
|
87
|
+
@autoclose
|
88
|
+
end
|
89
|
+
|
90
|
+
def binmode?
|
91
|
+
@binmode
|
92
|
+
end
|
93
|
+
|
94
|
+
def binmode
|
95
|
+
set_encoding(BINARY_ENCODING, nil)
|
96
|
+
@binmode = true
|
97
|
+
end
|
98
|
+
|
99
|
+
def closed?
|
100
|
+
@closed
|
101
|
+
end
|
102
|
+
|
103
|
+
def create?
|
104
|
+
@mode_bits & VFile::CREAT != 0
|
105
|
+
end
|
106
|
+
|
107
|
+
def excl?
|
108
|
+
@mode_bits & VFile::EXCL != 0
|
109
|
+
end
|
110
|
+
|
111
|
+
def noctty?
|
112
|
+
@mode_bits & VFile::NOCTTY != 0
|
113
|
+
end
|
114
|
+
|
115
|
+
def nonblock?
|
116
|
+
@mode_bits & VFile::NONBLOCK != 0
|
117
|
+
end
|
118
|
+
|
119
|
+
def rdonly?
|
120
|
+
@mode_bits == VFile::RDONLY # VFile::RDONLY = 0
|
121
|
+
end
|
122
|
+
|
123
|
+
def rdwr?
|
124
|
+
@mode_bits & VFile::RDWR != 0
|
125
|
+
end
|
126
|
+
|
127
|
+
def trunc?
|
128
|
+
@mode_bits & VFile::TRUNC != 0
|
129
|
+
end
|
130
|
+
|
131
|
+
def wronly?
|
132
|
+
@mode_bits & VFile::WRONLY != 0
|
133
|
+
end
|
134
|
+
|
135
|
+
def read?
|
136
|
+
rdonly? || rdwr?
|
137
|
+
end
|
138
|
+
|
139
|
+
def write?
|
140
|
+
wronly? || rdwr?
|
141
|
+
end
|
142
|
+
|
143
|
+
def close_read
|
144
|
+
return unless rdonly?
|
145
|
+
close
|
146
|
+
end
|
147
|
+
|
148
|
+
def close_write
|
149
|
+
return unless wronly?
|
150
|
+
close
|
151
|
+
end
|
152
|
+
|
153
|
+
def close
|
154
|
+
@closed = true
|
155
|
+
end
|
156
|
+
|
157
|
+
def set_encoding(*args) # rubocop:disable AccessorMethodName, AbcSize, PerceivedComplexity, CyclomaticComplexity, MethodLength, LineLength
|
158
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 1..2)" if args.length < 1 || args.length > 2
|
159
|
+
unless args[0].is_a?(Encoding) || args[0].respond_to?(:to_str)
|
160
|
+
raise TypeError, "no implicit conversion of #{args[0].class.name} into String"
|
161
|
+
end
|
162
|
+
|
163
|
+
if args.length == 2
|
164
|
+
unless args[1].is_a?(Encoding) || args[1].respond_to?(:to_str) || args[1].nil?
|
165
|
+
raise TypeError, "no implicit conversion of #{args[1].class.name} into String"
|
166
|
+
end
|
167
|
+
@external_encoding = args[0].is_a?(Encoding) ? args[0] : Encoding.find(args[0].to_str)
|
168
|
+
@internal_encoding = args[1].nil? || args[1].is_a?(Encoding) ? args[1] : Encoding.find(args[1].to_str)
|
169
|
+
return nil
|
170
|
+
end
|
171
|
+
|
172
|
+
if args[0].is_a?(Encoding)
|
173
|
+
@external_encoding = args[0]
|
174
|
+
return nil
|
175
|
+
end
|
176
|
+
|
177
|
+
if args[0].to_str.include?(":")
|
178
|
+
@external_encoding_str, @internal_encoding_str = args[0].split(":")
|
179
|
+
@external_encoding_str = @external_encoding_str.to_str
|
180
|
+
@internal_encoding_str = @internal_encoding_str.to_str
|
181
|
+
else
|
182
|
+
@external_encoding_str = @internal_encoding_str = args[0].to_str
|
183
|
+
end
|
184
|
+
|
185
|
+
@external_encoding = Encoding.find(@external_encoding_str) unless @external_encoding_str.empty?
|
186
|
+
@internal_encoding = Encoding.find(@internal_encoding_str) unless @internal_encoding_str.empty?
|
187
|
+
nil
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def process_args(args) # rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity
|
193
|
+
case args.length
|
194
|
+
when 0
|
195
|
+
# mode = "r"
|
196
|
+
when 1
|
197
|
+
# <mode | options>
|
198
|
+
if args[0].is_a?(Hash)
|
199
|
+
@options = args[0]
|
200
|
+
else
|
201
|
+
mode_arg(args[0])
|
202
|
+
end
|
203
|
+
when 2
|
204
|
+
# mode, <permissions | options>
|
205
|
+
mode_arg(args[0])
|
206
|
+
if args[1].is_a?(Hash)
|
207
|
+
@options = args[1]
|
208
|
+
else
|
209
|
+
@permissions = args[1].to_int
|
210
|
+
end
|
211
|
+
when 3
|
212
|
+
# mode, permissions, options
|
213
|
+
mode_arg(args[0])
|
214
|
+
@permissions = args[1].to_int
|
215
|
+
@options = args[2]
|
216
|
+
raise ArgumentError, "wrong number of arguments (4 for 1..3)" unless @options.is_a?(Hash)
|
217
|
+
end
|
218
|
+
process_options(@options)
|
219
|
+
end
|
220
|
+
|
221
|
+
def mode_arg(mode) # rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity
|
222
|
+
if mode.respond_to?(:to_int)
|
223
|
+
@mode_bits = mode.to_int
|
224
|
+
else
|
225
|
+
@mode_string = mode.to_str
|
226
|
+
@file_mode, external_encoding_str, internal_encoding_str = @mode_string.split(":")
|
227
|
+
|
228
|
+
unless external_encoding_str.nil? || external_encoding_str.empty?
|
229
|
+
raise ArgumentError, "encoding specified twice" unless @external_encoding_str.empty?
|
230
|
+
@external_encoding_str = external_encoding_str.to_s
|
231
|
+
end
|
232
|
+
unless internal_encoding_str.nil? || internal_encoding_str.empty?
|
233
|
+
raise ArgumentError, "encoding specified twice" unless @internal_encoding_str.empty?
|
234
|
+
@internal_encoding_str = internal_encoding_str.to_s
|
235
|
+
end
|
236
|
+
|
237
|
+
mode_str_to_bits(@file_mode)
|
238
|
+
end
|
239
|
+
@mode_provided = true
|
240
|
+
end
|
241
|
+
|
242
|
+
def mode_str_to_bits(mode_str) # rubocop:disable AbcSize, CyclomaticComplexity
|
243
|
+
if mode_str[-1] == "b"
|
244
|
+
binmode
|
245
|
+
mode_str[-1] = ""
|
246
|
+
return if mode_str.empty?
|
247
|
+
end
|
248
|
+
|
249
|
+
@mode_bits = case mode_str
|
250
|
+
when "r" then VFile::RDONLY
|
251
|
+
when "r+" then VFile::RDWR
|
252
|
+
when "w" then VFile::WRONLY | VFile::TRUNC | VFile::CREAT
|
253
|
+
when "w+" then VFile::RDWR | VFile::TRUNC | VFile::CREAT
|
254
|
+
when "a" then VFile::WRONLY | VFile::APPEND | VFile::CREAT
|
255
|
+
when "a+" then VFile::RDWR | VFile::APPEND | VFile::CREAT
|
256
|
+
else
|
257
|
+
raise ArgumentError, "invalid access mode #{mode_str}"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def process_options(opts) # rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity, MethodLength
|
262
|
+
return if opts.empty?
|
263
|
+
|
264
|
+
@autoclose = opts[:autoclose] if opts.key?(:autoclose)
|
265
|
+
if opts.key?(:binmode)
|
266
|
+
raise ArgumentError, "binmode specified twice" if binmode?
|
267
|
+
binmode
|
268
|
+
end
|
269
|
+
if opts[:encoding]
|
270
|
+
raise ArgumentError, "encoding specified twice" unless @external_encoding_str.empty? && @internal_encoding_str.empty? # rubocop:disable LineLength
|
271
|
+
@external_encoding_str, @internal_encoding_str = opts[:encoding].split(":")
|
272
|
+
@external_encoding_str = @external_encoding_str.to_s
|
273
|
+
@internal_encoding_str = @internal_encoding_str.to_s
|
274
|
+
end
|
275
|
+
if opts[:external_encoding]
|
276
|
+
raise ArgumentError, "encoding specified twice" unless @external_encoding_str.empty?
|
277
|
+
@external_encoding_str = opts[:external_encoding].dup
|
278
|
+
end
|
279
|
+
if opts[:internal_encoding]
|
280
|
+
raise ArgumentError, "encoding specified twice" unless @internal_encoding_str.empty?
|
281
|
+
@internal_encoding_str = opts[:internal_encoding]
|
282
|
+
end
|
283
|
+
if opts[:mode]
|
284
|
+
# raise ArgumentError, "mode specified twice" if @mode_provided
|
285
|
+
mode_arg(opts[:mode])
|
286
|
+
end
|
287
|
+
if opts.key?(:textmode)
|
288
|
+
raise ArgumentError, "binmode and textmode are mutually exclusive" if binmode?
|
289
|
+
end
|
290
|
+
nil
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|