KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > RubyIO


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
15  * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
16  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
17  * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
18  * Copyright (C) 2004-2006 Charles O Nutter <headius@headius.com>
19  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
20  * Copyright (C) 2006 Evan Buswell <ebuswell@gmail.com>
21  *
22  * Alternatively, the contents of this file may be used under the terms of
23  * either of the GNU General Public License Version 2 or later (the "GPL"),
24  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25  * in which case the provisions of the GPL or the LGPL are applicable instead
26  * of those above. If you wish to allow use of your version of this file only
27  * under the terms of either the GPL or the LGPL, and not to allow others to
28  * use your version of this file under the terms of the CPL, indicate your
29  * decision by deleting the provisions above and replace them with the notice
30  * and other provisions required by the GPL or the LGPL. If you do not delete
31  * the provisions above, a recipient may use your version of this file under
32  * the terms of any one of the CPL, the GPL or the LGPL.
33  ***** END LICENSE BLOCK *****/

34 package org.jruby;
35
36 import java.io.EOFException JavaDoc;
37 import java.io.FileNotFoundException JavaDoc;
38 import java.io.IOException JavaDoc;
39 import java.io.InputStream JavaDoc;
40 import java.io.OutputStream JavaDoc;
41 import java.lang.ref.WeakReference JavaDoc;
42 import java.nio.channels.Channel JavaDoc;
43 import org.jruby.runtime.Block;
44 import org.jruby.runtime.ThreadContext;
45 import org.jruby.runtime.builtin.IRubyObject;
46 import org.jruby.util.ByteList;
47 import org.jruby.util.IOHandler;
48 import org.jruby.util.IOHandlerJavaIO;
49 import org.jruby.util.IOHandlerNio;
50 import org.jruby.util.IOHandlerNull;
51 import org.jruby.util.IOHandlerProcess;
52 import org.jruby.util.IOHandlerSeekable;
53 import org.jruby.util.IOHandlerUnseekable;
54 import org.jruby.util.IOModes;
55
56 /**
57  *
58  * @author jpetersen
59  */

