2 **** BEGIN LICENSE BLOCK *****
3 * Version: CPL 1.0/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Common Public
6 * License Version 1.0 (the "License"); you may not use this file
7 * except in compliance with the License. You may obtain a copy of
8 * the License at http://www.eclipse.org/legal/cpl-v10.html
10 * Software distributed under the License is distributed on an "AS
11 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
12 * implied. See the License for the specific language governing
13 * rights and limitations under the License.
15 * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
16 * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
17 * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
18 * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
19 * Copyright (C) 2004-2006 Charles O Nutter <headius@headius.com>
20 * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
21 * Copyright (C) 2006 Evan Buswell <ebuswell@gmail.com>
22 * Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@gmail.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the CPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the CPL, the GPL or the LGPL.
35 ***** END LICENSE BLOCK *****/
38 import static java
.lang
.System
.out
;
39 import java
.util
.logging
.Level
;
40 import java
.util
.logging
.Logger
;
41 import org
.jruby
.util
.io
.FileExistsException
;
42 import org
.jruby
.util
.io
.STDIO
;
43 import org
.jruby
.util
.io
.OpenFile
;
44 import org
.jruby
.util
.io
.ChannelDescriptor
;
45 import java
.io
.EOFException
;
46 import java
.io
.FileDescriptor
;
47 import java
.io
.IOException
;
48 import java
.io
.InputStream
;
49 import java
.io
.OutputStream
;
50 import java
.lang
.ref
.Reference
;
51 import java
.lang
.ref
.WeakReference
;
52 import java
.nio
.channels
.Channel
;
53 import java
.nio
.channels
.Channels
;
54 import java
.nio
.channels
.Pipe
;
55 import java
.nio
.channels
.SelectableChannel
;
56 import java
.nio
.channels
.SelectionKey
;
57 import java
.nio
.channels
.Selector
;
58 import java
.util
.ArrayList
;
59 import java
.util
.HashSet
;
60 import java
.util
.Iterator
;
61 import java
.util
.List
;
63 import org
.jruby
.anno
.JRubyMethod
;
65 import org
.jruby
.common
.IRubyWarnings
.ID
;
66 import org
.jruby
.exceptions
.RaiseException
;
67 import org
.jruby
.runtime
.Block
;
68 import org
.jruby
.runtime
.CallType
;
69 import org
.jruby
.runtime
.CallbackFactory
;
70 import org
.jruby
.runtime
.MethodIndex
;
71 import org
.jruby
.runtime
.ObjectAllocator
;
72 import org
.jruby
.runtime
.ThreadContext
;
73 import org
.jruby
.runtime
.Visibility
;
74 import org
.jruby
.runtime
.builtin
.IRubyObject
;
75 import org
.jruby
.util
.ByteList
;
76 import org
.jruby
.util
.io
.Stream
;
77 import org
.jruby
.util
.io
.ModeFlags
;
78 import org
.jruby
.util
.ShellLauncher
;
79 import org
.jruby
.util
.TypeConverter
;
80 import org
.jruby
.util
.io
.BadDescriptorException
;
81 import org
.jruby
.util
.io
.ChannelStream
;
82 import org
.jruby
.util
.io
.InvalidValueException
;
83 import org
.jruby
.util
.io
.PipeException
;
89 public class RubyIO
extends RubyObject
{
90 protected OpenFile openFile
;
92 public void registerDescriptor(ChannelDescriptor descriptor
) {
93 getRuntime().getDescriptors().put(new Integer(descriptor
.getFileno()), new WeakReference
<ChannelDescriptor
>(descriptor
));
96 public void unregisterDescriptor(int aFileno
) {
97 getRuntime().getDescriptors().remove(new Integer(aFileno
));
100 public ChannelDescriptor
getDescriptorByFileno(int aFileno
) {
101 Reference
<ChannelDescriptor
> reference
= getRuntime().getDescriptors().get(new Integer(aFileno
));
102 if (reference
== null) {
105 return (ChannelDescriptor
) reference
.get();
108 // FIXME can't use static; would interfere with other runtimes in the same JVM
109 protected static int filenoIndex
= 2;
111 public static int getNewFileno() {
117 // This should only be called by this and RubyFile.
118 // It allows this object to be created without a IOHandler.
119 public RubyIO(Ruby runtime
, RubyClass type
) {
120 super(runtime
, type
);
122 openFile
= new OpenFile();
125 public RubyIO(Ruby runtime
, OutputStream outputStream
) {
126 super(runtime
, runtime
.getIO());
128 // We only want IO objects with valid streams (better to error now).
129 if (outputStream
== null) {
130 throw runtime
.newIOError("Opening invalid stream");
133 openFile
= new OpenFile();
136 openFile
.setMainStream(new ChannelStream(runtime
, new ChannelDescriptor(Channels
.newChannel(outputStream
), getNewFileno(), new FileDescriptor())));
137 } catch (InvalidValueException e
) {
138 throw getRuntime().newErrnoEINVALError();
141 openFile
.setMode(OpenFile
.WRITABLE
| OpenFile
.APPEND
);
143 registerDescriptor(openFile
.getMainStream().getDescriptor());
146 public RubyIO(Ruby runtime
, InputStream inputStream
) {
147 super(runtime
, runtime
.getIO());
149 if (inputStream
== null) {
150 throw runtime
.newIOError("Opening invalid stream");
153 openFile
= new OpenFile();
156 openFile
.setMainStream(new ChannelStream(runtime
, new ChannelDescriptor(Channels
.newChannel(inputStream
), getNewFileno(), new FileDescriptor())));
157 } catch (InvalidValueException e
) {
158 throw getRuntime().newErrnoEINVALError();
161 openFile
.setMode(OpenFile
.READABLE
);
163 registerDescriptor(openFile
.getMainStream().getDescriptor());
166 public RubyIO(Ruby runtime
, Channel channel
) {
167 super(runtime
, runtime
.getIO());
169 // We only want IO objects with valid streams (better to error now).
170 if (channel
== null) {
171 throw runtime
.newIOError("Opening invalid stream");
174 openFile
= new OpenFile();
177 openFile
.setMainStream(new ChannelStream(runtime
, new ChannelDescriptor(channel
, getNewFileno(), new FileDescriptor())));
178 } catch (InvalidValueException e
) {
179 throw getRuntime().newErrnoEINVALError();
182 openFile
.setMode(openFile
.getMainStream().getModes().getOpenFileFlags());
184 registerDescriptor(openFile
.getMainStream().getDescriptor());
187 public RubyIO(Ruby runtime
, Process process
, ModeFlags modes
) {
188 super(runtime
, runtime
.getIO());
190 openFile
= new OpenFile();
192 openFile
.setMode(modes
.getOpenFileFlags() | OpenFile
.SYNC
);
193 openFile
.setProcess(process
);
196 InputStream pipeIn
= process
.getInputStream();
197 ChannelDescriptor main
= new ChannelDescriptor(
198 Channels
.newChannel(pipeIn
),
200 new FileDescriptor());
202 OutputStream pipeOut
= process
.getOutputStream();
203 ChannelDescriptor pipe
= new ChannelDescriptor(
204 Channels
.newChannel(pipeOut
),
206 new FileDescriptor());
208 if (!openFile
.isReadable()) {
212 openFile
.setMainStream(new ChannelStream(getRuntime(), main
));
215 if (!openFile
.isWritable()) {
219 if (openFile
.getMainStream() != null) {
220 openFile
.setPipeStream(new ChannelStream(getRuntime(), pipe
));
222 openFile
.setMainStream(new ChannelStream(getRuntime(), pipe
));
226 registerDescriptor(main
);
227 registerDescriptor(pipe
);
228 } catch (BadDescriptorException ex
) {
229 throw getRuntime().newErrnoEBADFError();
230 } catch (IOException ex
) {
231 throw getRuntime().newIOErrorFromException(ex
);
232 } catch (InvalidValueException e
) {
233 throw getRuntime().newErrnoEINVALError();
237 public RubyIO(Ruby runtime
, STDIO stdio
) {
238 super(runtime
, runtime
.getIO());
240 openFile
= new OpenFile();
245 openFile
.setMainStream(
248 // special constructor that accepts stream, not channel
249 new ChannelDescriptor(runtime
.getIn(), 0, new ModeFlags(ModeFlags
.RDONLY
), FileDescriptor
.in
),
253 openFile
.setMainStream(
256 new ChannelDescriptor(Channels
.newChannel(runtime
.getOut()), 1, new ModeFlags(ModeFlags
.WRONLY
| ModeFlags
.APPEND
), FileDescriptor
.out
),
257 FileDescriptor
.out
));
258 openFile
.getMainStream().setSync(true);
261 openFile
.setMainStream(
264 new ChannelDescriptor(Channels
.newChannel(runtime
.getErr()), 2, new ModeFlags(ModeFlags
.WRONLY
| ModeFlags
.APPEND
), FileDescriptor
.out
),
265 FileDescriptor
.err
));
266 openFile
.getMainStream().setSync(true);
269 } catch (InvalidValueException ex
) {
270 throw getRuntime().newErrnoEINVALError();
273 openFile
.setMode(openFile
.getMainStream().getModes().getOpenFileFlags());
275 registerDescriptor(openFile
.getMainStream().getDescriptor());
278 public OpenFile
getOpenFile() {
282 protected OpenFile
getOpenFileChecked() {
283 openFile
.checkClosed(getRuntime());
287 private static ObjectAllocator IO_ALLOCATOR
= new ObjectAllocator() {
288 public IRubyObject
allocate(Ruby runtime
, RubyClass klass
) {
289 return new RubyIO(runtime
, klass
);
293 public static RubyClass
createIOClass(Ruby runtime
) {
294 RubyClass ioClass
= runtime
.defineClass("IO", runtime
.getObject(), IO_ALLOCATOR
);
295 CallbackFactory callbackFactory
= runtime
.callbackFactory(RubyIO
.class);
296 ioClass
.kindOf
= new RubyModule
.KindOf() {
298 public boolean isKindOf(IRubyObject obj
, RubyModule type
) {
299 return obj
instanceof RubyIO
;
303 ioClass
.includeModule(runtime
.getEnumerable());
305 // TODO: Implement tty? and isatty. We have no real capability to
306 // determine this from java, but if we could set tty status, then
307 // we could invoke jruby differently to allow stdin to return true
308 // on this. This would allow things like cgi.rb to work properly.
310 ioClass
.defineAnnotatedMethods(RubyIO
.class);
312 // Constants for seek
313 ioClass
.fastSetConstant("SEEK_SET", runtime
.newFixnum(Stream
.SEEK_SET
));
314 ioClass
.fastSetConstant("SEEK_CUR", runtime
.newFixnum(Stream
.SEEK_CUR
));
315 ioClass
.fastSetConstant("SEEK_END", runtime
.newFixnum(Stream
.SEEK_END
));
317 ioClass
.dispatcher
= callbackFactory
.createDispatcher(ioClass
);
322 public OutputStream
getOutStream() {
323 return getOpenFileChecked().getMainStream().newOutputStream();
326 public InputStream
getInStream() {
327 return getOpenFileChecked().getMainStream().newInputStream();
330 public Channel
getChannel() {
331 if (getOpenFileChecked().getMainStream() instanceof ChannelStream
) {
332 return ((ChannelStream
) openFile
.getMainStream()).getDescriptor().getChannel();
338 public Stream
getHandler() {
339 return getOpenFileChecked().getMainStream();
342 @JRubyMethod(name
= "reopen", required
= 1, optional
= 1)
343 public IRubyObject
reopen(IRubyObject
[] args
) throws InvalidValueException
{
344 if (args
.length
< 1) {
345 throw getRuntime().newArgumentError("wrong number of arguments");
348 IRubyObject tmp
= TypeConverter
.convertToTypeWithCheck(args
[0],
349 getRuntime().getIO(), MethodIndex
.getIndex("to_io"), "to_io");
352 RubyIO ios
= (RubyIO
) tmp
;
354 if (ios
.openFile
== this.openFile
) {
358 OpenFile originalFile
= ios
.getOpenFileChecked();
359 OpenFile selfFile
= getOpenFileChecked();
362 if (originalFile
.isReadable()) {
363 pos
= originalFile
.getMainStream().fgetpos();
366 if (originalFile
.getPipeStream() != null) {
367 originalFile
.getPipeStream().fflush();
368 } else if (originalFile
.isWritable()) {
369 originalFile
.getMainStream().fflush();
372 if (selfFile
.isWritable()) {
373 selfFile
.getWriteStream().fflush();
376 selfFile
.setMode(originalFile
.getMode());
377 selfFile
.setProcess(originalFile
.getProcess());
378 selfFile
.setLineNumber(originalFile
.getLineNumber());
379 selfFile
.setPath(originalFile
.getPath());
380 selfFile
.setFinalizer(originalFile
.getFinalizer());
382 ChannelDescriptor selfDescriptor
= selfFile
.getMainStream().getDescriptor();
383 ChannelDescriptor originalDescriptor
= originalFile
.getMainStream().getDescriptor();
385 // confirm we're not reopening self's channel
386 if (selfDescriptor
.getChannel() != originalDescriptor
.getChannel()) {
387 // check if we're a stdio IO, and ensure we're not badly mutilated
388 if (selfDescriptor
.getFileno() >=0 && selfDescriptor
.getFileno() <= 2) {
389 selfFile
.getMainStream().clearerr();
391 // dup2 new fd into self to preserve fileno and references to it
392 originalDescriptor
.dup2(selfDescriptor
);
394 // re-register, since fileno points at something new now
395 registerDescriptor(selfFile
.getMainStream().getDescriptor());
397 Stream pipeFile
= selfFile
.getPipeStream();
398 int mode
= selfFile
.getMode();
399 selfFile
.getMainStream().fclose();
400 selfFile
.setPipeStream(null);
402 // TODO: turn off readable? am I reading this right?
403 // This only seems to be used while duping below, since modes gets
404 // reset to actual modes afterward
405 //fptr->mode &= (m & FMODE_READABLE) ? ~FMODE_READABLE : ~FMODE_WRITABLE;
407 if (pipeFile
!= null) {
408 selfFile
.setMainStream(ChannelStream
.fdopen(getRuntime(), originalDescriptor
, new ModeFlags()));
409 selfFile
.setPipeStream(pipeFile
);
411 selfFile
.setMainStream(
414 originalDescriptor
.dup2(selfDescriptor
.getFileno())));
416 // re-register the descriptor
417 registerDescriptor(selfFile
.getMainStream().getDescriptor());
419 // since we're not actually duping the incoming channel into our handler, we need to
420 // copy the original sync behavior from the other handler
421 selfFile
.getMainStream().setSync(selfFile
.getMainStream().isSync());
423 selfFile
.setMode(mode
);
425 // TODO: anything threads attached to original fd are notified of the close...
426 // see rb_thread_fd_close
428 if (originalFile
.isReadable() && pos
>= 0) {
429 selfFile
.seek(pos
, Stream
.SEEK_SET
);
430 originalFile
.seek(pos
, Stream
.SEEK_SET
);
434 // TODO: more pipe logic
435 // if (fptr->f2 && fd != fileno(fptr->f2)) {
436 // fd = fileno(fptr->f2);
439 // rb_thread_fd_close(fd);
442 // else if (fd != (fd2 = fileno(orig->f2))) {
444 // rb_thread_fd_close(fd);
445 // if (dup2(fd2, fd) < 0)
446 // rb_sys_fail(orig->path);
447 // fptr->f2 = rb_fdopen(fd, "w");
451 // TODO: restore binary mode
452 // if (fptr->mode & FMODE_BINMODE) {
453 // rb_io_binmode(io);
456 // TODO: set our metaclass to target's class (i.e. scary!)
458 } catch (IOException ex
) { // TODO: better error handling
459 throw getRuntime().newIOError("could not reopen: " + ex
.getMessage());
460 } catch (BadDescriptorException ex
) {
461 throw getRuntime().newIOError("could not reopen: " + ex
.getMessage());
462 } catch (PipeException ex
) {
463 throw getRuntime().newIOError("could not reopen: " + ex
.getMessage());
466 IRubyObject pathString
= args
[0].convertToString();
468 // TODO: check safe, taint on incoming string
470 if (openFile
== null) {
471 openFile
= new OpenFile();
476 if (args
.length
> 1) {
477 IRubyObject modeString
= args
[1].convertToString();
478 modes
= getIOModes(getRuntime(), modeString
.toString());
480 openFile
.setMode(modes
.getOpenFileFlags());
482 modes
= getIOModes(getRuntime(), "r");
485 String path
= pathString
.toString();
487 // Ruby code frequently uses a platform check to choose "NUL:" on windows
488 // but since that check doesn't work well on JRuby, we help it out
490 openFile
.setPath(path
);
492 if (openFile
.getMainStream() == null) {
494 openFile
.setMainStream(ChannelStream
.fopen(getRuntime(), path
, modes
));
495 } catch (FileExistsException fee
) {
496 throw getRuntime().newErrnoEEXISTError(path
);
499 registerDescriptor(openFile
.getMainStream().getDescriptor());
500 if (openFile
.getPipeStream() != null) {
501 openFile
.getPipeStream().fclose();
502 unregisterDescriptor(openFile
.getPipeStream().getDescriptor().getFileno());
503 openFile
.setPipeStream(null);
507 // TODO: This is an freopen in MRI, this is close, but not quite the same
508 openFile
.getMainStream().freopen(path
, getIOModes(getRuntime(), openFile
.getModeAsString(getRuntime())));
511 registerDescriptor(openFile
.getMainStream().getDescriptor());
513 if (openFile
.getPipeStream() != null) {
514 // TODO: pipe handler to be reopened with path and "w" mode
517 } catch (PipeException pe
) {
518 throw getRuntime().newErrnoEPIPEError();
519 } catch (IOException ex
) {
520 throw getRuntime().newIOErrorFromException(ex
);
521 } catch (BadDescriptorException ex
) {
522 throw getRuntime().newErrnoEBADFError();
523 } catch (InvalidValueException e
) {
524 throw getRuntime().newErrnoEINVALError();
528 // A potentially previously close IO is being 'reopened'.
532 public static ModeFlags
getIOModes(Ruby runtime
, String modesString
) throws InvalidValueException
{
533 return new ModeFlags(getIOModesIntFromString(runtime
, modesString
));
536 public static int getIOModesIntFromString(Ruby runtime
, String modesString
) {
538 int length
= modesString
.length();
541 throw runtime
.newArgumentError("illegal access mode");
544 switch (modesString
.charAt(0)) {
546 modes
|= ModeFlags
.RDONLY
;
549 modes
|= ModeFlags
.APPEND
| ModeFlags
.WRONLY
| ModeFlags
.CREAT
;
552 modes
|= ModeFlags
.WRONLY
| ModeFlags
.TRUNC
| ModeFlags
.CREAT
;
555 throw runtime
.newArgumentError("illegal access mode " + modes
);
558 for (int n
= 1; n
< length
; n
++) {
559 switch (modesString
.charAt(n
)) {
561 modes
|= ModeFlags
.BINARY
;
564 modes
= (modes
& ~ModeFlags
.ACCMODE
) | ModeFlags
.RDWR
;
567 throw runtime
.newArgumentError("illegal access mode " + modes
);
574 private static ByteList
getSeparatorFromArgs(Ruby runtime
, IRubyObject
[] args
, int idx
) {
577 if (args
.length
> idx
) {
580 sepVal
= runtime
.getRecordSeparatorVar().get();
583 ByteList separator
= sepVal
.isNil() ?
null : sepVal
.convertToString().getByteList();
585 if (separator
!= null && separator
.realSize
== 0) {
586 separator
= Stream
.PARAGRAPH_DELIMETER
;
592 private ByteList
getSeparatorForGets(IRubyObject
[] args
) {
593 return getSeparatorFromArgs(getRuntime(), args
, 0);
596 public IRubyObject
getline(ByteList separator
) {
598 OpenFile myOpenFile
= getOpenFileChecked();
600 myOpenFile
.checkReadable(getRuntime());
601 boolean isParagraph
= separator
== Stream
.PARAGRAPH_DELIMETER
;
602 separator
= (separator
== Stream
.PARAGRAPH_DELIMETER
) ?
603 Stream
.PARAGRAPH_SEPARATOR
: separator
;
609 if (separator
== null) {
610 IRubyObject str
= readAll(null);
611 if (((RubyString
)str
).getByteList().length() == 0) {
612 return getRuntime().getNil();
614 incrementLineno(myOpenFile
);
616 } else if (separator
.length() == 1) {
617 return getlineFast(separator
.get(0));
619 Stream readStream
= myOpenFile
.getMainStream();
621 int newline
= separator
.get(separator
.length() - 1) & 0xFF;
623 ByteList buf
= new ByteList(1024);
624 boolean update
= false;
628 readCheck(readStream
);
629 readStream
.clearerr();
632 c
= readStream
.fgetc();
633 } catch (EOFException e
) {
638 // TODO: clear error, wait for it to become readable
645 } while (c
!= newline
); // loop until we see the nth separator char
647 // if we hit EOF, we're done
652 // if we've found the last char of the separator,
653 // and we've found at least as many characters as separator length,
654 // and the last n characters of our buffer match the separator, we're done
655 if (c
== newline
&& buf
.length() >= separator
.length() &&
656 0 == ByteList
.memcmp(buf
.unsafeBytes(), buf
.begin
+ buf
.realSize
- separator
.length(), separator
.unsafeBytes(), separator
.begin
, separator
.realSize
)) {
668 return getRuntime().getNil();
670 incrementLineno(myOpenFile
);
671 RubyString str
= RubyString
.newString(getRuntime(), buf
);
677 } catch (PipeException ex
) {
678 throw getRuntime().newErrnoEPIPEError();
679 } catch (InvalidValueException ex
) {
680 throw getRuntime().newErrnoEINVALError();
681 } catch (EOFException e
) {
682 return getRuntime().getNil();
683 } catch (BadDescriptorException e
) {
684 throw getRuntime().newErrnoEBADFError();
685 } catch (IOException e
) {
686 throw getRuntime().newIOError(e
.getMessage());
690 private void incrementLineno(OpenFile myOpenFile
) {
691 Ruby runtime
= getRuntime();
692 int lineno
= myOpenFile
.getLineNumber() + 1;
693 myOpenFile
.setLineNumber(lineno
);
694 runtime
.getGlobalVariables().set("$.", runtime
.newFixnum(lineno
));
695 // this is for a range check, near as I can tell
696 RubyNumeric
.int2fix(getRuntime(), myOpenFile
.getLineNumber());
699 protected boolean swallow(int term
) throws IOException
, BadDescriptorException
{
700 Stream readStream
= openFile
.getMainStream();
704 readCheck(readStream
);
707 c
= readStream
.fgetc();
708 } catch (EOFException e
) {
713 readStream
.ungetc(c
);
721 public IRubyObject
getlineFast(int delim
) throws IOException
, BadDescriptorException
{
722 Stream readStream
= openFile
.getMainStream();
725 ByteList buf
= new ByteList(1024);
726 boolean update
= false;
728 readCheck(readStream
);
729 readStream
.clearerr();
732 c
= readStream
.fgetc();
733 } catch (EOFException e
) {
738 // TODO: clear error, wait for it to become readable
745 } while (c
!= delim
);
748 return getRuntime().getNil();
750 incrementLineno(openFile
);
751 RubyString str
= RubyString
.newString(getRuntime(), buf
);
759 @JRubyMethod(name
= {"new", "for_fd"}, rest
= true, frame
= true, meta
= true)
760 public static IRubyObject
newInstance(IRubyObject recv
, IRubyObject
[] args
, Block block
) {
761 RubyClass klass
= (RubyClass
)recv
;
763 if (block
.isGiven()) {
764 String className
= klass
.getName();
765 recv
.getRuntime().getWarnings().warn(
766 ID
.BLOCK_NOT_ACCEPTED
,
767 className
+ "::new() does not take block; use " + className
+ "::open() instead",
768 className
+ "::open()");
771 return klass
.newInstance(args
, block
);
774 @JRubyMethod(name
= "initialize", required
= 1, optional
= 1, frame
= true, visibility
= Visibility
.PRIVATE
)
775 public IRubyObject
initialize(IRubyObject
[] args
, Block unusedBlock
) {
776 int argCount
= args
.length
;
779 int fileno
= RubyNumeric
.fix2int(args
[0]);
782 ChannelDescriptor descriptor
= getDescriptorByFileno(fileno
);
784 if (descriptor
== null) {
785 throw getRuntime().newErrnoEBADFError();
788 descriptor
.checkOpen();
791 if (args
[1] instanceof RubyFixnum
) {
792 modes
= new ModeFlags(RubyFixnum
.fix2long(args
[1]));
794 modes
= getIOModes(getRuntime(), (args
[1].convertToString().toString()));
797 // use original modes
798 modes
= descriptor
.getOriginalModes();
801 openFile
.setMode(modes
.getOpenFileFlags());
803 openFile
.setMainStream(fdopen(descriptor
, modes
));
804 } catch (BadDescriptorException ex
) {
805 throw getRuntime().newErrnoEBADFError();
806 } catch (InvalidValueException ive
) {
807 throw getRuntime().newErrnoEINVALError();
813 protected Stream
fdopen(ChannelDescriptor existingDescriptor
, ModeFlags modes
) throws InvalidValueException
{
814 // See if we already have this descriptor open.
815 // If so then we can mostly share the handler (keep open
816 // file, but possibly change the mode).
818 if (existingDescriptor
== null) {
819 // redundant, done above as well
821 // this seems unlikely to happen unless it's a totally bogus fileno
822 // ...so do we even need to bother trying to create one?
824 // IN FACT, we should probably raise an error, yes?
825 throw getRuntime().newErrnoEBADFError();
827 // if (mode == null) {
832 // openFile.setMainStream(streamForFileno(getRuntime(), fileno));
833 // } catch (BadDescriptorException e) {
834 // throw getRuntime().newErrnoEBADFError();
835 // } catch (IOException e) {
836 // throw getRuntime().newErrnoEBADFError();
838 // //modes = new IOModes(getRuntime(), mode);
840 // registerStream(openFile.getMainStream());
842 // We are creating a new IO object that shares the same
843 // IOHandler (and fileno).
844 return ChannelStream
.fdopen(getRuntime(), existingDescriptor
, modes
);
848 @JRubyMethod(name
= "open", required
= 1, optional
= 2, frame
= true, meta
= true)
849 public static IRubyObject
open(IRubyObject recv
, IRubyObject
[] args
, Block block
) {
850 Ruby runtime
= recv
.getRuntime();
851 RubyClass klass
= (RubyClass
)recv
;
853 RubyIO io
= (RubyIO
)klass
.newInstance(args
, block
);
855 if (block
.isGiven()) {
857 return block
.yield(runtime
.getCurrentContext(), io
);
860 io
.getMetaClass().invoke(runtime
.getCurrentContext(), io
, "close", IRubyObject
.NULL_ARRAY
, CallType
.FUNCTIONAL
, Block
.NULL_BLOCK
);
861 } catch (RaiseException re
) {
862 RubyException rubyEx
= re
.getException();
863 if (rubyEx
.kind_of_p(runtime
.getStandardError()).isTrue()) {
864 // MRI behavior: swallow StandardErorrs
875 // This appears to be some windows-only mode. On a java platform this is a no-op
876 @JRubyMethod(name
= "binmode")
877 public IRubyObject
binmode() {
881 protected void checkInitialized() {
882 if (openFile
== null) {
883 throw getRuntime().newIOError("uninitialized stream");
887 protected void checkClosed() {
888 if (openFile
.getMainStream() == null && openFile
.getPipeStream() == null) {
889 throw getRuntime().newIOError("closed stream");
893 @JRubyMethod(name
= "syswrite", required
= 1)
894 public IRubyObject
syswrite(IRubyObject obj
) {
896 RubyString string
= obj
.asString();
898 OpenFile myOpenFile
= getOpenFileChecked();
900 myOpenFile
.checkWritable(getRuntime());
902 Stream writeStream
= myOpenFile
.getWriteStream();
904 if (myOpenFile
.isWriteBuffered()) {
905 getRuntime().getWarnings().warn(
906 ID
.SYSWRITE_BUFFERED_IO
,
907 "syswrite for buffered IO");
910 if (!writeStream
.getDescriptor().isWritable()) {
911 myOpenFile
.checkClosed(getRuntime());
914 int read
= writeStream
.getDescriptor().write(string
.getByteList());
917 // TODO? I think this ends up propagating from normal Java exceptions
918 // sys_fail(openFile.getPath())
921 return getRuntime().newFixnum(read
);
922 } catch (InvalidValueException ex
) {
923 throw getRuntime().newErrnoEINVALError();
924 } catch (PipeException ex
) {
925 throw getRuntime().newErrnoEPIPEError();
926 } catch (BadDescriptorException e
) {
927 throw getRuntime().newErrnoEBADFError();
928 } catch (IOException e
) {
930 throw getRuntime().newSystemCallError(e
.getMessage());
934 @JRubyMethod(name
= "write_nonblock", required
= 1)
935 public IRubyObject
write_nonblock(IRubyObject obj
) {
936 // TODO: Obviously, we're not doing a non-blocking write here
943 @JRubyMethod(name
= "write", required
= 1)
944 public IRubyObject
write(IRubyObject obj
) {
945 getRuntime().secure(4);
947 RubyString str
= obj
.asString();
949 // TODO: Ruby reuses this logic for other "write" behavior by checking if it's an IO and calling write again
951 if (str
.getByteList().length() == 0) {
952 return getRuntime().newFixnum(0);
956 OpenFile myOpenFile
= getOpenFileChecked();
958 myOpenFile
.checkWritable(getRuntime());
960 int written
= fwrite(str
.getByteList());
966 // if not sync, we switch to write buffered mode
967 if (!myOpenFile
.isSync()) {
968 myOpenFile
.setWriteBuffered();
971 return getRuntime().newFixnum(written
);
972 } catch (IOException ex
) {
973 throw getRuntime().newIOErrorFromException(ex
);
974 } catch (BadDescriptorException ex
) {
975 throw getRuntime().newErrnoEBADFError();
976 } catch (InvalidValueException ex
) {
977 throw getRuntime().newErrnoEINVALError();
978 } catch (PipeException ex
) {
979 throw getRuntime().newErrnoEPIPEError();
983 protected int fwrite(ByteList buffer
) {
984 int n
, r
, l
, offset
= 0;
985 Stream writeStream
= openFile
.getWriteStream();
987 int len
= buffer
.length();
989 // if ((n = len) <= 0) return n;
990 if (len
== 0) return 0;
993 if (openFile
.isSync()) {
994 openFile
.fflush(writeStream
);
996 // TODO: why is this guarded?
997 // if (!rb_thread_fd_writable(fileno(f))) {
998 // rb_io_check_closed(fptr);
1000 // TODO: loop until it's all written
1001 //while (offset < len) {
1002 writeStream
.getDescriptor().write(buffer
);
1006 // TODO: all this stuff...some pipe logic, some async thread stuff
1009 // if (PIPE_BUF < l &&
1010 // !rb_thread_critical &&
1011 // !rb_thread_alone() &&
1012 // wsplit_p(fptr)) {
1016 // r = write(fileno(f), RSTRING(str)->ptr+offset, l);
1018 // if (r == n) return len;
1024 // if (rb_io_wait_writable(fileno(f))) {
1025 // rb_io_check_closed(fptr);
1026 // if (offset < RSTRING(str)->len)
1032 // TODO: handle errors in buffered write by retrying until finished or file is closed
1033 return writeStream
.fwrite(buffer
);
1034 // while (errno = 0, offset += (r = fwrite(RSTRING(str)->ptr+offset, 1, n, f)), (n -= r) > 0) {
1037 // if (rb_io_wait_writable(fileno(f))) {
1038 // rb_io_check_closed(fptr);
1040 // if (offset < RSTRING(str)->len)
1048 } catch (IOException ex
) {
1049 throw getRuntime().newIOErrorFromException(ex
);
1050 } catch (BadDescriptorException ex
) {
1051 throw getRuntime().newErrnoEBADFError();
1058 @JRubyMethod(name
= "<<", required
= 1)
1059 public IRubyObject
op_append(IRubyObject anObject
) {
1060 // Claims conversion is done via 'to_s' in docs.
1061 callMethod(getRuntime().getCurrentContext(), "write", anObject
);
1066 @JRubyMethod(name
= "fileno", alias
= "to_i")
1067 public RubyFixnum
fileno() {
1068 return getRuntime().newFixnum(getOpenFileChecked().getMainStream().getDescriptor().getFileno());
1071 /** Returns the current line number.
1073 * @return the current line number.
1075 @JRubyMethod(name
= "lineno")
1076 public RubyFixnum
lineno() {
1077 return getRuntime().newFixnum(getOpenFileChecked().getLineNumber());
1080 /** Sets the current line number.
1082 * @param newLineNumber The new line number.
1084 @JRubyMethod(name
= "lineno=", required
= 1)
1085 public RubyFixnum
lineno_set(IRubyObject newLineNumber
) {
1086 getOpenFileChecked().setLineNumber(RubyNumeric
.fix2int(newLineNumber
));
1088 return getRuntime().newFixnum(getOpenFileChecked().getLineNumber());
1091 /** Returns the current sync mode.
1093 * @return the current sync mode.
1095 @JRubyMethod(name
= "sync")
1096 public RubyBoolean
sync() {
1097 return getRuntime().newBoolean(getOpenFileChecked().getMainStream().isSync());
1101 * <p>Return the process id (pid) of the process this IO object
1102 * spawned. If no process exists (popen was not called), then
1103 * nil is returned. This is not how it appears to be defined
1104 * but ruby 1.8 works this way.</p>
1106 * @return the pid or nil
1108 @JRubyMethod(name
= "pid")
1109 public IRubyObject
pid() {
1110 OpenFile myOpenFile
= getOpenFileChecked();
1112 if (myOpenFile
.getProcess() == null) {
1113 return getRuntime().getNil();
1116 // Of course this isn't particularly useful.
1117 int pid
= myOpenFile
.getProcess().hashCode();
1119 return getRuntime().newFixnum(pid
);
1126 public boolean writeDataBuffered() {
1127 return openFile
.getMainStream().writeDataBuffered();
1130 @JRubyMethod(name
= {"pos", "tell"})
1131 public RubyFixnum
pos() {
1133 return getRuntime().newFixnum(getOpenFileChecked().getMainStream().fgetpos());
1134 } catch (InvalidValueException ex
) {
1135 throw getRuntime().newErrnoEINVALError();
1136 } catch (BadDescriptorException bde
) {
1137 throw getRuntime().newErrnoEBADFError();
1138 } catch (PipeException e
) {
1139 throw getRuntime().newErrnoESPIPEError();
1140 } catch (IOException e
) {
1141 throw getRuntime().newIOError(e
.getMessage());
1145 @JRubyMethod(name
= "pos=", required
= 1)
1146 public RubyFixnum
pos_set(IRubyObject newPosition
) {
1147 long offset
= RubyNumeric
.num2long(newPosition
);
1150 throw getRuntime().newSystemCallError("Negative seek offset");
1153 OpenFile myOpenFile
= getOpenFileChecked();
1156 myOpenFile
.getMainStream().fseek(offset
, Stream
.SEEK_SET
);
1157 } catch (BadDescriptorException e
) {
1158 throw getRuntime().newErrnoEBADFError();
1159 } catch (InvalidValueException e
) {
1160 throw getRuntime().newErrnoEINVALError();
1161 } catch (PipeException e
) {
1162 throw getRuntime().newErrnoESPIPEError();
1163 } catch (IOException e
) {
1164 throw getRuntime().newIOError(e
.getMessage());
1167 myOpenFile
.getMainStream().clearerr();
1169 return (RubyFixnum
) newPosition
;
1172 /** Print some objects to the stream.
1175 @JRubyMethod(name
= "print", rest
= true)
1176 public IRubyObject
print(IRubyObject
[] args
) {
1177 if (args
.length
== 0) {
1178 args
= new IRubyObject
[] { getRuntime().getCurrentContext().getCurrentFrame().getLastLine() };
1181 IRubyObject fs
= getRuntime().getGlobalVariables().get("$,");
1182 IRubyObject rs
= getRuntime().getGlobalVariables().get("$\\");
1183 ThreadContext context
= getRuntime().getCurrentContext();
1185 for (int i
= 0; i
< args
.length
; i
++) {
1186 if (i
> 0 && !fs
.isNil()) {
1187 callMethod(context
, "write", fs
);
1189 if (args
[i
].isNil()) {
1190 callMethod(context
, "write", getRuntime().newString("nil"));
1192 callMethod(context
, "write", args
[i
]);
1196 callMethod(context
, "write", rs
);
1199 return getRuntime().getNil();
1202 @JRubyMethod(name
= "printf", required
= 1, rest
= true)
1203 public IRubyObject
printf(IRubyObject
[] args
) {
1204 callMethod(getRuntime().getCurrentContext(), "write", RubyKernel
.sprintf(this, args
));
1205 return getRuntime().getNil();
1208 @JRubyMethod(name
= "putc", required
= 1)
1209 public IRubyObject
putc(IRubyObject object
) {
1212 if (getRuntime().getString().isInstance(object
)) {
1213 String value
= ((RubyString
) object
).toString();
1215 if (value
.length() > 0) {
1216 c
= value
.charAt(0);
1218 throw getRuntime().newTypeError("Cannot convert String to Integer");
1220 } else if (getRuntime().getFixnum().isInstance(object
)){
1221 c
= RubyNumeric
.fix2int(object
);
1222 } else { // What case will this work for?
1223 c
= RubyNumeric
.fix2int(object
.callMethod(getRuntime().getCurrentContext(), MethodIndex
.TO_I
, "to_i"));
1227 getOpenFileChecked().getMainStream().fputc(c
);
1228 } catch (BadDescriptorException e
) {
1229 return RubyFixnum
.zero(getRuntime());
1230 } catch (IOException e
) {
1231 return RubyFixnum
.zero(getRuntime());
1237 // This was a getOpt with one mandatory arg, but it did not work
1238 // so I am parsing it for now.
1239 @JRubyMethod(name
= "seek", required
= 1, optional
= 1)
1240 public RubyFixnum
seek(IRubyObject
[] args
) {
1241 long offset
= RubyNumeric
.num2long(args
[0]);
1242 int whence
= Stream
.SEEK_SET
;
1244 if (args
.length
> 1) {
1245 whence
= RubyNumeric
.fix2int(args
[1].convertToInteger());
1248 OpenFile myOpenFile
= getOpenFileChecked();
1251 myOpenFile
.seek(offset
, whence
);
1252 } catch (BadDescriptorException ex
) {
1253 throw getRuntime().newErrnoEBADFError();
1254 } catch (InvalidValueException e
) {
1255 throw getRuntime().newErrnoEINVALError();
1256 } catch (PipeException e
) {
1257 throw getRuntime().newErrnoESPIPEError();
1258 } catch (IOException e
) {
1259 throw getRuntime().newIOError(e
.getMessage());
1262 myOpenFile
.getMainStream().clearerr();
1264 return RubyFixnum
.zero(getRuntime());
1267 // This was a getOpt with one mandatory arg, but it did not work
1268 // so I am parsing it for now.
1269 @JRubyMethod(name
= "sysseek", required
= 1, optional
= 1)
1270 public RubyFixnum
sysseek(IRubyObject
[] args
) {
1271 long offset
= RubyNumeric
.num2long(args
[0]);
1273 int whence
= Stream
.SEEK_SET
;
1275 if (args
.length
> 1) {
1276 whence
= RubyNumeric
.fix2int(args
[1].convertToInteger());
1279 OpenFile myOpenFile
= getOpenFileChecked();
1283 if (myOpenFile
.isReadable() && myOpenFile
.isReadBuffered()) {
1284 throw getRuntime().newIOError("sysseek for buffered IO");
1286 if (myOpenFile
.isWritable() && myOpenFile
.isWriteBuffered()) {
1287 getRuntime().getWarnings().warn(
1288 ID
.SYSSEEK_BUFFERED_IO
,
1289 "sysseek for buffered IO");
1292 pos
= myOpenFile
.getMainStream().getDescriptor().lseek(offset
, whence
);
1293 } catch (BadDescriptorException ex
) {
1294 throw getRuntime().newErrnoEBADFError();
1295 } catch (InvalidValueException e
) {
1296 throw getRuntime().newErrnoEINVALError();
1297 } catch (PipeException e
) {
1298 throw getRuntime().newErrnoESPIPEError();
1299 } catch (IOException e
) {
1300 throw getRuntime().newIOError(e
.getMessage());
1303 myOpenFile
.getMainStream().clearerr();
1305 return getRuntime().newFixnum(pos
);
1308 @JRubyMethod(name
= "rewind")
1309 public RubyFixnum
rewind() {
1310 OpenFile myOpenfile
= getOpenFileChecked();
1313 myOpenfile
.getMainStream().fseek(0L, Stream
.SEEK_SET
);
1314 myOpenfile
.getMainStream().clearerr();
1316 // TODO: This is some goofy global file value from MRI..what to do?
1317 // if (io == current_file) {
1318 // gets_lineno -= fptr->lineno;
1320 } catch (BadDescriptorException e
) {
1321 throw getRuntime().newErrnoEBADFError();
1322 } catch (InvalidValueException e
) {
1323 throw getRuntime().newErrnoEINVALError();
1324 } catch (PipeException e
) {
1325 throw getRuntime().newErrnoESPIPEError();
1326 } catch (IOException e
) {
1327 throw getRuntime().newIOError(e
.getMessage());
1330 // Must be back on first line on rewind.
1331 myOpenfile
.setLineNumber(0);
1333 return RubyFixnum
.zero(getRuntime());
1336 @JRubyMethod(name
= "fsync")
1337 public RubyFixnum
fsync() {
1339 OpenFile myOpenFile
= getOpenFileChecked();
1341 myOpenFile
.checkWritable(getRuntime());
1343 myOpenFile
.getWriteStream().sync();
1344 } catch (InvalidValueException ex
) {
1345 throw getRuntime().newErrnoEINVALError();
1346 } catch (PipeException ex
) {
1347 throw getRuntime().newErrnoEPIPEError();
1348 } catch (IOException e
) {
1349 throw getRuntime().newIOError(e
.getMessage());
1350 } catch (BadDescriptorException e
) {
1351 throw getRuntime().newErrnoEBADFError();
1354 return RubyFixnum
.zero(getRuntime());
1357 /** Sets the current sync mode.
1359 * @param newSync The new sync mode.
1361 @JRubyMethod(name
= "sync=", required
= 1)
1362 public IRubyObject
sync_set(IRubyObject newSync
) {
1363 getOpenFileChecked().setSync(newSync
.isTrue());
1364 getOpenFileChecked().getMainStream().setSync(newSync
.isTrue());
1369 @JRubyMethod(name
= {"eof?", "eof"})
1370 public RubyBoolean
eof_p() {
1372 OpenFile myOpenFile
= getOpenFileChecked();
1374 myOpenFile
.checkReadable(getRuntime());
1376 if (myOpenFile
.getMainStream().feof()) {
1377 return getRuntime().getTrue();
1380 if (myOpenFile
.getMainStream().readDataBuffered()) {
1381 return getRuntime().getFalse();
1384 readCheck(myOpenFile
.getMainStream());
1386 myOpenFile
.getMainStream().clearerr();
1388 int c
= myOpenFile
.getMainStream().fgetc();
1391 myOpenFile
.getMainStream().ungetc(c
);
1392 return getRuntime().getFalse();
1395 myOpenFile
.checkClosed(getRuntime());
1397 myOpenFile
.getMainStream().clearerr();
1399 return getRuntime().getTrue();
1400 } catch (PipeException ex
) {
1401 throw getRuntime().newErrnoEPIPEError();
1402 } catch (InvalidValueException ex
) {
1403 throw getRuntime().newErrnoEINVALError();
1404 } catch (BadDescriptorException e
) {
1405 throw getRuntime().newErrnoEBADFError();
1406 } catch (IOException e
) {
1407 throw getRuntime().newIOError(e
.getMessage());
1411 @JRubyMethod(name
= {"tty?", "isatty"})
1412 public RubyBoolean
tty_p() {
1413 return getRuntime().newBoolean(getRuntime().getPosix().isatty(getOpenFileChecked().getMainStream().getDescriptor().getFileDescriptor()));
1416 @JRubyMethod(name
= "initialize_copy", required
= 1)
1418 public IRubyObject
initialize_copy(IRubyObject original
){
1419 if (this == original
) return this;
1421 RubyIO originalIO
= (RubyIO
) TypeConverter
.convertToTypeWithCheck(original
, getRuntime().getIO(), MethodIndex
.TO_IO
, "to_io");
1423 OpenFile originalFile
= originalIO
.getOpenFileChecked();
1424 OpenFile newFile
= openFile
;
1427 // TODO: I didn't see where MRI has this check, but it seems to be the right place
1428 originalFile
.checkClosed(getRuntime());
1430 if (originalFile
.getPipeStream() != null) {
1431 originalFile
.getPipeStream().fflush();
1432 originalFile
.getMainStream().fseek(0, Stream
.SEEK_CUR
);
1433 } else if (originalFile
.isWritable()) {
1434 originalFile
.getMainStream().fflush();
1436 originalFile
.getMainStream().fseek(0, Stream
.SEEK_CUR
);
1439 newFile
.setMode(originalFile
.getMode());
1440 newFile
.setProcess(originalFile
.getProcess());
1441 newFile
.setLineNumber(originalFile
.getLineNumber());
1442 newFile
.setPath(originalFile
.getPath());
1443 newFile
.setFinalizer(originalFile
.getFinalizer());
1446 if (newFile
.isReadable()) {
1447 if (newFile
.isWritable()) {
1448 if (newFile
.getPipeStream() != null) {
1449 modes
= new ModeFlags(ModeFlags
.RDONLY
);
1451 modes
= new ModeFlags(ModeFlags
.RDWR
);
1454 modes
= new ModeFlags(ModeFlags
.RDONLY
);
1457 if (newFile
.isWritable()) {
1458 modes
= new ModeFlags(ModeFlags
.WRONLY
);
1460 modes
= originalFile
.getMainStream().getModes();
1464 ChannelDescriptor descriptor
= originalFile
.getMainStream().getDescriptor().dup();
1466 newFile
.setMainStream(ChannelStream
.fdopen(getRuntime(), descriptor
, modes
));
1468 // TODO: the rest of this...seeking to same position is unnecessary since we share a channel
1469 // but some of this may be needed?
1471 // fseeko(fptr->f, ftello(orig->f), SEEK_SET);
1473 // if (fileno(orig->f) != fileno(orig->f2)) {
1474 // fd = ruby_dup(fileno(orig->f2));
1476 // fptr->f2 = rb_fdopen(fd, "w");
1477 // fseeko(fptr->f2, ftello(orig->f2), SEEK_SET);
1479 // if (fptr->mode & FMODE_BINMODE) {
1480 // rb_io_binmode(dest);
1483 // Register the new descriptor
1484 registerDescriptor(newFile
.getMainStream().getDescriptor());
1485 } catch (IOException ex
) {
1486 throw getRuntime().newIOError("could not init copy: " + ex
);
1487 } catch (BadDescriptorException ex
) {
1488 throw getRuntime().newIOError("could not init copy: " + ex
);
1489 } catch (PipeException ex
) {
1490 throw getRuntime().newIOError("could not init copy: " + ex
);
1491 } catch (InvalidValueException ex
) {
1492 throw getRuntime().newIOError("could not init copy: " + ex
);
1502 @JRubyMethod(name
= "closed?")
1503 public RubyBoolean
closed_p() {
1504 if (openFile
.getMainStream() == null && openFile
.getPipeStream() == null) {
1505 return getRuntime().getTrue();
1507 return getRuntime().getFalse();
1512 * <p>Closes all open resources for the IO. It also removes
1513 * it from our magical all open file descriptor pool.</p>
1517 @JRubyMethod(name
= "close")
1518 public IRubyObject
close() {
1519 if (getRuntime().getSafeLevel() >= 4 && isTaint()) {
1520 throw getRuntime().newSecurityError("Insecure: can't close");
1523 openFile
.checkClosed(getRuntime());
1527 protected IRubyObject
close2() {
1528 if (openFile
== null) {
1529 return getRuntime().getNil();
1532 // These would be used when we notify threads...if we notify threads
1533 ChannelDescriptor main
, pipe
;
1534 if (openFile
.getPipeStream() != null) {
1535 pipe
= openFile
.getPipeStream().getDescriptor();
1537 if (openFile
.getMainStream() == null) {
1538 return getRuntime().getNil();
1543 main
= openFile
.getMainStream().getDescriptor();
1545 // cleanup, raising errors if any
1546 openFile
.cleanup(getRuntime(), true);
1548 // TODO: notify threads waiting on descriptors/IO? probably not...
1550 if (openFile
.getProcess() != null) {
1552 IRubyObject processResult
= RubyProcess
.RubyStatus
.newProcessStatus(getRuntime(), openFile
.getProcess().waitFor());
1553 getRuntime().getGlobalVariables().set("$?", processResult
);
1554 } catch (InterruptedException ie
) {
1555 // TODO: do something here?
1559 return getRuntime().getNil();
1562 @JRubyMethod(name
= "close_write")
1563 public IRubyObject
close_write() throws BadDescriptorException
{
1565 if (getRuntime().getSafeLevel() >= 4 && isTaint()) {
1566 throw getRuntime().newSecurityError("Insecure: can't close");
1569 OpenFile myOpenFile
= getOpenFileChecked();
1571 if (myOpenFile
.getPipeStream() == null && myOpenFile
.isReadable()) {
1572 throw getRuntime().newIOError("closing non-duplex IO for writing");
1575 if (myOpenFile
.getPipeStream() == null) {
1578 myOpenFile
.getPipeStream().fclose();
1579 myOpenFile
.setPipeStream(null);
1580 myOpenFile
.setMode(myOpenFile
.getMode() & ~OpenFile
.WRITABLE
);
1582 // n is result of fclose; but perhaps having a SysError below is enough?
1583 // if (n != 0) rb_sys_fail(fptr->path);
1585 } catch (IOException ioe
) {
1591 @JRubyMethod(name
= "close_read")
1592 public IRubyObject
close_read() throws BadDescriptorException
{
1594 if (getRuntime().getSafeLevel() >= 4 && isTaint()) {
1595 throw getRuntime().newSecurityError("Insecure: can't close");
1598 OpenFile myOpenFile
= getOpenFileChecked();
1600 if (myOpenFile
.getPipeStream() == null && myOpenFile
.isWritable()) {
1601 throw getRuntime().newIOError("closing non-duplex IO for reading");
1604 if (myOpenFile
.getPipeStream() == null) {
1607 myOpenFile
.getMainStream().fclose();
1608 myOpenFile
.setMode(myOpenFile
.getMode() & ~OpenFile
.READABLE
);
1609 myOpenFile
.setMainStream(myOpenFile
.getPipeStream());
1610 myOpenFile
.setPipeStream(null);
1612 // n is result of fclose; but perhaps having a SysError below is enough?
1613 // if (n != 0) rb_sys_fail(fptr->path);
1615 } catch (IOException ioe
) {
1616 // I believe Ruby bails out with a "bug" if closing fails
1617 throw getRuntime().newIOErrorFromException(ioe
);
1622 /** Flushes the IO output stream.
1626 @JRubyMethod(name
= "flush")
1627 public RubyIO
flush() {
1629 getOpenFileChecked().getWriteStream().fflush();
1630 } catch (BadDescriptorException e
) {
1631 throw getRuntime().newErrnoEBADFError();
1632 } catch (IOException e
) {
1633 throw getRuntime().newIOError(e
.getMessage());
1642 @JRubyMethod(name
= "gets", optional
= 1)
1643 public IRubyObject
gets(IRubyObject
[] args
) {
1644 ByteList separator
= getSeparatorForGets(args
);
1646 IRubyObject result
= getline(separator
);
1648 if (!result
.isNil()) getRuntime().getCurrentContext().getCurrentFrame().setLastLine(result
);
1653 public boolean getBlocking() {
1654 return ((ChannelStream
) openFile
.getMainStream()).isBlocking();
1657 @JRubyMethod(name
= "fcntl", required
= 2)
1658 public IRubyObject
fcntl(IRubyObject cmd
, IRubyObject arg
) {
1659 // TODO: This version differs from ioctl by checking whether fcntl exists
1660 // and raising notimplemented if it doesn't; perhaps no difference for us?
1661 return ctl(cmd
, arg
);
1664 @JRubyMethod(name
= "ioctl", required
= 1, optional
= 1)
1665 public IRubyObject
ioctl(IRubyObject
[] args
) {
1666 IRubyObject cmd
= args
[0];
1669 if (args
.length
== 2) {
1672 arg
= getRuntime().getNil();
1675 return ctl(cmd
, arg
);
1678 public IRubyObject
ctl(IRubyObject cmd
, IRubyObject arg
) {
1679 long realCmd
= cmd
.convertToInteger().getLongValue();
1682 // FIXME: Arg may also be true, false, and nil and still be valid. Strangely enough,
1683 // protocol conversion is not happening in Ruby on this arg?
1684 if (arg
.isNil() || arg
== getRuntime().getFalse()) {
1686 } else if (arg
instanceof RubyFixnum
) {
1687 nArg
= RubyFixnum
.fix2long(arg
);
1688 } else if (arg
== getRuntime().getTrue()) {
1691 throw getRuntime().newNotImplementedError("JRuby does not support string for second fcntl/ioctl argument yet");
1694 OpenFile myOpenFile
= getOpenFileChecked();
1696 // Fixme: Only F_SETFL is current supported
1697 if (realCmd
== 1L) { // cmd is F_SETFL
1698 boolean block
= true;
1700 if ((nArg
& ModeFlags
.NONBLOCK
) == ModeFlags
.NONBLOCK
) {
1705 myOpenFile
.getMainStream().setBlocking(block
);
1706 } catch (IOException e
) {
1707 throw getRuntime().newIOError(e
.getMessage());
1710 throw getRuntime().newNotImplementedError("JRuby only supports F_SETFL for fcntl/ioctl currently");
1713 return getRuntime().newFixnum(0);
1716 private static final ByteList NIL_BYTELIST
= ByteList
.create("nil");
1717 private static final ByteList RECURSIVE_BYTELIST
= ByteList
.create("[...]");
1719 @JRubyMethod(name
= "puts", rest
= true)
1720 public IRubyObject
puts(IRubyObject
[] args
) {
1721 ThreadContext context
= getRuntime().getCurrentContext();
1723 assert getRuntime().getGlobalVariables().getDefaultSeparator() instanceof RubyString
;
1724 RubyString separator
= (RubyString
)getRuntime().getGlobalVariables().getDefaultSeparator();
1726 if (args
.length
== 0) {
1727 write(context
, separator
.getByteList());
1728 return getRuntime().getNil();
1731 for (int i
= 0; i
< args
.length
; i
++) {
1734 if (args
[i
].isNil()) {
1735 line
= NIL_BYTELIST
;
1736 } else if (getRuntime().isInspecting(args
[i
])) {
1737 line
= RECURSIVE_BYTELIST
;
1738 } else if (args
[i
] instanceof RubyArray
) {
1739 inspectPuts((RubyArray
) args
[i
]);
1742 line
= args
[i
].asString().getByteList();
1745 write(context
, line
);
1747 if (line
.length() == 0 || !line
.endsWith(separator
.getByteList())) {
1748 write(context
, separator
.getByteList());
1751 return getRuntime().getNil();
1754 protected void write(ThreadContext context
, ByteList byteList
) {
1755 callMethod(context
, "write", getRuntime().newStringShared(byteList
));
1758 private IRubyObject
inspectPuts(RubyArray array
) {
1760 getRuntime().registerInspecting(array
);
1761 return puts(array
.toJavaArray());
1763 getRuntime().unregisterInspecting(array
);
1770 @JRubyMethod(name
= "readline", optional
= 1)
1771 public IRubyObject
readline(IRubyObject
[] args
) {
1772 IRubyObject line
= gets(args
);
1775 throw getRuntime().newEOFError();
1781 /** Read a byte. On EOF returns nil.
1784 @JRubyMethod(name
= "getc")
1785 public IRubyObject
getc() {
1787 OpenFile myOpenFile
= getOpenFileChecked();
1789 myOpenFile
.checkReadable(getRuntime());
1791 Stream stream
= myOpenFile
.getMainStream();
1796 int c
= myOpenFile
.getMainStream().fgetc();
1799 // TODO: check for ferror, clear it, and try once more up above readCheck
1802 // if (!rb_io_wait_readable(fileno(f)))
1803 // rb_sys_fail(fptr->path);
1806 return getRuntime().getNil();
1809 return getRuntime().newFixnum(c
);
1810 } catch (PipeException ex
) {
1811 throw getRuntime().newErrnoEPIPEError();
1812 } catch (InvalidValueException ex
) {
1813 throw getRuntime().newErrnoEINVALError();
1814 } catch (BadDescriptorException e
) {
1815 throw getRuntime().newErrnoEBADFError();
1816 } catch (EOFException e
) {
1817 throw getRuntime().newEOFError();
1818 } catch (IOException e
) {
1819 throw getRuntime().newIOError(e
.getMessage());
1823 private void readCheck(Stream stream
) {
1824 if (!stream
.readDataBuffered()) {
1825 openFile
.checkClosed(getRuntime());
1830 * <p>Pushes char represented by int back onto IOS.</p>
1832 * @param number to push back
1834 @JRubyMethod(name
= "ungetc", required
= 1)
1835 public IRubyObject
ungetc(IRubyObject number
) {
1836 int ch
= RubyNumeric
.fix2int(number
);
1838 OpenFile myOpenFile
= getOpenFileChecked();
1840 if (!myOpenFile
.isReadBuffered()) {
1841 throw getRuntime().newIOError("unread stream");
1845 myOpenFile
.checkReadable(getRuntime());
1847 if (myOpenFile
.getMainStream().ungetc(ch
) == -1 && ch
!= -1) {
1848 throw getRuntime().newIOError("ungetc failed");
1850 } catch (PipeException ex
) {
1851 throw getRuntime().newErrnoEPIPEError();
1852 } catch (InvalidValueException ex
) {
1853 throw getRuntime().newErrnoEINVALError();
1854 } catch (BadDescriptorException e
) {
1855 throw getRuntime().newErrnoEBADFError();
1856 } catch (EOFException e
) {
1857 throw getRuntime().newEOFError();
1858 } catch (IOException e
) {
1859 throw getRuntime().newIOError(e
.getMessage());
1862 return getRuntime().getNil();
1865 @JRubyMethod(name
= "readpartial", required
= 1, optional
= 1)
1866 public IRubyObject
readpartial(IRubyObject
[] args
) {
1867 if(!(openFile
.getMainStream() instanceof ChannelStream
)) {
1868 // cryptic for the uninitiated...
1869 throw getRuntime().newNotImplementedError("readpartial only works with Nio based handlers");
1872 ByteList buf
= ((ChannelStream
)openFile
.getMainStream()).readpartial(RubyNumeric
.fix2int(args
[0]));
1873 IRubyObject strbuf
= RubyString
.newString(getRuntime(), buf
== null ?
new ByteList(ByteList
.NULL_ARRAY
) : buf
);
1874 if(args
.length
> 1) {
1875 args
[1].callMethod(getRuntime().getCurrentContext(),MethodIndex
.OP_LSHIFT
, "<<", strbuf
);
1880 } catch (BadDescriptorException e
) {
1881 throw getRuntime().newErrnoEBADFError();
1882 } catch (EOFException e
) {
1883 return getRuntime().getNil();
1884 } catch (IOException e
) {
1885 throw getRuntime().newIOError(e
.getMessage());
1889 @JRubyMethod(name
= "sysread", required
= 1, optional
= 1)
1890 public IRubyObject
sysread(IRubyObject
[] args
) {
1891 int len
= (int)RubyNumeric
.num2long(args
[0]);
1892 if (len
< 0) throw getRuntime().newArgumentError("Negative size");
1897 if (args
.length
== 1 || args
[1].isNil()) {
1899 return RubyString
.newStringShared(getRuntime(), ByteList
.EMPTY_BYTELIST
);
1902 buffer
= new ByteList(len
);
1903 str
= RubyString
.newString(getRuntime(), buffer
);
1905 str
= args
[1].convertToString();
1912 buffer
= str
.getByteList();
1915 OpenFile myOpenFile
= getOpenFileChecked();
1917 myOpenFile
.checkReadable(getRuntime());
1919 if (myOpenFile
.getMainStream().readDataBuffered()) {
1920 throw getRuntime().newIOError("sysread for buffered IO");
1923 // TODO: Ruby locks the string here
1925 getRuntime().getCurrentContext().getThread().beforeBlockingCall();
1926 myOpenFile
.checkClosed(getRuntime());
1928 // TODO: Ruby re-checks that the buffer string hasn't been modified
1930 int bytesRead
= myOpenFile
.getMainStream().getDescriptor().read(len
, str
.getByteList());
1932 // TODO: Ruby unlocks the string here
1934 // TODO: Ruby truncates string to specific size here, but our bytelist should handle this already?
1936 if (bytesRead
== -1 || (bytesRead
== 0 && len
> 0)) {
1937 throw getRuntime().newEOFError();
1943 } catch (BadDescriptorException e
) {
1944 throw getRuntime().newErrnoEBADFError();
1945 } catch (InvalidValueException e
) {
1946 throw getRuntime().newErrnoEINVALError();
1947 } catch (PipeException e
) {
1948 throw getRuntime().newErrnoEPIPEError();
1949 } catch (EOFException e
) {
1950 throw getRuntime().newEOFError();
1951 } catch (IOException e
) {
1952 // All errors to sysread should be SystemCallErrors, but on a closed stream
1953 // Ruby returns an IOError. Java throws same exception for all errors so
1954 // we resort to this hack...
1955 if ("File not open".equals(e
.getMessage())) {
1956 throw getRuntime().newIOError(e
.getMessage());
1958 throw getRuntime().newSystemCallError(e
.getMessage());
1960 getRuntime().getCurrentContext().getThread().afterBlockingCall();
1964 @JRubyMethod(name
= "read_nonblock", required
= 1, optional
= 1)
1965 public IRubyObject
read_nonblock(IRubyObject
[] args
) {
1966 // TODO: Obviously, we're not doing a nonblocking read here...
1970 @JRubyMethod(name
= "read", optional
= 2)
1971 public IRubyObject
read(IRubyObject
[] args
) {
1972 int argCount
= args
.length
;
1974 OpenFile myOpenFile
= getOpenFileChecked();
1976 if (argCount
== 0 || args
[0].isNil()) {
1978 myOpenFile
.checkReadable(getRuntime());
1980 if (args
.length
== 2) {
1981 return readAll(args
[1]);
1983 return readAll(getRuntime().getNil());
1985 } catch (PipeException ex
) {
1986 throw getRuntime().newErrnoEPIPEError();
1987 } catch (InvalidValueException ex
) {
1988 throw getRuntime().newErrnoEINVALError();
1989 } catch (EOFException ex
) {
1990 throw getRuntime().newEOFError();
1991 } catch (IOException ex
) {
1992 throw getRuntime().newIOErrorFromException(ex
);
1993 } catch (BadDescriptorException ex
) {
1994 throw getRuntime().newErrnoEBADFError();
1998 int length
= RubyNumeric
.num2int(args
[0]);
2001 throw getRuntime().newArgumentError("negative length " + length
+ " given");
2004 RubyString str
= null;
2005 // ByteList buffer = null;
2006 if (args
.length
== 1 || args
[1].isNil()) {
2007 // buffer = new ByteList(length);
2008 // str = RubyString.newString(getRuntime(), buffer);
2010 str
= args
[1].convertToString();
2017 // buffer = str.getByteList();
2021 myOpenFile
.checkReadable(getRuntime());
2023 if (myOpenFile
.getMainStream().feof()) {
2024 return getRuntime().getNil();
2027 // TODO: Ruby locks the string here
2029 // READ_CHECK from MRI io.c
2030 readCheck(myOpenFile
.getMainStream());
2032 // TODO: check buffer length again?
2033 // if (RSTRING(str)->len != len) {
2034 // rb_raise(rb_eRuntimeError, "buffer string modified");
2037 // TODO: read into buffer using all the fread logic
2038 // int read = openFile.getMainStream().fread(buffer);
2039 ByteList newBuffer
= myOpenFile
.getMainStream().fread(length
);
2041 // TODO: Ruby unlocks the string here
2043 // TODO: change this to check number read into buffer once that's working
2046 if (newBuffer
== null || newBuffer
.length() == 0) {
2047 if (myOpenFile
.getMainStream() == null) {
2048 return getRuntime().getNil();
2051 if (myOpenFile
.getMainStream().feof()) {
2052 // truncate buffer string to zero, if provided
2054 str
.setValue(ByteList
.EMPTY_BYTELIST
.dup());
2057 return getRuntime().getNil();
2061 // I think this is only partly correct; sys fail based on errno in Ruby
2062 throw getRuntime().newEOFError();
2067 // TODO: Ruby truncates string to specific size here, but our bytelist should handle this already?
2069 // FIXME: I don't like the null checks here
2071 if (newBuffer
== null) {
2072 str
= getRuntime().newString();
2074 str
= RubyString
.newString(getRuntime(), newBuffer
);
2077 if (newBuffer
== null) {
2078 str
.setValue(ByteList
.EMPTY_BYTELIST
.dup());
2080 str
.setValue(newBuffer
);
2086 } catch (EOFException ex
) {
2087 throw getRuntime().newEOFError();
2088 } catch (PipeException ex
) {
2089 throw getRuntime().newErrnoEPIPEError();
2090 } catch (InvalidValueException ex
) {
2091 throw getRuntime().newErrnoEINVALError();
2092 } catch (IOException ex
) {
2093 throw getRuntime().newIOErrorFromException(ex
);
2094 } catch (BadDescriptorException ex
) {
2095 throw getRuntime().newErrnoEBADFError();
2099 protected IRubyObject
readAll(IRubyObject buffer
) throws BadDescriptorException
, EOFException
, IOException
{
2100 // TODO: handle writing into original buffer better
2102 RubyString str
= null;
2103 if (buffer
instanceof RubyString
) {
2104 str
= (RubyString
)buffer
;
2107 // TODO: ruby locks the string here
2109 // READ_CHECK from MRI io.c
2110 if (openFile
.getMainStream().readDataBuffered()) {
2111 openFile
.checkClosed(getRuntime());
2114 ByteList newBuffer
= openFile
.getMainStream().readall();
2116 // TODO same zero-length checks as file above
2119 if (newBuffer
== null) {
2120 str
= RubyString
.newStringShared(getRuntime(), ByteList
.EMPTY_BYTELIST
);
2122 str
= RubyString
.newString(getRuntime(), newBuffer
);
2125 if (newBuffer
== null) {
2126 str
.setValue(ByteList
.EMPTY_BYTELIST
.dup());
2128 str
.setValue(newBuffer
);
2138 // if (siz == 0) siz = BUFSIZ;
2139 // if (NIL_P(str)) {
2140 // str = rb_str_new(0, siz);
2143 // rb_str_resize(str, siz);
2146 // rb_str_locktmp(str);
2147 // READ_CHECK(fptr->f);
2148 // n = io_fread(RSTRING(str)->ptr+bytes, siz-bytes, fptr);
2149 // rb_str_unlocktmp(str);
2150 // if (n == 0 && bytes == 0) {
2151 // if (!fptr->f) break;
2152 // if (feof(fptr->f)) break;
2153 // if (!ferror(fptr->f)) break;
2154 // rb_sys_fail(fptr->path);
2157 // if (bytes < siz) break;
2159 // rb_str_resize(str, siz);
2161 // if (bytes != siz) rb_str_resize(str, bytes);
2167 // TODO: There's a lot of complexity here due to error handling and
2168 // nonblocking IO; much of this goes away, but for now I'm just
2169 // having read call ChannelStream.fread directly.
2170 // protected int fread(int len, ByteList buffer) {
2176 // c = read_buffered_data(ptr, n, fptr->f);
2177 // if (c < 0) goto eof;
2180 // if ((n -= c) <= 0) break;
2182 // rb_thread_wait_fd(fileno(fptr->f));
2183 // rb_io_check_closed(fptr);
2184 // clearerr(fptr->f);
2186 // c = getc(fptr->f);
2190 // if (ferror(fptr->f)) {
2193 // #if defined(ERESTART)
2196 // clearerr(fptr->f);
2199 // #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
2200 // case EWOULDBLOCK:
2203 // clearerr(fptr->f);
2205 // saved_errno = errno;
2206 // rb_warning("nonblocking IO#read is obsolete; use IO#readpartial or IO#sysread");
2207 // errno = saved_errno;
2209 // if (len == n) return 0;
2220 /** Read a byte. On EOF throw EOFError.
2223 @JRubyMethod(name
= "readchar")
2224 public IRubyObject
readchar() {
2225 IRubyObject c
= getc();
2227 if (c
.isNil()) throw getRuntime().newEOFError();
2233 public IRubyObject
stat() {
2234 openFile
.checkClosed(getRuntime());
2235 return getRuntime().newFileStat(getOpenFileChecked().getMainStream().getDescriptor().getFileDescriptor());
2239 * <p>Invoke a block for each byte.</p>
2241 @JRubyMethod(name
= "each_byte", frame
= true)
2242 public IRubyObject
each_byte(Block block
) {
2244 Ruby runtime
= getRuntime();
2245 ThreadContext context
= runtime
.getCurrentContext();
2246 OpenFile myOpenFile
= getOpenFileChecked();
2249 myOpenFile
.checkReadable(runtime
);
2251 // TODO: READ_CHECK from MRI
2253 int c
= myOpenFile
.getMainStream().fgetc();
2256 // TODO: check for error, clear it, and wait until readable before trying once more
2259 // if (!rb_io_wait_readable(fileno(f)))
2260 // rb_sys_fail(fptr->path);
2267 block
.yield(context
, getRuntime().newFixnum(c
));
2270 // TODO: one more check for error
2271 // if (ferror(f)) rb_sys_fail(fptr->path);
2273 } catch (PipeException ex
) {
2274 throw getRuntime().newErrnoEPIPEError();
2275 } catch (InvalidValueException ex
) {
2276 throw getRuntime().newErrnoEINVALError();
2277 } catch (BadDescriptorException e
) {
2278 throw getRuntime().newErrnoEBADFError();
2279 } catch (EOFException e
) {
2280 return getRuntime().getNil();
2281 } catch (IOException e
) {
2282 throw getRuntime().newIOError(e
.getMessage());
2287 * <p>Invoke a block for each line.</p>
2289 @JRubyMethod(name
= {"each_line", "each"}, optional
= 1, frame
= true)
2290 public RubyIO
each_line(IRubyObject
[] args
, Block block
) {
2291 ThreadContext context
= getRuntime().getCurrentContext();
2292 ByteList separator
= getSeparatorForGets(args
);
2294 for (IRubyObject line
= getline(separator
); !line
.isNil();
2295 line
= getline(separator
)) {
2296 block
.yield(context
, line
);
2303 @JRubyMethod(name
= "readlines", optional
= 1)
2304 public RubyArray
readlines(IRubyObject
[] args
) {
2306 if (args
.length
> 0) {
2307 if (!getRuntime().getNilClass().isInstance(args
[0]) &&
2308 !getRuntime().getString().isInstance(args
[0])) {
2309 throw getRuntime().newTypeError(args
[0],
2310 getRuntime().getString());
2312 separator
= getSeparatorForGets(new IRubyObject
[] { args
[0] });
2314 separator
= getSeparatorForGets(IRubyObject
.NULL_ARRAY
);
2317 RubyArray result
= getRuntime().newArray();
2319 while (! (line
= getline(separator
)).isNil()) {
2320 result
.append(line
);
2325 @JRubyMethod(name
= "to_io")
2326 public RubyIO
to_io() {
2331 public String
toString() {
2332 return "RubyIO(" + openFile
.getMode() + ", " + openFile
.getMainStream().getDescriptor().getFileno() + ")";
2335 /* class methods for IO */
2340 @JRubyMethod(name
= "foreach", required
= 1, optional
= 1, frame
= true, meta
= true)
2341 public static IRubyObject
foreach(IRubyObject recv
, IRubyObject
[] args
, Block block
) {
2342 Ruby runtime
= recv
.getRuntime();
2343 int count
= args
.length
;
2344 IRubyObject filename
= args
[0].convertToString();
2345 runtime
.checkSafeString(filename
);
2347 ByteList separator
= getSeparatorFromArgs(runtime
, args
, 1);
2349 RubyIO io
= (RubyIO
)RubyFile
.open(runtime
.getFile(), new IRubyObject
[] { filename
}, Block
.NULL_BLOCK
);
2353 IRubyObject str
= io
.getline(separator
);
2354 ThreadContext context
= runtime
.getCurrentContext();
2355 while (!str
.isNil()) {
2356 block
.yield(context
, str
);
2357 str
= io
.getline(separator
);
2364 return runtime
.getNil();
2367 private static RubyIO
registerSelect(Selector selector
, IRubyObject obj
, int ops
) throws IOException
{
2370 if (!(obj
instanceof RubyIO
)) {
2372 if (!obj
.respondsTo("to_io")) return null;
2374 ioObj
= (RubyIO
) obj
.callMethod(obj
.getRuntime().getCurrentContext(), "to_io");
2376 ioObj
= (RubyIO
) obj
;
2379 Channel channel
= ioObj
.getChannel();
2380 if (channel
== null || !(channel
instanceof SelectableChannel
)) {
2384 ((SelectableChannel
) channel
).configureBlocking(false);
2385 int real_ops
= ((SelectableChannel
) channel
).validOps() & ops
;
2386 SelectionKey key
= ((SelectableChannel
) channel
).keyFor(selector
);
2389 ((SelectableChannel
) channel
).register(selector
, real_ops
, obj
);
2391 key
.interestOps(key
.interestOps()|real_ops
);
2397 @JRubyMethod(name
= "select", required
= 1, optional
= 3, meta
= true)
2398 public static IRubyObject
select(IRubyObject recv
, IRubyObject
[] args
) {
2399 return select_static(recv
.getRuntime(), args
);
2402 public static IRubyObject
select_static(Ruby runtime
, IRubyObject
[] args
) {
2404 // FIXME: This needs to be ported
2405 boolean atLeastOneDescriptor
= false;
2407 Set pending
= new HashSet();
2408 Selector selector
= Selector
.open();
2409 if (!args
[0].isNil()) {
2410 atLeastOneDescriptor
= true;
2413 for (Iterator i
= ((RubyArray
) args
[0]).getList().iterator(); i
.hasNext(); ) {
2414 IRubyObject obj
= (IRubyObject
) i
.next();
2415 RubyIO ioObj
= registerSelect(selector
, obj
,
2416 SelectionKey
.OP_READ
| SelectionKey
.OP_ACCEPT
);
2418 if (ioObj
!=null && ioObj
.writeDataBuffered()) pending
.add(obj
);
2421 if (args
.length
> 1 && !args
[1].isNil()) {
2422 atLeastOneDescriptor
= true;
2424 for (Iterator i
= ((RubyArray
) args
[1]).getList().iterator(); i
.hasNext(); ) {
2425 IRubyObject obj
= (IRubyObject
) i
.next();
2426 registerSelect(selector
, obj
, SelectionKey
.OP_WRITE
);
2429 if (args
.length
> 2 && !args
[2].isNil()) {
2430 atLeastOneDescriptor
= true;
2431 // Java's select doesn't do anything about this, so we leave it be.
2435 if(args
.length
> 3 && !args
[3].isNil()) {
2436 if (args
[3] instanceof RubyFloat
) {
2437 timeout
= Math
.round(((RubyFloat
) args
[3]).getDoubleValue() * 1000);
2439 timeout
= Math
.round(((RubyFixnum
) args
[3]).getDoubleValue() * 1000);
2443 throw runtime
.newArgumentError("negative timeout given");
2447 if (!atLeastOneDescriptor
) {
2448 return runtime
.getNil();
2451 if (pending
.isEmpty()) {
2452 if (args
.length
> 3) {
2454 selector
.selectNow();
2456 selector
.select(timeout
);
2462 selector
.selectNow();
2465 List r
= new ArrayList();
2466 List w
= new ArrayList();
2467 List e
= new ArrayList();
2468 for (Iterator i
= selector
.selectedKeys().iterator(); i
.hasNext(); ) {
2469 SelectionKey key
= (SelectionKey
) i
.next();
2470 if ((key
.interestOps() & key
.readyOps()
2471 & (SelectionKey
.OP_READ
|SelectionKey
.OP_ACCEPT
|SelectionKey
.OP_CONNECT
)) != 0) {
2472 r
.add(key
.attachment());
2473 pending
.remove(key
.attachment());
2475 if ((key
.interestOps() & key
.readyOps() & (SelectionKey
.OP_WRITE
)) != 0) {
2476 w
.add(key
.attachment());
2481 // make all sockets blocking as configured again
2482 for (Iterator i
= selector
.keys().iterator(); i
.hasNext(); ) {
2483 SelectionKey key
= (SelectionKey
) i
.next();
2484 SelectableChannel channel
= key
.channel();
2485 synchronized(channel
.blockingLock()) {
2486 boolean blocking
= ((RubyIO
) key
.attachment()).getBlocking();
2488 channel
.configureBlocking(blocking
);
2493 if (r
.size() == 0 && w
.size() == 0 && e
.size() == 0) {
2494 return runtime
.getNil();
2497 List ret
= new ArrayList();
2499 ret
.add(RubyArray
.newArray(runtime
, r
));
2500 ret
.add(RubyArray
.newArray(runtime
, w
));
2501 ret
.add(RubyArray
.newArray(runtime
, e
));
2503 return RubyArray
.newArray(runtime
, ret
);
2504 } catch(IOException e
) {
2505 throw runtime
.newIOError(e
.getMessage());
2509 @JRubyMethod(name
= "read", required
= 1, optional
= 2, meta
= true)
2510 public static IRubyObject
read(IRubyObject recv
, IRubyObject
[] args
, Block block
) {
2511 IRubyObject
[] fileArguments
= new IRubyObject
[] {args
[0]};
2512 RubyIO file
= (RubyIO
) RubyKernel
.open(recv
, fileArguments
, block
);
2513 IRubyObject
[] readArguments
;
2515 if (args
.length
>= 2 && !args
[1].isNil()) {
2516 readArguments
= new IRubyObject
[] {args
[1].convertToInteger()};
2518 readArguments
= new IRubyObject
[] {};
2523 if (args
.length
== 3 && !args
[2].isNil()) {
2524 file
.seek(new IRubyObject
[] {args
[2].convertToInteger()});
2527 return file
.read(readArguments
);
2533 @JRubyMethod(name
= "readlines", required
= 1, optional
= 1, meta
= true)
2534 public static RubyArray
readlines(IRubyObject recv
, IRubyObject
[] args
, Block block
) {
2535 int count
= args
.length
;
2537 IRubyObject
[] fileArguments
= new IRubyObject
[]{args
[0]};
2538 IRubyObject
[] separatorArguments
= count
>= 2 ?
new IRubyObject
[]{args
[1]} : IRubyObject
.NULL_ARRAY
;
2539 RubyIO file
= (RubyIO
) RubyKernel
.open(recv
, fileArguments
, block
);
2541 return file
.readlines(separatorArguments
);
2547 @JRubyMethod(name
= "popen", required
= 1, optional
= 1, meta
= true)
2548 public static IRubyObject
popen(IRubyObject recv
, IRubyObject
[] args
, Block block
) {
2549 Ruby runtime
= recv
.getRuntime();
2552 IRubyObject cmdObj
= args
[0].convertToString();
2553 runtime
.checkSafeString(cmdObj
);
2555 if ("-".equals(cmdObj
.toString())) {
2556 throw recv
.getRuntime().newNotImplementedError(
2557 "popen(\"-\") is unimplemented");
2561 if (args
.length
== 1) {
2562 mode
= ModeFlags
.RDONLY
;
2563 } else if (args
[1] instanceof RubyFixnum
) {
2564 mode
= RubyFixnum
.num2int(args
[1]);
2566 mode
= getIOModesIntFromString(runtime
, args
[1].convertToString().toString());
2569 ModeFlags modes
= new ModeFlags(mode
);
2571 Process process
= new ShellLauncher(runtime
).run(cmdObj
);
2572 RubyIO io
= new RubyIO(runtime
, process
, modes
);
2574 if (block
.isGiven()) {
2576 return block
.yield(runtime
.getCurrentContext(), io
);
2578 if (io
.openFile
.isOpen()) {
2581 runtime
.getGlobalVariables().set("$?", RubyProcess
.RubyStatus
.newProcessStatus(runtime
, (process
.waitFor() * 256)));
2585 } catch (InvalidValueException ex
) {
2586 throw runtime
.newErrnoEINVALError();
2587 } catch (IOException e
) {
2588 throw runtime
.newIOErrorFromException(e
);
2589 } catch (InterruptedException e
) {
2590 throw runtime
.newThreadError("unexpected interrupt");
2595 @JRubyMethod(name
= "pipe", meta
= true)
2596 public static IRubyObject
pipe(IRubyObject recv
) throws Exception
{
2597 // TODO: This isn't an exact port of MRI's pipe behavior, so revisit
2598 Ruby runtime
= recv
.getRuntime();
2599 Pipe pipe
= Pipe
.open();
2601 RubyIO source
= new RubyIO(runtime
, pipe
.source());
2602 RubyIO sink
= new RubyIO(runtime
, pipe
.sink());
2604 sink
.openFile
.getMainStream().setSync(true);
2605 return runtime
.newArrayNoCopy(new IRubyObject
[]{