0001: /*
0002: * FileUtil.java --
0003: *
0004: * This file contains utility methods for file-related operations.
0005: *
0006: * Copyright (c) 1997 Sun Microsystems, Inc.
0007: *
0008: * See the file "license.terms" for information on usage and
0009: * redistribution of this file, and for a DISCLAIMER OF ALL
0010: * WARRANTIES.
0011: *
0012: * RCS: @(#) $Id: FileUtil.java,v 1.7 2005/11/07 07:41:51 mdejong Exp $
0013: *
0014: */
0015:
0016: package tcl.lang;
0017:
0018: import java.io.*;
0019: import java.util.*;
0020:
0021: /*
0022: * This class implements utility methods for file-related operations.
0023: */
0024:
0025: class FileUtil {
0026:
0027: static final int PATH_RELATIVE = 0;
0028: static final int PATH_VOLUME_RELATIVE = 1;
0029: static final int PATH_ABSOLUTE = 2;
0030:
0031: /*
0032: *-----------------------------------------------------------------------------
0033: *
0034: * getWinHomePath --
0035: *
0036: * In the Windows file system, one type of absolute path follows this
0037: * regular expression: ^(//+[a-zA-Z]+/+[a-zA-Z]+)
0038: *
0039: * If "path" doesn't fit the pattern, then return 0.
0040: * If the stopEarly bool is true, then return the index of the first
0041: * non-slash character in path, as soon as we know that path fits the
0042: * pattern. Otherwise, return the index of the slash (or end of string)
0043: * following the entire absolute path.
0044: *
0045: * Results:
0046: * Returns an integer index in path.
0047: *
0048: * Side effects:
0049: * If "path" fits the pattern, and "stopEarly" is not chosen, the absolute
0050: * path is coppied (without extra slashes) to "absBuf". Otherwise, absBuf
0051: * is set to "".
0052: *
0053: *-----------------------------------------------------------------------------
0054: */
0055:
0056: private static int getWinHomePath(String path, // Path to compute home path of.
0057: boolean stopEarly, // Flag to skip side effect.
0058: StringBuffer absBuf) // Buffer to store side effect.
0059: {
0060: int pIndex, oldIndex, firstNonSlash;
0061:
0062: // The first 2 or more chars must be slashes.
0063:
0064: for (pIndex = 0; pIndex < path.length(); pIndex++) {
0065: if (path.charAt(pIndex) != '/') {
0066: break;
0067: }
0068: }
0069: if (pIndex < 2) {
0070: absBuf.setLength(0);
0071: return 0;
0072: }
0073: firstNonSlash = pIndex;
0074:
0075: // The next 1 or more chars may not be slashes.
0076:
0077: for (; pIndex < path.length(); pIndex++) {
0078: if (path.charAt(pIndex) == '/') {
0079: break;
0080: }
0081: }
0082: if (pIndex == firstNonSlash) {
0083: absBuf.setLength(0);
0084: return 0;
0085: }
0086: absBuf.ensureCapacity(absBuf.length() + path.length());
0087: absBuf.append("//");
0088: absBuf.append(path.substring(firstNonSlash, pIndex));
0089:
0090: // The next 1 or more chars must be slashes.
0091:
0092: oldIndex = pIndex;
0093: for (; pIndex < path.length(); pIndex++) {
0094: if (path.charAt(pIndex) != '/') {
0095: if (pIndex == oldIndex) {
0096: absBuf.setLength(0);
0097: return 0;
0098: }
0099:
0100: // We know that the path fits the pattern.
0101:
0102: if (stopEarly) {
0103: absBuf.setLength(0);
0104: return firstNonSlash;
0105: }
0106: firstNonSlash = pIndex;
0107:
0108: // Traverse the path until a new slash (or end of string) is found.
0109: // Return the index of the new slash.
0110:
0111: pIndex++;
0112: for (; pIndex < path.length(); pIndex++) {
0113: if (path.charAt(pIndex) == '/') {
0114: break;
0115: }
0116: }
0117: absBuf.append('/');
0118: absBuf.append(path.substring(firstNonSlash, pIndex));
0119: return pIndex;
0120: }
0121: }
0122: absBuf.setLength(0);
0123: return 0;
0124: }
0125:
0126: /*
0127: *-----------------------------------------------------------------------------
0128: *
0129: * beginsWithLetterColon --
0130: *
0131: * Determine whether a given windows path begins with [a-zA-Z]:
0132: * Return O if path doesn't begin with [a-zA-Z]:
0133: * Return 3 if path begins with [a-zA-Z]:/
0134: * Otherwise, return 2.
0135: *
0136: * Results:
0137: * Returns an integer.
0138: *
0139: * Side effects:
0140: * None.
0141: *
0142: *-----------------------------------------------------------------------------
0143: */
0144:
0145: private static int beginsWithLetterColon(String path) // Path to check start pattern.
0146: {
0147: if ((path.length() > 1) && (Character.isLetter(path.charAt(0)))
0148: && (path.charAt(1) == ':')) {
0149:
0150: int pIndex;
0151: for (pIndex = 2; pIndex < path.length(); pIndex++) {
0152: if (path.charAt(pIndex) != '/') {
0153: break;
0154: }
0155: }
0156: return pIndex;
0157: }
0158: return 0;
0159: }
0160:
0161: /*
0162: *-----------------------------------------------------------------------------
0163: *
0164: * getWinAbsPath --
0165: *
0166: * If "path" begins with [A-Z]: or '/', return the index of the character
0167: * (or end of string) following the absolute (or volume realtive) path.
0168: * Otherwise, return 0.
0169: *
0170: * Results:
0171: * Returns an integer index in path.
0172: *
0173: * Side effects:
0174: * If "path" begins with [A-Z]: or '/', copy the absolute (or volume
0175: * realtive) path up to the index returned into absBuf, removing extra
0176: * slashes.
0177: *
0178: *-----------------------------------------------------------------------------
0179: */
0180:
0181: private static int getWinAbsPath(String path, // Path for which we find abs path.
0182: StringBuffer absBuf) // Buffer to store side effect.
0183: {
0184: absBuf.setLength(0);
0185:
0186: if (path.length() < 1) {
0187: return 0;
0188: }
0189:
0190: absBuf.ensureCapacity(absBuf.length() + path.length());
0191:
0192: int colonIndex = beginsWithLetterColon(path);
0193: if (colonIndex > 0) {
0194: if (colonIndex > 2) {
0195: absBuf.append(path.substring(0, 3));
0196: } else {
0197: absBuf.append(path.substring(0, 2));
0198: }
0199: return colonIndex;
0200: } else {
0201: int absIndex = getWinHomePath(path, false, absBuf);
0202: if (absIndex > 0) {
0203: return absIndex;
0204: } else if (path.charAt(0) == '/') {
0205: int pIndex;
0206: for (pIndex = 1; pIndex < path.length(); pIndex++) {
0207: if (path.charAt(pIndex) != '/') {
0208: break;
0209: }
0210: }
0211: absBuf.append("/");
0212: return pIndex;
0213: }
0214: }
0215: return 0;
0216: }
0217:
0218: /*
0219: *-----------------------------------------------------------------------------
0220: *
0221: * getDegenerateUnixPath --
0222: *
0223: * Returns the index of the 1st char (or end of string) which nolonger
0224: * follows the degenerate unix-style name pattern:
0225: *
0226: * ^(/+([.][.]?/+)*([.][.]?)?)
0227: *
0228: * Results:
0229: * Returns an int index to "path".
0230: *
0231: * Side effects:
0232: * None.
0233: *
0234: *-----------------------------------------------------------------------------
0235: */
0236:
0237: private static int getDegenerateUnixPath(String path) // Path to check.
0238: {
0239: int pIndex = 0;
0240:
0241: while ((pIndex < path.length()) && (path.charAt(pIndex) == '/')) {
0242: ++pIndex;
0243: }
0244:
0245: // "path" doesn't begin with a '/'.
0246:
0247: if (pIndex == 0) {
0248: return 0;
0249: }
0250: while (pIndex < path.length()) {
0251: String tmpPath = path.substring(pIndex);
0252: if (tmpPath.startsWith("./")) {
0253: pIndex += 2;
0254: } else if (tmpPath.startsWith("../")) {
0255: pIndex += 3;
0256: } else {
0257: break;
0258: }
0259: while ((pIndex < path.length())
0260: && (path.charAt(pIndex) == '/')) {
0261: ++pIndex;
0262: }
0263: }
0264: if ((pIndex < path.length()) && (path.charAt(pIndex) == '.')) {
0265: ++pIndex;
0266: }
0267: if ((pIndex < path.length()) && (path.charAt(pIndex) == '.')) {
0268: ++pIndex;
0269: }
0270:
0271: // pIndex may be 1 past the end of "path".
0272:
0273: return pIndex;
0274: }
0275:
0276: /*
0277: *-----------------------------------------------------------------------------
0278: *
0279: * getPathType --
0280: *
0281: * Determine whether "path" is absolute, volumerelative, or
0282: * relative. It is necessary to perform system specific
0283: * operations.
0284: *
0285: * Results:
0286: * Returns an integer value representing the path type.
0287: *
0288: * Side effects:
0289: * None.
0290: *
0291: *-----------------------------------------------------------------------------
0292: */
0293:
0294: static int getPathType(String path) // Path for which we find pathtype.
0295: {
0296: char c;
0297: if (path.length() < 1) {
0298: return PATH_RELATIVE;
0299: }
0300:
0301: switch (JACL.PLATFORM) {
0302: case JACL.PLATFORM_WINDOWS:
0303: path = path.replace('\\', '/');
0304:
0305: // Windows absolute pathes start with '~' or [a-zA-Z]:/ or home
0306: // path.
0307:
0308: c = path.charAt(0);
0309: if (c == '~') {
0310: return PATH_ABSOLUTE;
0311: }
0312: if (c == '/') {
0313: StringBuffer absBuf = new StringBuffer();
0314: if (getWinHomePath(path, true, absBuf) > 0) {
0315: return PATH_ABSOLUTE;
0316: }
0317: return PATH_VOLUME_RELATIVE;
0318: }
0319: int colonIndex = beginsWithLetterColon(path);
0320: if (colonIndex > 0) {
0321: if (colonIndex > 2) {
0322: return PATH_ABSOLUTE;
0323: }
0324: return PATH_VOLUME_RELATIVE;
0325: }
0326: return PATH_RELATIVE;
0327:
0328: case JACL.PLATFORM_MAC:
0329: if (path.charAt(0) == '~') {
0330: return PATH_ABSOLUTE;
0331: }
0332:
0333: switch (path.indexOf(':')) {
0334: case -1:
0335: // Unix-style name contains no colons. Return absolute iff "path"
0336: // begins with '/' and is not degenerate. Otherwise, return
0337: // relative.
0338:
0339: if ((path.charAt(0) == '/')
0340: && (getDegenerateUnixPath(path) < path.length())) {
0341: return PATH_ABSOLUTE;
0342: }
0343: break;
0344: case 0:
0345: // Mac-style name contains a colon in the first position.
0346:
0347: return PATH_RELATIVE;
0348: default:
0349: // Mac-style name contains a colon, but not in the first position.
0350:
0351: return PATH_ABSOLUTE;
0352: }
0353: return PATH_RELATIVE;
0354:
0355: default:
0356: // Unix absolute pathes start with either '/' or '~'.
0357:
0358: c = path.charAt(0);
0359: if ((c == '/') || (c == '~')) {
0360: return PATH_ABSOLUTE;
0361: }
0362: }
0363: return PATH_RELATIVE;
0364: }
0365:
0366: /*
0367: *-----------------------------------------------------------------------------
0368: *
0369: * getNewFileObj --
0370: *
0371: * Create a new File object with the name "fileName".
0372: *
0373: * Results:
0374: * Returns the newly created File object.
0375: *
0376: * Side effects:
0377: * None.
0378: *
0379: *-----------------------------------------------------------------------------
0380: */
0381:
0382: static File getNewFileObj(Interp interp, // Current interpreter.
0383: String fileName) // File to create object for.
0384: throws TclException {
0385: final boolean debug = false;
0386: fileName = translateFileName(interp, fileName);
0387: if (debug) {
0388: System.out.println("File name is \"" + fileName + "\"");
0389: }
0390: switch (getPathType(fileName)) {
0391: case PATH_RELATIVE:
0392: if (debug) {
0393: System.out.println("File name is PATH_RELATIVE");
0394: }
0395: return new File(interp.getWorkingDir(), fileName);
0396: case PATH_VOLUME_RELATIVE:
0397: if (debug) {
0398: System.out.println("File name is PATH_VOLUME_RELATIVE");
0399: }
0400:
0401: // Something is very wrong if interp.getWorkingDir()
0402: // does not start with C: or another drive letter
0403: String cwd = interp.getWorkingDir().toString();
0404: int index = beginsWithLetterColon(cwd);
0405: if (index == 0) {
0406: throw new TclRuntimeError("interp working directory \""
0407: + cwd + "\" does not start with a drive letter");
0408: }
0409:
0410: // We can not use the joinPath() method because joing("D:/", "/f.txt")
0411: // returns "/f.txt" for some wacky reason. Just do it ourselves.
0412: StringBuffer buff = new StringBuffer();
0413: buff.append(cwd.substring(0, 2));
0414: buff.append('\\');
0415: for (int i = 0; i < fileName.length(); i++) {
0416: if (fileName.charAt(i) != '\\') {
0417: // Once we skip all the \ characters at the front
0418: // append the rest of the fileName onto the buffer
0419: buff.append(fileName.substring(i));
0420: break;
0421: }
0422: }
0423:
0424: fileName = buff.toString();
0425:
0426: if (debug) {
0427: System.out.println("After PATH_VOLUME_RELATIVE join \""
0428: + fileName + "\"");
0429: }
0430:
0431: return new File(fileName);
0432: case PATH_ABSOLUTE:
0433: if (debug) {
0434: System.out.println("File name is PATH_ABSOLUTE");
0435: }
0436: return new File(fileName);
0437: default:
0438: throw new TclRuntimeError("type for fileName \"" + fileName
0439: + "\" not matched in case statement");
0440: }
0441: }
0442:
0443: /*
0444: *-----------------------------------------------------------------------------
0445: *
0446: * appendComponent --
0447: *
0448: * Append "component" to "buf" while eliminating extra slashes.
0449: *
0450: * Results:
0451: * None.
0452: *
0453: * Side effects:
0454: * A mangled version of "component" is appended to "buf".
0455: *
0456: *-----------------------------------------------------------------------------
0457: */
0458:
0459: private static void appendComponent(String component, // Component to append.
0460: int compIndex, // Current index in the component.
0461: int compSize, // Index following last in component.
0462: StringBuffer buf) // Buffer to append the component.
0463: {
0464: for (; compIndex < component.length(); compIndex++) {
0465: char c = component.charAt(compIndex);
0466: if (c == '/') {
0467: // Eliminate duplicate slashes.
0468:
0469: while ((compIndex < compSize)
0470: && (component.charAt(compIndex + 1) == '/')) {
0471: compIndex++;
0472: }
0473:
0474: // Only add a slash if following non-slash elements exist.
0475:
0476: if (compIndex < compSize) {
0477: buf.ensureCapacity(buf.length() + 1);
0478: buf.append('/');
0479: }
0480: } else {
0481: buf.ensureCapacity(buf.length() + 1);
0482: buf.append(c);
0483: }
0484: }
0485: }
0486:
0487: /*
0488: *-----------------------------------------------------------------------------
0489: *
0490: * joinPath --
0491: *
0492: * Combine a list of pathes into one path. It is necessary to perform
0493: * system specific operations.
0494: *
0495: * Results:
0496: * Returns a path String.
0497: *
0498: * Side effects:
0499: * None.
0500: *
0501: *-----------------------------------------------------------------------------
0502: */
0503:
0504: static String joinPath(Interp interp, // Current interpreter for path join.
0505: TclObject[] argv, // List of pathes to be joined.
0506: int startIndex, // 1st item in argv to join.
0507: int endIndex) // 1st item to ignore.
0508: throws TclException // Thrown if TclList ops fail.
0509: {
0510: StringBuffer result = new StringBuffer();
0511:
0512: switch (JACL.PLATFORM) {
0513: case JACL.PLATFORM_WINDOWS:
0514: // Iterate over all of the components. If a component is
0515: // absolute, then reset the result and start building the
0516: // path from the current component on.
0517:
0518: for (int i = startIndex; i < endIndex; i++) {
0519:
0520: String p = argv[i].toString().replace('\\', '/');
0521: int pIndex = 0;
0522: int pLastIndex = p.length() - 1;
0523:
0524: if (p.length() == 0) {
0525: continue;
0526: }
0527:
0528: StringBuffer absBuf = new StringBuffer();
0529: pIndex = getWinAbsPath(p, absBuf);
0530: if (pIndex > 0) {
0531: // If the path is absolute or volume relative (except those
0532: // beginning with '~'), reset the result buffer to the absolute
0533: // substring.
0534:
0535: result = absBuf;
0536: } else if (p.charAt(0) == '~') {
0537: // If the path begins with '~', reset the result buffer to "".
0538:
0539: result.setLength(0);
0540: } else {
0541: // This is a relative path. Remove the ./ from tilde prefixed
0542: // elements unless it is the first component.
0543:
0544: if ((result.length() != 0)
0545: && (p.regionMatches(pIndex, "./~", 0, 3))) {
0546: pIndex = 2;
0547: }
0548:
0549: // Check to see if we need to append a separator before adding
0550: // this relative component.
0551:
0552: if (result.length() != 0) {
0553: char c = result.charAt(result.length() - 1);
0554: if ((c != '/') /*&& (c != ':')*/) {
0555: result.append('/');
0556: }
0557: }
0558: }
0559:
0560: // Append the element.
0561:
0562: appendComponent(p, pIndex, pLastIndex, result);
0563: pIndex = p.length();
0564: }
0565: return result.toString();
0566:
0567: case JACL.PLATFORM_MAC:
0568: // Iterate over all of the components. If a component is
0569: // absolute, then reset the result and start building the
0570: // path from the current component on.
0571:
0572: boolean needsSep = true;
0573: for (int i = startIndex; i < endIndex; i++) {
0574:
0575: TclObject splitArrayObj[] = TclList.getElements(interp,
0576: splitPath(interp, argv[i].toString()));
0577:
0578: if (splitArrayObj.length == 0) {
0579: continue;
0580: }
0581:
0582: // If 1st path element is absolute, reset the result to "" and
0583: // append the 1st path element to it.
0584:
0585: int start = 0;
0586: String p = splitArrayObj[0].toString();
0587: if ((p.charAt(0) != ':') && (p.indexOf(':') != -1)) {
0588: result.setLength(0);
0589: result.append(p);
0590: start++;
0591: needsSep = false;
0592: }
0593:
0594: // Now append the rest of the path elements, skipping
0595: // : unless it is the first element of the path, and
0596: // watching out for :: et al. so we don't end up with
0597: // too many colons in the result.
0598:
0599: for (int j = start; j < splitArrayObj.length; j++) {
0600:
0601: p = splitArrayObj[j].toString();
0602:
0603: if (p.equals(":")) {
0604: if (result.length() != 0) {
0605: continue;
0606: } else {
0607: needsSep = false;
0608: }
0609: } else {
0610: char c = 'o';
0611: if (p.length() > 1) {
0612: c = p.charAt(1);
0613: }
0614: if (p.charAt(0) == ':') {
0615: if (!needsSep) {
0616: p = p.substring(1);
0617: }
0618: } else {
0619: if (needsSep) {
0620: result.append(':');
0621: }
0622: }
0623: if (c == ':') {
0624: needsSep = false;
0625: } else {
0626: needsSep = true;
0627: }
0628: }
0629: result.append(p);
0630: }
0631: }
0632: return result.toString();
0633:
0634: default:
0635: // Unix platform.
0636:
0637: for (int i = startIndex; i < endIndex; i++) {
0638:
0639: String p = argv[i].toString();
0640: int pIndex = 0;
0641: int pLastIndex = p.length() - 1;
0642:
0643: if (p.length() == 0) {
0644: continue;
0645: }
0646:
0647: if (p.charAt(pIndex) == '/') {
0648: // If the path is absolute (except those beginning with '~'),
0649: // reset the result buffer to the absolute substring.
0650:
0651: while ((pIndex <= pLastIndex)
0652: && (p.charAt(pIndex) == '/')) {
0653: pIndex++;
0654: }
0655: result.setLength(0);
0656: result.append('/');
0657: } else if (p.charAt(pIndex) == '~') {
0658: // If the path begins with '~', reset the result buffer to "".
0659:
0660: result.setLength(0);
0661: } else {
0662: // This is a relative path. Remove the ./ from tilde prefixed
0663: // elements unless it is the first component.
0664:
0665: if ((result.length() != 0)
0666: && (p.regionMatches(pIndex, "./~", 0, 3))) {
0667: pIndex += 2;
0668: }
0669:
0670: // Append a separator if needed.
0671:
0672: if ((result.length() != 0)
0673: && (result.charAt(result.length() - 1) != '/')) {
0674: result.ensureCapacity(result.length() + 1);
0675: result.append('/');
0676: }
0677: }
0678:
0679: // Append the element.
0680:
0681: appendComponent(p, pIndex, pLastIndex, result);
0682: pIndex = p.length();
0683: }
0684: }
0685: return result.toString();
0686: }
0687:
0688: /*
0689: *-----------------------------------------------------------------------------
0690: *
0691: * splitPath --
0692: *
0693: * Turn one path into a list of components. It is necessary to perform
0694: * system specific operations.
0695: *
0696: * Results:
0697: * Returns a Tcl List Object.
0698: *
0699: * Side effects:
0700: * None.
0701: *
0702: *-----------------------------------------------------------------------------
0703: */
0704:
0705: static TclObject splitPath(Interp interp, // Current interpreter for path split.
0706: String path) // Path to be split.
0707: throws TclException // Thrown if TclList ops fail.
0708: {
0709: TclObject resultListObj = TclList.newInstance();
0710: TclObject componentObj;
0711: String component = "";
0712: String tmpPath;
0713: boolean foundComponent = false;
0714: boolean convertDotToColon = false;
0715: boolean isColonSeparator = false;
0716: boolean appendColon = false;
0717: boolean prependColon = false;
0718: String this Dir = "./";
0719:
0720: // If the path is the empty string, returnan empty result list.
0721:
0722: if (path.length() == 0) {
0723: return resultListObj;
0724: }
0725:
0726: // Handling the 1st component is file system dependent.
0727:
0728: switch (JACL.PLATFORM) {
0729: case JACL.PLATFORM_WINDOWS:
0730: tmpPath = path.replace('\\', '/');
0731:
0732: StringBuffer absBuf = new StringBuffer();
0733: int absIndex = getWinAbsPath(tmpPath, absBuf);
0734: if (absIndex > 0) {
0735: componentObj = TclString.newInstance(absBuf.toString());
0736: TclList.append(interp, resultListObj, componentObj);
0737: tmpPath = tmpPath.substring(absIndex);
0738: foundComponent = true;
0739: }
0740: break;
0741:
0742: case JACL.PLATFORM_MAC:
0743:
0744: tmpPath = "";
0745: this Dir = ":";
0746:
0747: switch (path.indexOf(':')) {
0748: case -1:
0749: // Unix-style name contains no colons.
0750:
0751: if (path.charAt(0) != '/') {
0752: tmpPath = path;
0753: convertDotToColon = true;
0754: if (path.charAt(0) == '~') {
0755: // If '~' is the first char, then append a colon to end
0756: // of the 1st component.
0757:
0758: appendColon = true;
0759: }
0760: break;
0761: }
0762: int degenIndex = getDegenerateUnixPath(path);
0763: if (degenIndex < path.length()) {
0764: // First component of absolute unix path is followed by a ':',
0765: // instead of being preceded by a degenerate unix-style
0766: // pattern.
0767:
0768: tmpPath = path.substring(degenIndex);
0769: convertDotToColon = true;
0770: appendColon = true;
0771: break;
0772: }
0773:
0774: // Degenerate unix path can't be split. Return a list with one
0775: // element: ":" prepended to "path".
0776:
0777: componentObj = TclString.newInstance(":" + path);
0778: TclList.append(interp, resultListObj, componentObj);
0779: return resultListObj;
0780: case 0:
0781: // Relative mac-style name contains a colon in the first position.
0782:
0783: if (path.length() == 1) {
0784: // If path == ":", then return a list with ":" as its only
0785: // element.
0786:
0787: componentObj = TclString.newInstance(":");
0788: TclList.append(interp, resultListObj, componentObj);
0789: return resultListObj;
0790: }
0791:
0792: // For each component, if slashes exist in the remaining filename,
0793: // prepend a colon to the component. Since this path is relative,
0794: // pretend that we have already processed 1 components so a
0795: // tilde-prefixed 1st component will have ":" prepended to it.
0796:
0797: tmpPath = path.substring(1);
0798: foundComponent = true;
0799: prependColon = true;
0800: isColonSeparator = true;
0801: break;
0802:
0803: default:
0804: // Absolute mac-style name contains a colon, but not in the first
0805: // position. Append a colon to the first component, and, for each
0806: // following component, if slashes exist in the remaining filename,
0807: // prepend a colon to the component.
0808:
0809: tmpPath = path;
0810: appendColon = true;
0811: prependColon = true;
0812: isColonSeparator = true;
0813: break;
0814: }
0815: break;
0816:
0817: default:
0818: // Unix file name: if the first char is a "/", append "/" to the result
0819: // list.
0820:
0821: if (path.charAt(0) == '/') {
0822: componentObj = TclString.newInstance("/");
0823: TclList.append(interp, resultListObj, componentObj);
0824: tmpPath = path.substring(1);
0825: foundComponent = true;
0826: } else {
0827: tmpPath = path;
0828: }
0829: }
0830:
0831: // Iterate over all of the components of the path.
0832:
0833: int sIndex = 0;
0834: while (sIndex != -1) {
0835: if (isColonSeparator) {
0836: sIndex = tmpPath.indexOf(":");
0837: // process adjacent ':'
0838:
0839: if (sIndex == 0) {
0840: componentObj = TclString.newInstance("::");
0841: TclList.append(interp, resultListObj, componentObj);
0842: foundComponent = true;
0843: tmpPath = tmpPath.substring(sIndex + 1);
0844: continue;
0845: }
0846: } else {
0847: sIndex = tmpPath.indexOf("/");
0848: // Ignore a redundant '/'
0849:
0850: if (sIndex == 0) {
0851: tmpPath = tmpPath.substring(sIndex + 1);
0852: continue;
0853: }
0854: }
0855: if (sIndex == -1) {
0856: // Processing the last component. If it is empty, exit loop.
0857:
0858: if (tmpPath.length() == 0) {
0859: break;
0860: }
0861: component = tmpPath;
0862: } else {
0863: component = tmpPath.substring(0, sIndex);
0864: }
0865:
0866: if (convertDotToColon
0867: && (component.equals(".") || component.equals(".."))) {
0868: // If platform = MAC, convert .. to :: or . to :
0869:
0870: component = component.replace('.', ':');
0871: }
0872: if (foundComponent) {
0873: if (component.charAt(0) == '~') {
0874: // If a '~' preceeds a component (other than the 1st one), then
0875: // prepend "./" or ":" to the component.
0876:
0877: component = this Dir + component;
0878: } else if (prependColon) {
0879: // If the prependColon flag is set, either unset it or prepend
0880: // ":" to the component, depending on whether any '/'s remain
0881: // in tmpPath.
0882:
0883: if (tmpPath.indexOf('/') == -1) {
0884: prependColon = false;
0885: } else {
0886: component = ":" + component;
0887: }
0888: }
0889: } else if (appendColon) {
0890: //If platform = MAC, append a ':' to the first component.
0891:
0892: component = component + ":";
0893: }
0894: componentObj = TclString.newInstance(component);
0895: TclList.append(interp, resultListObj, componentObj);
0896: foundComponent = true;
0897: tmpPath = tmpPath.substring(sIndex + 1);
0898: }
0899: return resultListObj;
0900: }
0901:
0902: /*
0903: *-----------------------------------------------------------------------------
0904: *
0905: * doTildeSubst --
0906: *
0907: * Given a string following a tilde, this routine returns the
0908: * corresponding home directory.
0909: *
0910: * Results:
0911: * The result is a string containing the home directory in native format.
0912: * Throws an error if it can't find the env(HOME) variable or the
0913: * specified user doesn't exist..
0914: *
0915: * Side effects:
0916: * None.
0917: *
0918: *-----------------------------------------------------------------------------
0919: */
0920:
0921: static String doTildeSubst(Interp interp, // Current interpreter.
0922: String user) // User whose home we must find.
0923: throws TclException // Thrown if env(HOME) is not set or if
0924: // another user is requested.
0925: {
0926: String dir;
0927:
0928: if (user.length() == 0) {
0929: try {
0930: dir = interp.getVar("env", "HOME", TCL.GLOBAL_ONLY)
0931: .toString();
0932: } catch (Exception e) {
0933: throw new TclException(interp,
0934: "couldn't find HOME environment variable to expand path");
0935: }
0936: return dir;
0937: }
0938:
0939: // WARNING: Java does not support other users. "dir" is always null,
0940: // but it should be the home directory (corresponding to the user name), as
0941: // specified in the password file.
0942:
0943: dir = null;
0944: if (dir == null) {
0945: throw new TclException(interp, "user \"" + user
0946: + "\" doesn't exist");
0947: }
0948: return dir;
0949: }
0950:
0951: /*
0952: *-----------------------------------------------------------------------------
0953: *
0954: * translateFileName --
0955: *
0956: * If the path starts with a tilde, do tilde substitution on the first
0957: * component and join it with the remainder of the path.
0958: * Otherwise, do nothing.
0959: *
0960: * Results:
0961: * Returns the tilde-substituted path.
0962: *
0963: * Side effects:
0964: * None.
0965: *
0966: *-----------------------------------------------------------------------------
0967: */
0968:
0969: static String translateFileName(Interp interp, // Current interpreter for path split.
0970: String path) // Path to be split.
0971: throws TclException // Thrown if tilde subst fails.
0972: {
0973: String fileName = "";
0974:
0975: if ((path.length() == 0) || (path.charAt(0) != '~')) {
0976: // fileName = path;
0977: TclObject joinArrayObj[] = new TclObject[1];
0978: joinArrayObj[0] = TclString.newInstance(path);
0979: fileName = joinPath(interp, joinArrayObj, 0, 1);
0980: } else {
0981: TclObject splitArrayObj[] = TclList.getElements(interp,
0982: splitPath(interp, path));
0983:
0984: String user = splitArrayObj[0].toString().substring(1);
0985:
0986: // Strip the trailing ':' off of a Mac path
0987: // before passing the user name to DoTildeSubst.
0988:
0989: if ((JACL.PLATFORM == JACL.PLATFORM_MAC)
0990: && (user.endsWith(":"))) {
0991: user = user.substring(0, user.length() - 1);
0992: }
0993:
0994: user = doTildeSubst(interp, user);
0995:
0996: // if (splitArrayObj.length < 2) {
0997: // fileName = user;
0998: // } else {
0999: splitArrayObj[0] = TclString.newInstance(user);
1000: fileName = joinPath(interp, splitArrayObj, 0,
1001: splitArrayObj.length);
1002: // }
1003: }
1004:
1005: // Convert forward slashes to backslashes in Windows paths because
1006: // some system interfaces don't accept forward slashes.
1007:
1008: if (JACL.PLATFORM == JACL.PLATFORM_WINDOWS) {
1009: fileName = fileName.replace('/', '\\');
1010: }
1011: return fileName;
1012: }
1013:
1014: /*
1015: *-----------------------------------------------------------------------------
1016: *
1017: * splitAndTranslate --
1018: *
1019: * Split the path. If there is only one component, and it starts with a
1020: * tilde, do tilde substitution and split its result.
1021: *
1022: * Results:
1023: * Returns a Tcl List Object.
1024: *
1025: * Side effects:
1026: * None.
1027: *
1028: *-----------------------------------------------------------------------------
1029: */
1030:
1031: static TclObject splitAndTranslate(Interp interp, // Current interpreter for path split.
1032: String path) // Path to be split.
1033: throws TclException // Thrown if tilde subst, which may be
1034: // called by translateFileName, fails.
1035: {
1036: TclObject splitResult = splitPath(interp, path);
1037:
1038: int len = TclList.getLength(interp, splitResult);
1039: if (len == 1) {
1040: String fileName = TclList.index(interp, splitResult, 0)
1041: .toString();
1042: if (fileName.charAt(0) == '~') {
1043: String user = translateFileName(interp, fileName);
1044: splitResult = splitPath(interp, user);
1045: }
1046: }
1047: return splitResult;
1048: }
1049:
1050: } // end FileUtil class
|