60 public class RubyIO extends RubyObject {
61     public static final int STDIN = 0;
62     public static final int STDOUT = 1;
63     public static final int STDERR = 2;
64     
65     protected IOHandler handler = null;
66     protected IOModes modes = null;
67     protected int lineNumber = 0;
68     
69     // Does THIS IO object think it is still open
70
// as opposed to the IO Handler which knows the
71
// actual truth. If two IO objects share the
72
// same IO Handler, then it is possible for
73
// one object to think that the handler is open
74
// when it really isn't. Keeping track of this yields
75
// the right errors.
76
protected boolean isOpen = true;
77     private boolean atEOF = false;
78
79     /*
80      * Random notes:
81      *
82      * 1. When a second IO object is created with the same fileno odd
83      * concurrency issues happen when the underlying implementation
84      * commits data. So:
85      *
86      * f = File.new("some file", "w")
87      * f.puts("heh")
88      * g = IO.new(f.fileno)
89      * g.puts("hoh")
90      * ... more operations of g and f ...
91      *
92      * Will generate a mess in "some file". The problem is that most
93      * operations are buffered. When those buffers flush and get
94      * written to the physical file depends on the implementation
95      * (semantically I would think that it should be last op wins -- but
96      * it isn't). I doubt java could mimic ruby in this way. I also
97      * doubt many people are taking advantage of this. How about
98      * syswrite/sysread though? I think the fact that sysread/syswrite
99      * are defined to be a low-level system calls, allows implementations
100      * to be somewhat different?
101      *
102      * 2. In the case of:
103      * f = File.new("some file", "w")
104      * f.puts("heh")
105      * print f.pos
106      * g = IO.new(f.fileno)
107      * print g.pos
108      * Both printed positions will be the same. But:
109      * f = File.new("some file", "w")
110      * f.puts("heh")
111      * g = IO.new(f.fileno)
112      * print f.pos, g.pos
113      * won't be the same position. Seem peculiar enough not to touch
114      * (this involves pos() actually causing a seek?)
115      *
116      * 3. All IO objects reference a IOHandler. If multiple IO objects
117      * have the same fileno, then they also share the same IOHandler.
118      * It is possible that some IO objects may share the same IOHandler
119      * but not have the same permissions. However, all subsequent IO
120      * objects created after the first must be a subset of the original
121      * IO Object (see below for an example).
122      *
123      * The idea that two or more IO objects can have different access
124      * modes means that IO objects must keep track of their own
125      * permissions. In addition the IOHandler itself must know what
126      * access modes it has.
127      *
128      * The above sharing situation only occurs in a situation like:
129      * f = File.new("some file", "r+")
130      * g = IO.new(f.fileno, "r")
131      * Where g has reduced (subset) permissions.
132      *
133      * On reopen, the fileno's IOHandler gets replaced by a new handler.
134      */

135     
136     /*
137      * I considered making all callers of this be moved into IOHandlers
138      * constructors (since it would be less error prone to forget there).
139      * However, reopen() makes doing this a little funky.
140      */

141     public void registerIOHandler(IOHandler newHandler) {
142         getRuntime().getIoHandlers().put(new Integer JavaDoc(newHandler.getFileno()), new WeakReference JavaDoc(newHandler));
143     }
144     
145     public void unregisterIOHandler(int aFileno) {
146         getRuntime().getIoHandlers().remove(new Integer JavaDoc(aFileno));
147     }
148     
149     public IOHandler getIOHandlerByFileno(int aFileno) {
150         return (IOHandler) ((WeakReference JavaDoc) getRuntime().getIoHandlers().get(new Integer JavaDoc(aFileno))).get();
151     }
152     
153     // FIXME can't use static; would interfere with other runtimes in the same JVM
154
protected static int fileno = 2;
155     
156     public static int getNewFileno() {
157         fileno++;
158         
159         return fileno;
160     }
161
162     // This should only be called by this and RubyFile.
163
// It allows this object to be created without a IOHandler.
164
public RubyIO(Ruby runtime, RubyClass type) {
165         super(runtime, type);
166     }
167
168     public RubyIO(Ruby runtime, OutputStream JavaDoc outputStream) {
169         super(runtime, runtime.getClass("IO"));
170         
171         // We only want IO objects with valid streams (better to error now).
172
if (outputStream == null) {
173             throw runtime.newIOError("Opening invalid stream");
174         }
175         
176         try {
177             handler = new IOHandlerUnseekable(runtime, null, outputStream);
178         } catch (IOException JavaDoc e) {
179             throw runtime.newIOError(e.getMessage());
180         }
181         modes = handler.getModes();
182         
183         registerIOHandler(handler);
184     }
185     
186     public RubyIO(Ruby runtime, InputStream JavaDoc inputStream) {
187         super(runtime, runtime.getClass("IO"));
188         
189         if (inputStream == null) {
190             throw runtime.newIOError("Opening invalid stream");
191         }
192         
193         try {
194             handler = new IOHandlerUnseekable(runtime, inputStream, null);
195         } catch (IOException JavaDoc e) {
196             throw runtime.newIOError(e.getMessage());
197         }
198         
199         modes = handler.getModes();
200         
201         registerIOHandler(handler);
202     }
203     
204     public RubyIO(Ruby runtime, Channel JavaDoc channel) {
205         super(runtime, runtime.getClass("IO"));
206         
207         // We only want IO objects with valid streams (better to error now).
208
if (channel == null) {
209             throw runtime.newIOError("Opening invalid stream");
210         }
211         
212         try {
213             handler = new IOHandlerNio(runtime, channel);
214         } catch (IOException JavaDoc e) {
215             throw runtime.newIOError(e.getMessage());
216         }
217         modes = handler.getModes();
218         
219         registerIOHandler(handler);
220     }
221
222     public RubyIO(Ruby runtime, Process JavaDoc process) {
223         super(runtime, runtime.getClass("IO"));
224
225         modes = new IOModes(runtime, "w+");
226
227         try {
228             handler = new IOHandlerProcess(runtime, process, modes);
229         } catch (IOException JavaDoc e) {
230             throw runtime.newIOError(e.getMessage());
231         }
232         modes = handler.getModes();
233         
234         registerIOHandler(handler);
235     }
236     
237     public RubyIO(Ruby runtime, int descriptor) {
238         super(runtime, runtime.getClass("IO"));
239
240         try {
241             handler = new IOHandlerUnseekable(runtime, descriptor);
242         } catch (IOException JavaDoc e) {
243             throw runtime.newIOError(e.getMessage());
244         }
245         modes = handler.getModes();
246         
247         registerIOHandler(handler);
248     }
249     
250     /**
251      * <p>Open a file descriptor, unless it is already open, then return
252      * it.</p>
253      */

254     public static IRubyObject fdOpen(Ruby runtime, int descriptor) {
255         return new RubyIO(runtime, descriptor);
256     }
257
258     /*
259      * See checkReadable for commentary.
260      */

261     protected void checkWriteable() {
262         if (!isOpen() || !modes.isWriteable()) {
263             throw getRuntime().newIOError("not opened for writing");
264         }
265     }
266
267     /*
268      * What the IO object "thinks" it can do. If two IO objects share
269      * the same fileno (IOHandler), then it is possible for one to pull
270      * the rug out from the other. This will make the second object still
271      * "think" that the file is open. Secondly, if two IO objects share
272      * the same fileno, but the second one only has a subset of the access
273      * permissions, then it will "think" that it cannot do certain
274      * operations.
275      */

276     protected void checkReadable() {
277         if (!isOpen() || !modes.isReadable()) {
278             throw getRuntime().newIOError("not opened for reading");
279         }
280     }
281     
282     public boolean isOpen() {
283         return isOpen;
284     }
285
286     public OutputStream JavaDoc getOutStream() {
287         if(handler instanceof IOHandlerJavaIO) {
288             return ((IOHandlerJavaIO) handler).getOutputStream();
289         } else {
290             return null;
291         }
292     }
293
294     public InputStream JavaDoc getInStream() {
295         if (handler instanceof IOHandlerJavaIO) {
296             return ((IOHandlerJavaIO) handler).getInputStream();
297         } else {
298             return null;
299         }
300     }
301
302     public Channel JavaDoc getChannel() {
303         if (handler instanceof IOHandlerNio) {
304             return ((IOHandlerNio) handler).getChannel();
305         } else {
306             return null;
307         }
308     }
309
310     public IRubyObject reopen(IRubyObject[] args) {
311         if (args.length < 1) {
312             throw getRuntime().newArgumentError("wrong number of arguments");
313         }
314         
315         if (args[0].isKindOf(getRuntime().getClass("IO"))) {
316             RubyIO ios = (RubyIO) args[0];
317
318             int keepFileno = handler.getFileno();
319             
320             // close the old handler before it gets overwritten
321
if (handler.isOpen()) {
322                 try {
323                     handler.close();
324                 } catch (IOHandler.BadDescriptorException e) {
325                     throw getRuntime().newErrnoEBADFError();
326                 } catch (EOFException JavaDoc e) {
327                     return getRuntime().getNil();
328                 } catch (IOException JavaDoc e) {
329                     throw getRuntime().newIOError(e.getMessage());
330                 }
331             }
332
333             // When we reopen, we want our fileno to be preserved even
334
// though we have a new IOHandler.
335
// Note: When we clone we get a new fileno...then we replace it.
336
// This ends up incrementing our fileno index up, which makes the
337
// fileno we choose different from ruby. Since this seems a bit
338
// too implementation specific, I did not bother trying to get
339
// these to agree (what scary code would depend on fileno generating
340
// a particular way?)
341
try {
342                 handler = ios.handler.cloneIOHandler();
343             } catch (IOHandler.InvalidValueException e) {
344                 throw getRuntime().newErrnoEINVALError();
345             } catch (IOHandler.PipeException e) {
346                 throw getRuntime().newErrnoESPIPEError();
347             } catch (FileNotFoundException JavaDoc e) {
348                 throw getRuntime().newErrnoENOENTError();
349             } catch (IOException JavaDoc e) {
350                 throw getRuntime().newIOError(e.getMessage());
351             }
352             handler.setFileno(keepFileno);
353
354             // Update fileno list with our new handler
355
registerIOHandler(handler);
356         } else if (args[0].isKindOf(getRuntime().getString())) {
357             String JavaDoc path = ((RubyString) args[0]).toString();
358             IOModes newModes = null;
359
360             if (args.length > 1) {
361                 if (!args[1].isKindOf(getRuntime().getString())) {
362                     throw getRuntime().newTypeError(args[1], getRuntime().getString());
363                 }
364                     
365                 newModes = new IOModes(getRuntime(), ((RubyString) args[1]).toString());
366             }
367
368             try {
369                 if (handler != null) {
370                     close();
371                 }
372
373                 if (newModes != null) {
374                     modes = newModes;
375                 }
376                 if ("/dev/null".equals(path)) {
377                     handler = new IOHandlerNull(getRuntime(), modes);
378                 } else {
379                     handler = new IOHandlerSeekable(getRuntime(), path, modes);
380                 }
381                 
382                 registerIOHandler(handler);
383             } catch (IOHandler.InvalidValueException e) {
384                 throw getRuntime().newErrnoEINVALError();
385             } catch (IOException JavaDoc e) {
386                 throw getRuntime().newIOError(e.toString());
387             }
388         }
389         
390         // A potentially previously close IO is being 'reopened'.
391
isOpen = true;
392         return this;
393     }
394     /** Read a line.
395      *
396      */

397     // TODO: Most things loop over this and always pass it the same arguments
398
// meaning they are an invariant of the loop. Think about fixing this.
399
public IRubyObject internalGets(IRubyObject[] args) {
400         checkReadable();
401
402         IRubyObject sepVal = getRuntime().getGlobalVariables().get("$/");
403
404         if (args.length > 0) {
405             sepVal = args[0];
406         }
407
408         
409         ByteList separator = sepVal.isNil() ? null : ((RubyString) sepVal).getByteList();
410
411         if (separator != null && separator.realSize == 0) {
412             separator = IOHandler.PARAGRAPH_DELIMETER;
413         }
414
415         try {
416             ByteList newLine = handler.gets(separator);
417
418             if (newLine != null) {
419                 lineNumber++;
420                 getRuntime().getGlobalVariables().set("$.", getRuntime().newFixnum(lineNumber));
421                 RubyString result = RubyString.newString(getRuntime(), newLine);
422                 result.taint();
423                 
424                 return result;
425             }
426             
427             return getRuntime().getNil();
428         } catch (IOHandler.BadDescriptorException e) {
429             throw getRuntime().newErrnoEBADFError();
430         } catch (IOException JavaDoc e) {
431             throw getRuntime().newIOError(e.getMessage());
432         }
433     }
434
435     // IO class methods.
436

437     public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
438         int count = checkArgumentCount(args, 1, 2);
439         int newFileno = RubyNumeric.fix2int(args[0]);
440         String JavaDoc mode = null;
441         
442         if (count > 1) {
443             mode = args[1].convertToString().toString();
444         }
445
446         // See if we already have this descriptor open.
447
// If so then we can mostly share the handler (keep open
448
// file, but possibly change the mode).
449
IOHandler existingIOHandler = getIOHandlerByFileno(newFileno);
450         
451         if (existingIOHandler == null) {
452             if (mode == null) {
453                 mode = "r";
454             }
455             
456             try {
457                 handler = new IOHandlerUnseekable(getRuntime(), newFileno, mode);
458             } catch (IOException JavaDoc e) {
459                 throw getRuntime().newIOError(e.getMessage());
460             }
461             modes = new IOModes(getRuntime(), mode);
462             
463             registerIOHandler(handler);
464         } else {
465             // We are creating a new IO object that shares the same
466
// IOHandler (and fileno).
467
handler = existingIOHandler;
468             
469             // Inherit if no mode specified otherwise create new one
470
modes = mode == null ? handler.getModes() :
471                 new IOModes(getRuntime(), mode);
472
473             // Reset file based on modes.
474
try {
475                 handler.reset(modes);
476             } catch (IOHandler.InvalidValueException e) {
477                 throw getRuntime().newErrnoEINVALError();
478             } catch (IOException JavaDoc e) {
479                 throw getRuntime().newIOError(e.getMessage());
480             }
481         }
482         
483         return this;
484     }
485     
486     // This appears to be some windows-only mode. On a java platform this is a no-op
487
public IRubyObject binmode() {
488         return this;
489     }
490
491     public IRubyObject syswrite(IRubyObject obj) {
492         try {
493             if (obj instanceof RubyString) {
494                 return getRuntime().newFixnum(handler.syswrite(((RubyString)obj).getByteList()));
495             } else {
496                 // FIXME: unlikely to be efficient, but probably correct
497
return getRuntime().newFixnum(
498                         handler.syswrite(
499                         ((RubyString)obj.callMethod(
500                         obj.getRuntime().getCurrentContext(), "to_s")).getByteList()));
501             }
502         } catch (IOHandler.BadDescriptorException e) {
503             throw getRuntime().newErrnoEBADFError();
504         } catch (IOException JavaDoc e) {
505             throw getRuntime().newSystemCallError(e.getMessage());
506         }
507     }
508     
509     /** io_write
510      *
511      */

