PageRenderTime 59ms CodeModel.GetById 40ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/io/FileVFS.java

#
Java | 487 lines | 320 code | 69 blank | 98 comment | 55 complexity | ac935b1130321eaeb58599a9a1cf2ddb MD5 | raw file
  1/*
  2 * FileVFS.java - Local filesystem VFS
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 1998, 1999, 2000, 2001, 2002 Slava Pestov
  7 * Portions copyright (C) 1998, 1999, 2000 Peter Graves
  8 *
  9 * This program is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU General Public License
 11 * as published by the Free Software Foundation; either version 2
 12 * of the License, or any later version.
 13 *
 14 * This program is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 17 * GNU General Public License for more details.
 18 *
 19 * You should have received a copy of the GNU General Public License
 20 * along with this program; if not, write to the Free Software
 21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 22 */
 23
 24package org.gjt.sp.jedit.io;
 25
 26//{{{ Imports
 27import java.awt.Component;
 28import java.io.*;
 29import java.text.*;
 30import java.util.Date;
 31import org.gjt.sp.jedit.*;
 32import org.gjt.sp.util.Log;
 33//}}}
 34
 35/**
 36 * Local filesystem VFS.
 37 * @author Slava Pestov
 38 * @version $Id: FileVFS.java 4986 2004-03-06 22:56:08Z spestov $
 39 */
 40public class FileVFS extends VFS
 41{
 42	public static final String PERMISSIONS_PROPERTY = "FileVFS__perms";
 43
 44	//{{{ FileVFS method
 45	public FileVFS()
 46	{
 47		super("file",READ_CAP | WRITE_CAP | DELETE_CAP
 48			| RENAME_CAP | MKDIR_CAP | LOW_LATENCY_CAP
 49			| ((OperatingSystem.isMacOS()
 50			|| OperatingSystem.isDOSDerived())
 51			? CASE_INSENSITIVE_CAP : 0),
 52			new String[] { EA_TYPE, EA_SIZE, EA_STATUS,
 53			EA_MODIFIED });
 54	} //}}}
 55
 56	//{{{ getParentOfPath() method
 57	public String getParentOfPath(String path)
 58	{
 59		if(OperatingSystem.isDOSDerived())
 60		{
 61			if(path.length() == 2 && path.charAt(1) == ':')
 62				return FileRootsVFS.PROTOCOL + ":";
 63			else if(path.length() == 3 && path.endsWith(":\\"))
 64				return FileRootsVFS.PROTOCOL + ":";
 65			else if(path.startsWith("\\\\") && path.indexOf('\\',2) == -1)
 66				return path;
 67		}
 68
 69		return super.getParentOfPath(path);
 70	} //}}}
 71
 72	//{{{ constructPath() method
 73	public String constructPath(String parent, String path)
 74	{
 75		if(parent.endsWith(File.separator)
 76			|| parent.endsWith("/"))
 77			return parent + path;
 78		else
 79			return parent + File.separator + path;
 80	} //}}}
 81
 82	//{{{ getFileSeparator() method
 83	public char getFileSeparator()
 84	{
 85		return File.separatorChar;
 86	} //}}}
 87
 88	//{{{ save() method
 89	public boolean save(View view, Buffer buffer, String path)
 90	{
 91		if(OperatingSystem.isUnix())
 92		{
 93			int permissions = getPermissions(buffer.getPath());
 94			Log.log(Log.DEBUG,this,buffer.getPath() + " has permissions 0"
 95				+ Integer.toString(permissions,8));
 96			buffer.setIntegerProperty(PERMISSIONS_PROPERTY,permissions);
 97		}
 98
 99		return super.save(view,buffer,path);
100	} //}}}
101
102	//{{{ insert() method
103	public boolean insert(View view, Buffer buffer, String path)
104	{
105		File file = new File(path);
106
107		//{{{ Check if file is valid
108		if(!file.exists())
109			return false;
110
111		if(file.isDirectory())
112		{
113			VFSManager.error(view,file.getPath(),
114				"ioerror.open-directory",null);
115			return false;
116		}
117
118		if(!file.canRead())
119		{
120			VFSManager.error(view,file.getPath(),
121				"ioerror.no-read",null);
122			return false;
123		} //}}}
124
125		return super.insert(view,buffer,path);
126	} //}}}
127
128	//{{{ _canonPath() method
129	/**
130	 * Returns the canonical form if the specified path name. For example,
131	 * <code>~</code> might be expanded to the user's home directory.
132	 * @param session The session
133	 * @param path The path
134	 * @param comp The component that will parent error dialog boxes
135	 * @exception IOException if an I/O error occurred
136	 * @since jEdit 4.0pre2
137	 */
138	public String _canonPath(Object session, String path, Component comp)
139		throws IOException
140	{
141		return MiscUtilities.canonPath(path);
142	} //}}}
143
144	//{{{ LocalDirectoryEntry class
145	public static class LocalDirectoryEntry extends VFS.DirectoryEntry
146	{
147		// use system default short format
148		public static DateFormat DATE_FORMAT
149			= DateFormat.getInstance();
150
151		public long modified;
152
153		public LocalDirectoryEntry(File file)
154		{
155			super(file.getName(),file.getPath(),
156				file.getPath(),file.isDirectory() ? DIRECTORY : FILE,file.length(),file.isHidden());
157			this.modified = file.lastModified();
158			this.canRead = file.canRead();
159			this.canWrite = file.canWrite();
160			this.symlinkPath = MiscUtilities.resolveSymlinks(path);
161		}
162
163		public String getExtendedAttribute(String name)
164		{
165			if(name.equals(EA_MODIFIED))
166				return DATE_FORMAT.format(new Date(modified));
167			else
168				return super.getExtendedAttribute(name);
169		}
170	} //}}}
171
172	//{{{ _listDirectory() method
173	public VFS.DirectoryEntry[] _listDirectory(Object session, String path,
174		Component comp)
175	{
176		//{{{ Windows work around
177		/* On Windows, paths of the form X: list the last *working
178		 * directory* on that drive. To list the root of the drive,
179		 * you must use X:\.
180		 *
181		 * However, the VFS browser and friends strip off trailing
182		 * path separators, for various reasons. So to work around
183		 * that, we add a '\' to drive letter paths on Windows.
184		 */
185		if(OperatingSystem.isWindows())
186		{
187			if(path.length() == 2 && path.charAt(1) == ':')
188				path = path.concat(File.separator);
189		} //}}}
190
191		File directory = new File(path);
192		File[] list = directory.listFiles();
193		if(list == null)
194		{
195			VFSManager.error(comp,path,"ioerror.directory-error-nomsg",null);
196			return null;
197		}
198
199		VFS.DirectoryEntry[] list2 = new VFS.DirectoryEntry[list.length];
200		for(int i = 0; i < list.length; i++)
201			list2[i] = new LocalDirectoryEntry(list[i]);
202
203		return list2;
204	} //}}}
205
206	//{{{ _getDirectoryEntry() method
207	public DirectoryEntry _getDirectoryEntry(Object session, String path,
208		Component comp)
209	{
210		if(path.equals("/") && OperatingSystem.isUnix())
211		{
212			return new VFS.DirectoryEntry(path,path,path,
213				VFS.DirectoryEntry.DIRECTORY,0L,false);
214		}
215
216		File file = new File(path);
217		if(!file.exists())
218			return null;
219
220		return new LocalDirectoryEntry(file);
221	} //}}}
222
223	//{{{ _delete() method
224	public boolean _delete(Object session, String path, Component comp)
225	{
226		File file = new File(path);
227		// do some platforms throw exceptions if the file does not exist
228		// when we ask for the canonical path?
229		String canonPath;
230		try
231		{
232			canonPath = file.getCanonicalPath();
233		}
234		catch(IOException io)
235		{
236			canonPath = path;
237		}
238
239		boolean retVal = file.delete();
240		if(retVal)
241			VFSManager.sendVFSUpdate(this,canonPath,true);
242		return retVal;
243	} //}}}
244
245	//{{{ _rename() method
246	public boolean _rename(Object session, String from, String to,
247		Component comp)
248	{
249		File _to = new File(to);
250
251		String toCanonPath;
252		try
253		{
254			toCanonPath = _to.getCanonicalPath();
255		}
256		catch(IOException io)
257		{
258			toCanonPath = to;
259		}
260
261		// this is needed because on OS X renaming to a non-existent
262		// directory causes problems
263		File parent = new File(_to.getParent());
264		if(parent.exists())
265		{
266			if(!parent.isDirectory())
267				return false;
268		}
269		else
270		{
271			parent.mkdirs();
272			if(!parent.exists())
273				return false;
274		}
275
276		File _from = new File(from);
277
278		String fromCanonPath;
279		try
280		{
281			fromCanonPath = _from.getCanonicalPath();
282		}
283		catch(IOException io)
284		{
285			fromCanonPath = from;
286		}
287
288		// Case-insensitive fs workaround
289		if(!fromCanonPath.equalsIgnoreCase(toCanonPath))
290			_to.delete();
291
292		boolean retVal = _from.renameTo(_to);
293		VFSManager.sendVFSUpdate(this,fromCanonPath,true);
294		VFSManager.sendVFSUpdate(this,toCanonPath,true);
295		return retVal;
296	} //}}}
297
298	//{{{ _mkdir() method
299	public boolean _mkdir(Object session, String directory, Component comp)
300	{
301		String parent = getParentOfPath(directory);
302		if(!new File(parent).exists())
303		{
304			if(!_mkdir(session,parent,comp))
305				return false;
306		}
307
308		File file = new File(directory);
309
310		boolean retVal = file.mkdir();
311		String canonPath;
312		try
313		{
314			canonPath = file.getCanonicalPath();
315		}
316		catch(IOException io)
317		{
318			canonPath = directory;
319		}
320		VFSManager.sendVFSUpdate(this,canonPath,true);
321		return retVal;
322	} //}}}
323
324	//{{{ _backup() method
325	public void _backup(Object session, String path, Component comp)
326		throws IOException
327	{
328		// Fetch properties
329		int backups = jEdit.getIntegerProperty("backups",1);
330
331		if(backups == 0)
332			return;
333
334		String backupPrefix = jEdit.getProperty("backup.prefix");
335		String backupSuffix = jEdit.getProperty("backup.suffix");
336
337		String backupDirectory = jEdit.getProperty("backup.directory");
338
339		int backupTimeDistance = jEdit.getIntegerProperty("backup.minTime",0);
340		File file = new File(path);
341
342		// Check for backup.directory, and create that
343		// directory if it doesn't exist
344		if(backupDirectory == null || backupDirectory.length() == 0)
345			backupDirectory = file.getParent();
346		else
347		{
348			backupDirectory = MiscUtilities.constructPath(
349				System.getProperty("user.home"),backupDirectory);
350
351			// Perhaps here we would want to guard with
352			// a property for parallel backups or not.
353			backupDirectory = MiscUtilities.concatPath(
354				backupDirectory,file.getParent());
355
356			File dir = new File(backupDirectory);
357
358			if (!dir.exists())
359				dir.mkdirs();
360		}
361
362		MiscUtilities.saveBackup(file,backups,backupPrefix,
363			backupSuffix,backupDirectory,backupTimeDistance);
364	} //}}}
365
366	//{{{ _createInputStream() method
367	public InputStream _createInputStream(Object session, String path,
368		boolean ignoreErrors, Component comp) throws IOException
369	{
370		try
371		{
372			return new FileInputStream(path);
373		}
374		catch(IOException io)
375		{
376			if(ignoreErrors)
377				return null;
378			else
379				throw io;
380		}
381	} //}}}
382
383	//{{{ _createOutputStream() method
384	public OutputStream _createOutputStream(Object session, String path,
385		Component comp) throws IOException
386	{
387		return new FileOutputStream(path);
388	} //}}}
389
390	//{{{ _saveComplete() method
391	public void _saveComplete(Object session, Buffer buffer, String path,
392		Component comp)
393	{
394		int permissions = buffer.getIntegerProperty(PERMISSIONS_PROPERTY,0);
395		setPermissions(path,permissions);
396	} //}}}
397
398	//{{{ Permission preservation code
399
400	/** Code borrowed from j text editor (http://www.armedbear.org) */
401	/** I made some changes to make it support suid, sgid and sticky files */
402
403	//{{{ getPermissions() method
404	/**
405	 * Returns numeric permissions of a file. On non-Unix systems, always
406	 * returns zero.
407	 * @since jEdit 3.2pre9
408	 */
409	public static int getPermissions(String path)
410	{
411		int permissions = 0;
412
413		if(jEdit.getBooleanProperty("chmodDisabled"))
414			return permissions;
415
416		if(OperatingSystem.isUnix())
417		{
418			String[] cmdarray = { "ls", "-ld", path };
419
420			try
421			{
422				Process process = Runtime.getRuntime().exec(cmdarray);
423
424				BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
425
426				String output = reader.readLine();
427
428				if(output != null)
429				{
430					String s = output.substring(1, 10);
431
432					permissions = MiscUtilities
433						.parsePermissions(s);
434				}
435			}
436
437			// Feb 4 2000 5:30 PM
438			// Catch Throwable here rather than Exception.
439			// Kaffe's implementation of Runtime.exec throws java.lang.InternalError.
440			catch (Throwable t)
441			{
442			}
443		}
444
445		return permissions;
446	} //}}}
447
448	//{{{ setPermissions() method
449	/**
450	 * Sets numeric permissions of a file. On non-Unix platforms,
451	 * does nothing.
452	 * @since jEdit 3.2pre9
453	 */
454	public static void setPermissions(String path, int permissions)
455	{
456		if(jEdit.getBooleanProperty("chmodDisabled"))
457			return;
458
459		if(permissions != 0)
460		{
461			if(OperatingSystem.isUnix())
462			{
463				String[] cmdarray = { "chmod", Integer.toString(permissions, 8), path };
464
465				try
466				{
467					Process process = Runtime.getRuntime().exec(cmdarray);
468					process.getInputStream().close();
469					process.getOutputStream().close();
470					process.getErrorStream().close();
471					int exitCode = process.waitFor();
472					if(exitCode != 0)
473						Log.log(Log.NOTICE,FileVFS.class,"chmod exited with code " + exitCode);
474				}
475
476				// Feb 4 2000 5:30 PM
477				// Catch Throwable here rather than Exception.
478				// Kaffe's implementation of Runtime.exec throws java.lang.InternalError.
479				catch (Throwable t)
480				{
481				}
482			}
483		}
484	} //}}}
485
486	//}}}
487}