0001: /***** BEGIN LICENSE BLOCK *****
0002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
0003: *
0004: * The contents of this file are subject to the Common Public
0005: * License Version 1.0 (the "License"); you may not use this file
0006: * except in compliance with the License. You may obtain a copy of
0007: * the License at http://www.eclipse.org/legal/cpl-v10.html
0008: *
0009: * Software distributed under the License is distributed on an "AS
0010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
0011: * implied. See the License for the specific language governing
0012: * rights and limitations under the License.
0013: *
0014: * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
0015: * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
0016: * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
0017: * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
0018: * Copyright (C) 2004-2006 Charles O Nutter <headius@headius.com>
0019: * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
0020: * Copyright (C) 2006 Evan Buswell <ebuswell@gmail.com>
0021: * Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@gmail.com>
0022: *
0023: * Alternatively, the contents of this file may be used under the terms of
0024: * either of the GNU General Public License Version 2 or later (the "GPL"),
0025: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
0026: * in which case the provisions of the GPL or the LGPL are applicable instead
0027: * of those above. If you wish to allow use of your version of this file only
0028: * under the terms of either the GPL or the LGPL, and not to allow others to
0029: * use your version of this file under the terms of the CPL, indicate your
0030: * decision by deleting the provisions above and replace them with the notice
0031: * and other provisions required by the GPL or the LGPL. If you do not delete
0032: * the provisions above, a recipient may use your version of this file under
0033: * the terms of any one of the CPL, the GPL or the LGPL.
0034: ***** END LICENSE BLOCK *****/package org.jruby;
0035:
0036: import java.io.EOFException;
0037: import java.io.FileNotFoundException;
0038: import java.io.IOException;
0039: import java.io.InputStream;
0040: import java.io.OutputStream;
0041: import java.lang.ref.WeakReference;
0042: import java.nio.channels.Channel;
0043: import java.nio.channels.Pipe;
0044: import java.nio.channels.SelectableChannel;
0045: import java.nio.channels.SelectionKey;
0046: import java.nio.channels.Selector;
0047: import java.util.ArrayList;
0048: import java.util.HashSet;
0049: import java.util.Iterator;
0050: import java.util.List;
0051: import java.util.Set;
0052:
0053: import org.jruby.runtime.Arity;
0054: import org.jruby.runtime.Block;
0055: import org.jruby.runtime.CallbackFactory;
0056: import org.jruby.runtime.MethodIndex;
0057: import org.jruby.runtime.ObjectAllocator;
0058: import org.jruby.runtime.ThreadContext;
0059: import org.jruby.runtime.builtin.IRubyObject;
0060: import org.jruby.util.ByteList;
0061: import org.jruby.util.IOHandler;
0062: import org.jruby.util.IOHandlerJavaIO;
0063: import org.jruby.util.IOHandlerNio;
0064: import org.jruby.util.IOHandlerNull;
0065: import org.jruby.util.IOHandlerProcess;
0066: import org.jruby.util.IOHandlerSeekable;
0067: import org.jruby.util.IOHandlerUnseekable;
0068: import org.jruby.util.IOModes;
0069: import org.jruby.util.ShellLauncher;
0070:
0071: /**
0072: *
0073: * @author jpetersen
0074: */
0075: public class RubyIO extends RubyObject {
0076: public static final int STDIN = 0;
0077: public static final int STDOUT = 1;
0078: public static final int STDERR = 2;
0079:
0080: protected IOHandler handler;
0081: protected IOModes modes = null;
0082: protected int lineNumber = 0;
0083:
0084: // Does THIS IO object think it is still open
0085: // as opposed to the IO Handler which knows the
0086: // actual truth. If two IO objects share the
0087: // same IO Handler, then it is possible for
0088: // one object to think that the handler is open
0089: // when it really isn't. Keeping track of this yields
0090: // the right errors.
0091: protected boolean isOpen = true;
0092: private boolean atEOF = false;
0093:
0094: /*
0095: * Random notes:
0096: *
0097: * 1. When a second IO object is created with the same fileno odd
0098: * concurrency issues happen when the underlying implementation
0099: * commits data. So:
0100: *
0101: * f = File.new("some file", "w")
0102: * f.puts("heh")
0103: * g = IO.new(f.fileno)
0104: * g.puts("hoh")
0105: * ... more operations of g and f ...
0106: *
0107: * Will generate a mess in "some file". The problem is that most
0108: * operations are buffered. When those buffers flush and get
0109: * written to the physical file depends on the implementation
0110: * (semantically I would think that it should be last op wins -- but
0111: * it isn't). I doubt java could mimic ruby in this way. I also
0112: * doubt many people are taking advantage of this. How about
0113: * syswrite/sysread though? I think the fact that sysread/syswrite
0114: * are defined to be a low-level system calls, allows implementations
0115: * to be somewhat different?
0116: *
0117: * 2. In the case of:
0118: * f = File.new("some file", "w")
0119: * f.puts("heh")
0120: * print f.pos
0121: * g = IO.new(f.fileno)
0122: * print g.pos
0123: * Both printed positions will be the same. But:
0124: * f = File.new("some file", "w")
0125: * f.puts("heh")
0126: * g = IO.new(f.fileno)
0127: * print f.pos, g.pos
0128: * won't be the same position. Seem peculiar enough not to touch
0129: * (this involves pos() actually causing a seek?)
0130: *
0131: * 3. All IO objects reference a IOHandler. If multiple IO objects
0132: * have the same fileno, then they also share the same IOHandler.
0133: * It is possible that some IO objects may share the same IOHandler
0134: * but not have the same permissions. However, all subsequent IO
0135: * objects created after the first must be a subset of the original
0136: * IO Object (see below for an example).
0137: *
0138: * The idea that two or more IO objects can have different access
0139: * modes means that IO objects must keep track of their own
0140: * permissions. In addition the IOHandler itself must know what
0141: * access modes it has.
0142: *
0143: * The above sharing situation only occurs in a situation like:
0144: * f = File.new("some file", "r+")
0145: * g = IO.new(f.fileno, "r")
0146: * Where g has reduced (subset) permissions.
0147: *
0148: * On reopen, the fileno's IOHandler gets replaced by a new handler.
0149: */
0150:
0151: /*
0152: * I considered making all callers of this be moved into IOHandlers
0153: * constructors (since it would be less error prone to forget there).
0154: * However, reopen() makes doing this a little funky.
0155: */
0156: public void registerIOHandler(IOHandler newHandler) {
0157: getRuntime().getIoHandlers().put(
0158: new Integer(newHandler.getFileno()),
0159: new WeakReference(newHandler));
0160: }
0161:
0162: public void unregisterIOHandler(int aFileno) {
0163: getRuntime().getIoHandlers().remove(new Integer(aFileno));
0164: }
0165:
0166: public IOHandler getIOHandlerByFileno(int aFileno) {
0167: return (IOHandler) ((WeakReference) getRuntime()
0168: .getIoHandlers().get(new Integer(aFileno))).get();
0169: }
0170:
0171: // FIXME can't use static; would interfere with other runtimes in the same JVM
0172: protected static int fileno = 2;
0173:
0174: public static int getNewFileno() {
0175: fileno++;
0176:
0177: return fileno;
0178: }
0179:
0180: // This should only be called by this and RubyFile.
0181: // It allows this object to be created without a IOHandler.
0182: public RubyIO(Ruby runtime, RubyClass type) {
0183: super (runtime, type);
0184: }
0185:
0186: public RubyIO(Ruby runtime, OutputStream outputStream) {
0187: super (runtime, runtime.getClass("IO"));
0188:
0189: // We only want IO objects with valid streams (better to error now).
0190: if (outputStream == null) {
0191: throw runtime.newIOError("Opening invalid stream");
0192: }
0193:
0194: try {
0195: handler = new IOHandlerUnseekable(runtime, null,
0196: outputStream);
0197: } catch (IOException e) {
0198: throw runtime.newIOError(e.getMessage());
0199: }
0200: modes = handler.getModes();
0201:
0202: registerIOHandler(handler);
0203: }
0204:
0205: public RubyIO(Ruby runtime, InputStream inputStream) {
0206: super (runtime, runtime.getClass("IO"));
0207:
0208: if (inputStream == null) {
0209: throw runtime.newIOError("Opening invalid stream");
0210: }
0211:
0212: try {
0213: handler = new IOHandlerUnseekable(runtime, inputStream,
0214: null);
0215: } catch (IOException e) {
0216: throw runtime.newIOError(e.getMessage());
0217: }
0218:
0219: modes = handler.getModes();
0220:
0221: registerIOHandler(handler);
0222: }
0223:
0224: public RubyIO(Ruby runtime, Channel channel) {
0225: super (runtime, runtime.getClass("IO"));
0226:
0227: // We only want IO objects with valid streams (better to error now).
0228: if (channel == null) {
0229: throw runtime.newIOError("Opening invalid stream");
0230: }
0231:
0232: try {
0233: handler = new IOHandlerNio(runtime, channel);
0234: } catch (IOException e) {
0235: throw runtime.newIOError(e.getMessage());
0236: }
0237: modes = handler.getModes();
0238:
0239: registerIOHandler(handler);
0240: }
0241:
0242: public RubyIO(Ruby runtime, Process process) {
0243: super (runtime, runtime.getClass("IO"));
0244:
0245: modes = new IOModes(runtime, "w+");
0246:
0247: try {
0248: handler = new IOHandlerProcess(runtime, process, modes);
0249: } catch (IOException e) {
0250: throw runtime.newIOError(e.getMessage());
0251: }
0252: modes = handler.getModes();
0253:
0254: registerIOHandler(handler);
0255: }
0256:
0257: public RubyIO(Ruby runtime, int descriptor) {
0258: super (runtime, runtime.getClass("IO"));
0259:
0260: try {
0261: handler = new IOHandlerUnseekable(runtime, descriptor);
0262: } catch (IOException e) {
0263: throw runtime.newIOError(e.getMessage());
0264: }
0265: modes = handler.getModes();
0266:
0267: registerIOHandler(handler);
0268: }
0269:
0270: private static ObjectAllocator IO_ALLOCATOR = new ObjectAllocator() {
0271: public IRubyObject allocate(Ruby runtime, RubyClass klass) {
0272: return new RubyIO(runtime, klass);
0273: }
0274: };
0275:
0276: public static RubyClass createIOClass(Ruby runtime) {
0277: RubyClass ioClass = runtime.defineClass("IO", runtime
0278: .getObject(), IO_ALLOCATOR);
0279: CallbackFactory callbackFactory = runtime
0280: .callbackFactory(RubyIO.class);
0281: RubyClass ioMetaClass = ioClass.getMetaClass();
0282:
0283: ioClass.includeModule(runtime.getModule("Enumerable"));
0284:
0285: // TODO: Implement tty? and isatty. We have no real capability to
0286: // determine this from java, but if we could set tty status, then
0287: // we could invoke jruby differently to allow stdin to return true
0288: // on this. This would allow things like cgi.rb to work properly.
0289:
0290: ioMetaClass.defineMethod("foreach", callbackFactory
0291: .getOptSingletonMethod("foreach"));
0292: ioMetaClass.defineMethod("read", callbackFactory
0293: .getOptSingletonMethod("read"));
0294: ioMetaClass.defineMethod("readlines", callbackFactory
0295: .getOptSingletonMethod("readlines"));
0296: ioMetaClass.defineMethod("popen", callbackFactory
0297: .getOptSingletonMethod("popen"));
0298: ioMetaClass.defineFastMethod("select", callbackFactory
0299: .getFastOptSingletonMethod("select"));
0300: ioMetaClass.defineFastMethod("pipe", callbackFactory
0301: .getFastSingletonMethod("pipe"));
0302:
0303: ioClass.defineFastMethod("<<", callbackFactory.getFastMethod(
0304: "addString", IRubyObject.class));
0305: ioClass.defineFastMethod("binmode", callbackFactory
0306: .getFastMethod("binmode"));
0307: ioClass.defineFastMethod("close", callbackFactory
0308: .getFastMethod("close"));
0309: ioClass.defineFastMethod("close_write", callbackFactory
0310: .getFastMethod("closeWrite"));
0311: ioClass.defineFastMethod("closed?", callbackFactory
0312: .getFastMethod("closed"));
0313: ioClass.defineMethod("each", callbackFactory
0314: .getOptMethod("each_line"));
0315: ioClass.defineMethod("each_byte", callbackFactory
0316: .getMethod("each_byte"));
0317: ioClass.defineMethod("each_line", callbackFactory
0318: .getOptMethod("each_line"));
0319: ioClass.defineFastMethod("eof", callbackFactory
0320: .getFastMethod("eof"));
0321: ioClass.defineAlias("eof?", "eof");
0322: ioClass.defineFastMethod("fcntl", callbackFactory
0323: .getFastMethod("fcntl", IRubyObject.class,
0324: IRubyObject.class));
0325: ioClass.defineFastMethod("fileno", callbackFactory
0326: .getFastMethod("fileno"));
0327: ioClass.defineFastMethod("flush", callbackFactory
0328: .getFastMethod("flush"));
0329: ioClass.defineFastMethod("fsync", callbackFactory
0330: .getFastMethod("fsync"));
0331: ioClass.defineFastMethod("getc", callbackFactory
0332: .getFastMethod("getc"));
0333: ioClass.defineFastMethod("gets", callbackFactory
0334: .getFastOptMethod("gets"));
0335: ioClass.defineMethod("initialize", callbackFactory
0336: .getOptMethod("initialize"));
0337: ioClass.defineFastMethod("initialize_copy", callbackFactory
0338: .getFastMethod("initialize_copy", IRubyObject.class));
0339: ioClass.defineFastMethod("lineno", callbackFactory
0340: .getFastMethod("lineno"));
0341: ioClass.defineFastMethod("lineno=", callbackFactory
0342: .getFastMethod("lineno_set", IRubyObject.class));
0343: ioClass.defineFastMethod("pid", callbackFactory
0344: .getFastMethod("pid"));
0345: ioClass.defineFastMethod("pos", callbackFactory
0346: .getFastMethod("pos"));
0347: ioClass.defineFastMethod("pos=", callbackFactory.getFastMethod(
0348: "pos_set", IRubyObject.class));
0349: ioClass.defineFastMethod("print", callbackFactory
0350: .getFastOptMethod("print"));
0351: ioClass.defineFastMethod("printf", callbackFactory
0352: .getFastOptMethod("printf"));
0353: ioClass.defineFastMethod("putc", callbackFactory.getFastMethod(
0354: "putc", IRubyObject.class));
0355: ioClass.defineFastMethod("puts", callbackFactory
0356: .getFastOptMethod("puts"));
0357: ioClass.defineFastMethod("readpartial", callbackFactory
0358: .getFastOptMethod("readpartial"));
0359: ioClass.defineFastMethod("read", callbackFactory
0360: .getFastOptMethod("read"));
0361: ioClass.defineFastMethod("readchar", callbackFactory
0362: .getFastMethod("readchar"));
0363: ioClass.defineFastMethod("readline", callbackFactory
0364: .getFastOptMethod("readline"));
0365: ioClass.defineFastMethod("readlines", callbackFactory
0366: .getFastOptMethod("readlines"));
0367: ioClass.defineFastMethod("reopen", callbackFactory
0368: .getFastOptMethod("reopen"));
0369: ioClass.defineFastMethod("rewind", callbackFactory
0370: .getFastMethod("rewind"));
0371: ioClass.defineFastMethod("seek", callbackFactory
0372: .getFastOptMethod("seek"));
0373: ioClass.defineFastMethod("sync", callbackFactory
0374: .getFastMethod("sync"));
0375: ioClass.defineFastMethod("sync=", callbackFactory
0376: .getFastMethod("sync_set", IRubyObject.class));
0377: ioClass.defineFastMethod("sysread", callbackFactory
0378: .getFastOptMethod("sysread"));
0379: ioClass.defineFastMethod("syswrite", callbackFactory
0380: .getFastMethod("syswrite", IRubyObject.class));
0381: ioClass.defineAlias("tell", "pos");
0382: ioClass.defineAlias("to_i", "fileno");
0383: ioClass.defineFastMethod("to_io", callbackFactory
0384: .getFastMethod("to_io"));
0385: ioClass.defineFastMethod("ungetc", callbackFactory
0386: .getFastMethod("ungetc", IRubyObject.class));
0387: ioClass.defineFastMethod("write", callbackFactory
0388: .getFastMethod("write", IRubyObject.class));
0389: ioClass.defineFastMethod("tty?", callbackFactory
0390: .getFastMethod("tty"));
0391: ioClass.defineAlias("isatty", "tty?");
0392:
0393: // Constants for seek
0394: ioClass.setConstant("SEEK_SET", runtime
0395: .newFixnum(IOHandler.SEEK_SET));
0396: ioClass.setConstant("SEEK_CUR", runtime
0397: .newFixnum(IOHandler.SEEK_CUR));
0398: ioClass.setConstant("SEEK_END", runtime
0399: .newFixnum(IOHandler.SEEK_END));
0400:
0401: return ioClass;
0402: }
0403:
0404: /**
0405: * <p>Open a file descriptor, unless it is already open, then return
0406: * it.</p>
0407: */
0408: public static IRubyObject fdOpen(Ruby runtime, int descriptor) {
0409: return new RubyIO(runtime, descriptor);
0410: }
0411:
0412: /*
0413: * See checkReadable for commentary.
0414: */
0415: protected void checkWriteable() {
0416: if (!isOpen() || !modes.isWriteable()) {
0417: throw getRuntime().newIOError("not opened for writing");
0418: }
0419: }
0420:
0421: /*
0422: * What the IO object "thinks" it can do. If two IO objects share
0423: * the same fileno (IOHandler), then it is possible for one to pull
0424: * the rug out from the other. This will make the second object still
0425: * "think" that the file is open. Secondly, if two IO objects share
0426: * the same fileno, but the second one only has a subset of the access
0427: * permissions, then it will "think" that it cannot do certain
0428: * operations.
0429: */
0430: protected void checkReadable() {
0431: if (!isOpen() || !modes.isReadable()) {
0432: throw getRuntime().newIOError("not opened for reading");
0433: }
0434: }
0435:
0436: public boolean isOpen() {
0437: return isOpen;
0438: }
0439:
0440: public OutputStream getOutStream() {
0441: if (handler instanceof IOHandlerJavaIO) {
0442: return ((IOHandlerJavaIO) handler).getOutputStream();
0443: } else {
0444: return null;
0445: }
0446: }
0447:
0448: public InputStream getInStream() {
0449: if (handler instanceof IOHandlerJavaIO) {
0450: return ((IOHandlerJavaIO) handler).getInputStream();
0451: } else {
0452: return null;
0453: }
0454: }
0455:
0456: public Channel getChannel() {
0457: if (handler instanceof IOHandlerNio) {
0458: return ((IOHandlerNio) handler).getChannel();
0459: } else {
0460: return null;
0461: }
0462: }
0463:
0464: public IRubyObject reopen(IRubyObject[] args) {
0465: if (args.length < 1) {
0466: throw getRuntime().newArgumentError(
0467: "wrong number of arguments");
0468: }
0469:
0470: if (args[0].isKindOf(getRuntime().getClass("IO"))) {
0471: RubyIO ios = (RubyIO) args[0];
0472:
0473: int keepFileno = handler.getFileno();
0474:
0475: // close the old handler before it gets overwritten
0476: if (handler.isOpen()) {
0477: try {
0478: handler.close();
0479: } catch (IOHandler.BadDescriptorException e) {
0480: throw getRuntime().newErrnoEBADFError();
0481: } catch (EOFException e) {
0482: return getRuntime().getNil();
0483: } catch (IOException e) {
0484: throw getRuntime().newIOError(e.getMessage());
0485: }
0486: }
0487:
0488: // When we reopen, we want our fileno to be preserved even
0489: // though we have a new IOHandler.
0490: // Note: When we clone we get a new fileno...then we replace it.
0491: // This ends up incrementing our fileno index up, which makes the
0492: // fileno we choose different from ruby. Since this seems a bit
0493: // too implementation specific, I did not bother trying to get
0494: // these to agree (what scary code would depend on fileno generating
0495: // a particular way?)
0496: try {
0497: handler = ios.handler.cloneIOHandler();
0498: } catch (IOHandler.InvalidValueException e) {
0499: throw getRuntime().newErrnoEINVALError();
0500: } catch (IOHandler.PipeException e) {
0501: throw getRuntime().newErrnoESPIPEError();
0502: } catch (FileNotFoundException e) {
0503: throw getRuntime().newErrnoENOENTError();
0504: } catch (IOException e) {
0505: throw getRuntime().newIOError(e.getMessage());
0506: }
0507: handler.setFileno(keepFileno);
0508:
0509: // Update fileno list with our new handler
0510: registerIOHandler(handler);
0511: } else if (args[0].isKindOf(getRuntime().getString())) {
0512: String path = ((RubyString) args[0]).toString();
0513: IOModes newModes = null;
0514:
0515: if (args.length > 1) {
0516: if (!args[1].isKindOf(getRuntime().getString())) {
0517: throw getRuntime().newTypeError(args[1],
0518: getRuntime().getString());
0519: }
0520:
0521: newModes = new IOModes(getRuntime(),
0522: ((RubyString) args[1]).toString());
0523: }
0524:
0525: try {
0526: if (handler != null) {
0527: close();
0528: }
0529:
0530: if (newModes != null) {
0531: modes = newModes;
0532: }
0533: if ("/dev/null".equals(path)) {
0534: handler = new IOHandlerNull(getRuntime(), modes);
0535: } else {
0536: handler = new IOHandlerSeekable(getRuntime(), path,
0537: modes);
0538: }
0539:
0540: registerIOHandler(handler);
0541: } catch (IOHandler.InvalidValueException e) {
0542: throw getRuntime().newErrnoEINVALError();
0543: } catch (IOException e) {
0544: throw getRuntime().newIOError(e.toString());
0545: }
0546: }
0547:
0548: // A potentially previously close IO is being 'reopened'.
0549: isOpen = true;
0550: return this ;
0551: }
0552:
0553: /** Read a line.
0554: *
0555: */
0556: // TODO: Most things loop over this and always pass it the same arguments
0557: // meaning they are an invariant of the loop. Think about fixing this.
0558: public IRubyObject internalGets(IRubyObject[] args) {
0559: checkReadable();
0560:
0561: IRubyObject sepVal;
0562:
0563: if (args.length > 0) {
0564: sepVal = args[0];
0565: } else {
0566: sepVal = getRuntime().getGlobalVariables().get("$/");
0567: }
0568:
0569: ByteList separator = sepVal.isNil() ? null
0570: : ((RubyString) sepVal).getByteList();
0571:
0572: if (separator != null && separator.realSize == 0) {
0573: separator = IOHandler.PARAGRAPH_DELIMETER;
0574: }
0575:
0576: try {
0577:
0578: ByteList newLine = handler.gets(separator);
0579:
0580: if (newLine != null) {
0581: lineNumber++;
0582: getRuntime().getGlobalVariables().set("$.",
0583: getRuntime().newFixnum(lineNumber));
0584: RubyString result = RubyString.newString(getRuntime(),
0585: newLine);
0586: result.taint();
0587:
0588: return result;
0589: }
0590:
0591: return getRuntime().getNil();
0592: } catch (EOFException e) {
0593: return getRuntime().getNil();
0594: } catch (IOHandler.BadDescriptorException e) {
0595: throw getRuntime().newErrnoEBADFError();
0596: } catch (IOException e) {
0597: throw getRuntime().newIOError(e.getMessage());
0598: }
0599: }
0600:
0601: // IO class methods.
0602:
0603: public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
0604: int count = Arity.checkArgumentCount(getRuntime(), args, 1, 2);
0605: int newFileno = RubyNumeric.fix2int(args[0]);
0606: String mode = null;
0607:
0608: if (count > 1) {
0609: mode = args[1].convertToString().toString();
0610: }
0611:
0612: // See if we already have this descriptor open.
0613: // If so then we can mostly share the handler (keep open
0614: // file, but possibly change the mode).
0615: IOHandler existingIOHandler = getIOHandlerByFileno(newFileno);
0616:
0617: if (existingIOHandler == null) {
0618: if (mode == null) {
0619: mode = "r";
0620: }
0621:
0622: try {
0623: handler = new IOHandlerUnseekable(getRuntime(),
0624: newFileno, mode);
0625: } catch (IOException e) {
0626: throw getRuntime().newIOError(e.getMessage());
0627: }
0628: modes = new IOModes(getRuntime(), mode);
0629:
0630: registerIOHandler(handler);
0631: } else {
0632: // We are creating a new IO object that shares the same
0633: // IOHandler (and fileno).
0634: handler = existingIOHandler;
0635:
0636: // Inherit if no mode specified otherwise create new one
0637: modes = mode == null ? handler.getModes() : new IOModes(
0638: getRuntime(), mode);
0639:
0640: // Reset file based on modes.
0641: try {
0642: handler.reset(modes);
0643: } catch (IOHandler.InvalidValueException e) {
0644: throw getRuntime().newErrnoEINVALError();
0645: } catch (IOException e) {
0646: throw getRuntime().newIOError(e.getMessage());
0647: }
0648: }
0649:
0650: return this ;
0651: }
0652:
0653: // This appears to be some windows-only mode. On a java platform this is a no-op
0654: public IRubyObject binmode() {
0655: return this ;
0656: }
0657:
0658: public IRubyObject syswrite(IRubyObject obj) {
0659: try {
0660: if (obj instanceof RubyString) {
0661: return getRuntime().newFixnum(
0662: handler.syswrite(((RubyString) obj)
0663: .getByteList()));
0664: } else {
0665: // FIXME: unlikely to be efficient, but probably correct
0666: return getRuntime().newFixnum(
0667: handler.syswrite(((RubyString) obj.callMethod(
0668: obj.getRuntime().getCurrentContext(),
0669: MethodIndex.TO_S, "to_s"))
0670: .getByteList()));
0671: }
0672: } catch (IOHandler.BadDescriptorException e) {
0673: throw getRuntime().newErrnoEBADFError();
0674: } catch (IOException e) {
0675: throw getRuntime().newSystemCallError(e.getMessage());
0676: }
0677: }
0678:
0679: /** io_write
0680: *
0681: */
0682: public IRubyObject write(IRubyObject obj) {
0683: checkWriteable();
0684:
0685: try {
0686: if (obj instanceof RubyString) {
0687: return getRuntime()
0688: .newFixnum(
0689: handler.write(((RubyString) obj)
0690: .getByteList()));
0691: } else {
0692: // FIXME: unlikely to be efficient, but probably correct
0693: return getRuntime().newFixnum(
0694: handler.write(((RubyString) obj.callMethod(obj
0695: .getRuntime().getCurrentContext(),
0696: MethodIndex.TO_S, "to_s"))
0697: .getByteList()));
0698: }
0699: } catch (IOHandler.BadDescriptorException e) {
0700: return RubyFixnum.zero(getRuntime());
0701: } catch (IOException e) {
0702: return RubyFixnum.zero(getRuntime());
0703: }
0704: }
0705:
0706: /** rb_io_addstr
0707: *
0708: */
0709: public IRubyObject addString(IRubyObject anObject) {
0710: // Claims conversion is done via 'to_s' in docs.
0711: IRubyObject strObject = anObject.callMethod(getRuntime()
0712: .getCurrentContext(), MethodIndex.TO_S, "to_s");
0713:
0714: write(strObject);
0715:
0716: return this ;
0717: }
0718:
0719: public RubyFixnum fileno() {
0720: return getRuntime().newFixnum(handler.getFileno());
0721: }
0722:
0723: /** Returns the current line number.
0724: *
0725: * @return the current line number.
0726: */
0727: public RubyFixnum lineno() {
0728: return getRuntime().newFixnum(lineNumber);
0729: }
0730:
0731: /** Sets the current line number.
0732: *
0733: * @param newLineNumber The new line number.
0734: */
0735: public RubyFixnum lineno_set(IRubyObject newLineNumber) {
0736: lineNumber = RubyNumeric.fix2int(newLineNumber);
0737:
0738: return (RubyFixnum) newLineNumber;
0739: }
0740:
0741: /** Returns the current sync mode.
0742: *
0743: * @return the current sync mode.
0744: */
0745: public RubyBoolean sync() {
0746: return getRuntime().newBoolean(handler.isSync());
0747: }
0748:
0749: /**
0750: * <p>Return the process id (pid) of the process this IO object
0751: * spawned. If no process exists (popen was not called), then
0752: * nil is returned. This is not how it appears to be defined
0753: * but ruby 1.8 works this way.</p>
0754: *
0755: * @return the pid or nil
0756: */
0757: public IRubyObject pid() {
0758: int pid = handler.pid();
0759:
0760: return pid == -1 ? getRuntime().getNil() : getRuntime()
0761: .newFixnum(pid);
0762: }
0763:
0764: public boolean hasPendingBuffered() {
0765: return handler.hasPendingBuffered();
0766: }
0767:
0768: public RubyFixnum pos() {
0769: try {
0770: return getRuntime().newFixnum(handler.pos());
0771: } catch (IOHandler.PipeException e) {
0772: throw getRuntime().newErrnoESPIPEError();
0773: } catch (IOException e) {
0774: throw getRuntime().newIOError(e.getMessage());
0775: }
0776: }
0777:
0778: public RubyFixnum pos_set(IRubyObject newPosition) {
0779: long offset = RubyNumeric.fix2long(newPosition);
0780:
0781: if (offset < 0) {
0782: throw getRuntime().newSystemCallError(
0783: "Negative seek offset");
0784: }
0785:
0786: try {
0787: handler.seek(offset, IOHandler.SEEK_SET);
0788: } catch (IOHandler.InvalidValueException e) {
0789: throw getRuntime().newErrnoEINVALError();
0790: } catch (IOHandler.PipeException e) {
0791: throw getRuntime().newErrnoESPIPEError();
0792: } catch (IOException e) {
0793: throw getRuntime().newIOError(e.getMessage());
0794: }
0795:
0796: return (RubyFixnum) newPosition;
0797: }
0798:
0799: /** Print some objects to the stream.
0800: *
0801: */
0802: public IRubyObject print(IRubyObject[] args) {
0803: if (args.length == 0) {
0804: args = new IRubyObject[] { getRuntime().getCurrentContext()
0805: .getLastline() };
0806: }
0807:
0808: IRubyObject fs = getRuntime().getGlobalVariables().get("$,");
0809: IRubyObject rs = getRuntime().getGlobalVariables().get("$\\");
0810: ThreadContext context = getRuntime().getCurrentContext();
0811:
0812: for (int i = 0; i < args.length; i++) {
0813: if (i > 0 && !fs.isNil()) {
0814: callMethod(context, "write", fs);
0815: }
0816: if (args[i].isNil()) {
0817: callMethod(context, "write", getRuntime().newString(
0818: "nil"));
0819: } else {
0820: callMethod(context, "write", args[i]);
0821: }
0822: }
0823: if (!rs.isNil()) {
0824: callMethod(context, "write", rs);
0825: }
0826:
0827: return getRuntime().getNil();
0828: }
0829:
0830: public IRubyObject printf(IRubyObject[] args) {
0831: Arity.checkArgumentCount(getRuntime(), args, 1, -1);
0832: callMethod(getRuntime().getCurrentContext(), "write",
0833: RubyKernel.sprintf(this , args));
0834: return getRuntime().getNil();
0835: }
0836:
0837: public IRubyObject putc(IRubyObject object) {
0838: int c;
0839:
0840: if (object.isKindOf(getRuntime().getString())) {
0841: String value = ((RubyString) object).toString();
0842:
0843: if (value.length() > 0) {
0844: c = value.charAt(0);
0845: } else {
0846: throw getRuntime().newTypeError(
0847: "Cannot convert String to Integer");
0848: }
0849: } else if (object.isKindOf(getRuntime().getFixnum())) {
0850: c = RubyNumeric.fix2int(object);
0851: } else { // What case will this work for?
0852: c = RubyNumeric.fix2int(object.callMethod(getRuntime()
0853: .getCurrentContext(), MethodIndex.TO_I, "to_i"));
0854: }
0855:
0856: try {
0857: handler.putc(c);
0858: } catch (IOHandler.BadDescriptorException e) {
0859: return RubyFixnum.zero(getRuntime());
0860: } catch (IOException e) {
0861: return RubyFixnum.zero(getRuntime());
0862: }
0863:
0864: return object;
0865: }
0866:
0867: // This was a getOpt with one mandatory arg, but it did not work
0868: // so I am parsing it for now.
0869: public RubyFixnum seek(IRubyObject[] args) {
0870: if (args.length == 0) {
0871: throw getRuntime().newArgumentError(
0872: "wrong number of arguments");
0873: }
0874:
0875: long offset = RubyNumeric.fix2long(args[0]);
0876: int type = IOHandler.SEEK_SET;
0877:
0878: if (args.length > 1) {
0879: type = RubyNumeric.fix2int(args[1].convertToInteger());
0880: }
0881:
0882: try {
0883: handler.seek(offset, type);
0884: } catch (IOHandler.InvalidValueException e) {
0885: throw getRuntime().newErrnoEINVALError();
0886: } catch (IOHandler.PipeException e) {
0887: throw getRuntime().newErrnoESPIPEError();
0888: } catch (IOException e) {
0889: throw getRuntime().newIOError(e.getMessage());
0890: }
0891:
0892: return RubyFixnum.zero(getRuntime());
0893: }
0894:
0895: public RubyFixnum rewind() {
0896: try {
0897: handler.rewind();
0898: } catch (IOHandler.InvalidValueException e) {
0899: throw getRuntime().newErrnoEINVALError();
0900: } catch (IOHandler.PipeException e) {
0901: throw getRuntime().newErrnoESPIPEError();
0902: } catch (IOException e) {
0903: throw getRuntime().newIOError(e.getMessage());
0904: }
0905:
0906: // Must be back on first line on rewind.
0907: lineNumber = 0;
0908:
0909: return RubyFixnum.zero(getRuntime());
0910: }
0911:
0912: public RubyFixnum fsync() {
0913: checkWriteable();
0914:
0915: try {
0916: handler.sync();
0917: } catch (IOException e) {
0918: throw getRuntime().newIOError(e.getMessage());
0919: } catch (IOHandler.BadDescriptorException e) {
0920: throw getRuntime().newErrnoEBADFError();
0921: }
0922:
0923: return RubyFixnum.zero(getRuntime());
0924: }
0925:
0926: /** Sets the current sync mode.
0927: *
0928: * @param newSync The new sync mode.
0929: */
0930: public IRubyObject sync_set(IRubyObject newSync) {
0931: handler.setIsSync(newSync.isTrue());
0932:
0933: return this ;
0934: }
0935:
0936: public RubyBoolean eof() {
0937: try {
0938: boolean isEOF = handler.isEOF();
0939: return isEOF ? getRuntime().getTrue() : getRuntime()
0940: .getFalse();
0941: } catch (IOHandler.BadDescriptorException e) {
0942: throw getRuntime().newErrnoEBADFError();
0943: } catch (IOException e) {
0944: throw getRuntime().newIOError(e.getMessage());
0945: }
0946: }
0947:
0948: public RubyBoolean tty() {
0949: // TODO: this is less than ideal but might be as close as we'll get
0950: int fileno = handler.getFileno();
0951: if (fileno == STDOUT || fileno == STDIN || fileno == STDERR) {
0952: return getRuntime().getTrue();
0953: } else {
0954: return getRuntime().getFalse();
0955: }
0956: }
0957:
0958: public IRubyObject initialize_copy(IRubyObject original) {
0959: if (this == original)
0960: return this ;
0961:
0962: RubyIO originalIO = (RubyIO) original;
0963:
0964: // Two pos pointers?
0965: // http://blade.nagaokaut.ac.jp/ruby/ruby-talk/81513
0966: // So if I understand this correctly, the descriptor level stuff
0967: // shares things like position, but the higher level stuff uses
0968: // a different set of libc functions (FILE*), which does not share
0969: // position. Our current implementation implements our higher
0970: // level operations on top of our 'sys' versions. So we could in
0971: // fact share everything. Unfortunately, we want to clone ruby's
0972: // behavior (i.e. show how this interface bleeds their
0973: // implementation). So our best bet, is to just create a yet another
0974: // copy of the handler. In fact, ruby 1.8 must do this as the cloned
0975: // resource is in fact a different fileno. What is clone for again?
0976:
0977: handler = originalIO.handler;
0978: modes = (IOModes) originalIO.modes.clone();
0979:
0980: return this ;
0981: }
0982:
0983: /** Closes the IO.
0984: *
0985: * @return The IO.
0986: */
0987: public RubyBoolean closed() {
0988: return isOpen() ? getRuntime().getFalse() : getRuntime()
0989: .getTrue();
0990: }
0991:
0992: /**
0993: * <p>Closes all open resources for the IO. It also removes
0994: * it from our magical all open file descriptor pool.</p>
0995: *
0996: * @return The IO.
0997: */
0998: public IRubyObject close() {
0999: isOpen = false;
1000:
1001: try {
1002: handler.close();
1003: } catch (IOHandler.BadDescriptorException e) {
1004: throw getRuntime().newErrnoEBADFError();
1005: } catch (IOException e) {
1006: throw getRuntime().newIOError(e.getMessage());
1007: }
1008:
1009: unregisterIOHandler(handler.getFileno());
1010:
1011: return this ;
1012: }
1013:
1014: public IRubyObject closeWrite() {
1015: return this ;
1016: }
1017:
1018: /** Flushes the IO output stream.
1019: *
1020: * @return The IO.
1021: */
1022: public RubyIO flush() {
1023: try {
1024: handler.flush();
1025: } catch (IOHandler.BadDescriptorException e) {
1026: throw getRuntime().newErrnoEBADFError();
1027: } catch (IOException e) {
1028: throw getRuntime().newIOError(e.getMessage());
1029: }
1030:
1031: return this ;
1032: }
1033:
1034: /** Read a line.
1035: *
1036: */
1037: public IRubyObject gets(IRubyObject[] args) {
1038: IRubyObject result = internalGets(args);
1039:
1040: if (!result.isNil())
1041: getRuntime().getCurrentContext().setLastline(result);
1042:
1043: return result;
1044: }
1045:
1046: public boolean getBlocking() {
1047: if (!(handler instanceof IOHandlerNio))
1048: return true;
1049:
1050: return ((IOHandlerNio) handler).getBlocking();
1051: }
1052:
1053: public IRubyObject fcntl(IRubyObject cmd, IRubyObject arg)
1054: throws IOException {
1055: long realCmd = cmd.convertToInteger().getLongValue();
1056:
1057: // FIXME: Arg may also be true, false, and nil and still be valid. Strangely enough,
1058: // protocol conversion is not happening in Ruby on this arg?
1059: if (!(arg instanceof RubyNumeric))
1060: return getRuntime().newFixnum(0);
1061:
1062: long realArg = ((RubyNumeric) arg).getLongValue();
1063:
1064: // Fixme: Only F_SETFL is current supported
1065: if (realCmd == 1L) { // cmd is F_SETFL
1066: boolean block = true;
1067:
1068: if ((realArg & IOModes.NONBLOCK) == IOModes.NONBLOCK) {
1069: block = false;
1070: }
1071:
1072: if (!(handler instanceof IOHandlerNio)) {
1073: // cryptic for the uninitiated...
1074: throw getRuntime().newNotImplementedError(
1075: "FCNTL only works with Nio based handlers");
1076: }
1077:
1078: try {
1079: ((IOHandlerNio) handler).setBlocking(block);
1080: } catch (IOException e) {
1081: throw getRuntime().newIOError(e.getMessage());
1082: }
1083: }
1084:
1085: return getRuntime().newFixnum(0);
1086: }
1087:
1088: public IRubyObject puts(IRubyObject[] args) {
1089: Arity.checkArgumentCount(getRuntime(), args, 0, -1);
1090:
1091: ThreadContext context = getRuntime().getCurrentContext();
1092:
1093: if (args.length == 0) {
1094: callMethod(context, "write", getRuntime().newString("\n"));
1095: return getRuntime().getNil();
1096: }
1097:
1098: for (int i = 0; i < args.length; i++) {
1099: String line = null;
1100: if (args[i].isNil()) {
1101: line = "nil";
1102: } else if (args[i] instanceof RubyArray) {
1103: puts(((RubyArray) args[i]).toJavaArray());
1104: continue;
1105: } else {
1106: line = args[i].toString();
1107: }
1108: callMethod(getRuntime().getCurrentContext(), "write",
1109: getRuntime().newString(
1110: line + (line.endsWith("\n") ? "" : "\n")));
1111: }
1112: return getRuntime().getNil();
1113: }
1114:
1115: /** Read a line.
1116: *
1117: */
1118: public IRubyObject readline(IRubyObject[] args) {
1119: IRubyObject line = gets(args);
1120:
1121: if (line.isNil()) {
1122: throw getRuntime().newEOFError();
1123: }
1124:
1125: return line;
1126: }
1127:
1128: /** Read a byte. On EOF returns nil.
1129: *
1130: */
1131: public IRubyObject getc() {
1132: checkReadable();
1133:
1134: try {
1135: int c = handler.getc();
1136:
1137: return c == -1 ? getRuntime().getNil() : getRuntime()
1138: .newFixnum(c);
1139: } catch (IOHandler.BadDescriptorException e) {
1140: throw getRuntime().newErrnoEBADFError();
1141: } catch (EOFException e) {
1142: throw getRuntime().newEOFError();
1143: } catch (IOException e) {
1144: throw getRuntime().newIOError(e.getMessage());
1145: }
1146: }
1147:
1148: /**
1149: * <p>Pushes char represented by int back onto IOS.</p>
1150: *
1151: * @param number to push back
1152: */
1153: public IRubyObject ungetc(IRubyObject number) {
1154: handler.ungetc(RubyNumeric.fix2int(number));
1155:
1156: return getRuntime().getNil();
1157: }
1158:
1159: public IRubyObject readpartial(IRubyObject[] args) {
1160: if (!(handler instanceof IOHandlerNio)) {
1161: // cryptic for the uninitiated...
1162: throw getRuntime().newNotImplementedError(
1163: "readpartial only works with Nio based handlers");
1164: }
1165: try {
1166: ByteList buf = ((IOHandlerNio) handler)
1167: .readpartial(RubyNumeric.fix2int(args[0]));
1168: IRubyObject strbuf = RubyString.newString(getRuntime(),
1169: buf == null ? new ByteList(ByteList.NULL_ARRAY)
1170: : buf);
1171: if (args.length > 1) {
1172: args[1].callMethod(getRuntime().getCurrentContext(),
1173: MethodIndex.OP_LSHIFT, "<<", strbuf);
1174: return args[1];
1175: }
1176:
1177: return strbuf;
1178: } catch (IOHandler.BadDescriptorException e) {
1179: throw getRuntime().newErrnoEBADFError();
1180: } catch (EOFException e) {
1181: return getRuntime().getNil();
1182: } catch (IOException e) {
1183: throw getRuntime().newIOError(e.getMessage());
1184: }
1185: }
1186:
1187: public IRubyObject sysread(IRubyObject[] args) {
1188: Arity.checkArgumentCount(getRuntime(), args, 1, 2);
1189:
1190: int len = (int) RubyNumeric.num2long(args[0]);
1191: if (len < 0)
1192: throw getRuntime().newArgumentError("Negative size");
1193:
1194: try {
1195: RubyString str;
1196: if (args.length == 1 || args[1].isNil()) {
1197: if (len == 0)
1198: return RubyString.newString(getRuntime(), "");
1199: str = RubyString.newString(getRuntime(), handler
1200: .sysread(len));
1201: } else {
1202: str = args[1].convertToString();
1203: if (len == 0) {
1204: str.setValue(new ByteList());
1205: return str;
1206: }
1207: str.setValue(handler.sysread(len)); // should preserve same instance
1208: }
1209: str.setTaint(true);
1210: return str;
1211:
1212: } catch (IOHandler.BadDescriptorException e) {
1213: throw getRuntime().newErrnoEBADFError();
1214: } catch (EOFException e) {
1215: throw getRuntime().newEOFError();
1216: } catch (IOException e) {
1217: // All errors to sysread should be SystemCallErrors, but on a closed stream
1218: // Ruby returns an IOError. Java throws same exception for all errors so
1219: // we resort to this hack...
1220: if ("File not open".equals(e.getMessage())) {
1221: throw getRuntime().newIOError(e.getMessage());
1222: }
1223: throw getRuntime().newSystemCallError(e.getMessage());
1224: }
1225: }
1226:
1227: public IRubyObject read(IRubyObject[] args) {
1228:
1229: int argCount = Arity.checkArgumentCount(getRuntime(), args, 0,
1230: 2);
1231: RubyString callerBuffer = null;
1232: boolean readEntireStream = (argCount == 0 || args[0].isNil());
1233:
1234: try {
1235: // Reads when already at EOF keep us at EOF
1236: // We do retain the possibility of un-EOFing if the handler
1237: // gets new data
1238: if (atEOF && handler.isEOF())
1239: throw new EOFException();
1240:
1241: if (argCount == 2) {
1242: callerBuffer = args[1].convertToString();
1243: }
1244:
1245: ByteList buf;
1246: if (readEntireStream) {
1247: buf = handler.getsEntireStream();
1248: } else {
1249: long len = RubyNumeric.num2long(args[0]);
1250: if (len < 0)
1251: throw getRuntime().newArgumentError(
1252: "negative length " + len + " given");
1253: buf = handler.read((int) len);
1254: }
1255:
1256: if (buf == null)
1257: throw new EOFException();
1258:
1259: // If we get here then no EOFException was thrown in the handler. We
1260: // might still need to set our atEOF flag back to true depending on
1261: // whether we were reading the entire stream (see the finally block below)
1262: atEOF = false;
1263: if (callerBuffer != null) {
1264: callerBuffer.setValue(buf);
1265: return callerBuffer;
1266: }
1267:
1268: return RubyString.newString(getRuntime(), buf);
1269: } catch (IOHandler.BadDescriptorException e) {
1270: throw getRuntime().newErrnoEBADFError();
1271: } catch (EOFException e) {
1272: // on EOF, IO#read():
1273: // with no args or a nil first arg will return an empty string
1274: // with a non-nil first arg will return nil
1275: atEOF = true;
1276: if (callerBuffer != null) {
1277: callerBuffer.setValue("");
1278: return readEntireStream ? callerBuffer : getRuntime()
1279: .getNil();
1280: }
1281:
1282: return readEntireStream ? getRuntime().newString("")
1283: : getRuntime().getNil();
1284: } catch (IOException e) {
1285: throw getRuntime().newIOError(e.getMessage());
1286: } finally {
1287: // reading the entire stream always puts us at EOF
1288: if (readEntireStream) {
1289: atEOF = true;
1290: }
1291: }
1292: }
1293:
1294: /** Read a byte. On EOF throw EOFError.
1295: *
1296: */
1297: public IRubyObject readchar() {
1298: checkReadable();
1299:
1300: try {
1301: int c = handler.getc();
1302:
1303: if (c == -1)
1304: throw getRuntime().newEOFError();
1305:
1306: return getRuntime().newFixnum(c);
1307: } catch (IOHandler.BadDescriptorException e) {
1308: throw getRuntime().newErrnoEBADFError();
1309: } catch (EOFException e) {
1310: throw getRuntime().newEOFError();
1311: } catch (IOException e) {
1312: throw getRuntime().newIOError(e.getMessage());
1313: }
1314: }
1315:
1316: /**
1317: * <p>Invoke a block for each byte.</p>
1318: */
1319: public IRubyObject each_byte(Block block) {
1320: try {
1321: ThreadContext context = getRuntime().getCurrentContext();
1322: for (int c = handler.getc(); c != -1; c = handler.getc()) {
1323: assert c < 256;
1324: block.yield(context, getRuntime().newFixnum(c));
1325: }
1326:
1327: return getRuntime().getNil();
1328: } catch (IOHandler.BadDescriptorException e) {
1329: throw getRuntime().newErrnoEBADFError();
1330: } catch (EOFException e) {
1331: return getRuntime().getNil();
1332: } catch (IOException e) {
1333: throw getRuntime().newIOError(e.getMessage());
1334: }
1335: }
1336:
1337: /**
1338: * <p>Invoke a block for each line.</p>
1339: */
1340: public RubyIO each_line(IRubyObject[] args, Block block) {
1341: IRubyObject rs;
1342:
1343: if (args.length == 0) {
1344: rs = getRuntime().getGlobalVariables().get("$/");
1345: } else {
1346: Arity.checkArgumentCount(getRuntime(), args, 1, 1);
1347: rs = args[0];
1348: if (!rs.isNil())
1349: rs = rs.convertToString();
1350: }
1351:
1352: ThreadContext context = getRuntime().getCurrentContext();
1353: for (IRubyObject line = internalGets(args); !line.isNil(); line = internalGets(args)) {
1354: block.yield(context, line);
1355: }
1356:
1357: return this ;
1358: }
1359:
1360: public RubyArray readlines(IRubyObject[] args) {
1361: IRubyObject[] separatorArgument;
1362: if (args.length > 0) {
1363: if (!args[0].isKindOf(getRuntime().getNilClass())
1364: && !args[0].isKindOf(getRuntime().getString())) {
1365: throw getRuntime().newTypeError(args[0],
1366: getRuntime().getString());
1367: }
1368: separatorArgument = new IRubyObject[] { args[0] };
1369: } else {
1370: separatorArgument = IRubyObject.NULL_ARRAY;
1371: }
1372:
1373: RubyArray result = getRuntime().newArray();
1374: IRubyObject line;
1375: while (!(line = internalGets(separatorArgument)).isNil()) {
1376: result.append(line);
1377: }
1378: return result;
1379: }
1380:
1381: public RubyIO to_io() {
1382: return this ;
1383: }
1384:
1385: public String toString() {
1386: return "RubyIO(" + modes + ", " + fileno + ")";
1387: }
1388:
1389: /* class methods for IO */
1390:
1391: /** rb_io_s_foreach
1392: *
1393: */
1394: public static IRubyObject foreach(IRubyObject recv,
1395: IRubyObject[] args, Block block) {
1396: Ruby runtime = recv.getRuntime();
1397: int count = Arity.checkArgumentCount(runtime, args, 1, -1);
1398: IRubyObject filename = args[0].convertToString();
1399: runtime.checkSafeString(filename);
1400: RubyIO io = (RubyIO) RubyFile.open(recv,
1401: new IRubyObject[] { filename }, false, block);
1402:
1403: if (!io.isNil() && io.isOpen()) {
1404: try {
1405: IRubyObject[] newArgs = new IRubyObject[count - 1];
1406: System.arraycopy(args, 1, newArgs, 0, count - 1);
1407:
1408: IRubyObject nextLine = io.internalGets(newArgs);
1409: while (!nextLine.isNil()) {
1410: block.yield(runtime.getCurrentContext(), nextLine);
1411: nextLine = io.internalGets(newArgs);
1412: }
1413: } finally {
1414: io.close();
1415: }
1416: }
1417:
1418: return runtime.getNil();
1419: }
1420:
1421: private static RubyIO registerSelect(Selector selector,
1422: IRubyObject obj, int ops) throws IOException {
1423: RubyIO ioObj;
1424:
1425: if (!(obj instanceof RubyIO)) {
1426: // invoke to_io
1427: if (!obj.respondsTo("to_io"))
1428: return null;
1429:
1430: ioObj = (RubyIO) obj.callMethod(obj.getRuntime()
1431: .getCurrentContext(), "to_io");
1432: } else {
1433: ioObj = (RubyIO) obj;
1434: }
1435:
1436: Channel channel = ioObj.getChannel();
1437: if (channel == null || !(channel instanceof SelectableChannel)) {
1438: return null;
1439: }
1440:
1441: ((SelectableChannel) channel).configureBlocking(false);
1442: int real_ops = ((SelectableChannel) channel).validOps() & ops;
1443: SelectionKey key = ((SelectableChannel) channel)
1444: .keyFor(selector);
1445:
1446: if (key == null) {
1447: ((SelectableChannel) channel).register(selector, real_ops,
1448: obj);
1449: } else {
1450: key.interestOps(key.interestOps() | real_ops);
1451: }
1452:
1453: return ioObj;
1454: }
1455:
1456: public static IRubyObject select(IRubyObject recv,
1457: IRubyObject[] args) {
1458: return select_static(recv.getRuntime(), args);
1459: }
1460:
1461: public static IRubyObject select_static(Ruby runtime,
1462: IRubyObject[] args) {
1463: try {
1464: boolean atLeastOneDescriptor = false;
1465:
1466: Set pending = new HashSet();
1467: Selector selector = Selector.open();
1468: if (!args[0].isNil()) {
1469: atLeastOneDescriptor = true;
1470:
1471: // read
1472: for (Iterator i = ((RubyArray) args[0]).getList()
1473: .iterator(); i.hasNext();) {
1474: IRubyObject obj = (IRubyObject) i.next();
1475: RubyIO ioObj = registerSelect(selector, obj,
1476: SelectionKey.OP_READ
1477: | SelectionKey.OP_ACCEPT);
1478:
1479: if (ioObj != null && ioObj.hasPendingBuffered())
1480: pending.add(obj);
1481: }
1482: }
1483: if (args.length > 1 && !args[1].isNil()) {
1484: atLeastOneDescriptor = true;
1485: // write
1486: for (Iterator i = ((RubyArray) args[1]).getList()
1487: .iterator(); i.hasNext();) {
1488: IRubyObject obj = (IRubyObject) i.next();
1489: registerSelect(selector, obj, SelectionKey.OP_WRITE);
1490: }
1491: }
1492: if (args.length > 2 && !args[2].isNil()) {
1493: atLeastOneDescriptor = true;
1494: // Java's select doesn't do anything about this, so we leave it be.
1495: }
1496:
1497: long timeout = 0;
1498: if (args.length > 3 && !args[3].isNil()) {
1499: if (args[3] instanceof RubyFloat) {
1500: timeout = Math.round(((RubyFloat) args[3])
1501: .getDoubleValue() * 1000);
1502: } else {
1503: timeout = Math.round(((RubyFixnum) args[3])
1504: .getDoubleValue() * 1000);
1505: }
1506:
1507: if (timeout < 0) {
1508: throw runtime
1509: .newArgumentError("negative timeout given");
1510: }
1511: }
1512:
1513: if (!atLeastOneDescriptor) {
1514: return runtime.getNil();
1515: }
1516:
1517: if (pending.isEmpty()) {
1518: if (args.length > 3) {
1519: if (timeout == 0) {
1520: selector.selectNow();
1521: } else {
1522: selector.select(timeout);
1523: }
1524: } else {
1525: selector.select();
1526: }
1527: } else {
1528: selector.selectNow();
1529: }
1530:
1531: List r = new ArrayList();
1532: List w = new ArrayList();
1533: List e = new ArrayList();
1534: for (Iterator i = selector.selectedKeys().iterator(); i
1535: .hasNext();) {
1536: SelectionKey key = (SelectionKey) i.next();
1537: if ((key.interestOps() & key.readyOps() & (SelectionKey.OP_READ
1538: | SelectionKey.OP_ACCEPT | SelectionKey.OP_CONNECT)) != 0) {
1539: r.add(key.attachment());
1540: pending.remove(key.attachment());
1541: }
1542: if ((key.interestOps() & key.readyOps() & (SelectionKey.OP_WRITE)) != 0) {
1543: w.add(key.attachment());
1544: }
1545: }
1546: r.addAll(pending);
1547:
1548: // make all sockets blocking as configured again
1549: for (Iterator i = selector.keys().iterator(); i.hasNext();) {
1550: SelectionKey key = (SelectionKey) i.next();
1551: SelectableChannel channel = key.channel();
1552: synchronized (channel.blockingLock()) {
1553: boolean blocking = ((RubyIO) key.attachment())
1554: .getBlocking();
1555: key.cancel();
1556: channel.configureBlocking(blocking);
1557: }
1558: }
1559: selector.close();
1560:
1561: if (r.size() == 0 && w.size() == 0 && e.size() == 0) {
1562: return runtime.getNil();
1563: }
1564:
1565: List ret = new ArrayList();
1566:
1567: ret.add(RubyArray.newArray(runtime, r));
1568: ret.add(RubyArray.newArray(runtime, w));
1569: ret.add(RubyArray.newArray(runtime, e));
1570:
1571: return RubyArray.newArray(runtime, ret);
1572: } catch (IOException e) {
1573: throw runtime.newIOError(e.getMessage());
1574: }
1575: }
1576:
1577: public static IRubyObject read(IRubyObject recv,
1578: IRubyObject[] args, Block block) {
1579: Ruby runtime = recv.getRuntime();
1580: Arity.checkArgumentCount(runtime, args, 1, 3);
1581: IRubyObject[] fileArguments = new IRubyObject[] { args[0] };
1582: RubyIO file = (RubyIO) RubyKernel.open(recv, fileArguments,
1583: block);
1584: IRubyObject[] readArguments;
1585:
1586: if (args.length >= 2) {
1587: readArguments = new IRubyObject[] { args[1].convertToType(
1588: runtime.getFixnum(), MethodIndex.TO_INT, "to_int",
1589: true) };
1590: } else {
1591: readArguments = new IRubyObject[] {};
1592: }
1593:
1594: try {
1595:
1596: if (args.length == 3) {
1597: file.seek(new IRubyObject[] { args[2].convertToType(
1598: runtime.getFixnum(), MethodIndex.TO_INT,
1599: "to_int", true) });
1600: }
1601:
1602: return file.read(readArguments);
1603: } finally {
1604: file.close();
1605: }
1606: }
1607:
1608: public static RubyArray readlines(IRubyObject recv,
1609: IRubyObject[] args, Block block) {
1610: int count = Arity.checkArgumentCount(recv.getRuntime(), args,
1611: 1, 2);
1612:
1613: IRubyObject[] fileArguments = new IRubyObject[] { args[0] };
1614: IRubyObject[] separatorArguments = count >= 2 ? new IRubyObject[] { args[1] }
1615: : IRubyObject.NULL_ARRAY;
1616: RubyIO file = (RubyIO) RubyKernel.open(recv, fileArguments,
1617: block);
1618: try {
1619: return file.readlines(separatorArguments);
1620: } finally {
1621: file.close();
1622: }
1623: }
1624:
1625: //XXX Hacked incomplete popen implementation to make
1626: public static IRubyObject popen(IRubyObject recv,
1627: IRubyObject[] args, Block block) {
1628: Ruby runtime = recv.getRuntime();
1629: Arity.checkArgumentCount(runtime, args, 1, 2);
1630: IRubyObject cmdObj = args[0].convertToString();
1631: runtime.checkSafeString(cmdObj);
1632:
1633: try {
1634: Process process = new ShellLauncher(runtime).run(cmdObj);
1635: RubyIO io = new RubyIO(runtime, process);
1636:
1637: if (block.isGiven()) {
1638: try {
1639: block.yield(runtime.getCurrentContext(), io);
1640: return runtime.getNil();
1641: } finally {
1642: io.close();
1643: runtime
1644: .getGlobalVariables()
1645: .set(
1646: "$?",
1647: RubyProcess.RubyStatus
1648: .newProcessStatus(
1649: runtime,
1650: (process.waitFor() * 256)));
1651: }
1652: }
1653: return io;
1654: } catch (IOException e) {
1655: throw runtime.newIOErrorFromException(e);
1656: } catch (InterruptedException e) {
1657: throw runtime.newThreadError("unexpected interrupt");
1658: }
1659: }
1660:
1661: // NIO based pipe
1662: public static IRubyObject pipe(IRubyObject recv) throws Exception {
1663: Ruby runtime = recv.getRuntime();
1664: Pipe pipe = Pipe.open();
1665: return runtime.newArrayNoCopy(new IRubyObject[] {
1666: new RubyIO(runtime, pipe.source()),
1667: new RubyIO(runtime, pipe.sink()) });
1668: }
1669:
1670: /**
1671: * returns non-nil if input available without blocking, false if EOF or not open/readable, otherwise nil.
1672: */
1673: public IRubyObject ready() {
1674: try {
1675: if (!handler.isOpen() || !handler.isReadable()
1676: || handler.isEOF()) {
1677: return getRuntime().getFalse();
1678: }
1679:
1680: int avail = handler.ready();
1681: if (avail > 0) {
1682: return getRuntime().newFixnum(avail);
1683: }
1684: } catch (Exception anyEx) {
1685: return getRuntime().getFalse();
1686: }
1687: return getRuntime().getNil();
1688: }
1689:
1690: /**
1691: * waits until input available or timed out and returns self, or nil when EOF reached.
1692: */
1693: public IRubyObject io_wait() {
1694: try {
1695: if (handler.isEOF()) {
1696: return getRuntime().getNil();
1697: }
1698: handler.waitUntilReady();
1699: } catch (Exception anyEx) {
1700: return getRuntime().getNil();
1701: }
1702: return this;
1703: }
1704: }
|