512     public IRubyObject write(IRubyObject obj) {
513         checkWriteable();
514
515         try {
516             if (obj instanceof RubyString) {
517                 return getRuntime().newFixnum(handler.write(((RubyString)obj).getByteList()));
518             } else {
519                 // FIXME: unlikely to be efficient, but probably correct
520
return getRuntime().newFixnum(
521                         handler.write(
522                         ((RubyString)obj.callMethod(
523                         obj.getRuntime().getCurrentContext(), "to_s")).getByteList()));
524             }
525         } catch (IOHandler.BadDescriptorException e) {
526             return RubyFixnum.zero(getRuntime());
527         } catch (IOException JavaDoc e) {
528             return RubyFixnum.zero(getRuntime());
529         }
530     }
531
532     /** rb_io_addstr
533      *
534      */

535     public IRubyObject addString(IRubyObject anObject) {
536         // Claims conversion is done via 'to_s' in docs.
537
IRubyObject strObject = anObject.callMethod(getRuntime().getCurrentContext(), "to_s");
538
539         write(strObject);
540         
541         return this;
542     }
543
544     public RubyFixnum fileno() {
545         return getRuntime().newFixnum(handler.getFileno());
546     }
547     
548     /** Returns the current line number.
549      *
550      * @return the current line number.
551      */

