001: /*
002: * Load.java
003: *
004: * Copyright (C) 2002-2004 Peter Graves
005: * $Id: Load.java,v 1.77 2004/09/24 18:05:50 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.lisp;
023:
024: import java.io.File;
025: import java.io.FileInputStream;
026: import java.io.FileNotFoundException;
027: import java.io.IOException;
028: import java.io.InputStream;
029: import java.net.URL;
030: import java.util.ArrayList;
031:
032: public final class Load extends Lisp {
033: public static final LispObject load(String filename)
034: throws ConditionThrowable {
035: final LispThread thread = LispThread.currentThread();
036: return load(filename,
037: _LOAD_VERBOSE_.symbolValue(thread) != NIL, _LOAD_PRINT_
038: .symbolValue(thread) != NIL, true);
039: }
040:
041: public static final LispObject load(final String filename,
042: boolean verbose, boolean print, boolean ifDoesNotExist)
043: throws ConditionThrowable {
044: File file = null;
045: boolean isFile = false;
046: if (Utilities.isFilenameAbsolute(filename)) {
047: file = new File(filename);
048: if (file != null) {
049: isFile = file.isFile();
050: if (!isFile) {
051: String extension = getExtension(filename);
052: if (extension == null) {
053: // No extension specified. Try appending ".lisp".
054: file = new File(filename.concat(".lisp"));
055: isFile = file.isFile();
056: }
057: }
058: }
059: } else {
060: // Filename is not absolute.
061: String dir = Pathname.coerceToPathname(
062: _DEFAULT_PATHNAME_DEFAULTS_.symbolValue())
063: .getNamestring();
064: file = new File(dir, filename);
065: if (file != null) {
066: isFile = file.isFile();
067: if (!isFile) {
068: String extension = getExtension(filename);
069: if (extension == null) {
070: // No extension specified. Try appending ".lisp".
071: file = new File(dir, filename.concat(".lisp"));
072: isFile = file.isFile();
073: }
074: }
075: }
076: }
077: if (!isFile) {
078: if (ifDoesNotExist)
079: return signal(new FileError("File not found: "
080: + filename));
081: else
082: return NIL;
083: }
084: String truename = filename;
085: InputStream in = null;
086: try {
087: in = new FileInputStream(file);
088: truename = file.getCanonicalPath();
089: } catch (FileNotFoundException e) {
090: if (ifDoesNotExist)
091: return signal(new FileError("File not found: "
092: + filename));
093: else
094: return NIL;
095: } catch (IOException e) {
096: return signal(new LispError(e.getMessage()));
097: }
098: try {
099: return loadFileFromStream(null, truename, in, verbose,
100: print, false);
101: } catch (FaslVersionMismatch e) {
102: StringBuffer sb = new StringBuffer(
103: "Incorrect fasl version: ");
104: sb.append(truename);
105: return signal(new SimpleError(sb.toString()));
106: } finally {
107: try {
108: in.close();
109: } catch (IOException e) {
110: return signal(new LispError(e.getMessage()));
111: }
112: }
113: }
114:
115: public static final LispObject loadSystemFile(String filename)
116: throws ConditionThrowable {
117: final LispThread thread = LispThread.currentThread();
118: return loadSystemFile(filename, _LOAD_VERBOSE_
119: .symbolValue(thread) != NIL, _LOAD_PRINT_
120: .symbolValue(thread) != NIL, false);
121: }
122:
123: public static final LispObject loadSystemFile(String filename,
124: boolean auto) throws ConditionThrowable {
125: LispThread thread = LispThread.currentThread();
126: if (auto) {
127: Environment oldDynEnv = thread.getDynamicEnvironment();
128: thread.bindSpecial(_READTABLE_,
129: Readtable._STANDARD_READTABLE_.symbolValue(thread));
130: thread.bindSpecial(_PACKAGE_, PACKAGE_CL_USER);
131: try {
132: return loadSystemFile(filename, _AUTOLOAD_VERBOSE_
133: .symbolValue(thread) != NIL, _LOAD_PRINT_
134: .symbolValue(thread) != NIL, auto);
135: } finally {
136: thread.setDynamicEnvironment(oldDynEnv);
137: }
138: } else {
139: return loadSystemFile(filename, _LOAD_VERBOSE_
140: .symbolValue(thread) != NIL, _LOAD_PRINT_
141: .symbolValue(thread) != NIL, auto);
142: }
143: }
144:
145: public static final LispObject loadSystemFile(
146: final String filename, boolean verbose, boolean print,
147: boolean auto) throws ConditionThrowable {
148: final int ARRAY_SIZE = 2;
149: String[] candidates = new String[ARRAY_SIZE];
150: String extension = getExtension(filename);
151: if (extension == null) {
152: // No extension specified.
153: candidates[0] = filename + '.' + COMPILE_FILE_TYPE;
154: candidates[1] = filename.concat(".lisp");
155: } else if (extension.equals(".abcl")) {
156: candidates[0] = filename;
157: candidates[1] = filename
158: .substring(0, filename.length() - 5)
159: .concat(".lisp");
160: } else
161: candidates[0] = filename;
162: InputStream in = null;
163: Pathname pathname = null;
164: String truename = null;
165: for (int i = 0; i < ARRAY_SIZE; i++) {
166: String s = candidates[i];
167: if (s == null)
168: break;
169: final String dir = Site.getLispHome();
170: if (dir != null) {
171: File file = new File(dir, s);
172: if (file.isFile()) {
173: try {
174: in = new FileInputStream(file);
175: truename = file.getCanonicalPath();
176: } catch (IOException e) {
177: in = null;
178: }
179: }
180: } else {
181: URL url = Lisp.class.getResource(s);
182: if (url != null) {
183: try {
184: in = url.openStream();
185: if ("jar".equals(url.getProtocol()))
186: pathname = new Pathname(url);
187: truename = getPath(url);
188: } catch (IOException e) {
189: in = null;
190: }
191: }
192: }
193: if (in != null) {
194: try {
195: return loadFileFromStream(pathname, truename, in,
196: verbose, print, auto);
197: } catch (FaslVersionMismatch e) {
198: StringBuffer sb = new StringBuffer(
199: "; Incorrect fasl version: ");
200: sb.append(truename);
201: System.err.println(sb.toString());
202: } finally {
203: try {
204: in.close();
205: } catch (IOException e) {
206: return signal(new LispError(e.getMessage()));
207: }
208: }
209: }
210: }
211: return signal(new LispError("file not found: " + filename));
212: }
213:
214: // ### *fasl-source*
215: // internal symbol
216: public static final Symbol _FASL_SOURCE_ = internSpecial(
217: "*FASL-SOURCE*", PACKAGE_SYS, NIL);
218:
219: // ### *fasl-version*
220: // internal symbol
221: private static final Symbol _FASL_VERSION_ = internConstant(
222: "*FASL-VERSION*", PACKAGE_SYS, new Fixnum(13));
223:
224: // ### *fasl-anonymous-package*
225: // internal symbol
226: public static final Symbol _FASL_ANONYMOUS_PACKAGE_ = internSpecial(
227: "*FASL-ANONYMOUS-PACKAGE*", PACKAGE_SYS, NIL);
228:
229: // ### init-fasl
230: private static final Primitive2 INIT_FASL = new Primitive2(
231: "init-fasl", PACKAGE_SYS, true, "&key version") {
232: public LispObject execute(LispObject first, LispObject second)
233: throws ConditionThrowable {
234: if (first == Keyword.VERSION) {
235: if (second.eql(_FASL_VERSION_.getSymbolValue())) {
236: // OK
237: final LispThread thread = LispThread
238: .currentThread();
239: Readtable readtable = new Readtable(NIL);
240: readtable.setDispatchMacroCharacter('#', ':',
241: FASL_SHARP_COLON);
242: thread.bindSpecial(_READTABLE_, readtable);
243: thread.bindSpecial(_FASL_ANONYMOUS_PACKAGE_, NIL);
244: thread.bindSpecial(_FASL_SOURCE_, NIL);
245: thread.bindSpecial(_DEFAULT_PATHNAME_DEFAULTS_,
246: _LOAD_TRUENAME_.symbolValue(thread));
247: return T;
248: }
249: }
250: throw new FaslVersionMismatch(second);
251: }
252: };
253:
254: private static final LispObject loadFileFromStream(
255: Pathname pathname, String truename, InputStream in,
256: boolean verbose, boolean print, boolean auto)
257: throws ConditionThrowable {
258: long start = System.currentTimeMillis();
259: LispThread thread = LispThread.currentThread();
260: Environment oldDynEnv = thread.getDynamicEnvironment();
261: thread.bindSpecial(_PACKAGE_, _PACKAGE_.symbolValue(thread));
262: int loadDepth = Fixnum.getValue(_LOAD_DEPTH_
263: .symbolValue(thread));
264: thread.bindSpecial(_LOAD_DEPTH_, new Fixnum(++loadDepth));
265: // Compiler policy.
266: thread.bindSpecial(_SPEED_, _SPEED_.symbolValue(thread));
267: thread.bindSpecial(_SAFETY_, _SAFETY_.symbolValue(thread));
268: final String prefix = getLoadVerbosePrefix(loadDepth);
269: try {
270: if (pathname == null)
271: pathname = Pathname.parseNamestring(truename);
272: thread.bindSpecial(_LOAD_PATHNAME_, pathname);
273: thread.bindSpecial(_LOAD_TRUENAME_, pathname);
274: if (verbose) {
275: Stream out = getStandardOutput();
276: out.freshLine();
277: out._writeString(prefix);
278: out._writeString(auto ? " Autoloading " : " Loading ");
279: out._writeString(truename);
280: out._writeLine(" ...");
281: out._finishOutput();
282: LispObject result = loadStream(in, print);
283: long elapsed = System.currentTimeMillis() - start;
284: out.freshLine();
285: out._writeString(prefix);
286: out._writeString(auto ? " Autoloaded " : " Loaded ");
287: out._writeString(truename);
288: out._writeString(" (");
289: out._writeString(String
290: .valueOf(((float) elapsed) / 1000));
291: out._writeLine(" seconds)");
292: out._finishOutput();
293: return result;
294: } else
295: return loadStream(in, print);
296: } finally {
297: thread.setDynamicEnvironment(oldDynEnv);
298: }
299: }
300:
301: public static String getLoadVerbosePrefix(int loadDepth) {
302: StringBuffer sb = new StringBuffer(";");
303: for (int i = loadDepth - 1; i-- > 0;)
304: sb.append(' ');
305: return sb.toString();
306: }
307:
308: // ### fasl-sharp-colon
309: public static final DispatchMacroFunction FASL_SHARP_COLON = new DispatchMacroFunction(
310: "sharp-colon", PACKAGE_SYS, false, "stream sub-char numarg") {
311: public LispObject execute(Stream stream, char c, int n)
312: throws ConditionThrowable {
313: LispThread thread = LispThread.currentThread();
314: Symbol symbol = (Symbol) stream.readSymbol();
315: LispObject pkg = _FASL_ANONYMOUS_PACKAGE_
316: .symbolValue(thread);
317: if (pkg == NIL) {
318: thread.bindSpecial(_FASL_ANONYMOUS_PACKAGE_,
319: pkg = new Package());
320: }
321: symbol = ((Package) pkg).intern(symbol.getName());
322: symbol.setPackage(NIL);
323: return symbol;
324: }
325: };
326:
327: private static final LispObject loadStream(InputStream inputStream,
328: boolean print) throws ConditionThrowable {
329: Stream in = new Stream(inputStream, Symbol.CHARACTER);
330: final LispThread thread = LispThread.currentThread();
331: Environment oldDynEnv = thread.getDynamicEnvironment();
332: thread.bindSpecial(_LOAD_STREAM_, in);
333: try {
334: final Environment env = new Environment();
335: while (true) {
336: LispObject obj = in.read(false, EOF, true);
337: if (obj == EOF)
338: break;
339: LispObject result = eval(obj, env, thread);
340: if (print) {
341: Stream out = getStandardOutput();
342: out._writeLine(result.writeToString());
343: out._finishOutput();
344: }
345: }
346: return T;
347: } finally {
348: thread.setDynamicEnvironment(oldDynEnv);
349: }
350: }
351:
352: // Returns extension including leading '.'
353: private static final String getExtension(String filename) {
354: int index = filename.lastIndexOf('.');
355: if (index < 0)
356: return null;
357: if (index < filename.lastIndexOf(File.separatorChar))
358: return null; // Last dot was in path part of filename.
359: return filename.substring(index);
360: }
361:
362: private static final String getPath(URL url) {
363: if (url != null) {
364: String path = url.getPath();
365: if (path != null) {
366: if (Utilities.isPlatformWindows()) {
367: if (path.length() > 0 && path.charAt(0) == '/')
368: path = path.substring(1);
369: }
370: return path;
371: }
372: }
373: return null;
374: }
375:
376: // ### %load filespec verbose print if-does-not-exist => generalized-boolean
377: private static final Primitive4 _LOAD = new Primitive4("%load",
378: PACKAGE_SYS, false,
379: "filespec verbose print if-does-not-exist") {
380: public LispObject execute(LispObject filespec,
381: LispObject verbose, LispObject print,
382: LispObject ifDoesNotExist) throws ConditionThrowable {
383: return load(Pathname.coerceToPathname(filespec)
384: .getNamestring(), verbose != NIL, print != NIL,
385: ifDoesNotExist != NIL);
386: }
387: };
388:
389: // ### load-system-file
390: private static final Primitive1 LOAD_SYSTEM_FILE = new Primitive1(
391: "load-system-file", PACKAGE_SYS, true) {
392: public LispObject execute(LispObject arg)
393: throws ConditionThrowable {
394: final LispThread thread = LispThread.currentThread();
395: return loadSystemFile(arg.getStringValue(), _LOAD_VERBOSE_
396: .symbolValue(thread) != NIL, _LOAD_PRINT_
397: .symbolValue(thread) != NIL, false);
398: }
399: };
400:
401: private static class FaslVersionMismatch extends Error {
402: private final LispObject version;
403:
404: public FaslVersionMismatch(LispObject version) {
405: this .version = version;
406: }
407:
408: public LispObject getVersion() {
409: return version;
410: }
411: }
412: }
|