001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
015: * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
016: * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
017: * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
018: * Copyright (C) 2007 Ola Bini <ola@ologix.com>
019: *
020: * Alternatively, the contents of this file may be used under the terms of
021: * either of the GNU General Public License Version 2 or later (the "GPL"),
022: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
023: * in which case the provisions of the GPL or the LGPL are applicable instead
024: * of those above. If you wish to allow use of your version of this file only
025: * under the terms of either the GPL or the LGPL, and not to allow others to
026: * use your version of this file under the terms of the CPL, indicate your
027: * decision by deleting the provisions above and replace them with the notice
028: * and other provisions required by the GPL or the LGPL. If you do not delete
029: * the provisions above, a recipient may use your version of this file under
030: * the terms of any one of the CPL, the GPL or the LGPL.
031: ***** END LICENSE BLOCK *****/package org.jruby;
032:
033: import org.jruby.runtime.Arity;
034: import org.jruby.runtime.Block;
035: import org.jruby.runtime.CallbackFactory;
036: import org.jruby.runtime.ThreadContext;
037: import org.jruby.runtime.builtin.IRubyObject;
038:
039: public class RubyArgsFile extends RubyObject {
040:
041: public RubyArgsFile(Ruby runtime) {
042: super (runtime, runtime.getObject());
043: }
044:
045: private IRubyObject currentFile = null;
046: private int currentLineNumber;
047:
048: public void setCurrentLineNumber(int newLineNumber) {
049: this .currentLineNumber = newLineNumber;
050: }
051:
052: public void initArgsFile() {
053: getRuntime().getModule("Enumerable").extend_object(this );
054:
055: getRuntime().defineReadonlyVariable("$<", this );
056: getRuntime().defineGlobalConstant("ARGF", this );
057:
058: CallbackFactory callbackFactory = getRuntime().callbackFactory(
059: RubyArgsFile.class);
060:
061: getMetaClass().defineFastMethod("read",
062: callbackFactory.getFastOptMethod("read"));
063: getMetaClass().defineFastMethod("getc",
064: callbackFactory.getFastMethod("getc"));
065: getMetaClass().defineFastMethod("readchar",
066: callbackFactory.getFastMethod("readchar"));
067: getMetaClass().defineFastMethod("seek",
068: callbackFactory.getFastOptMethod("seek"));
069: getMetaClass().defineFastMethod(
070: "pos=",
071: callbackFactory.getFastMethod("set_pos",
072: IRubyObject.class));
073: getMetaClass().defineFastMethod("tell",
074: callbackFactory.getFastMethod("tell"));
075: getMetaClass().defineFastMethod("pos",
076: callbackFactory.getFastMethod("tell"));
077:
078: getMetaClass().defineFastMethod("rewind",
079: callbackFactory.getFastMethod("rewind"));
080:
081: getMetaClass().defineFastMethod("eof",
082: callbackFactory.getFastMethod("eof"));
083: getMetaClass().defineFastMethod("eof?",
084: callbackFactory.getFastMethod("eof"));
085: getMetaClass().defineFastMethod("binmode",
086: callbackFactory.getFastMethod("binmode"));
087:
088: getMetaClass().defineFastMethod("fileno",
089: callbackFactory.getFastMethod("fileno"));
090: getMetaClass().defineFastMethod("to_i",
091: callbackFactory.getFastMethod("fileno"));
092: getMetaClass().defineFastMethod("to_io",
093: callbackFactory.getFastMethod("to_io"));
094:
095: getMetaClass().defineMethod("each_byte",
096: callbackFactory.getMethod("each_byte"));
097: getMetaClass().defineMethod("each",
098: callbackFactory.getOptMethod("each_line"));
099: getMetaClass().defineMethod("each_line",
100: callbackFactory.getOptMethod("each_line"));
101:
102: getMetaClass().defineFastMethod("path",
103: callbackFactory.getFastMethod("filename"));
104: getMetaClass().defineFastMethod("filename",
105: callbackFactory.getFastMethod("filename"));
106: getMetaClass().defineFastMethod("file",
107: callbackFactory.getFastMethod("file"));
108: getMetaClass().defineFastMethod("skip",
109: callbackFactory.getFastMethod("skip"));
110: getMetaClass().defineFastMethod("close",
111: callbackFactory.getFastMethod("close_m"));
112: getMetaClass().defineFastMethod("closed?",
113: callbackFactory.getFastMethod("closed_p"));
114: getMetaClass().defineFastMethod("gets",
115: callbackFactory.getFastOptMethod("gets"));
116: getMetaClass().defineFastMethod("readline",
117: callbackFactory.getFastOptMethod("readline"));
118: getMetaClass().defineFastMethod("readlines",
119: callbackFactory.getFastOptMethod("readlines"));
120:
121: getMetaClass().defineFastMethod("lineno",
122: callbackFactory.getFastMethod("lineno"));
123: getMetaClass().defineFastMethod(
124: "lineno=",
125: callbackFactory.getFastMethod("set_lineno",
126: IRubyObject.class));
127: getMetaClass().defineFastMethod("to_a",
128: callbackFactory.getFastOptMethod("readlines"));
129: getMetaClass().defineFastMethod("to_s",
130: callbackFactory.getFastMethod("to_s"));
131:
132: getRuntime().defineReadonlyVariable("$FILENAME",
133: getRuntime().newString("-"));
134:
135: // This is ugly. nextArgsFile both checks existence of another
136: // file and the setup of any files. On top of that it handles
137: // the logic for figuring out stdin versus a list of files.
138: // I hacked this to make a null currentFile indicate that things
139: // have not been set up yet. This seems fragile, but it at least
140: // it passes tests now.
141: // currentFile = (RubyIO) getRuntime().getGlobalVariables().get("$stdin");
142: }
143:
144: protected boolean nextArgsFile() {
145: RubyArray args = (RubyArray) getRuntime().getGlobalVariables()
146: .get("$*");
147:
148: if (args.getLength() == 0) {
149: if (currentFile == null) {
150: currentFile = getRuntime().getGlobalVariables().get(
151: "$stdin");
152: ((RubyString) getRuntime().getGlobalVariables().get(
153: "$FILENAME")).setValue(new StringBuffer("-"));
154: currentLineNumber = 0;
155: return true;
156: }
157:
158: return false;
159: }
160:
161: String filename = ((RubyString) args.shift()).toString();
162: ((RubyString) getRuntime().getGlobalVariables()
163: .get("$FILENAME")).setValue(new StringBuffer(filename));
164:
165: if (filename.equals("-")) {
166: currentFile = getRuntime().getGlobalVariables().get(
167: "$stdin");
168: } else {
169: currentFile = new RubyFile(getRuntime(), filename);
170: }
171:
172: return true;
173: }
174:
175: public IRubyObject fileno() {
176: if (currentFile == null && !nextArgsFile()) {
177: throw getRuntime().newArgumentError("no stream");
178: }
179: return ((RubyIO) currentFile).fileno();
180: }
181:
182: public IRubyObject to_io() {
183: if (currentFile == null && !nextArgsFile()) {
184: throw getRuntime().newArgumentError("no stream");
185: }
186: return currentFile;
187: }
188:
189: public IRubyObject internalGets(IRubyObject[] args) {
190: if (currentFile == null && !nextArgsFile()) {
191: return getRuntime().getNil();
192: }
193:
194: ThreadContext context = getRuntime().getCurrentContext();
195:
196: IRubyObject line = currentFile
197: .callMethod(context, "gets", args);
198:
199: while (line instanceof RubyNil) {
200: currentFile.callMethod(context, "close");
201: if (!nextArgsFile()) {
202: currentFile = null;
203: return line;
204: }
205: line = currentFile.callMethod(context, "gets", args);
206: }
207:
208: currentLineNumber++;
209: getRuntime().getGlobalVariables().set("$.",
210: getRuntime().newFixnum(currentLineNumber));
211:
212: return line;
213: }
214:
215: // ARGF methods
216:
217: /** Read a line.
218: *
219: */
220: public IRubyObject gets(IRubyObject[] args) {
221: IRubyObject result = internalGets(args);
222:
223: if (!result.isNil()) {
224: getRuntime().getCurrentContext().setLastline(result);
225: }
226:
227: return result;
228: }
229:
230: /** Read a line.
231: *
232: */
233: public IRubyObject readline(IRubyObject[] args) {
234: IRubyObject line = gets(args);
235:
236: if (line.isNil()) {
237: throw getRuntime().newEOFError();
238: }
239:
240: return line;
241: }
242:
243: public RubyArray readlines(IRubyObject[] args) {
244: IRubyObject[] separatorArgument;
245: if (args.length > 0) {
246: if (!args[0].isKindOf(getRuntime().getNilClass())
247: && !args[0].isKindOf(getRuntime().getString())) {
248: throw getRuntime().newTypeError(args[0],
249: getRuntime().getString());
250: }
251: separatorArgument = new IRubyObject[] { args[0] };
252: } else {
253: separatorArgument = IRubyObject.NULL_ARRAY;
254: }
255:
256: RubyArray result = getRuntime().newArray();
257: IRubyObject line;
258: while (!(line = internalGets(separatorArgument)).isNil()) {
259: result.append(line);
260: }
261: return result;
262: }
263:
264: public IRubyObject each_byte(Block block) {
265: IRubyObject bt;
266: ThreadContext ctx = getRuntime().getCurrentContext();
267:
268: while (!(bt = getc()).isNil()) {
269: block.yield(ctx, bt);
270: }
271:
272: return this ;
273: }
274:
275: /** Invoke a block for each line.
276: *
277: */
278: public IRubyObject each_line(IRubyObject[] args, Block block) {
279: IRubyObject nextLine = internalGets(args);
280:
281: while (!nextLine.isNil()) {
282: block.yield(getRuntime().getCurrentContext(), nextLine);
283: nextLine = internalGets(args);
284: }
285:
286: return this ;
287: }
288:
289: public IRubyObject file() {
290: if (currentFile == null && !nextArgsFile()) {
291: return getRuntime().getNil();
292: }
293:
294: return currentFile;
295: }
296:
297: public IRubyObject skip() {
298: currentFile = null;
299: return this ;
300: }
301:
302: public IRubyObject close_m() {
303: if (currentFile == null && !nextArgsFile()) {
304: return this ;
305: }
306: currentFile = null;
307: currentLineNumber = 0;
308: return this ;
309: }
310:
311: public IRubyObject closed_p() {
312: if (currentFile == null && !nextArgsFile()) {
313: return this ;
314: }
315: return ((RubyIO) currentFile).closed();
316: }
317:
318: public IRubyObject binmode() {
319: if (currentFile == null && !nextArgsFile()) {
320: throw getRuntime().newArgumentError("no stream");
321: }
322:
323: return ((RubyIO) currentFile).binmode();
324: }
325:
326: public IRubyObject lineno() {
327: return getRuntime().newFixnum(currentLineNumber);
328: }
329:
330: public IRubyObject tell() {
331: if (currentFile == null && !nextArgsFile()) {
332: throw getRuntime().newArgumentError("no stream to tell");
333: }
334: return ((RubyIO) currentFile).pos();
335: }
336:
337: public IRubyObject rewind() {
338: if (currentFile == null && !nextArgsFile()) {
339: throw getRuntime().newArgumentError("no stream to rewind");
340: }
341: return ((RubyIO) currentFile).rewind();
342: }
343:
344: public IRubyObject eof() {
345: if (currentFile != null && !nextArgsFile()) {
346: return getRuntime().getTrue();
347: }
348: return ((RubyIO) currentFile).eof();
349: }
350:
351: public IRubyObject set_pos(IRubyObject offset) {
352: if (currentFile == null && !nextArgsFile()) {
353: throw getRuntime().newArgumentError(
354: "no stream to set position");
355: }
356: return ((RubyIO) currentFile).pos_set(offset);
357: }
358:
359: public IRubyObject seek(IRubyObject[] args) {
360: if (currentFile == null && !nextArgsFile()) {
361: throw getRuntime().newArgumentError("no stream to seek");
362: }
363: return ((RubyIO) currentFile).seek(args);
364: }
365:
366: public IRubyObject set_lineno(IRubyObject line) {
367: currentLineNumber = RubyNumeric.fix2int(line);
368: return getRuntime().getNil();
369: }
370:
371: public IRubyObject readchar() {
372: IRubyObject c = getc();
373: if (c.isNil()) {
374: throw getRuntime().newEOFError();
375: }
376: return c;
377: }
378:
379: public IRubyObject getc() {
380: IRubyObject bt;
381: while (true) {
382: if (currentFile == null && !nextArgsFile()) {
383: return getRuntime().getNil();
384: }
385: if (!(currentFile instanceof RubyFile)) {
386: bt = currentFile.callMethod(getRuntime()
387: .getCurrentContext(), "getc");
388: } else {
389: bt = ((RubyIO) currentFile).getc();
390: }
391: if (bt.isNil()) {
392: currentFile = null;
393: continue;
394: }
395: return bt;
396: }
397: }
398:
399: public IRubyObject read(IRubyObject[] args) {
400: IRubyObject tmp, str, length;
401: long len = 0;
402: Arity.checkArgumentCount(getRuntime(), args, 0, 2);
403: if (args.length > 0) {
404: length = args[0];
405: if (args.length > 1) {
406: str = args[1];
407: } else {
408: str = getRuntime().getNil();
409: }
410: } else {
411: length = getRuntime().getNil();
412: str = getRuntime().getNil();
413: }
414:
415: if (!length.isNil()) {
416: len = RubyNumeric.num2long(length);
417: }
418: if (!str.isNil()) {
419: str = str.convertToString();
420: ((RubyString) str).modify();
421: ((RubyString) str).getByteList().length(0);
422: args[1] = getRuntime().getNil();
423: }
424: while (true) {
425: if (currentFile == null && !nextArgsFile()) {
426: return str;
427: }
428: if (!(currentFile instanceof RubyIO)) {
429: tmp = currentFile.callMethod(getRuntime()
430: .getCurrentContext(), "read", args);
431: } else {
432: tmp = ((RubyIO) currentFile).read(args);
433: }
434: if (str.isNil()) {
435: str = tmp;
436: } else if (!tmp.isNil()) {
437: ((RubyString) str).append(tmp);
438: }
439: if (tmp.isNil() || length.isNil()) {
440: currentFile = null;
441: continue;
442: } else if (args.length >= 1) {
443: if (((RubyString) str).getByteList().length() < len) {
444: len -= ((RubyString) str).getByteList().length();
445: args[0] = getRuntime().newFixnum(len);
446: continue;
447: }
448: }
449: return str;
450: }
451: }
452:
453: public RubyString filename() {
454: return (RubyString) getRuntime().getGlobalVariables().get(
455: "$FILENAME");
456: }
457:
458: public IRubyObject to_s() {
459: return getRuntime().newString("ARGF");
460: }
461: }
|