552     public RubyFixnum lineno() {
553         return getRuntime().newFixnum(lineNumber);
554     }
555
556     /** Sets the current line number.
557      *
558      * @param newLineNumber The new line number.
559      */

560     public RubyFixnum lineno_set(IRubyObject newLineNumber) {
561         lineNumber = RubyNumeric.fix2int(newLineNumber);
562
563         return (RubyFixnum) newLineNumber;
564     }
565
566     /** Returns the current sync mode.
567      *
568      * @return the current sync mode.
569      */

570     public RubyBoolean sync() {
571         return getRuntime().newBoolean(handler.isSync());
572     }
573     
574     /**
575      * <p>Return the process id (pid) of the process this IO object
576      * spawned. If no process exists (popen was not called), then
577      * nil is returned. This is not how it appears to be defined
578      * but ruby 1.8 works this way.</p>
579      *
580      * @return the pid or nil
581      */

582     public IRubyObject pid() {
583         int pid = handler.pid();
584         
585         if (pid == -1) {
586             return getRuntime().getNil();
587         }
588         
589         return getRuntime().newFixnum(pid);
590     }
591     
592     public RubyFixnum pos() {
593         try {
594             return getRuntime().newFixnum(handler.pos());
595         } catch (IOHandler.PipeException e) {
596             throw getRuntime().newErrnoESPIPEError();
597         } catch (IOException JavaDoc e) {
598             throw getRuntime().newIOError(e.getMessage());
599         }
600     }
601     
602     public RubyFixnum pos_set(IRubyObject newPosition) {
603         long offset = RubyNumeric.fix2long(newPosition);
604
605         if (offset < 0) {
606             throw getRuntime().newSystemCallError("Negative seek offset");
607         }
608         
609         try {
610             handler.seek(offset, IOHandler.SEEK_SET);
611         } catch (IOHandler.InvalidValueException e) {
612             throw getRuntime().newErrnoEINVALError();
613         } catch (IOHandler.PipeException e) {
614             throw getRuntime().newErrnoESPIPEError();
615         } catch (IOException JavaDoc e) {
616             throw getRuntime().newIOError(e.getMessage());
617         }
618         
619         return (RubyFixnum) newPosition;
620     }
621     
622     /** Print some objects to the stream.
623      *
624      */

