0001: /*
0002: * Directory.java
0003: *
0004: * Copyright (C) 1998-2004 Peter Graves
0005: * $Id: Directory.java,v 1.28 2004/05/22 00:04:59 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.j;
0023:
0024: import gnu.regexp.RE;
0025: import gnu.regexp.REMatch;
0026: import gnu.regexp.RESyntax;
0027: import gnu.regexp.UncheckedRE;
0028: import java.awt.AWTEvent;
0029: import java.awt.event.MouseEvent;
0030: import java.io.BufferedReader;
0031: import java.io.IOException;
0032: import java.io.InputStreamReader;
0033: import java.io.StringReader;
0034: import java.util.ArrayList;
0035: import java.util.Collections;
0036: import java.util.Comparator;
0037: import java.util.List;
0038: import java.util.Vector;
0039: import javax.swing.Icon;
0040: import javax.swing.SwingUtilities;
0041:
0042: public final class Directory extends Buffer {
0043: private static final Preferences preferences = Editor.preferences();
0044:
0045: // This is the flag for this particular directory buffer.
0046: private boolean usingNativeFormat;
0047:
0048: public static final int SORT_BY_NAME = 0;
0049: public static final int SORT_BY_DATE = 1;
0050: public static final int SORT_BY_SIZE = 2;
0051:
0052: private int sortBy = SORT_BY_NAME;
0053:
0054: private String limitPattern;
0055:
0056: private ArrayList entries = new ArrayList();
0057:
0058: private int numMarked = 0;
0059:
0060: private DirectoryHistory history = new DirectoryHistory();
0061:
0062: private static final RE nativeMoveToFilenameRegExp;
0063: private static final RE internalMoveToFilenameRegExp;
0064:
0065: private boolean loadError;
0066:
0067: static {
0068: // Letters.
0069: final String letter = "[[:alpha:]]";
0070:
0071: // Month is 2 or more letters, possibly padded on the right with spaces.
0072: final String month = letter + letter + "+";
0073:
0074: // Year.
0075: final String yyyy = "[0-9][0-9][0-9][0-9]";
0076:
0077: // Day of month.
0078: final String dd = "[ 0-3][0-9][.]?";
0079:
0080: // Month and day (includes following space character).
0081: final String monthAndDay = "(" + month + " *" + dd + " " + "|"
0082: + dd + " " + month + " *" + ")";
0083:
0084: // Time of day.
0085: final String HHMM = "[ 0-2][0-9]:[0-5][0-9]";
0086:
0087: // Time or year.
0088: final String timeOrYear = "(" + HHMM + "|" + " " + yyyy + "|"
0089: + yyyy + " " + ")";
0090:
0091: RESyntax syntax = new RESyntax(RESyntax.RE_SYNTAX_PERL5);
0092: syntax.set(RESyntax.RE_CHAR_CLASSES);
0093:
0094: String traditional = "[0-9]+" + " " + monthAndDay + timeOrYear
0095: + " ";
0096:
0097: // --time-style=long-iso
0098: // -rw-r--r-- 1 peter peter 147 2002-11-13 13:10 notes
0099: // --time-style=iso
0100: // -rw-r--r-- 1 peter peter 69016 11-16 18:29 Directory.java
0101: // -rw-r--r-- 1 peter peter 13274 2001-09-08 thinbox.tar.gz
0102: final String isoMaybeYear = "(" + yyyy + "-)?";
0103: final String isoMonthAndDay = "[01][0-9]-[0-3][0-9]";
0104: final String isoMaybeTime = "(" + HHMM + ")?";
0105: String iso = "[0-9]+" + " " + isoMaybeYear + isoMonthAndDay
0106: + " " + isoMaybeTime + " *";
0107:
0108: // Mac OS X (Pete Kirkham)
0109: // --time-style="+%e %b %Y"
0110: // -rw-r--r-- 1 pete pete 6065 23 Oct 2003 Directories.java
0111: // --time-style="+%e %b %H:%M"
0112: // -rw-r--r-- 1 pete pete 75350 21 May 19:58 Directory.java
0113: String osx = "[0-9]+" + " " + dd + " " + month + " "
0114: + timeOrYear + " ";
0115:
0116: nativeMoveToFilenameRegExp = new UncheckedRE("(" + traditional
0117: + ")|(" + iso + ")|(" + osx + ")", 0, syntax);
0118:
0119: internalMoveToFilenameRegExp = new UncheckedRE(":[0-5][0-9]"
0120: + " ");
0121: }
0122:
0123: public static final RE getNativeMoveToFilenameRegExp() {
0124: return nativeMoveToFilenameRegExp;
0125: }
0126:
0127: public static final RE getInternalMoveToFilenameRegExp() {
0128: return internalMoveToFilenameRegExp;
0129: }
0130:
0131: public Directory(File dir) {
0132: super ();
0133: supportsUndo = false;
0134: setFile(dir);
0135: type = TYPE_DIRECTORY;
0136: readOnly = true;
0137: mode = DirectoryMode.getMode();
0138: formatter = mode.getFormatter(this );
0139: setInitialized(true);
0140: }
0141:
0142: public Directory(File dir, String listing) {
0143: this (dir);
0144: setListing(listing);
0145: }
0146:
0147: public final boolean isUsingNativeFormat() {
0148: return usingNativeFormat;
0149: }
0150:
0151: public File getCurrentDirectory() {
0152: return getFile();
0153: }
0154:
0155: public int getSortBy() {
0156: return sortBy;
0157: }
0158:
0159: public Position getInitialDotPos() {
0160: Line line = getFirstLine();
0161: Line upLine = null; // We'll put dot here if directory is empty.
0162: while (true) {
0163: if (line.next() == null)
0164: break;
0165: String name = getName(line);
0166: if (name != null) {
0167: if (name.equals(".."))
0168: upLine = line;
0169: else if (!name.equals("."))
0170: return new Position(line, getNameOffset(line));
0171: }
0172: line = line.next();
0173: }
0174: // Directory is empty.
0175: if (upLine != null)
0176: return new Position(upLine, getNameOffset(upLine));
0177: else
0178: return new Position(line, getNameOffset(line));
0179: }
0180:
0181: private void setLimitPattern(String s) {
0182: if (s == null || s.length() == 0)
0183: limitPattern = null;
0184: else
0185: limitPattern = s;
0186: }
0187:
0188: public final String getLimitPattern() {
0189: return limitPattern;
0190: }
0191:
0192: public static void dirLimit() {
0193: final Editor editor = Editor.currentEditor();
0194: final Buffer buffer = editor.getBuffer();
0195: if (buffer instanceof Directory) {
0196: Directory directory = (Directory) buffer;
0197: InputDialog dialog = new InputDialog(editor, "Pattern:",
0198: "Limit", directory.getLimitPattern());
0199: dialog.setHistory(new History("dirLimit"));
0200: editor.centerDialog(dialog);
0201: dialog.show();
0202: String pattern = dialog.getInput();
0203: // A null pattern means the user cancelled the input dialog.
0204: if (pattern != null) {
0205: editor.repaintNow();
0206: directory.limit(pattern);
0207: }
0208: }
0209: }
0210:
0211: public static void dirLimit(String pattern) {
0212: final Editor editor = Editor.currentEditor();
0213: final Buffer buffer = editor.getBuffer();
0214: if (buffer instanceof Directory)
0215: ((Directory) buffer).limit(pattern);
0216: }
0217:
0218: public static void dirUnlimit() {
0219: final Editor editor = Editor.currentEditor();
0220: final Buffer buffer = editor.getBuffer();
0221: if (buffer instanceof Directory) {
0222: Directory directory = (Directory) buffer;
0223: if (directory.getLimitPattern() != null)
0224: directory.limit(null);
0225: }
0226: }
0227:
0228: private void limit(String pattern) {
0229: if (pattern != null) {
0230: pattern = pattern.trim();
0231: if (pattern.length() == 0)
0232: pattern = null;
0233: }
0234: boolean reload = false;
0235: if (pattern == null && limitPattern != null)
0236: reload = true;
0237: else if (pattern != null && !pattern.equals(limitPattern))
0238: reload = true;
0239: if (reload) {
0240: final Editor editor = Editor.currentEditor();
0241: editor.setWaitCursor();
0242: String name = null;
0243: Line dotLine = editor.getDotLine();
0244: if (dotLine instanceof DirectoryLine)
0245: name = getName(dotLine);
0246: setLimitPattern(pattern);
0247: if (getListing() != null)
0248: reloadFromListing();
0249: else
0250: reload();
0251: Line line = findName(name);
0252: Position pos;
0253: if (line != null)
0254: pos = new Position(line, getNameOffset(line));
0255: else
0256: pos = getInitialDotPos();
0257: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
0258: Editor ed = it.nextEditor();
0259: if (ed.getBuffer() == this ) {
0260: ed.setTopLine(getFirstLine());
0261: ed.setDot(pos);
0262: ed.setMark(null);
0263: ed.moveCaretToDotCol();
0264: ed.setUpdateFlag(REFRAME | REPAINT);
0265: }
0266: }
0267: editor.setDefaultCursor();
0268: }
0269: }
0270:
0271: public void rescan() {
0272: final File file = getFile();
0273: if (file.isRemote())
0274: DirectoryCache.getDirectoryCache()
0275: .purge(file.getHostName());
0276: reload();
0277: if (loadError)
0278: return;
0279: boolean rescanned = false;
0280: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
0281: Editor ed = it.nextEditor();
0282: if (ed.getBuffer() == this ) {
0283: if (ed.getSidebar() != null) {
0284: NavigationComponent c = ed.getSidebar()
0285: .getBottomComponent();
0286: if (c instanceof DirectoryTree) {
0287: DirectoryTree tree = (DirectoryTree) c;
0288: DirectoryTreeModel treeModel = tree
0289: .getTreeModel();
0290: if (!rescanned) {
0291: treeModel.rescan(file);
0292: rescanned = true;
0293: }
0294: tree.refresh();
0295: }
0296: }
0297: }
0298: }
0299: }
0300:
0301: public synchronized void reload() {
0302: ArrayList editors = new ArrayList();
0303:
0304: // Remember the name of the current file in every editor.
0305: ArrayList names = new ArrayList();
0306:
0307: // Remember the line number in every editor.
0308: ArrayList lineNumbers = new ArrayList();
0309:
0310: // Remember the top line of the display in every editor.
0311: ArrayList topLineNumbers = new ArrayList();
0312:
0313: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
0314: Editor ed = it.nextEditor();
0315: String name = null;
0316: int lineNumber = 0;
0317: int topLineNumber = 0;
0318: if (ed.getBuffer() == this ) {
0319: name = getName(ed.getDotLine());
0320: lineNumber = ed.getDotLineNumber();
0321: topLineNumber = ed.getDisplay().getTopLineNumber();
0322: }
0323: editors.add(ed);
0324: names.add(name);
0325: lineNumbers.add(new Integer(lineNumber));
0326: topLineNumbers.add(new Integer(topLineNumber));
0327: }
0328:
0329: empty();
0330: entries.clear();
0331: numMarked = 0;
0332: setListing(null);
0333:
0334: load();
0335:
0336: // Restore the status quo in every window.
0337: for (int i = 0; i < editors.size(); i++) {
0338: final Editor ed = (Editor) editors.get(i);
0339: if (ed.getBuffer() == this ) {
0340: Line dotLine = findName((String) names.get(i));
0341: if (dotLine == null) {
0342: dotLine = getLine(((Integer) lineNumbers.get(i))
0343: .intValue());
0344: if (dotLine == null) {
0345: if (getFirstLine() == null) {
0346: Debug.bug();
0347: appendLine("");
0348: }
0349: dotLine = getFirstLine();
0350: while (dotLine.next() != null)
0351: dotLine = dotLine.next();
0352: }
0353: }
0354: ed.setDot(dotLine, getNameOffset(dotLine));
0355: ed.setMark(null);
0356: final Display display = ed.getDisplay();
0357: display.setShift(0);
0358: display.moveCaretToDotCol();
0359:
0360: Line line = getLine(((Integer) topLineNumbers.get(i))
0361: .intValue());
0362: if (line == null) {
0363: Debug.assertTrue(getFirstLine() != null);
0364: line = getFirstLine();
0365: while (line.next() != null)
0366: line = line.next();
0367: }
0368: display.setTopLine(line);
0369: display.setUpdateFlag(REPAINT);
0370: ed.updateLocation();
0371: }
0372: }
0373: }
0374:
0375: private synchronized void reloadFromListing() {
0376: Debug.assertTrue(getListing() != null);
0377: empty();
0378: entries.clear();
0379: numMarked = 0;
0380: load();
0381: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
0382: Editor ed = it.nextEditor();
0383: if (ed.getBuffer() == this ) {
0384: final Line line = getFirstLine();
0385: ed.setDot(line, getNameOffset(line));
0386: ed.setMark(null);
0387: final Display display = ed.getDisplay();
0388: display.setShift(0);
0389: display.moveCaretToDotCol();
0390: display.setTopLine(getFirstLine());
0391: display.setUpdateFlag(REPAINT);
0392: ed.updateLocation();
0393: }
0394: }
0395: }
0396:
0397: // Called only from synchronized methods.
0398: private void addEntry(String name) {
0399: Debug.assertTrue(!usingNativeFormat);
0400: File f = File.getInstance(getFile(), name);
0401: if (f == null)
0402: return;
0403: DirectoryEntry de;
0404: if (f.isDirectory())
0405: de = new DirectoryEntry(name, f.lastModified(), 0, true);
0406: else
0407: de = new DirectoryEntry(name, f.lastModified(), f.length());
0408: if (!name.equals(".") && !name.equals("..")) {
0409: try {
0410: String cp = f.getCanonicalPath();
0411: String ap = f.getAbsolutePath();
0412: if (!cp.equals(ap)) {
0413: final String canonicalPath = getFile()
0414: .canonicalPath();
0415: if (cp.startsWith(canonicalPath
0416: + LocalFile.getSeparator()))
0417: de.setLinkedTo(cp.substring(canonicalPath
0418: .length() + 1));
0419: else
0420: de.setLinkedTo(cp);
0421: }
0422: } catch (IOException e) {
0423: Log.error(e);
0424: }
0425: }
0426: entries.add(de);
0427: }
0428:
0429: private final void appendLine(DirectoryEntry entry) {
0430: appendLine(new DirectoryLine(entry));
0431: }
0432:
0433: private synchronized DirectoryEntry findEntry(String name) {
0434: for (int i = 0; i < entries.size(); i++) {
0435: DirectoryEntry entry = (DirectoryEntry) entries.get(i);
0436: if (name.equals(entry.getName()))
0437: return entry;
0438: }
0439: return null;
0440: }
0441:
0442: private synchronized DirectoryEntry findNativeEntry(String string) {
0443: for (int i = 0; i < entries.size(); i++) {
0444: DirectoryEntry entry = (DirectoryEntry) entries.get(i);
0445: if (string.equals(entry.getString()))
0446: return entry;
0447: }
0448: return null;
0449: }
0450:
0451: private synchronized void sort() {
0452: if (usingNativeFormat) {
0453: Debug.bug();
0454: return;
0455: }
0456: if (sortBy == SORT_BY_NAME)
0457: sortByName();
0458: else if (sortBy == SORT_BY_DATE)
0459: sortByDate();
0460: else if (sortBy == SORT_BY_SIZE)
0461: sortBySize();
0462: }
0463:
0464: // Called only from sort().
0465: private void sortByName() {
0466: Debug.assertTrue(!usingNativeFormat);
0467: Comparator comparator = new Comparator() {
0468: public int compare(Object o1, Object o2) {
0469: String name1 = ((DirectoryEntry) o1).getName();
0470: String name2 = ((DirectoryEntry) o2).getName();
0471: return name1.compareToIgnoreCase(name2);
0472: }
0473: };
0474: Collections.sort(entries, comparator);
0475: }
0476:
0477: // Called only from sort().
0478: private void sortByDate() {
0479: Comparator comparator = new Comparator() {
0480: public int compare(Object o1, Object o2) {
0481: // Most recent dates first.
0482: long date1 = ((DirectoryEntry) o1).getDate();
0483: long date2 = ((DirectoryEntry) o2).getDate();
0484: if (date1 > date2)
0485: return -1;
0486: if (date1 < date2)
0487: return 1;
0488: return 0;
0489: }
0490: };
0491: Collections.sort(entries, comparator);
0492: }
0493:
0494: // Called only from sort().
0495: private void sortBySize() {
0496: Comparator comparator = new Comparator() {
0497: public int compare(Object o1, Object o2) {
0498: // Biggest files first.
0499: long size1 = ((DirectoryEntry) o1).getSize();
0500: long size2 = ((DirectoryEntry) o2).getSize();
0501: if (size1 > size2)
0502: return -1;
0503: if (size1 < size2)
0504: return 1;
0505: return 0;
0506: }
0507: };
0508: Collections.sort(entries, comparator);
0509: }
0510:
0511: // Called only from synchronized methods.
0512: private void addEntriesToBuffer() {
0513: final int size = entries.size();
0514: if (preferences
0515: .getBooleanProperty(
0516: Property.DIR_SORT_DIRECTORIES_FIRST,
0517: !usingNativeFormat)) {
0518: // Add lines to the buffer in two passes so directories will always be on top.
0519: for (int i = 0; i < size; i++) {
0520: DirectoryEntry entry = (DirectoryEntry) entries.get(i);
0521: if (entry.isDirectory())
0522: appendLine(entry);
0523: }
0524: for (int i = 0; i < size; i++) {
0525: DirectoryEntry entry = (DirectoryEntry) entries.get(i);
0526: if (!entry.isDirectory())
0527: appendLine(entry);
0528: }
0529: } else {
0530: for (int i = 0; i < size; i++) {
0531: DirectoryEntry entry = (DirectoryEntry) entries.get(i);
0532: appendLine(entry);
0533: }
0534: }
0535: if (getFirstLine() == null)
0536: appendLine("");
0537: setLoaded(true);
0538: }
0539:
0540: public static void dirCycleSortBy() {
0541: final Editor editor = Editor.currentEditor();
0542: final Buffer buffer = editor.getBuffer();
0543: if (buffer instanceof Directory) {
0544: editor.setWaitCursor();
0545: ((Directory) buffer).cycleSortBy();
0546: editor.setDefaultCursor();
0547: }
0548: }
0549:
0550: private synchronized void cycleSortBy() {
0551: if (sortBy == SORT_BY_NAME)
0552: sortBy = SORT_BY_DATE;
0553: else if (sortBy == SORT_BY_DATE)
0554: sortBy = SORT_BY_SIZE;
0555: else
0556: sortBy = SORT_BY_NAME;
0557: resort();
0558: }
0559:
0560: public void resort(int sortBy) {
0561: if (this .sortBy != sortBy) {
0562: this .sortBy = sortBy;
0563: resort();
0564: }
0565: }
0566:
0567: private void resort() {
0568: if (usingNativeFormat) {
0569: reload();
0570: } else {
0571: sort();
0572: try {
0573: lockWrite();
0574: } catch (InterruptedException e) {
0575: Log.error(e);
0576: return; // Shouldn't happen.
0577: }
0578: try {
0579: empty();
0580: addEntriesToBuffer();
0581: renumber();
0582: } finally {
0583: unlockWrite();
0584: }
0585: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
0586: Editor ed = it.nextEditor();
0587: if (ed.getBuffer() == this ) {
0588: ed.setDot(getFirstLine(), 0);
0589: ed.setMark(null);
0590: final Display display = ed.getDisplay();
0591: display.setTopLine(getFirstLine());
0592: display.setShift(0);
0593: display.setCaretCol(0);
0594: display.setUpdateFlag(REPAINT);
0595: ed.updateLocation();
0596: }
0597: }
0598: }
0599: }
0600:
0601: private synchronized void loadInternal() {
0602: // Default is true for Unix, false otherwise. User can override default.
0603: boolean useNativeFormat = preferences.getBooleanProperty(
0604: Property.DIR_USE_NATIVE_FORMAT, Platform
0605: .isPlatformUnix());
0606: if (useNativeFormat) {
0607: if (!Utilities.haveLs())
0608: useNativeFormat = false;
0609: }
0610: loadError = false;
0611: try {
0612: final DirectoryFilenameFilter dff;
0613: if (limitPattern != null)
0614: dff = new DirectoryFilenameFilter(limitPattern);
0615: else
0616: dff = null;
0617: final File file = getFile();
0618: if (useNativeFormat || file.isRemote()) {
0619: usingNativeFormat = true;
0620: String flags = "-la"; // Default is sort by name.
0621: if (sortBy == SORT_BY_DATE)
0622: flags = "-lat";
0623: else if (sortBy == SORT_BY_SIZE)
0624: flags = "-laS";
0625: String extraOptions = getStringProperty(Property.LS_EXTRA_OPTIONS);
0626: if (extraOptions != null) {
0627: flags += " ";
0628: flags += extraOptions;
0629: Log
0630: .debug("Directory.loadInternal flags = "
0631: + flags);
0632: }
0633: BufferedReader reader = null;
0634: if (getListing() != null) {
0635: reader = new BufferedReader(new StringReader(
0636: getListing()));
0637: } else if (file instanceof FtpFile
0638: || file instanceof SshFile) {
0639: setListing(file.getDirectoryListing());
0640: if (getListing() != null)
0641: reader = new BufferedReader(new StringReader(
0642: getListing()));
0643: else
0644: loadError = true;
0645: } else {
0646: // Local file.
0647: Process process = null;
0648: if (Platform.isPlatformUnix()) {
0649: String cmd = "(\\cd \"" + file.canonicalPath()
0650: + "\" && \\ls " + flags + ")";
0651: String[] cmdarray = { "/bin/sh", "-c", cmd };
0652: process = Runtime.getRuntime().exec(cmdarray);
0653: } else {
0654: // Windows.
0655: String cp = file.canonicalPath();
0656: // Convert "C:\" into "//c" for Cygwin ls.
0657: if (cp.length() == 3 && cp.charAt(1) == ':'
0658: && cp.charAt(2) == '\\')
0659: cp = "//"
0660: + Character.toLowerCase(cp
0661: .charAt(0));
0662: String[] cmdarray = { "ls", flags, cp };
0663: process = Runtime.getRuntime().exec(cmdarray);
0664: }
0665: reader = new BufferedReader(new InputStreamReader(
0666: process.getInputStream()));
0667: }
0668: if (reader != null) {
0669: String s;
0670: while ((s = reader.readLine()) != null) {
0671: DirectoryEntry entry = DirectoryEntry
0672: .getDirectoryEntry(s, dff);
0673: if (entry != null)
0674: entries.add(entry);
0675: }
0676: }
0677: } else {
0678: usingNativeFormat = false;
0679: boolean dirIsRoot = false;
0680: if (Platform.isPlatformWindows()) {
0681: String cp = file.canonicalPath();
0682: if (cp.length() == 3 && cp.endsWith(":\\")) // "C:\"
0683: dirIsRoot = true;
0684: }
0685: if (!dirIsRoot) {
0686: addEntry(".");
0687: addEntry("..");
0688: }
0689: String[] names = file.list();
0690: if (names != null) {
0691: if (dff == null) {
0692: for (int i = 0; i < names.length; i++)
0693: addEntry(names[i]);
0694: } else {
0695: for (int i = 0; i < names.length; i++) {
0696: if (dff.accepts(names[i]))
0697: addEntry(names[i]);
0698: else {
0699: File f = File.getInstance(file,
0700: names[i]);
0701: if (f != null && f.isDirectory())
0702: addEntry(names[i]);
0703: }
0704: }
0705: }
0706: }
0707: sort();
0708: }
0709: try {
0710: lockWrite();
0711: } catch (InterruptedException e) {
0712: Log.error(e);
0713: return; // Shouldn't happen.
0714: }
0715: try {
0716: addEntriesToBuffer();
0717: long totalSize = getTotalSize();
0718: if (totalSize > 0) {
0719: int end;
0720: if (usingNativeFormat) {
0721: end = getFileSizeEndOffset();
0722: if (end <= 0)
0723: end = 45;
0724: } else {
0725: int nameOffset = getNameOffset();
0726: end = nameOffset - 19;
0727: if (end <= 0)
0728: end = 13;
0729: }
0730: String s = String.valueOf(totalSize);
0731: int begin = end - s.length();
0732: if (begin < 0)
0733: begin = 0;
0734: FastStringBuffer sb = new FastStringBuffer(80);
0735: sb.append(Utilities.spaces(begin));
0736: for (int i = s.length(); i > 0; i--)
0737: sb.append('-');
0738: appendLine(sb.toString());
0739: sb.setLength(0);
0740: sb.append(Utilities.spaces(begin));
0741: sb.append(s);
0742: appendLine(sb.toString());
0743: }
0744: renumber();
0745: } finally {
0746: unlockWrite();
0747: }
0748: } catch (Exception e) {
0749: Log.error(e);
0750: }
0751: }
0752:
0753: private long getTotalSize() {
0754: long totalSize = 0;
0755: int endOffset = -1;
0756: final int limit = entries.size();
0757: for (int i = 0; i < limit; i++) {
0758: DirectoryEntry entry = (DirectoryEntry) entries.get(i);
0759: long size = entry.getSize();
0760: if (size >= 0) {
0761: totalSize += size;
0762: continue;
0763: }
0764: String text = entry.getString();
0765: if (endOffset < 0) {
0766: REMatch match = nativeMoveToFilenameRegExp
0767: .getMatch(text);
0768: if (match != null) {
0769: // The file size is followed by a single space.
0770: endOffset = text
0771: .indexOf(' ', match.getStartIndex());
0772: }
0773: if (endOffset < 0)
0774: endOffset = 42;
0775: }
0776: if (endOffset < text.length()) {
0777: int end;
0778: if (text.charAt(endOffset) == ' ') {
0779: // Expected.
0780: end = endOffset;
0781: } else {
0782: // Encountered unexpected char at endOffset.
0783: REMatch match = nativeMoveToFilenameRegExp
0784: .getMatch(text);
0785: if (match == null)
0786: return totalSize = -1;
0787: end = text.indexOf(' ', match.getStartIndex());
0788: if (end < endOffset) {
0789: // Correct anomaly.
0790: endOffset = end;
0791: Log.debug("endOffset = " + endOffset);
0792: }
0793: }
0794: int begin = text.lastIndexOf(' ', end - 1);
0795: if (begin < 0)
0796: return -1;
0797: try {
0798: size = Long.parseLong(text
0799: .substring(begin + 1, end));
0800: entry.setSize(size);
0801: totalSize += size;
0802: } catch (NumberFormatException e) {
0803: Log.error(e);
0804: return -1;
0805: }
0806: } else
0807: return -1; // Shouldn't happen.
0808: }
0809: return totalSize;
0810: }
0811:
0812: public int load() {
0813: if (!isLoaded()) {
0814: final Editor editor = Editor.currentEditor();
0815: String reading = "Reading directory...";
0816: if (editor != null)
0817: editor.status(reading);
0818: loadInternal();
0819: if (editor != null)
0820: editor.status(reading + "done");
0821: }
0822: return LOAD_COMPLETED;
0823: }
0824:
0825: public void tagFileAtDot() {
0826: final Editor editor = Editor.currentEditor();
0827: Line line = editor.getDotLine();
0828: if (!(line instanceof DirectoryLine))
0829: return;
0830: String name = getName(line);
0831: if (name == null)
0832: return;
0833: if (name.equals("."))
0834: return;
0835: if (name.equals(".."))
0836: return;
0837: DirectoryEntry de = null;
0838: if (usingNativeFormat) {
0839: // Text of line has "T " or " " prepended to DirectoryEntry string.
0840: de = findNativeEntry(line.substring(2));
0841: } else {
0842: de = findEntry(name);
0843: }
0844: if (de != null) {
0845: if (de.isMarked()) {
0846: de.setMarked(false);
0847: --numMarked;
0848: } else {
0849: de.setMarked(true);
0850: ++numMarked;
0851: }
0852: line.setText(de.toString());
0853: editor.update(line);
0854: editor.down();
0855: resetUndo();
0856: }
0857: }
0858:
0859: public void upDir() {
0860: final Editor editor = Editor.currentEditor();
0861: editor.setWaitCursor();
0862: final File parent = getFile().getParentFile();
0863: if (parent == null)
0864: return;
0865: history.truncate();
0866: history.append(getFile(), getName(editor.getDotLine()), editor
0867: .getDotOffset());
0868: history.reset();
0869: String name = getFile().getName();
0870: setFile(parent);
0871: reload();
0872: Line line = findName(name);
0873: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
0874: Editor ed = it.nextEditor();
0875: if (ed.getBuffer() == this ) {
0876: if (line != null)
0877: ed.setDot(line, getNameOffset(line));
0878: else
0879: ed.setDot(getFirstLine(),
0880: getNameOffset(getFirstLine()));
0881: ed.moveCaretToDotCol();
0882: }
0883: }
0884: Sidebar.setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST);
0885: }
0886:
0887: public String getPathAtDot() {
0888: Editor editor = Editor.currentEditor();
0889: if (!(editor.getDotLine() instanceof DirectoryLine))
0890: return null;
0891: String name = getName(editor.getDotLine());
0892: if (name == null)
0893: return null;
0894: final File dir = getFile();
0895: if (name.equals("."))
0896: return dir.netPath();
0897: if (name.equals("..")) {
0898: File parent = dir.getParentFile();
0899: if (parent == null)
0900: return null;
0901: else
0902: return parent.netPath();
0903: }
0904: File file = File.getInstance(dir, name);
0905: if (file == null)
0906: return null;
0907: else
0908: return file.netPath();
0909: }
0910:
0911: public static void dir() {
0912: final Editor editor = Editor.currentEditor();
0913: final Buffer buffer = editor.getBuffer();
0914: if (buffer instanceof Directory)
0915: return;
0916: File directory = editor.getCurrentDirectory();
0917: if (directory == null)
0918: return;
0919: if (directory.getProtocol() == File.PROTOCOL_HTTP)
0920: return;
0921: // FTP, SSH or local.
0922: Buffer buf = Editor.getBuffer(directory);
0923: // buf may be a RemoteBuffer and not specifically a Directory.
0924: if (buf != null) {
0925: File file = buffer.getFile();
0926: editor.makeNext(buf);
0927: editor.switchToBuffer(buf);
0928: if (buf instanceof Directory && file != null) {
0929: Directory dir = (Directory) buf;
0930: Line line = dir.findName(file.getName());
0931: if (line != null) {
0932: editor.setDot(new Position(line, dir
0933: .getNameOffset(line)));
0934: editor.setUpdateFlag(REFRAME);
0935: editor.reframe();
0936: }
0937: }
0938: }
0939: }
0940:
0941: public static void dirOpenFile() {
0942: _dirOpenFile(false);
0943: }
0944:
0945: public static void dirOpenFileAndKillDirectory() {
0946: _dirOpenFile(true);
0947: }
0948:
0949: private static void _dirOpenFile(boolean killDirectory) {
0950: final Editor editor = Editor.currentEditor();
0951: final Buffer buffer = editor.getBuffer();
0952: if (buffer instanceof Directory) {
0953: // If this method is invoked via a mouse event mapping, move dot to
0954: // location of mouse click first.
0955: AWTEvent e = editor.getDispatcher().getLastEvent();
0956: if (e instanceof MouseEvent)
0957: editor.mouseMoveDotToPoint((MouseEvent) e);
0958: ((Directory) buffer).openFileAtDot(killDirectory);
0959: }
0960: }
0961:
0962: private synchronized void openFileAtDot(boolean killDirectory) {
0963: final Editor editor = Editor.currentEditor();
0964: editor.setWaitCursor();
0965: if (!(editor.getDotLine() instanceof DirectoryLine))
0966: return;
0967: String name = getName(editor.getDotLine());
0968: if (name == null)
0969: return;
0970: if (name.equals("."))
0971: return;
0972: if (name.equals("..")) {
0973: upDir();
0974: return;
0975: }
0976: final File dir = getFile();
0977: if (dir.isRemote()) {
0978: String fullpath = dir.canonicalPath();
0979: if (!fullpath.endsWith("/"))
0980: fullpath += "/";
0981: fullpath += name;
0982: File newFile = File.getInstance(dir, fullpath);
0983: Buffer buf = Editor.getBufferList().findBuffer(newFile);
0984: if (buf != null) {
0985: editor.makeNext(buf);
0986: editor.activate(buf);
0987: return;
0988: }
0989: DirectoryLine line = (DirectoryLine) editor.getDotLine();
0990: DirectoryEntry de = line.getDirectoryEntry();
0991: boolean isDirectory = false;
0992: if (de.isDirectory())
0993: isDirectory = true;
0994: else if (de.isLink())
0995: isDirectory = newFile.isDirectory();
0996: if (isDirectory) {
0997: setBusy(true);
0998: history.truncate();
0999: history.append(dir, getName(editor.getDotLine()),
1000: editor.getDotOffset());
1001: history.reset();
1002: empty();
1003: entries.clear();
1004: numMarked = 0;
1005: setListing(null);
1006: for (EditorIterator it = new EditorIterator(); it
1007: .hasNext();) {
1008: Editor ed = it.nextEditor();
1009: if (ed.getBuffer() == this ) {
1010: ed.setTopLine(null);
1011: ed.setDot(null);
1012: ed.setMark(null);
1013: }
1014: }
1015: setFile(newFile);
1016: Runnable reloadRunnable = new Runnable() {
1017: public void run() {
1018: load();
1019: Runnable updateRunnable = new Runnable() {
1020: public void run() {
1021: setBusy(false);
1022: for (EditorIterator it = new EditorIterator(); it
1023: .hasNext();) {
1024: Editor ed = it.nextEditor();
1025: if (ed.getBuffer() == Directory.this ) {
1026: ed.setTopLine(getFirstLine());
1027: ed.setDot(getInitialDotPos());
1028: ed.setMark(null);
1029: ed.moveCaretToDotCol();
1030: ed.setUpdateFlag(REPAINT);
1031: ed.updateDisplay();
1032: ed.updateLocation();
1033: }
1034: }
1035: Sidebar
1036: .setUpdateFlagInAllFrames(SIDEBAR_ALL);
1037: }
1038: };
1039: SwingUtilities.invokeLater(updateRunnable);
1040: }
1041: };
1042: new Thread(reloadRunnable).start();
1043: } else {
1044: // Not a directory.
1045: if (newFile instanceof FtpFile)
1046: buf = new RemoteBuffer((FtpFile) newFile,
1047: FtpSession.getSession((FtpFile) newFile));
1048: else if (newFile instanceof SshFile)
1049: buf = new RemoteBuffer(newFile);
1050: else
1051: Debug.assertTrue(false);
1052: editor.makeNext(buf);
1053: editor.activate(buf);
1054: }
1055: return;
1056: }
1057:
1058: // Local file.
1059: File f = File.getInstance(getFile(), name);
1060: if (!f.exists()) {
1061: editor.status("File not found");
1062: return;
1063: }
1064: if (f.isDirectory()) {
1065: changeDirectory(f);
1066: return;
1067: }
1068: Buffer buf = editor.getBuffer(f);
1069: if (buf != null) {
1070: editor.makeNext(buf);
1071: editor.activate(buf);
1072: }
1073: if (killDirectory)
1074: kill();
1075: }
1076:
1077: public synchronized void changeDirectory(File f) {
1078: if (f.isDirectory()) {
1079: final Editor editor = Editor.currentEditor();
1080: if (f.isLocal() && !f.canRead()) {
1081: showMessageDialog("Directory is not readable");
1082: return;
1083: }
1084: final String name;
1085: final int offset;
1086: if (editor.getDot() != null) {
1087: name = getName(editor.getDotLine());
1088: offset = editor.getDotOffset();
1089: } else {
1090: name = null;
1091: offset = 0;
1092: }
1093: history.truncate();
1094: history.append(getFile(), name, offset);
1095: history.reset();
1096: empty();
1097: entries.clear();
1098: numMarked = 0;
1099: setListing(null);
1100: setFile(f);
1101: load();
1102: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
1103: Editor ed = it.nextEditor();
1104: if (ed.getBuffer() == this ) {
1105: ed.setTopLine(getFirstLine());
1106: ed.setDot(getInitialDotPos());
1107: ed.setMark(null);
1108: ed.moveCaretToDotCol();
1109: ed.setUpdateFlag(REPAINT);
1110: ed.updateLocation();
1111: }
1112: }
1113: Sidebar
1114: .setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST);
1115: }
1116: }
1117:
1118: // No history.
1119: private synchronized void changeDirectory(
1120: DirectoryHistoryEntry entry) {
1121: empty();
1122: entries.clear();
1123: numMarked = 0;
1124: setListing(null);
1125: setFile(entry.file);
1126: load();
1127: Line line = findName(entry.name);
1128: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
1129: Editor ed = it.nextEditor();
1130: if (ed.getBuffer() == this ) {
1131: ed.getDisplay().setTopLine(getFirstLine());
1132: ed.setUpdateFlag(REPAINT);
1133: if (line != null)
1134: ed.setDot(line, entry.offset);
1135: else
1136: ed.setDot(getFirstLine(),
1137: getNameOffset(getFirstLine()));
1138: ed.setMark(null);
1139: ed.moveCaretToDotCol();
1140: ed.updateLocation();
1141: }
1142: }
1143: Sidebar.setUpdateFlagInAllFrames(SIDEBAR_REPAINT_BUFFER_LIST);
1144: }
1145:
1146: public static void dirBack() {
1147: final Editor editor = Editor.currentEditor();
1148: final Buffer buffer = editor.getBuffer();
1149: if (buffer instanceof Directory) {
1150: editor.setWaitCursor();
1151: ((Directory) buffer).back(editor);
1152: editor.setDefaultCursor();
1153: }
1154: }
1155:
1156: private void back(Editor editor) {
1157: boolean atEnd = history.atEnd();
1158: DirectoryHistoryEntry entry = history.getPrevious();
1159: if (entry != null) {
1160: if (atEnd) {
1161: String name = getName(editor.getDotLine());
1162: int offset = editor.getDotOffset();
1163: history.append(getFile(), name, offset);
1164: }
1165: changeDirectory(entry);
1166: } else
1167: editor.status("Can't go back");
1168: }
1169:
1170: public static void dirForward() {
1171: final Editor editor = Editor.currentEditor();
1172: final Buffer buffer = editor.getBuffer();
1173: if (buffer instanceof Directory) {
1174: editor.setWaitCursor();
1175: ((Directory) buffer).forward(editor);
1176: editor.setDefaultCursor();
1177: }
1178: }
1179:
1180: private void forward(Editor editor) {
1181: DirectoryHistoryEntry entry = history.getNext();
1182: if (entry != null)
1183: changeDirectory(entry);
1184: else
1185: editor.status("Can't go forward");
1186: }
1187:
1188: public void browseFileAtDot() {
1189: Editor editor = Editor.currentEditor();
1190: String name = getName(editor.getDotLine());
1191: if (name == null)
1192: return;
1193: File f = File.getInstance(getFile(), name);
1194: if (!f.exists()) {
1195: editor.status("File not found");
1196: return;
1197: }
1198: if (f.isFile()) {
1199: String browser = preferences
1200: .getStringProperty(Property.BROWSER);
1201: if (browser == null || browser.equals("j")) {
1202: WebBuffer.browse(editor, f, null);
1203: return;
1204: }
1205: // Use external browser.
1206: try {
1207: String url = "file://".concat(f.canonicalPath());
1208: String browserOpts = preferences
1209: .getStringProperty(Property.BROWSER_OPTS);
1210: if (browserOpts != null) {
1211: String[] cmdarray = { browser, browserOpts, url };
1212: Process process = Runtime.getRuntime().exec(
1213: cmdarray);
1214: } else {
1215: String[] cmdarray = { browser, url };
1216: Process process = Runtime.getRuntime().exec(
1217: cmdarray);
1218: }
1219: } catch (Exception e) {
1220: Log.error(e);
1221: }
1222: }
1223: }
1224:
1225: public void deleteFiles() {
1226: if (numMarked > 0)
1227: deleteMarkedFiles();
1228: else
1229: deleteFileAtDot();
1230: }
1231:
1232: private void showMessageDialog(String message) {
1233: MessageDialog.showMessageDialog(message, "Error");
1234: }
1235:
1236: private synchronized void deleteMarkedFiles() {
1237: if (numMarked == 0)
1238: return;
1239: Debug.assertTrue(numMarked > 0);
1240: String message = null;
1241: final Editor editor = Editor.currentEditor();
1242: if (numMarked > 1)
1243: message = "Delete " + numMarked + " tagged files?";
1244: else
1245: message = "Delete 1 tagged file?";
1246: boolean confirmed = editor.confirm("Delete Files", message);
1247: if (!confirmed)
1248: return;
1249: boolean directoryWasDeleted = false;
1250: FtpSession session = null;
1251: for (int i = 0; i < entries.size(); i++) {
1252: DirectoryEntry entry = (DirectoryEntry) entries.get(i);
1253: if (!entry.isMarked())
1254: continue;
1255: String name = entry.extractName();
1256: if (name == null) {
1257: // Shouldn't happen.
1258: Debug.assertTrue(false);
1259: continue;
1260: }
1261: File file = File.getInstance(getFile(), name);
1262: if (!file.isRemote() && !file.exists()) {
1263: showMessageDialog(file.canonicalPath() + " not found");
1264: return;
1265: }
1266: String displayName = file.canonicalPath();
1267: boolean isDirectory;
1268: if (file.isRemote()) {
1269: displayName += " on " + file.getHostName();
1270: isDirectory = entry.isDirectory();
1271: } else
1272: isDirectory = file.isDirectory();
1273: boolean succeeded = false;
1274: if (file instanceof FtpFile) {
1275: if (session == null)
1276: session = FtpSession.getSession((FtpFile) file);
1277: if (session != null) {
1278: if (isDirectory)
1279: succeeded = session.removeDirectory(file
1280: .canonicalPath());
1281: else
1282: succeeded = session.deleteFile(file
1283: .canonicalPath());
1284: }
1285: } else {
1286: // Local file.
1287: // Delete the file on disk.
1288: file.delete();
1289:
1290: // Did it work?
1291: succeeded = !file.exists();
1292: }
1293: if (succeeded) {
1294: if (isDirectory)
1295: directoryWasDeleted = true;
1296: } else {
1297: // Deletion failed.
1298: if (isDirectory) {
1299: confirmed = editor.confirm("Delete Files",
1300: "Unable to remove directory " + displayName
1301: + ". Continue?");
1302: } else {
1303: confirmed = editor.confirm("Delete Files",
1304: "Unable to delete " + displayName
1305: + ". Continue?");
1306: }
1307: if (!confirmed)
1308: break;
1309: }
1310: }
1311: if (session != null)
1312: session.unlock();
1313: if (directoryWasDeleted) {
1314: rescan();
1315: } else {
1316: if (getFile().isRemote())
1317: DirectoryCache.getDirectoryCache().purge(getFile());
1318: reload();
1319: }
1320: }
1321:
1322: private void deleteFileAtDot() {
1323: final Editor editor = Editor.currentEditor();
1324: String name = getName(editor.getDotLine());
1325: if (name == null)
1326: return;
1327: if (name.equals("."))
1328: return;
1329: if (name.equals(".."))
1330: return;
1331: File file = File.getInstance(getFile(), name);
1332: if (!file.isRemote() && !file.exists()) {
1333: showMessageDialog("File not found");
1334: return;
1335: }
1336: String displayName = file.getName();
1337: final boolean isDirectory;
1338: if (file.isRemote()) {
1339: displayName += " on " + file.getHostName();
1340: isDirectory = editor.getDotLine().getText().trim()
1341: .startsWith("d");
1342: } else
1343: isDirectory = file.isDirectory();
1344: final String title, prompt;
1345: if (isDirectory) {
1346: title = "Remove Directory";
1347: prompt = "Remove directory " + displayName + "?";
1348: } else {
1349: title = "Delete File";
1350: prompt = "Delete " + displayName + "?";
1351: }
1352: if (!editor.confirm(title, prompt))
1353: return;
1354: boolean succeeded = false;
1355: editor.setWaitCursor();
1356: if (file instanceof FtpFile) {
1357: FtpSession session = FtpSession.getSession((FtpFile) file);
1358: if (session != null) {
1359: if (isDirectory)
1360: succeeded = session.removeDirectory(file
1361: .canonicalPath());
1362: else
1363: succeeded = session
1364: .deleteFile(file.canonicalPath());
1365: session.unlock();
1366: }
1367: } else {
1368: // Local file.
1369: file.delete();
1370: succeeded = !file.exists();
1371: }
1372: if (succeeded) {
1373: if (isDirectory) {
1374: rescan();
1375: } else {
1376: if (getFile().isRemote())
1377: DirectoryCache.getDirectoryCache().purge(getFile());
1378: reload();
1379: }
1380: }
1381: editor.setDefaultCursor();
1382: if (!succeeded) {
1383: if (isDirectory)
1384: showMessageDialog("Unable to remove directory "
1385: + displayName);
1386: else
1387: showMessageDialog("Unable to delete " + displayName);
1388: }
1389: }
1390:
1391: public static void dirDoShellCommand() {
1392: if (!Editor.checkExperimental())
1393: return;
1394: if (!Platform.isPlatformUnix())
1395: return;
1396: final Editor editor = Editor.currentEditor();
1397: final Buffer buffer = editor.getBuffer();
1398: if (buffer instanceof Directory) {
1399: if (buffer.getFile().isRemote()) {
1400: MessageDialog
1401: .showMessageDialog(
1402: editor,
1403: "Shell commands are not supported in remote directory buffers.",
1404: "Error");
1405: return;
1406: }
1407: ((Directory) buffer).doShellCommand(editor);
1408: }
1409: }
1410:
1411: private void doShellCommand(Editor editor) {
1412: List names = getMarkedNames();
1413: if (names == null) {
1414: names = new ArrayList();
1415: names.add(getName(editor.getDotLine()));
1416: }
1417: String prompt = null;
1418: if (names.size() == 1)
1419: prompt = "Command (on " + (String) names.get(0) + "):";
1420: else if (names.size() > 1)
1421: prompt = "Command (on " + names.size() + " tagged files):";
1422: else
1423: prompt = "Command:";
1424: InputDialog d = new InputDialog(editor, prompt,
1425: "Shell Command", null);
1426: d.setHistory(new History("dirDoShellCommand.command"));
1427: editor.centerDialog(d);
1428: d.show();
1429: String command = d.getInput();
1430: if (command == null)
1431: return;
1432: command = command.trim();
1433: if (command.length() == 0)
1434: return;
1435: editor.setWaitCursor();
1436: editor.repaintNow();
1437: String output = null;
1438: if (names != null) {
1439: if (command.indexOf('*') >= 0) {
1440: output = doCommandOnMultipleFiles(command, names);
1441: } else {
1442: FastStringBuffer sb = new FastStringBuffer();
1443: for (int i = 0; i < names.size(); i++) {
1444: String filename = (String) names.get(i);
1445: String s = doCommandOnFile(command, filename);
1446: if (s != null && s.length() > 0)
1447: sb.append(s);
1448: }
1449: output = sb.toString();
1450: }
1451: }
1452: reload();
1453: if (output != null && output.length() > 0) {
1454: OutputBuffer buf = OutputBuffer.getOutputBuffer(output);
1455: if (buf != null) {
1456: buf.setTitle(command);
1457: editor.makeNext(buf);
1458: editor.displayInOtherWindow(buf);
1459: }
1460: }
1461: editor.setDefaultCursor();
1462: }
1463:
1464: private String doCommandOnFile(String command, String filename) {
1465: FastStringBuffer sb = new FastStringBuffer(command);
1466: sb.append(' ');
1467: if (filename.indexOf(' ') >= 0) {
1468: sb.append('"');
1469: sb.append(filename);
1470: sb.append('"');
1471: } else
1472: sb.append(filename);
1473: ShellCommand shellCommand = new ShellCommand(sb.toString(),
1474: getFile());
1475: shellCommand.run();
1476: return shellCommand.getOutput();
1477: }
1478:
1479: private String doCommandOnMultipleFiles(String command, List files) {
1480: if (files.size() < 1)
1481: return null;
1482: String before, after;
1483: int index = command.indexOf('*');
1484: if (index >= 0) {
1485: before = command.substring(0, index).trim();
1486: after = command.substring(index + 1).trim();
1487: } else {
1488: before = command.trim();
1489: after = "";
1490: }
1491: if (before.length() == 0) {
1492: showMessageDialog("No command specified");
1493: return null;
1494: }
1495: FastStringBuffer sb = new FastStringBuffer(before);
1496: for (int i = 0; i < files.size(); i++) {
1497: sb.append(' ');
1498: String filename = (String) files.get(i);
1499: if (filename.indexOf(' ') >= 0) {
1500: sb.append('"');
1501: sb.append(filename);
1502: sb.append('"');
1503: } else
1504: sb.append(filename);
1505: }
1506: if (after.length() > 0) {
1507: sb.append(' ');
1508: sb.append(after);
1509: }
1510: ShellCommand shellCommand = new ShellCommand(sb.toString(),
1511: getFile());
1512: shellCommand.run();
1513: return shellCommand.getOutput();
1514: }
1515:
1516: private synchronized List getMarkedNames() {
1517: if (numMarked > 0) {
1518: ArrayList names = new ArrayList(numMarked);
1519: final int size = entries.size();
1520: for (int i = 0; i < size; i++) {
1521: DirectoryEntry de = (DirectoryEntry) entries.get(i);
1522: if (de.isMarked()) {
1523: String name = de.extractName();
1524: if (name != null)
1525: names.add(name);
1526: }
1527: }
1528: return names;
1529: }
1530: return null;
1531: }
1532:
1533: public void copyFileAtDot() {
1534: copyFiles();
1535: }
1536:
1537: public void moveFileAtDot() {
1538: moveFiles();
1539: }
1540:
1541: private List getSourceFiles(Editor editor) {
1542: ArrayList sources = new ArrayList();
1543: if (numMarked > 0) {
1544: List names = getMarkedNames();
1545: for (int i = 0; i < names.size(); i++) {
1546: String name = (String) names.get(i);
1547: if (name != null)
1548: sources.add(File.getInstance(getFile(), name));
1549: }
1550: } else if (editor.getDotLine() instanceof DirectoryLine) {
1551: String name = getName(editor.getDotLine());
1552: if (name != null)
1553: sources.add(File.getInstance(getFile(), name));
1554: }
1555: return sources;
1556: }
1557:
1558: private File getDestinationForCopy(Editor editor, List sources) {
1559: return getDestination(editor, sources, "Copy");
1560: }
1561:
1562: private File getDestinationForMove(Editor editor, List sources) {
1563: return getDestination(editor, sources, "Move");
1564: }
1565:
1566: private File getDestination(Editor editor, List sources,
1567: String operation) {
1568: String title = operation + " File";
1569: String prompt = operation + " ";
1570: String name = null;
1571: if (sources.size() == 1) {
1572: File source = (File) sources.get(0);
1573: name = source.getName();
1574: prompt += name;
1575: } else
1576: prompt += String.valueOf(sources.size()) + " tagged files";
1577: prompt += " to: ";
1578: CopyFileDialog d = new CopyFileDialog(editor, title, prompt,
1579: name);
1580: editor.centerDialog(d);
1581: d.show();
1582: editor.repaintNow();
1583: return d.getDestination();
1584: }
1585:
1586: private void copyFiles() {
1587: final Editor editor = Editor.currentEditor();
1588: List sources = getSourceFiles(editor);
1589: File destination = getDestinationForCopy(editor, sources);
1590: if (destination == null)
1591: return;
1592: final String title = "Copy Files";
1593: if (destination.isLocal())
1594: copyLocalToLocal(sources, destination, editor, title);
1595: else
1596: MessageDialog.showMessageDialog(editor,
1597: "Destination must be local!", title);
1598: }
1599:
1600: private void copyLocalToLocal(List sources, File destination,
1601: Editor editor, String title) {
1602: int numFilesCopied = 0;
1603: boolean mustConfirm = true;
1604: boolean cancelled = false;
1605: final int limit = sources.size();
1606: File destDir = destination.isDirectory() ? destination
1607: : destination.getParentFile();
1608: for (int i = 0; i < limit; i++) {
1609: File from = (File) sources.get(i);
1610: File to;
1611: if (destination.isDirectory())
1612: to = File.getInstance(destination, from.getName());
1613: else
1614: to = destination;
1615: if (mustConfirm && to.isFile()) {
1616: String message = "Overwrite existing file "
1617: + to.canonicalPath() + "?";
1618: if (i < limit - 1) {
1619: // More than one file is left. Provide "Yes To All" and
1620: // "Cancel" buttons.
1621: int response = editor.confirmAll(title, message);
1622: switch (response) {
1623: case RESPONSE_YES:
1624: break;
1625: case RESPONSE_NO:
1626: continue;
1627: case RESPONSE_YES_TO_ALL:
1628: mustConfirm = false;
1629: break;
1630: case RESPONSE_CANCEL:
1631: cancelled = true;
1632: break;
1633: default:
1634: break;
1635: }
1636: if (cancelled)
1637: break;
1638: } else if (!editor.confirm(title, message)) {
1639: // Last file (or only file).
1640: break;
1641: }
1642: }
1643: if (Utilities.copyFile(from, to)) {
1644: ++numFilesCopied;
1645: } else {
1646: String text = "Unable to copy " + from.getName()
1647: + " to " + to.canonicalPath() + ".";
1648: if (i < limit - 1) {
1649: text += " Continue?";
1650: if (!editor.confirm(title, text))
1651: break;
1652: } else
1653: MessageDialog
1654: .showMessageDialog(editor, text, title);
1655: }
1656: }
1657: String statusText = String.valueOf(numFilesCopied) + " file";
1658: if (numFilesCopied > 1)
1659: statusText += 's';
1660: statusText += " copied";
1661: editor.status(statusText);
1662: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
1663: Editor ed = it.nextEditor();
1664: if (destDir.equals(ed.getBuffer().getFile())) {
1665: ((Directory) ed.getBuffer()).reload();
1666: if (ed != Editor.currentEditor())
1667: ed.updateDisplay();
1668: }
1669: }
1670: }
1671:
1672: private void moveFiles() {
1673: final Editor editor = Editor.currentEditor();
1674: List sources = getSourceFiles(editor);
1675: File destination = getDestinationForMove(editor, sources);
1676: if (destination == null)
1677: return;
1678: Log.debug("ready to move");
1679: if (destination.isRemote()) {
1680: MessageDialog.showMessageDialog(editor,
1681: "Destination must be local!", "Move Files");
1682: return;
1683: }
1684: final int count = sources.size();
1685: for (int i = 0; i < count; i++) {
1686: File source = (File) sources.get(i);
1687:
1688: // Move.
1689: boolean success = source.renameTo(destination);
1690:
1691: if (!success) {
1692: Log.warn("renameTo failed; trying copyFile...");
1693: success = Utilities.copyFile(source, destination);
1694: if (success) {
1695: Log.debug("copyFile succeeded, deleting source...");
1696: source.delete();
1697: success = !source.exists();
1698: } else
1699: Log.error("copyFile failed");
1700: } else
1701: Log.debug("renameTo succeeded");
1702:
1703: if (success) {
1704: // Change file information for any buffers (there should only
1705: // be one!) associated with moved file.
1706: for (BufferIterator it = new BufferIterator(); it
1707: .hasNext();) {
1708: Buffer buf = it.nextBuffer();
1709: if (source.equals(buf.getFile()))
1710: buf.changeFile(destination);
1711: }
1712: } else {
1713: MessageDialog.showMessageDialog(editor, "Move failed",
1714: "Error");
1715: break;
1716: }
1717: }
1718:
1719: // Current directory may have changed.
1720: reload();
1721: }
1722:
1723: public void getFileAtDot() {
1724: final Editor editor = Editor.currentEditor();
1725: final Line dotLine = editor.getDotLine();
1726: final String name = getName(dotLine);
1727: if (name == null)
1728: return;
1729:
1730: final FtpFile sourceFile = (FtpFile) File.getInstance(
1731: getFile(), name);
1732: if (sourceFile == null)
1733: return;
1734:
1735: int status = -1;
1736:
1737: // We'll get in trouble here if the directory format changes.
1738: if (editor.getDotLine().length() >= 3) {
1739: char c = dotLine.charAt(2);
1740: if (c == 'd') {
1741: status = 2; // Directory.
1742: } else if (c == '-') {
1743: status = 1; // Normal file.
1744: }
1745: }
1746:
1747: if (status == -1) {
1748: // Symbolic link.
1749: MessageDialog.showMessageDialog("Unknown file type",
1750: "Get File");
1751: return;
1752: }
1753: if (status == 2) {
1754: MessageDialog.showMessageDialog(sourceFile.canonicalPath()
1755: + " is a directory", "Get File");
1756: return;
1757: }
1758: if (status != 1) {
1759: MessageDialog.showMessageDialog("File not found",
1760: "Get File");
1761: return;
1762: }
1763:
1764: CopyFileDialog d = new CopyFileDialog(editor, "Get File",
1765: "Destination:", name);
1766: d.setConfirmOverwrite(true);
1767: editor.centerDialog(d);
1768: d.show();
1769: final File destination = d.getDestination();
1770: editor.repaintNow();
1771: if (destination == null)
1772: return;
1773: if (destination.isRemote()) {
1774: MessageDialog.showMessageDialog(editor,
1775: "Destination must be local!", title);
1776: return;
1777: }
1778: final File destinationFile = destination.isDirectory() ? File
1779: .getInstance(destination, name) : destination;
1780: final FtpSession session = FtpSession
1781: .getSession((FtpFile) getFile());
1782: if (session == null)
1783: return;
1784: final long fileSize = getFileSize(dotLine.getText());
1785: final FtpLoadProcess loadProcess = new FtpLoadProcess(this ,
1786: sourceFile, session);
1787: final Runnable successRunnable = new Runnable() {
1788: public void run() {
1789: File cache = loadProcess.getCache();
1790: if (cache != null)
1791: Utilities.deleteRename(cache, destination);
1792: for (EditorIterator it = new EditorIterator(); it
1793: .hasNext();) {
1794: Editor ed = it.nextEditor();
1795: if (ed.getBuffer() == Directory.this )
1796: ed.setDefaultCursor();
1797: }
1798: }
1799: };
1800: final ErrorRunnable errorRunnable = new ErrorRunnable(
1801: "Operation failed") {
1802: public void run() {
1803: File cache = loadProcess.getCache();
1804: if (cache != null && cache.isFile())
1805: cache.delete();
1806: for (EditorIterator it = new EditorIterator(); it
1807: .hasNext();) {
1808: Editor ed = it.nextEditor();
1809: if (ed.getBuffer() == Directory.this )
1810: ed.setDefaultCursor();
1811: }
1812: String text = session.getErrorText();
1813: if (text == null || text.length() == 0)
1814: text = "Unable to retrieve " + name;
1815: MessageDialog.showMessageDialog(text, "Error");
1816: }
1817: };
1818: loadProcess.setSuccessRunnable(successRunnable);
1819: loadProcess.setErrorRunnable(errorRunnable);
1820: loadProcess.setProgressNotifier(new StatusBarProgressNotifier(
1821: this ));
1822: loadProcess.start();
1823: }
1824:
1825: public boolean isModified() {
1826: return false;
1827: }
1828:
1829: public String getTitle() {
1830: File dir = getFile();
1831: title = dir.canonicalPath();
1832: if (limitPattern != null) {
1833: title += LocalFile.getSeparator();
1834: title += limitPattern;
1835: }
1836: title += " (sorted by ";
1837: if (sortBy == SORT_BY_NAME)
1838: title += "name)";
1839: else if (sortBy == SORT_BY_DATE)
1840: title += "date)";
1841: else if (sortBy == SORT_BY_SIZE)
1842: title += "size)";
1843: if (dir.isRemote())
1844: title += " " + dir.getHostName();
1845: return title;
1846: }
1847:
1848: private String getName(Line line) {
1849: return getName(line.getText());
1850: }
1851:
1852: private String getName(String s) {
1853: // Strip symbolic link (if any) from end of line.
1854: int end = s.indexOf(" ->");
1855:
1856: if (end >= 0)
1857: s = s.substring(0, end);
1858:
1859: REMatch match;
1860:
1861: if (usingNativeFormat)
1862: match = nativeMoveToFilenameRegExp.getMatch(s);
1863: else
1864: match = internalMoveToFilenameRegExp.getMatch(s);
1865:
1866: if (match != null)
1867: return s.substring(match.getEndIndex());
1868:
1869: return null;
1870: }
1871:
1872: private long getFileSize(String s) {
1873: if (usingNativeFormat) {
1874: try {
1875: REMatch match = nativeMoveToFilenameRegExp.getMatch(s);
1876: if (match != null) {
1877: String toBeParsed = s.substring(match
1878: .getStartIndex());
1879: int index = toBeParsed.indexOf(' ');
1880: if (index >= 0) {
1881: toBeParsed = toBeParsed.substring(0, index);
1882: return Long.parseLong(toBeParsed);
1883: }
1884: }
1885: } catch (Exception e) {
1886: Log.error(e);
1887: }
1888: }
1889: return 0;
1890: }
1891:
1892: public void home() {
1893: final Editor editor = Editor.currentEditor();
1894: if (editor.getDotOffset() == 0)
1895: return;
1896: editor.addUndo(SimpleEdit.MOVE);
1897: editor.beginningOfBlock();
1898: int offset = getNameOffset(editor.getDotLine());
1899:
1900: // If we're already at the first character of the name (or to the left
1901: // of it), go to column 0.
1902: if (editor.getDotOffset() <= offset)
1903: offset = 0;
1904:
1905: editor.getDot().setOffset(offset);
1906: editor.moveCaretToDotCol();
1907: }
1908:
1909: public static void chmod() {
1910: final Editor editor = Editor.currentEditor();
1911: if (!(editor.getDotLine() instanceof DirectoryLine))
1912: return;
1913: final Buffer buffer = editor.getBuffer();
1914: if (!(buffer instanceof Directory))
1915: return;
1916: final Directory directory = (Directory) buffer;
1917: String name = directory.getName(editor.getDotLine());
1918: if (name == null)
1919: return;
1920: final File file = File.getInstance(directory.getFile(), name);
1921: if (file == null)
1922: return;
1923: // Verify that operation is supported.
1924: if (file.isLocal()) {
1925: if (!Platform.isPlatformUnix())
1926: return;
1927: } else if (file.isRemote()) {
1928: int protocol = file.getProtocol();
1929: if (protocol != File.PROTOCOL_FTP
1930: && protocol != File.PROTOCOL_SSH)
1931: return;
1932: }
1933: FastStringBuffer sb = new FastStringBuffer("Change mode of ");
1934: sb.append(file.getName());
1935: sb.append(" to:");
1936: final String input = InputDialog.showInputDialog(editor, sb
1937: .toString(), "Change Mode");
1938: if (input == null || input.length() == 0)
1939: return;
1940: int n = 0;
1941: try {
1942: n = Integer.parseInt(input, 8);
1943: } catch (NumberFormatException e) {
1944: Log.error(e);
1945: }
1946: if (n == 0) {
1947: MessageDialog.showMessageDialog("Invalid mode string",
1948: "Change Mode");
1949: return;
1950: }
1951: final int permissions = n;
1952: if (file.isLocal()) {
1953: editor.setWaitCursor();
1954: file.setPermissions(permissions);
1955: editor.setDefaultCursor();
1956: } else if (file instanceof FtpFile) {
1957: final FtpSession session = FtpSession
1958: .getSession((FtpFile) file);
1959: if (session != null) {
1960: final Runnable completionRunnable = new Runnable() {
1961: public void run() {
1962: for (EditorIterator it = new EditorIterator(); it
1963: .hasNext();) {
1964: Editor ed = it.nextEditor();
1965: if (ed.getBuffer() == directory)
1966: ed.setDefaultCursor();
1967: }
1968: }
1969: };
1970: final Runnable chmodRunnable = new Runnable() {
1971: public void run() {
1972: directory.setBusy(true);
1973: for (EditorIterator it = new EditorIterator(); it
1974: .hasNext();) {
1975: Editor ed = it.nextEditor();
1976: if (ed.getBuffer() == directory)
1977: ed.setWaitCursor();
1978: }
1979: if (session.verifyConnected()) {
1980: session.chmod((FtpFile) file, permissions);
1981: session.unlock();
1982: }
1983: directory.setBusy(false);
1984: SwingUtilities.invokeLater(completionRunnable);
1985: }
1986: };
1987: new Thread(chmodRunnable).start();
1988: }
1989: } else if (file instanceof SshFile) {
1990: final SshSession session = SshSession
1991: .getSession((SshFile) file);
1992: if (session != null) {
1993: final Runnable completionRunnable = new Runnable() {
1994: public void run() {
1995: for (EditorIterator it = new EditorIterator(); it
1996: .hasNext();) {
1997: Editor ed = it.nextEditor();
1998: if (ed.getBuffer() == directory)
1999: ed.setDefaultCursor();
2000: }
2001: }
2002: };
2003: final Runnable chmodRunnable = new Runnable() {
2004: public void run() {
2005: directory.setBusy(true);
2006: for (EditorIterator it = new EditorIterator(); it
2007: .hasNext();) {
2008: Editor ed = it.nextEditor();
2009: if (ed.getBuffer() == directory)
2010: ed.setWaitCursor();
2011: }
2012: if (session.connect()) {
2013: session.chmod((SshFile) file, permissions);
2014: session.unlock();
2015: }
2016: directory.setBusy(false);
2017: SwingUtilities.invokeLater(completionRunnable);
2018: }
2019: };
2020: new Thread(chmodRunnable).start();
2021: }
2022: }
2023: }
2024:
2025: private int getNameOffset(Line line) {
2026: if (line != null) {
2027: REMatch match;
2028: if (usingNativeFormat)
2029: match = nativeMoveToFilenameRegExp.getMatch(line
2030: .getText());
2031: else
2032: match = internalMoveToFilenameRegExp.getMatch(line
2033: .getText());
2034: if (match != null)
2035: return match.getEndIndex();
2036: }
2037: return 0;
2038: }
2039:
2040: private int getNameOffset() {
2041: return getNameOffset(getFirstLine());
2042: }
2043:
2044: private int getFileSizeEndOffset() {
2045: Line line = getFirstLine();
2046: if (line != null) {
2047: final String text = line.getText();
2048: REMatch match;
2049: if (usingNativeFormat)
2050: match = nativeMoveToFilenameRegExp.getMatch(text);
2051: else
2052: match = internalMoveToFilenameRegExp.getMatch(text);
2053: if (match != null) {
2054: int start = match.getStartIndex();
2055: // The file size is followed by a single space.
2056: return text.indexOf(' ', start);
2057: }
2058: }
2059: return -1; // Error!
2060: }
2061:
2062: private Line findName(String name) {
2063: if (name != null) {
2064: if (Platform.isPlatformWindows()) {
2065: // Case-insensitive filesystem.
2066: name = name.toLowerCase();
2067: for (Line line = getFirstLine(); line != null; line = line
2068: .next()) {
2069: String text = line.getText().toLowerCase();
2070: if (text.indexOf(name) >= 0) // Performance!
2071: if (name.equalsIgnoreCase(getName(line)))
2072: return line;
2073: }
2074: } else {
2075: for (Line line = getFirstLine(); line != null; line = line
2076: .next()) {
2077: if (line.getText().indexOf(name) >= 0) // Performance!
2078: if (name.equals(getName(line)))
2079: return line;
2080: }
2081: }
2082: }
2083: return null;
2084: }
2085:
2086: public final String toString() {
2087: File file = getFile();
2088: if (file.isRemote()) {
2089: FastStringBuffer sb = new FastStringBuffer(file
2090: .canonicalPath());
2091: sb.append(" [");
2092: sb.append(file.netPath());
2093: sb.append(']');
2094: return sb.toString();
2095: }
2096: if (Platform.isPlatformUnix()) {
2097: String userHome = Utilities.getUserHome();
2098: if (userHome != null && userHome.length() > 0) {
2099: String s = file.canonicalPath();
2100: if (s.equals(userHome))
2101: return "~";
2102: if (s.startsWith(userHome.concat("/")))
2103: return "~".concat(s.substring(userHome.length()));
2104: }
2105: }
2106: return file.canonicalPath();
2107: }
2108:
2109: // For the buffer list.
2110: public final Icon getIcon() {
2111: return Utilities.getIconFromFile("directory.png");
2112: }
2113: }
2114:
2115: class DirectoryHistory {
2116: private Vector v = new Vector();
2117: private int index = -1;
2118:
2119: DirectoryHistory() {
2120: }
2121:
2122: boolean atEnd() {
2123: return index == -1;
2124: }
2125:
2126: void truncate() {
2127: if (index != -1)
2128: v.setSize(index);
2129: }
2130:
2131: void append(File file, String name, int offset) {
2132: v.add(new DirectoryHistoryEntry(file, name, offset));
2133: }
2134:
2135: DirectoryHistoryEntry getPrevious() {
2136: if (v.size() == 0)
2137: return null;
2138: if (index == -1)
2139: index = v.size();
2140: if (index > 0)
2141: return (DirectoryHistoryEntry) v.get(--index);
2142: return null;
2143: }
2144:
2145: DirectoryHistoryEntry getNext() {
2146: if (v.size() == 0)
2147: return null;
2148: if (index == -1)
2149: return null;
2150: if (index < v.size() - 1)
2151: return (DirectoryHistoryEntry) v.get(++index);
2152: return null;
2153: }
2154:
2155: public void reset() {
2156: index = -1;
2157: }
2158: }
2159:
2160: class DirectoryHistoryEntry {
2161: File file;
2162: String name;
2163: int offset;
2164:
2165: DirectoryHistoryEntry(File file, String name, int offset) {
2166: this.file = file;
2167: this.name = name;
2168: this.offset = offset;
2169: }
2170: }
|