0001: /*
0002: * Pathname.java
0003: *
0004: * Copyright (C) 2003-2004 Peter Graves
0005: * $Id: Pathname.java,v 1.68 2004/09/18 02:06:57 piso Exp $
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License
0009: * as published by the Free Software Foundation; either version 2
0010: * of the License, or (at your option) any later version.
0011: *
0012: * This program is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015: * GNU General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * along with this program; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0020: */
0021:
0022: package org.armedbear.lisp;
0023:
0024: import java.io.File;
0025: import java.io.IOException;
0026: import java.net.URL;
0027: import java.util.StringTokenizer;
0028:
0029: public class Pathname extends LispObject {
0030: protected LispObject host = NIL;
0031: protected LispObject device = NIL;
0032: protected LispObject directory = NIL;
0033: protected LispObject name = NIL;
0034:
0035: // A string, NIL, :wild or :unspecific.
0036: protected LispObject type = NIL;
0037:
0038: // A positive integer, or NIL, :WILD, :UNSPECIFIC, or :NEWEST.
0039: protected LispObject version = NIL;
0040:
0041: private String namestring;
0042:
0043: protected Pathname() {
0044: }
0045:
0046: public Pathname(String s) throws ConditionThrowable {
0047: init(s);
0048: }
0049:
0050: public Pathname(URL url) throws ConditionThrowable {
0051: String protocol = url.getProtocol();
0052: if ("jar".equals(protocol)) {
0053: String s = url.getPath();
0054: if (s.startsWith("file:")) {
0055: int index = s.indexOf("!/");
0056: String container = s.substring(5, index);
0057: if (Utilities.isPlatformWindows()) {
0058: if (container.length() > 0
0059: && container.charAt(0) == '/')
0060: container = container.substring(1);
0061: }
0062: device = new Pathname(container);
0063: s = s.substring(index + 1);
0064: Pathname p = new Pathname(s);
0065: directory = p.directory;
0066: name = p.name;
0067: type = p.type;
0068: return;
0069: }
0070: } else if ("file".equals(protocol)) {
0071: String s = url.getPath();
0072: if (s != null && s.startsWith("file:")) {
0073: init(s.substring(5));
0074: return;
0075: }
0076: }
0077: signal(new LispError("Unsupported URL: \"" + url.toString()
0078: + '"'));
0079: }
0080:
0081: private final void init(String s) throws ConditionThrowable {
0082: if (s == null)
0083: return;
0084: if (Utilities.isPlatformWindows())
0085: s = s.replace('/', '\\');
0086: // Jar file support.
0087: int bang = s.indexOf("!/");
0088: if (bang >= 0) {
0089: Pathname container = new Pathname(s.substring(0, bang));
0090: LispObject containerType = container.type;
0091: if (containerType instanceof AbstractString) {
0092: if (containerType.getStringValue().equalsIgnoreCase(
0093: "jar")) {
0094: device = container;
0095: s = s.substring(bang + 1);
0096: Pathname p = new Pathname(s);
0097: directory = p.directory;
0098: name = p.name;
0099: type = p.type;
0100: return;
0101: }
0102: }
0103: }
0104: if (Utilities.isPlatformUnix()) {
0105: if (s.equals("~"))
0106: s = System.getProperty("user.home").concat("/");
0107: else if (s.startsWith("~/"))
0108: s = System.getProperty("user.home").concat(
0109: s.substring(1));
0110: }
0111: namestring = s;
0112: if (s.equals(".") || s.equals("./")) {
0113: directory = new Cons(Keyword.RELATIVE);
0114: return;
0115: }
0116: if (s.equals("..") || s.equals("../")) {
0117: directory = list2(Keyword.RELATIVE, Keyword.BACK);
0118: return;
0119: }
0120: if (Utilities.isPlatformWindows()) {
0121: if (s.length() >= 2 && s.charAt(1) == ':') {
0122: device = new SimpleString(s.charAt(0));
0123: s = s.substring(2);
0124: }
0125: }
0126: String d = null;
0127: // Find last file separator char.
0128: if (Utilities.isPlatformWindows()) {
0129: for (int i = s.length(); i-- > 0;) {
0130: char c = s.charAt(i);
0131: if (c == '/' || c == '\\') {
0132: d = s.substring(0, i + 1);
0133: s = s.substring(i + 1);
0134: break;
0135: }
0136: }
0137: } else {
0138: for (int i = s.length(); i-- > 0;) {
0139: if (s.charAt(i) == '/') {
0140: d = s.substring(0, i + 1);
0141: s = s.substring(i + 1);
0142: break;
0143: }
0144: }
0145: }
0146: if (d != null) {
0147: if (s.equals("..")) {
0148: d = d.concat(s);
0149: s = "";
0150: }
0151: directory = parseDirectory(d);
0152: }
0153: if (s.startsWith(".")) {
0154: name = new SimpleString(s);
0155: return;
0156: }
0157: int index = s.lastIndexOf('.');
0158: String n = null;
0159: String t = null;
0160: if (index > 0) {
0161: n = s.substring(0, index);
0162: t = s.substring(index + 1);
0163: } else if (s.length() > 0)
0164: n = s;
0165: if (n != null) {
0166: if (n.equals("*"))
0167: name = Keyword.WILD;
0168: else
0169: name = new SimpleString(n);
0170: }
0171: if (t != null) {
0172: if (t.equals("*"))
0173: type = Keyword.WILD;
0174: else
0175: type = new SimpleString(t);
0176: }
0177: }
0178:
0179: private static final LispObject parseDirectory(String d)
0180: throws ConditionThrowable {
0181: if (d.equals("/")
0182: || (Utilities.isPlatformWindows() && d.equals("\\")))
0183: return new Cons(Keyword.ABSOLUTE);
0184: LispObject result;
0185: if (d.startsWith("/")
0186: || (Utilities.isPlatformWindows() && d.startsWith("\\")))
0187: result = new Cons(Keyword.ABSOLUTE);
0188: else
0189: result = new Cons(Keyword.RELATIVE);
0190: StringTokenizer st = new StringTokenizer(d, "/\\");
0191: while (st.hasMoreTokens()) {
0192: String token = st.nextToken();
0193: LispObject obj;
0194: if (token.equals("*"))
0195: obj = Keyword.WILD;
0196: else if (token.equals("**"))
0197: obj = Keyword.WILD_INFERIORS;
0198: else if (token.equals("..")) {
0199: if (result.car() instanceof AbstractString) {
0200: result = result.cdr();
0201: continue;
0202: }
0203: obj = Keyword.UP;
0204: } else
0205: obj = new SimpleString(token);
0206: result = new Cons(obj, result);
0207: }
0208: return result.nreverse();
0209: }
0210:
0211: public LispObject typeOf() {
0212: return Symbol.PATHNAME;
0213: }
0214:
0215: public LispClass classOf() {
0216: return BuiltInClass.PATHNAME;
0217: }
0218:
0219: public LispObject typep(LispObject type) throws ConditionThrowable {
0220: if (type == Symbol.PATHNAME)
0221: return T;
0222: if (type == BuiltInClass.PATHNAME)
0223: return T;
0224: return super .typep(type);
0225: }
0226:
0227: public final LispObject getDevice() {
0228: return device;
0229: }
0230:
0231: public String getNamestring() throws ConditionThrowable {
0232: if (namestring != null)
0233: return namestring;
0234: if (name == NIL && type != NIL) {
0235: Debug.assertTrue(namestring == null);
0236: return null;
0237: }
0238: if (directory instanceof AbstractString)
0239: Debug.assertTrue(false);
0240: StringBuffer sb = new StringBuffer(getDirectoryNamestring());
0241: if (name instanceof AbstractString)
0242: sb.append(name.getStringValue());
0243: else if (name == Keyword.WILD)
0244: sb.append('*');
0245: if (type != NIL) {
0246: sb.append('.');
0247: if (type instanceof AbstractString)
0248: sb.append(type.getStringValue());
0249: else if (type == Keyword.WILD)
0250: sb.append('*');
0251: else
0252: Debug.assertTrue(false);
0253: }
0254: return namestring = sb.toString();
0255: }
0256:
0257: private String getDirectoryNamestring() throws ConditionThrowable {
0258: StringBuffer sb = new StringBuffer();
0259: // "If a pathname is converted to a namestring, the symbols NIL and
0260: // :UNSPECIFIC cause the field to be treated as if it were empty. That
0261: // is, both NIL and :UNSPECIFIC cause the component not to appear in
0262: // the namestring." 19.2.2.2.3.1
0263: if (device == NIL)
0264: ;
0265: else if (device == Keyword.UNSPECIFIC)
0266: ;
0267: else if (device instanceof AbstractString) {
0268: sb.append(device.getStringValue());
0269: sb.append(':');
0270: } else if (device instanceof Pathname) {
0271: sb.append(((Pathname) device).getNamestring());
0272: sb.append("!");
0273: } else
0274: Debug.assertTrue(false);
0275: final char separatorChar;
0276: if (device instanceof Pathname)
0277: separatorChar = '/'; // Jar file.
0278: else
0279: separatorChar = File.separatorChar;
0280: if (directory != NIL) {
0281: LispObject temp = directory;
0282: LispObject part = temp.car();
0283: if (part == Keyword.ABSOLUTE)
0284: sb.append(separatorChar);
0285: else if (part == Keyword.RELATIVE)
0286: ;
0287: else
0288: signal(new LispError("Unsupported directory component "
0289: + part.writeToString() + "."));
0290: temp = temp.cdr();
0291: while (temp != NIL) {
0292: part = temp.car();
0293: if (part instanceof AbstractString)
0294: sb.append(part.getStringValue());
0295: else if (part == Keyword.WILD)
0296: sb.append("*");
0297: else
0298: signal(new LispError(
0299: "Unsupported directory component "
0300: + part.writeToString() + "."));
0301: sb.append(separatorChar);
0302: temp = temp.cdr();
0303: }
0304: }
0305: return sb.toString();
0306: }
0307:
0308: public boolean equal(LispObject obj) throws ConditionThrowable {
0309: if (this == obj)
0310: return true;
0311: if (obj instanceof Pathname) {
0312: Pathname p = (Pathname) obj;
0313: if (Utilities.isPlatformWindows()) {
0314: if (!host.equalp(p.host))
0315: return false;
0316: if (!device.equalp(p.device))
0317: return false;
0318: if (!directory.equalp(p.directory))
0319: return false;
0320: if (!name.equalp(p.name))
0321: return false;
0322: if (!type.equalp(p.type))
0323: return false;
0324: if (!version.equalp(p.version))
0325: return false;
0326: } else {
0327: // Unix.
0328: if (!host.equal(p.host))
0329: return false;
0330: if (!device.equal(p.device))
0331: return false;
0332: if (!directory.equal(p.directory))
0333: return false;
0334: if (!name.equal(p.name))
0335: return false;
0336: if (!type.equal(p.type))
0337: return false;
0338: if (!version.equal(p.version))
0339: return false;
0340: }
0341: return true;
0342: }
0343: return false;
0344: }
0345:
0346: public boolean equalp(LispObject obj) throws ConditionThrowable {
0347: return equal(obj);
0348: }
0349:
0350: public int sxhash() throws ConditionThrowable {
0351: return ((host.sxhash() ^ device.sxhash() ^ directory.sxhash()
0352: ^ name.sxhash() ^ type.sxhash() ^ version.sxhash()) & 0x7fffffff);
0353: }
0354:
0355: private boolean matches(Pathname wildcard)
0356: throws ConditionThrowable {
0357: if (Utilities.isPlatformWindows()) {
0358: if (wildcard.device != Keyword.WILD) {
0359: if (!device.equalp(wildcard.device))
0360: return false;
0361: }
0362: if (wildcard.name != Keyword.WILD) {
0363: if (!name.equalp(wildcard.name))
0364: return false;
0365: }
0366: if (wildcard.directory != Keyword.WILD) {
0367: if (!directory.equalp(wildcard.directory))
0368: return false;
0369: }
0370: if (wildcard.type != Keyword.WILD) {
0371: if (!type.equalp(wildcard.type))
0372: return false;
0373: }
0374: } else {
0375: // Unix.
0376: if (wildcard.name != Keyword.WILD) {
0377: if (!name.equal(wildcard.name))
0378: return false;
0379: }
0380: if (wildcard.directory != Keyword.WILD) {
0381: if (!directory.equal(wildcard.directory))
0382: return false;
0383: }
0384: if (wildcard.type != Keyword.WILD) {
0385: if (!type.equal(wildcard.type))
0386: return false;
0387: }
0388: }
0389: return true;
0390: }
0391:
0392: public String writeToString() throws ConditionThrowable {
0393: try {
0394: StringBuffer sb = new StringBuffer("#P");
0395: boolean printReadably = (_PRINT_READABLY_.symbolValue() != NIL);
0396: boolean useNamestring;
0397: String s = getNamestring();
0398: if (s != null) {
0399: useNamestring = true;
0400: if (printReadably) {
0401: // We have a namestring. Check for non-NIL values of pathname
0402: // components that can't be read from the namestring.
0403: if (host != NIL || version != NIL) {
0404: useNamestring = false;
0405: } else if (Utilities.isPlatformWindows()) {
0406: if (version != NIL)
0407: useNamestring = false;
0408: }
0409: }
0410: } else
0411: useNamestring = false;
0412: if (useNamestring) {
0413: sb.append(quoteEscape(s));
0414: } else {
0415: sb.append('(');
0416: if (host != NIL) {
0417: sb.append(":HOST ");
0418: sb.append(host.writeToString());
0419: sb.append(' ');
0420: }
0421: if (device != NIL) {
0422: sb.append(":DEVICE ");
0423: sb.append(device.writeToString());
0424: sb.append(' ');
0425: }
0426: if (directory != NIL) {
0427: sb.append(":DIRECTORY ");
0428: sb.append(directory.writeToString());
0429: sb.append(" ");
0430: }
0431: if (name != NIL) {
0432: sb.append(":NAME ");
0433: sb.append(name.writeToString());
0434: sb.append(' ');
0435: }
0436: if (type != NIL) {
0437: sb.append(":TYPE ");
0438: sb.append(type.writeToString());
0439: sb.append(' ');
0440: }
0441: if (version != NIL) {
0442: sb.append(":VERSION ");
0443: sb.append(version.writeToString());
0444: sb.append(' ');
0445: }
0446: if (sb.charAt(sb.length() - 1) == ' ')
0447: sb.setLength(sb.length() - 1);
0448: sb.append(')');
0449: }
0450: return sb.toString();
0451: } catch (ConditionThrowable t) {
0452: return unreadableString("PATHNAME");
0453: }
0454: }
0455:
0456: private static final String quoteEscape(String s)
0457: throws ConditionThrowable {
0458: StringBuffer sb = new StringBuffer();
0459: sb.append('"');
0460: final int limit = s.length();
0461: for (int i = 0; i < limit; i++) {
0462: char c = s.charAt(i);
0463: if (c == '\"' || c == '\\')
0464: sb.append('\\');
0465: sb.append(c);
0466: }
0467: sb.append('"');
0468: return sb.toString();
0469: }
0470:
0471: public static Pathname parseNamestring(String namestring)
0472: throws ConditionThrowable {
0473: return new Pathname(namestring);
0474: }
0475:
0476: public static Pathname coerceToPathname(LispObject arg)
0477: throws ConditionThrowable {
0478: if (arg instanceof Pathname)
0479: return (Pathname) arg;
0480: if (arg instanceof AbstractString)
0481: return new Pathname(arg.getStringValue());
0482: if (arg instanceof FileStream)
0483: return ((FileStream) arg).getPathname();
0484: signal(new TypeError(arg.writeToString()
0485: + " cannot be converted to a pathname."));
0486: // Not reached.
0487: return null;
0488: }
0489:
0490: private static final void checkCaseArgument(LispObject arg)
0491: throws ConditionThrowable {
0492: if (arg != Keyword.COMMON && arg != Keyword.LOCAL)
0493: signal(new TypeError(arg, list3(Symbol.MEMBER,
0494: Keyword.COMMON, Keyword.LOCAL)));
0495: }
0496:
0497: private static final Primitive2 _PATHNAME_HOST = new Primitive2(
0498: "%pathname-host", PACKAGE_SYS, false) {
0499: public LispObject execute(LispObject first, LispObject second)
0500: throws ConditionThrowable {
0501: checkCaseArgument(second);
0502: return coerceToPathname(first).host;
0503: }
0504: };
0505:
0506: private static final Primitive2 _PATHNAME_DEVICE = new Primitive2(
0507: "%pathname-device", PACKAGE_SYS, false) {
0508: public LispObject execute(LispObject first, LispObject second)
0509: throws ConditionThrowable {
0510: checkCaseArgument(second);
0511: return coerceToPathname(first).device;
0512: }
0513: };
0514:
0515: private static final Primitive2 _PATHNAME_DIRECTORY = new Primitive2(
0516: "%pathname-directory", PACKAGE_SYS, false) {
0517: public LispObject execute(LispObject first, LispObject second)
0518: throws ConditionThrowable {
0519: checkCaseArgument(second);
0520: return coerceToPathname(first).directory;
0521: }
0522: };
0523:
0524: private static final Primitive2 _PATHNAME_NAME = new Primitive2(
0525: "%pathname-name", PACKAGE_SYS, false) {
0526: public LispObject execute(LispObject first, LispObject second)
0527: throws ConditionThrowable {
0528: checkCaseArgument(second);
0529: return coerceToPathname(first).name;
0530: }
0531: };
0532:
0533: private static final Primitive2 _PATHNAME_TYPE = new Primitive2(
0534: "%pathname-type", PACKAGE_SYS, false) {
0535: public LispObject execute(LispObject first, LispObject second)
0536: throws ConditionThrowable {
0537: checkCaseArgument(second);
0538: return coerceToPathname(first).type;
0539: }
0540: };
0541:
0542: private static final Primitive1 PATHNAME_VERSION = new Primitive1(
0543: "pathname-version", "pathname") {
0544: public LispObject execute(LispObject arg)
0545: throws ConditionThrowable {
0546: return coerceToPathname(arg).version;
0547: }
0548: };
0549:
0550: // ### namestring
0551: // namestring pathname => namestring
0552: private static final Primitive1 NAMESTRING = new Primitive1(
0553: "namestring", "pathname") {
0554: public LispObject execute(LispObject arg)
0555: throws ConditionThrowable {
0556: Pathname pathname = coerceToPathname(arg);
0557: String namestring = pathname.getNamestring();
0558: if (namestring == null)
0559: signal(new SimpleError("Pathname has no namestring: "
0560: + pathname.writeToString() + "."));
0561: return new SimpleString(namestring);
0562: }
0563: };
0564:
0565: // ### directory-namestring
0566: // directory-namestring pathname => namestring
0567: private static final Primitive1 DIRECTORY_NAMESTRING = new Primitive1(
0568: "directory-namestring", "pathname") {
0569: public LispObject execute(LispObject arg)
0570: throws ConditionThrowable {
0571: return new SimpleString(coerceToPathname(arg)
0572: .getDirectoryNamestring());
0573: }
0574: };
0575:
0576: // ### pathname
0577: // pathname pathspec => pathname
0578: private static final Primitive1 PATHNAME = new Primitive1(
0579: "pathname", "pathspec") {
0580: public LispObject execute(LispObject arg)
0581: throws ConditionThrowable {
0582: return coerceToPathname(arg);
0583: }
0584: };
0585:
0586: // ### make-pathname
0587: private static final Primitive MAKE_PATHNAME = new Primitive(
0588: "make-pathname",
0589: "&key host device directory name type version defaults case") {
0590: public LispObject execute(LispObject[] args)
0591: throws ConditionThrowable {
0592: return _makePathname(args);
0593: }
0594: };
0595:
0596: public static final Pathname makePathname(LispObject args)
0597: throws ConditionThrowable {
0598: return _makePathname(args.copyToArray());
0599: }
0600:
0601: private static final Pathname _makePathname(LispObject[] args)
0602: throws ConditionThrowable {
0603: if (args.length % 2 != 0)
0604: signal(new ProgramError("Odd number of keyword arguments."));
0605: Pathname p = new Pathname();
0606: Pathname defaults = null;
0607: boolean deviceSupplied = false;
0608: boolean nameSupplied = false;
0609: boolean typeSupplied = false;
0610: for (int i = 0; i < args.length; i += 2) {
0611: LispObject key = args[i];
0612: LispObject value = args[i + 1];
0613: if (key == Keyword.HOST) {
0614: p.host = value;
0615: } else if (key == Keyword.DEVICE) {
0616: p.device = value;
0617: deviceSupplied = true;
0618: } else if (key == Keyword.DIRECTORY) {
0619: if (value instanceof AbstractString)
0620: p.directory = list2(Keyword.ABSOLUTE, value);
0621: else if (value == Keyword.WILD)
0622: p.directory = list2(Keyword.ABSOLUTE, Keyword.WILD);
0623: else
0624: p.directory = validateDirectory(value);
0625: } else if (key == Keyword.NAME) {
0626: p.name = value;
0627: nameSupplied = true;
0628: } else if (key == Keyword.TYPE) {
0629: p.type = value;
0630: typeSupplied = true;
0631: } else if (key == Keyword.VERSION) {
0632: p.version = value;
0633: } else if (key == Keyword.DEFAULTS) {
0634: defaults = coerceToPathname(value);
0635: } else if (key == Keyword.CASE) {
0636: ; // Ignored.
0637: }
0638: }
0639: if (defaults != null) {
0640: // Ignore host.
0641: p.directory = mergeDirectories(p.directory,
0642: defaults.directory);
0643: if (!deviceSupplied)
0644: p.device = defaults.device;
0645: if (!nameSupplied)
0646: p.name = defaults.name;
0647: if (!typeSupplied)
0648: p.type = defaults.type;
0649: }
0650: return p;
0651: }
0652:
0653: private static final LispObject validateDirectory(LispObject obj)
0654: throws ConditionThrowable {
0655: LispObject temp = obj;
0656: while (temp != NIL) {
0657: LispObject first = temp.car();
0658: temp = temp.cdr();
0659: if (first == Keyword.ABSOLUTE
0660: || first == Keyword.WILD_INFERIORS) {
0661: LispObject second = temp.car();
0662: if (second == Keyword.UP || second == Keyword.BACK) {
0663: StringBuffer sb = new StringBuffer();
0664: sb.append(first.writeToString());
0665: sb.append(" may not be followed immediately by ");
0666: sb.append(second.writeToString());
0667: sb.append('.');
0668: return signal(new FileError(sb.toString()));
0669: }
0670: }
0671: }
0672: return obj;
0673: }
0674:
0675: // ### pathnamep
0676: private static final Primitive1 PATHNAMEP = new Primitive1(
0677: "pathnamep", "object") {
0678: public LispObject execute(LispObject arg)
0679: throws ConditionThrowable {
0680: return arg instanceof Pathname ? T : NIL;
0681: }
0682: };
0683:
0684: // ### user-homedir-pathname
0685: // user-homedir-pathname &optional host => pathname
0686: private static final Primitive USER_HOMEDIR_PATHNAME = new Primitive(
0687: "user-homedir-pathname", "&optional host") {
0688: public LispObject execute(LispObject[] args)
0689: throws ConditionThrowable {
0690: switch (args.length) {
0691: case 0: {
0692: String s = System.getProperty("user.home");
0693: // For compatibility with SBCL and ACL (and maybe other
0694: // Lisps), we want the namestring of a directory to end
0695: // with a file separator character.
0696: if (!s.endsWith(File.separator))
0697: s = s.concat(File.separator);
0698: return new Pathname(s);
0699: }
0700: case 1:
0701: return NIL;
0702: default:
0703: return signal(new WrongNumberOfArgumentsException(this ));
0704: }
0705: }
0706: };
0707:
0708: // ### pathname-match-p pathname wildcard => generalized-boolean
0709: private static final Primitive2 PATHNAME_MATCH_P = new Primitive2(
0710: "pathname-match-p", "pathname wildcard") {
0711: public LispObject execute(LispObject first, LispObject second)
0712: throws ConditionThrowable {
0713: Pathname pathname = coerceToPathname(first);
0714: Pathname wildcard = coerceToPathname(second);
0715: return pathname.matches(wildcard) ? T : NIL;
0716: }
0717: };
0718:
0719: // ### list-directory
0720: private static final Primitive1 LIST_DIRECTORY = new Primitive1(
0721: "list-directory", PACKAGE_SYS, false) {
0722: public LispObject execute(LispObject arg)
0723: throws ConditionThrowable {
0724: Pathname pathname = Pathname.coerceToPathname(arg);
0725: LispObject result = NIL;
0726: String s = pathname.getNamestring();
0727: if (s != null) {
0728: File f = new File(s);
0729: if (f.isDirectory()) {
0730: File[] files = f.listFiles();
0731: try {
0732: for (int i = files.length; i-- > 0;) {
0733: File file = files[i];
0734: Pathname p;
0735: if (file.isDirectory())
0736: p = Utilities
0737: .getDirectoryPathname(file);
0738: else
0739: p = new Pathname(file
0740: .getCanonicalPath());
0741: result = new Cons(p, result);
0742: }
0743: } catch (IOException e) {
0744: return signal(new FileError(
0745: "Unable to list directory "
0746: + pathname.writeToString()
0747: + "."));
0748: }
0749: }
0750: }
0751: return result;
0752: }
0753: };
0754:
0755: public boolean isWild() throws ConditionThrowable {
0756: if (host == Keyword.WILD || host == Keyword.WILD_INFERIORS)
0757: return true;
0758: if (device == Keyword.WILD || device == Keyword.WILD_INFERIORS)
0759: return true;
0760: if (directory instanceof Cons) {
0761: if (memq(Keyword.WILD, directory))
0762: return true;
0763: if (memq(Keyword.WILD_INFERIORS, directory))
0764: return true;
0765: }
0766: if (name == Keyword.WILD || name == Keyword.WILD_INFERIORS)
0767: return true;
0768: if (type == Keyword.WILD || type == Keyword.WILD_INFERIORS)
0769: return true;
0770: if (version == Keyword.WILD
0771: || version == Keyword.WILD_INFERIORS)
0772: return true;
0773: return false;
0774: }
0775:
0776: private static final Primitive2 _WILD_PATHNAME_P = new Primitive2(
0777: "%wild-pathname-p", PACKAGE_SYS, false) {
0778: public LispObject execute(LispObject first, LispObject second)
0779: throws ConditionThrowable {
0780: Pathname pathname = Pathname.coerceToPathname(first);
0781: if (second == NIL)
0782: return pathname.isWild() ? T : NIL;
0783: if (second == Keyword.DIRECTORY) {
0784: if (pathname.directory instanceof Cons) {
0785: if (memq(Keyword.WILD, pathname.directory))
0786: return T;
0787: if (memq(Keyword.WILD_INFERIORS, pathname.directory))
0788: return T;
0789: }
0790: return NIL;
0791: }
0792: LispObject value;
0793: if (second == Keyword.HOST)
0794: value = pathname.host;
0795: else if (second == Keyword.DEVICE)
0796: value = pathname.device;
0797: else if (second == Keyword.NAME)
0798: value = pathname.name;
0799: else if (second == Keyword.TYPE)
0800: value = pathname.type;
0801: else if (second == Keyword.VERSION)
0802: value = pathname.version;
0803: else
0804: return signal(new ProgramError("Unrecognized keyword "
0805: + second.writeToString() + "."));
0806: if (value == Keyword.WILD
0807: || value == Keyword.WILD_INFERIORS)
0808: return T;
0809: else
0810: return NIL;
0811: }
0812: };
0813:
0814: private static final Primitive MERGE_PATHNAMES = new Primitive(
0815: "merge-pathnames",
0816: "pathname &optional default-pathname default-version") {
0817: public LispObject execute(LispObject arg)
0818: throws ConditionThrowable {
0819: Pathname pathname = coerceToPathname(arg);
0820: Pathname defaultPathname = coerceToPathname(_DEFAULT_PATHNAME_DEFAULTS_
0821: .symbolValue());
0822: LispObject defaultVersion = Keyword.NEWEST;
0823: return mergePathnames(pathname, defaultPathname,
0824: defaultVersion);
0825: }
0826:
0827: public LispObject execute(LispObject first, LispObject second)
0828: throws ConditionThrowable {
0829: Pathname pathname = coerceToPathname(first);
0830: Pathname defaultPathname = coerceToPathname(second);
0831: LispObject defaultVersion = Keyword.NEWEST;
0832: return mergePathnames(pathname, defaultPathname,
0833: defaultVersion);
0834: }
0835:
0836: public LispObject execute(LispObject first, LispObject second,
0837: LispObject third) throws ConditionThrowable {
0838: Pathname pathname = coerceToPathname(first);
0839: Pathname defaultPathname = coerceToPathname(second);
0840: LispObject defaultVersion = third;
0841: return mergePathnames(pathname, defaultPathname,
0842: defaultVersion);
0843: }
0844: };
0845:
0846: public static final Pathname mergePathnames(Pathname pathname,
0847: Pathname defaultPathname, LispObject defaultVersion)
0848: throws ConditionThrowable {
0849: if (pathname instanceof LogicalPathname)
0850: signal(new LispError("Bad place for a logical pathname."));
0851: Pathname p = new Pathname();
0852: if (pathname.host != NIL)
0853: p.host = pathname.host;
0854: else
0855: p.host = defaultPathname.host;
0856: if (pathname.device != NIL)
0857: p.device = pathname.device;
0858: else
0859: p.device = defaultPathname.device;
0860: p.directory = mergeDirectories(pathname.directory,
0861: defaultPathname.directory);
0862: if (pathname.name != NIL)
0863: p.name = pathname.name;
0864: else
0865: p.name = defaultPathname.name;
0866: if (pathname.type != NIL)
0867: p.type = pathname.type;
0868: else
0869: p.type = defaultPathname.type;
0870: if (pathname.version != NIL)
0871: p.version = pathname.version;
0872: else if (pathname.name instanceof AbstractString)
0873: p.version = defaultVersion;
0874: else if (defaultPathname.version != NIL)
0875: p.version = defaultPathname.version;
0876: else
0877: p.version = defaultVersion;
0878: return p;
0879: }
0880:
0881: private static final LispObject mergeDirectories(LispObject dir,
0882: LispObject defaultDir) throws ConditionThrowable {
0883: if (dir == NIL)
0884: return defaultDir;
0885: if (dir.car() == Keyword.RELATIVE && defaultDir != NIL) {
0886: LispObject result = NIL;
0887: while (defaultDir != NIL) {
0888: result = new Cons(defaultDir.car(), result);
0889: defaultDir = defaultDir.cdr();
0890: }
0891: dir = dir.cdr(); // Skip :RELATIVE.
0892: while (dir != NIL) {
0893: result = new Cons(dir.car(), result);
0894: dir = dir.cdr();
0895: }
0896: LispObject[] array = result.copyToArray();
0897: for (int i = 0; i < array.length - 1; i++) {
0898: if (array[i] == Keyword.BACK) {
0899: if (array[i + 1] instanceof AbstractString
0900: || array[i + 1] == Keyword.WILD) {
0901: array[i] = null;
0902: array[i + 1] = null;
0903: }
0904: }
0905: }
0906: result = NIL;
0907: for (int i = 0; i < array.length; i++) {
0908: if (array[i] != null)
0909: result = new Cons(array[i], result);
0910: }
0911: return result;
0912: }
0913: return dir;
0914: }
0915:
0916: public static final LispObject truename(LispObject arg,
0917: boolean errorIfDoesNotExist) throws ConditionThrowable {
0918: final Pathname pathname = Pathname.coerceToPathname(arg);
0919: if (pathname.isWild())
0920: signal(new FileError("Bad place for a wild pathname.",
0921: pathname));
0922: Pathname defaultedPathname = mergePathnames(pathname, Pathname
0923: .coerceToPathname(_DEFAULT_PATHNAME_DEFAULTS_
0924: .symbolValue()), NIL);
0925: File file = Utilities.getFile(defaultedPathname);
0926: if (file.isDirectory())
0927: return Utilities.getDirectoryPathname(file);
0928: if (file.exists()) {
0929: try {
0930: return new Pathname(file.getCanonicalPath());
0931: } catch (IOException e) {
0932: return signal(new LispError(e.getMessage()));
0933: }
0934: }
0935: if (errorIfDoesNotExist) {
0936: StringBuffer sb = new StringBuffer("The file ");
0937: sb.append(defaultedPathname.writeToString());
0938: sb.append(" does not exist.");
0939: return signal(new FileError(sb.toString(),
0940: defaultedPathname));
0941: }
0942: return NIL;
0943: }
0944:
0945: // ### mkdir
0946: private static final Primitive1 MKDIR = new Primitive1("mkdir",
0947: PACKAGE_SYS, false) {
0948: public LispObject execute(LispObject arg)
0949: throws ConditionThrowable {
0950: final Pathname pathname = Pathname.coerceToPathname(arg);
0951: if (pathname.isWild())
0952: signal(new FileError("Bad place for a wild pathname.",
0953: pathname));
0954: Pathname defaultedPathname = mergePathnames(
0955: pathname,
0956: Pathname
0957: .coerceToPathname(_DEFAULT_PATHNAME_DEFAULTS_
0958: .symbolValue()), NIL);
0959: File file = Utilities.getFile(defaultedPathname);
0960: return file.mkdir() ? T : NIL;
0961: }
0962: };
0963:
0964: // ### rename-file filespec new-name => defaulted-new-name, old-truename, new-truename
0965: public static final Primitive2 RENAME_FILE = new Primitive2(
0966: "rename-file", "filespec new-name") {
0967: public LispObject execute(LispObject first, LispObject second)
0968: throws ConditionThrowable {
0969: final Pathname filespec = (Pathname) truename(first, true);
0970: Pathname newName = coerceToPathname(second);
0971: if (newName.isWild())
0972: signal(new FileError("Bad place for a wild pathname."));
0973: newName = mergePathnames(newName, filespec, NIL);
0974: File source = new File(filespec.getNamestring());
0975: File destination = new File(newName.getNamestring());
0976: if (Utilities.isPlatformWindows()) {
0977: if (destination.isFile())
0978: destination.delete();
0979: }
0980: if (!source.renameTo(destination))
0981: return signal(new FileError("Unable to rename "
0982: + filespec.writeToString() + " to "
0983: + newName.writeToString() + "."));
0984: LispThread.currentThread().setValues(newName, filespec,
0985: truename(newName, true));
0986: return newName;
0987: }
0988: };
0989:
0990: // ### file-namestring pathname => namestring
0991: private static final Primitive1 FILE_NAMESTRING = new Primitive1(
0992: "file-namestring", "pathname") {
0993: public LispObject execute(LispObject arg)
0994: throws ConditionThrowable {
0995: Pathname p = coerceToPathname(arg);
0996: StringBuffer sb = new StringBuffer();
0997: if (p.name instanceof AbstractString)
0998: sb.append(p.name.getStringValue());
0999: else if (p.name == Keyword.WILD)
1000: sb.append('*');
1001: else
1002: signal(new SimpleError(
1003: "Pathname has no name component: "
1004: + p.writeToString() + "."));
1005: if (p.type instanceof AbstractString) {
1006: sb.append('.');
1007: sb.append(p.type.getStringValue());
1008: } else if (p.type == Keyword.WILD)
1009: sb.append(".*");
1010: return new SimpleString(sb);
1011: }
1012: };
1013:
1014: // ### host-namestring pathname => namestring
1015: private static final Primitive1 HOST_NAMESTRING = new Primitive1(
1016: "host-namestring", "pathname") {
1017: public LispObject execute(LispObject arg) {
1018: return NIL;
1019: }
1020: };
1021:
1022: static {
1023: try {
1024: LispObject obj = _DEFAULT_PATHNAME_DEFAULTS_
1025: .getSymbolValue();
1026: _DEFAULT_PATHNAME_DEFAULTS_
1027: .setSymbolValue(coerceToPathname(obj));
1028: } catch (Throwable t) {
1029: Debug.trace(t);
1030: }
1031: }
1032: }
|