625     public IRubyObject print(IRubyObject[] args) {
626         if (args.length == 0) {
627             args = new IRubyObject[] { getRuntime().getCurrentContext().getLastline() };
628         }
629
630         IRubyObject fs = getRuntime().getGlobalVariables().get("$,");
631         IRubyObject rs = getRuntime().getGlobalVariables().get("$\\");
632         ThreadContext context = getRuntime().getCurrentContext();
633         
634         for (int i = 0; i < args.length; i++) {
635             if (i > 0 && !fs.isNil()) {
636                 callMethod(context, "write", fs);
637             }
638             if (args[i].isNil()) {
639                 callMethod(context, "write", getRuntime().newString("nil"));
640             } else {
641                 callMethod(context, "write", args[i]);
642             }
643         }
644         if (!rs.isNil()) {
645             callMethod(context, "write", rs);
646         }
647
648         return getRuntime().getNil();
649     }
650
651     public IRubyObject printf(IRubyObject[] args) {
652         checkArgumentCount(args, 1, -1);
653         callMethod(getRuntime().getCurrentContext(), "write", RubyKernel.sprintf(this, args));
654         return getRuntime().getNil();
655     }
656     
657     public IRubyObject putc(IRubyObject object) {
658         int c;
659         
660         if (object.isKindOf(getRuntime().getString())) {
661             String JavaDoc value = ((RubyString) object).toString();
662             
663             if (value.length() > 0) {
664                 c = value.charAt(0);
665             } else {
666                 throw getRuntime().newTypeError(
667                         "Cannot convert String to Integer");
668             }
669         } else if (object.isKindOf(getRuntime().getFixnum())){
670             c = RubyNumeric.fix2int(object);
671         } else { // What case will this work for?
672
c = RubyNumeric.fix2int(object.callMethod(getRuntime().getCurrentContext(), "to_i"));
673         }
674
675         try {
676             handler.putc(c);
677         } catch (IOHandler.BadDescriptorException e) {
678             return RubyFixnum.zero(getRuntime());
679         } catch (IOException JavaDoc e) {
680             return RubyFixnum.zero(getRuntime());
681         }
682         
683         return getRuntime().newFixnum(c);
684     }
685     
686     // This was a getOpt with one mandatory arg, but it did not work
687
// so I am parsing it for now.
688
public RubyFixnum seek(IRubyObject[] args) {
689         if (args.length == 0) {
690             throw getRuntime().newArgumentError("wrong number of arguments");
691         }
692         
693         long offset = RubyNumeric.fix2long(args[0]);
694         int type = IOHandler.SEEK_SET;
695         
696         if (args.length > 1) {
697             type = RubyNumeric.fix2int(args[1].convertToInteger());
698         }
699         
700         try {
701             handler.seek(offset, type);
702         } catch (IOHandler.InvalidValueException e) {
703             throw getRuntime().newErrnoEINVALError();
704         } catch (IOHandler.PipeException e) {
705             throw getRuntime().newErrnoESPIPEError();
706         } catch (IOException JavaDoc e) {
707             throw getRuntime().newIOError(e.getMessage());
708         }
709         
710         return RubyFixnum.zero(getRuntime());
711     }
712
713     public RubyFixnum rewind() {
714         try {
715             handler.rewind();
716         } catch (IOHandler.InvalidValueException e) {
717             throw getRuntime().newErrnoEINVALError();
718         } catch (IOHandler.PipeException e) {
719             throw getRuntime().newErrnoESPIPEError();
720         } catch (IOException JavaDoc e) {
721             throw getRuntime().newIOError(e.getMessage());
722         }
723
724         // Must be back on first line on rewind.
725
lineNumber = 0;
726         
727         return RubyFixnum.zero(getRuntime());
728     }
729     
730     public RubyFixnum fsync() {
731         checkWriteable();
732
733         try {
734             handler.sync();
735         } catch (IOException JavaDoc e) {
736             throw getRuntime().newIOError(e.getMessage());
737         } catch (IOHandler.BadDescriptorException e) {
738             throw getRuntime().newErrnoEBADFError();
739         }
740
741         return RubyFixnum.zero(getRuntime());
742     }
743
744     /** Sets the current sync mode.
745      *
746      * @param newSync The new sync mode.
747      */

748     public IRubyObject sync_set(IRubyObject newSync) {
749         handler.setIsSync(newSync.isTrue());
750
751         return this;
752     }
753
754     public RubyBoolean eof() {
755         try {
756             boolean isEOF = handler.isEOF();
757             return isEOF ? getRuntime().getTrue() : getRuntime().getFalse();
758         } catch (IOHandler.BadDescriptorException e) {
759             throw getRuntime().newErrnoEBADFError();
760         } catch (IOException JavaDoc e) {
761             throw getRuntime().newIOError(e.getMessage());
762         }
763     }
764
765     public RubyBoolean tty() {
766         // TODO: this is less than ideal but might be as close as we'll get
767
int fileno = handler.getFileno();
768         if (fileno == STDOUT || fileno == STDIN || fileno == STDERR) {
769             return getRuntime().getTrue();
770         } else {
771             return getRuntime().getFalse();
772         }
773     }
774     
775     public IRubyObject initialize_copy(IRubyObject original){
776         if (this == original) return this;
777
778         RubyIO originalIO = (RubyIO) original;
779         
780         // Two pos pointers?
781
// http://blade.nagaokaut.ac.jp/ruby/ruby-talk/81513
782
// So if I understand this correctly, the descriptor level stuff
783
// shares things like position, but the higher level stuff uses
784
// a different set of libc functions (FILE*), which does not share
785
// position. Our current implementation implements our higher
786
// level operations on top of our 'sys' versions. So we could in
787
// fact share everything. Unfortunately, we want to clone ruby's
788
// behavior (i.e. show how this interface bleeds their
789
// implementation). So our best bet, is to just create a yet another
790
// copy of the handler. In fact, ruby 1.8 must do this as the cloned
791
// resource is in fact a different fileno. What is clone for again?
792

793         handler = originalIO.handler;
794         modes = (IOModes) originalIO.modes.clone();
795         
796         return this;
797     }
798     
799     /** Closes the IO.
800      *
801      * @return The IO.
802      */

803     public RubyBoolean closed() {
804         return isOpen() ? getRuntime().getFalse() :
805             getRuntime().getTrue();
806     }
807
808     /**
809      * <p>Closes all open resources for the IO. It also removes
810      * it from our magical all open file descriptor pool.</p>
811      *
812      * @return The IO.
813      */

814     public IRubyObject close() {
815         isOpen = false;
816         
817         try {
818             handler.close();
819         } catch (IOHandler.BadDescriptorException e) {
820             throw getRuntime().newErrnoEBADFError();
821         } catch (IOException JavaDoc e) {
822             throw getRuntime().newIOError(e.getMessage());
823         }
824         
825         unregisterIOHandler(handler.getFileno());
826         
827         return this;
828     }
829
830     /** Flushes the IO output stream.
831      *
832      * @return The IO.
833      */

834     public RubyIO flush() {
835         try {
836             handler.flush();
837         } catch (IOHandler.BadDescriptorException e) {
838             throw getRuntime().newErrnoEBADFError();
839         } catch (IOException JavaDoc e) {
840             throw getRuntime().newIOError(e.getMessage());
841         }
842
843         return this;
844     }
845
846     /** Read a line.
847      *
848      */

849     public IRubyObject gets(IRubyObject[] args) {
850         IRubyObject result = internalGets(args);
851
852         if (!result.isNil()) {
853             getRuntime().getCurrentContext().setLastline(result);
854         }
855
856         return result;
857     }
858
859     public boolean getBlocking() {
860         if (!(handler instanceof IOHandlerNio)) {
861             return true;
862         }
863
864         return ((IOHandlerNio) handler).getBlocking();
865      }
866
867      public IRubyObject fcntl(IRubyObject cmd, IRubyObject arg) throws IOException JavaDoc {
868         long realCmd = cmd.convertToInteger().getLongValue();
869         
870         // FIXME: Arg may also be true, false, and nil and still be valid. Strangely enough,
871
// protocol conversion is not happening in Ruby on this arg?
872
if (!(arg instanceof RubyNumeric)) {
873             return getRuntime().newFixnum(0);
874         }
875         
876         long realArg = ((RubyNumeric)arg).getLongValue();
877
878         // Fixme: Only F_SETFL is current supported
879
if (realCmd == 1L) { // cmd is F_SETFL
880
boolean block = true;
881             
882             if((realArg & IOModes.NONBLOCK) == IOModes.NONBLOCK) {
883                 block = false;
884             }
885             
886         if(!(handler instanceof IOHandlerNio)) {
887         // cryptic for the uninitiated...
888
throw getRuntime().newNotImplementedError("FCNTL only works with Nio based handlers");
889         }
890     
891         try {
892                 ((IOHandlerNio) handler).setBlocking(block);
893             } catch (IOException JavaDoc e) {
894                 throw getRuntime().newIOError(e.getMessage());
895             }
896         }
897         
898         return getRuntime().newFixnum(0);
899     }
900
901     public IRubyObject puts(IRubyObject[] args) {
902         checkArgumentCount(args, 0, -1);
903         
904         ThreadContext context = getRuntime().getCurrentContext();
905         
906         if (args.length == 0) {
907             callMethod(context, "write", getRuntime().newString("\n"));
908             return getRuntime().getNil();
909         }
910
911         for (int i = 0; i < args.length; i++) {
912             String JavaDoc line = null;
913             if (args[i].isNil()) {
914                 line = "nil";
915             } else if (args[i] instanceof RubyArray) {
916                 puts(((RubyArray) args[i]).toJavaArray());
917                 continue;
918             } else {
919                 line = args[i].toString();
920             }
921             callMethod(getRuntime().getCurrentContext(), "write", getRuntime().newString(line+
922                     (line.endsWith("\n") ? "" : "\n")));
923         }
924         return getRuntime().getNil();
925     }
926
927     /** Read a line.
928      *
929      */

930     public IRubyObject readline(IRubyObject[] args) {
931         IRubyObject line = gets(args);
932
933         if (line.isNil()) {
934             throw getRuntime().newEOFError();
935         }
936         
937         return line;
938     }
939
940     /** Read a byte. On EOF returns nil.
941      *
942      */

943     public IRubyObject getc() {
944         checkReadable();
945         
946         try {
947             int c = handler.getc();
948         
949             return c == -1 ? getRuntime().getNil() : getRuntime().newFixnum(c);
950         } catch (IOHandler.BadDescriptorException e) {
951             throw getRuntime().newErrnoEBADFError();
952         } catch (EOFException JavaDoc e) {
953             return getRuntime().getNil();
954         } catch (IOException JavaDoc e) {
955             throw getRuntime().newIOError(e.getMessage());
956         }
957     }
958     
959     /**
960      * <p>Pushes char represented by int back onto IOS.</p>
961      *
962      * @param number to push back
963      */

964     public IRubyObject ungetc(IRubyObject number) {
965         handler.ungetc(RubyNumeric.fix2int(number));
966
967         return getRuntime().getNil();
968     }
969     
970     public IRubyObject readpartial(IRubyObject[] args) {
971         if(!(handler instanceof IOHandlerNio)) {
972             // cryptic for the uninitiated...
973
throw getRuntime().newNotImplementedError("readpartial only works with Nio based handlers");
974         }
975         try {
976             ByteList buf = ((IOHandlerNio)handler).readpartial(RubyNumeric.fix2int(args[0]));
977             IRubyObject strbuf = RubyString.newString(getRuntime(), buf == null ? new ByteList(ByteList.NULL_ARRAY) : buf);
978             if(args.length > 1) {
979                 args[1].callMethod(getRuntime().getCurrentContext(),"<<", strbuf);
980                 return args[1];
981             }
982
983             return strbuf;
984         } catch (IOHandler.BadDescriptorException e) {
985             throw getRuntime().newErrnoEBADFError();
986         } catch (EOFException JavaDoc e) {
987             return getRuntime().getNil();
988         } catch (IOException JavaDoc e) {
989             throw getRuntime().newIOError(e.getMessage());
990         }
991     }
992
993     public IRubyObject sysread(IRubyObject number) {
994         try {
995             ByteList buf = handler.sysread(RubyNumeric.fix2int(number));
996         
997             return RubyString.newString(getRuntime(), buf);
998         } catch (IOHandler.BadDescriptorException e) {
999             throw getRuntime().newErrnoEBADFError();
1000        } catch (EOFException JavaDoc e) {
1001            throw getRuntime().newEOFError();
1002        } catch (IOException JavaDoc e) {
1003            // All errors to sysread should be SystemCallErrors, but on a closed stream
1004
// Ruby returns an IOError. Java throws same exception for all errors so
1005
// we resort to this hack...
1006
if ("File not open".equals(e.getMessage())) {
1007                throw getRuntime().newIOError(e.getMessage());
1008            }
1009            throw getRuntime().newSystemCallError(e.getMessage());
1010        }
1011    }
1012    
1013    public IRubyObject read(IRubyObject[] args) {
1014               
1015        int argCount = checkArgumentCount(args, 0, 2);
1016        RubyString callerBuffer = null;
1017        boolean readEntireStream = (argCount == 0 || args[0].isNil());
1018
1019        try {
1020            // Reads when already at EOF keep us at EOF
1021
// We do retain the possibility of un-EOFing if the handler
1022
// gets new data
1023
if (atEOF && handler.isEOF()) {
1024                throw new EOFException JavaDoc();
1025            }
1026
1027            if (argCount == 2) {
1028                // rdocs say the second arg must be a String if present,
1029
// it can't just walk and quack like one
1030
if (!(args[1] instanceof RubyString)) {
1031                    getRuntime().newTypeError(args[1], getRuntime().getString());
1032                }
1033                callerBuffer = (RubyString) args[1];
1034            }
1035
1036            ByteList buf = readEntireStream ? handler.getsEntireStream() : handler.read(RubyNumeric
1037                    .fix2int(args[0]));
1038            
1039            if (buf == null) {
1040                throw new EOFException JavaDoc();
1041            }
1042
1043            // If we get here then no EOFException was thrown in the handler. We
1044
// might still need to set our atEOF flag back to true depending on
1045
// whether we were reading the entire stream (see the finally block below)
1046
atEOF = false;
1047            if (callerBuffer != null) {
1048                callerBuffer.setValue(buf);
1049                return callerBuffer;
1050            }
1051            
1052            return RubyString.newString(getRuntime(), buf);
1053            
1054        } catch (IOHandler.BadDescriptorException e) {
1055            throw getRuntime().newErrnoEBADFError();
1056        } catch (EOFException JavaDoc e) {
1057            // on EOF, IO#read():
1058
// with no args or a nil first arg will return an empty string
1059
// with a non-nil first arg will return nil
1060
atEOF = true;
1061            if (callerBuffer != null) {
1062                callerBuffer.setValue("");
1063                return readEntireStream ? callerBuffer : getRuntime().getNil();
1064            }
1065
1066            return readEntireStream ? getRuntime().newString("") : getRuntime().getNil();
1067        } catch (IOException JavaDoc e) {
1068            throw getRuntime().newIOError(e.getMessage());
1069        } finally {
1070            // reading the entire stream always puts us at EOF
1071
if (readEntireStream) {
1072                atEOF = true;
1073            }
1074        }
1075    }
1076
1077    /** Read a byte. On EOF throw EOFError.
1078     *
1079     */

1080    public IRubyObject readchar() {
1081        checkReadable();
1082        
1083        try {
1084            int c = handler.getc();
1085        
1086            if (c == -1) {
1087                throw getRuntime().newEOFError();
1088            }
1089        
1090            return getRuntime().newFixnum(c);
1091        } catch (IOHandler.BadDescriptorException e) {
1092            throw getRuntime().newErrnoEBADFError();
1093        } catch (EOFException JavaDoc e) {
1094            throw getRuntime().newEOFError();
1095        } catch (IOException JavaDoc e) {
1096            throw getRuntime().newIOError(e.getMessage());
1097        }
1098    }
1099
1100    /**
1101     * <p>Invoke a block for each byte.</p>
1102     */

1103    public IRubyObject each_byte(Block block) {
1104        try {
1105            ThreadContext context = getRuntime().getCurrentContext();
1106            for (int c = handler.getc(); c != -1; c = handler.getc()) {
1107                assert c < 256;
1108                context.yield(getRuntime().newFixnum(c), block);
1109            }
1110
1111            return getRuntime().getNil();
1112        } catch (IOHandler.BadDescriptorException e) {
1113            throw getRuntime().newErrnoEBADFError();
1114        } catch (EOFException JavaDoc e) {
1115            return getRuntime().getNil();
1116        } catch (IOException JavaDoc e) {
1117            throw getRuntime().newIOError(e.getMessage());
1118        }
1119    }
1120
1121    /**
1122     * <p>Invoke a block for each line.</p>
1123     */

1124    public RubyIO each_line(IRubyObject[] args, Block block) {
1125        ThreadContext context = getRuntime().getCurrentContext();
1126        for (IRubyObject line = internalGets(args); !line.isNil();
1127            line = internalGets(args)) {
1128            context.yield(line, block);
1129        }
1130        
1131        return this;
1132    }
1133
1134
1135    public RubyArray readlines(IRubyObject[] args) {
1136        IRubyObject[] separatorArgument;
1137        if (args.length > 0) {
1138            if (!args[0].isKindOf(getRuntime().getNilClass()) &&
1139                !args[0].isKindOf(getRuntime().getString())) {
1140                throw getRuntime().newTypeError(args[0],
1141                        getRuntime().getString());
1142            }
1143            separatorArgument = new IRubyObject[] { args[0] };
1144        } else {
1145            separatorArgument = IRubyObject.NULL_ARRAY;
1146        }
1147
1148        RubyArray result = getRuntime().newArray();
1149        IRubyObject line;
1150        while (! (line = internalGets(separatorArgument)).isNil()) {
1151            result.append(line);
1152        }
1153        return result;
1154    }
1155    
1156    public RubyIO to_io() {
1157        return this;
1158    }
1159
1160    public String JavaDoc toString() {
1161        return "RubyIO(" + modes + ", " + fileno + ")";
1162    }
1163}
1164
Popular Tags