PageRenderTime 140ms CodeModel.GetById 117ms app.highlight 18ms RepoModel.GetById 2ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/io/FileVFS.java

#
Java | 578 lines | 373 code | 84 blank | 121 comment | 55 complexity | c7b7bf1dd5da656ef6d29a4ca06798e7 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, 2005 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 javax.swing.filechooser.FileSystemView;
 28import java.awt.Component;
 29import java.io.*;
 30import java.text.*;
 31import java.util.Date;
 32import org.gjt.sp.jedit.*;
 33import org.gjt.sp.util.Log;
 34//}}}
 35
 36/**
 37 * Local filesystem VFS.
 38 * @author Slava Pestov
 39 * @version $Id: FileVFS.java 5211 2005-03-22 00:36:31Z spestov $
 40 */
 41public class FileVFS extends VFS
 42{
 43	public static final String PERMISSIONS_PROPERTY = "FileVFS__perms";
 44
 45	//{{{ FileVFS method
 46	public FileVFS()
 47	{
 48		super("file",READ_CAP | WRITE_CAP | DELETE_CAP
 49			| RENAME_CAP | MKDIR_CAP | LOW_LATENCY_CAP
 50			| ((OperatingSystem.isCaseInsensitiveFS())
 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	//{{{ getTwoStageSaveName() method
 89	/**
 90	 * Returns a temporary file name based on the given path.
 91	 *
 92	 * <p>If the directory where the file would be created cannot be
 93	 * written (i.e., no new files can be created in that directory),
 94	 * this method returns <code>null</code>.</p>
 95	 *
 96	 * @param path The path name
 97	 */
 98	public String getTwoStageSaveName(String path)
 99	{
100		File parent = new File(getParentOfPath(path));
101		return (parent.canWrite())
102			? super.getTwoStageSaveName(path)
103			: null;
104	} //}}}
105
106	//{{{ save() method
107	public boolean save(View view, Buffer buffer, String path)
108	{
109		if(OperatingSystem.isUnix())
110		{
111			int permissions = getPermissions(buffer.getPath());
112			Log.log(Log.DEBUG,this,buffer.getPath() + " has permissions 0"
113				+ Integer.toString(permissions,8));
114			buffer.setIntegerProperty(PERMISSIONS_PROPERTY,permissions);
115		}
116
117		return super.save(view,buffer,path);
118	} //}}}
119
120	//{{{ insert() method
121	public boolean insert(View view, Buffer buffer, String path)
122	{
123		File file = new File(path);
124
125		//{{{ Check if file is valid
126		if(!file.exists())
127			return false;
128
129		if(file.isDirectory())
130		{
131			VFSManager.error(view,file.getPath(),
132				"ioerror.open-directory",null);
133			return false;
134		}
135
136		if(!file.canRead())
137		{
138			VFSManager.error(view,file.getPath(),
139				"ioerror.no-read",null);
140			return false;
141		} //}}}
142
143		return super.insert(view,buffer,path);
144	} //}}}
145
146	//{{{ _canonPath() method
147	/**
148	 * Returns the canonical form if the specified path name. For example,
149	 * <code>~</code> might be expanded to the user's home directory.
150	 * @param session The session
151	 * @param path The path
152	 * @param comp The component that will parent error dialog boxes
153	 * @exception IOException if an I/O error occurred
154	 * @since jEdit 4.0pre2
155	 */
156	public String _canonPath(Object session, String path, Component comp)
157		throws IOException
158	{
159		return MiscUtilities.canonPath(path);
160	} //}}}
161
162	//{{{ LocalFile class
163	public static class LocalFile extends VFSFile
164	{
165		private File file;
166
167		// use system default short format
168		public static DateFormat DATE_FORMAT
169			= DateFormat.getInstance();
170
171		/**
172		 * @deprecated Call getModified() instead.
173		 */
174		public long modified;
175
176		public LocalFile(File file)
177		{
178			this.file = file;
179
180			/* These attributes are fetched relatively
181			quickly. The rest are lazily filled in. */
182			setName(file.getName());
183			String path = file.getPath();
184			setPath(path);
185			setDeletePath(path);
186			setHidden(file.isHidden());
187			setType(file.isDirectory()
188				? VFSFile.DIRECTORY
189				: VFSFile.FILE);
190		}
191
192		public String getExtendedAttribute(String name)
193		{
194			if(name.equals(EA_MODIFIED))
195				return DATE_FORMAT.format(new Date(modified));
196			else
197				return super.getExtendedAttribute(name);
198		}
199		
200		protected void fetchAttrs()
201		{
202			if(fetchedAttrs())
203				return;
204
205			super.fetchAttrs();
206
207			setSymlinkPath(MiscUtilities.resolveSymlinks(
208				file.getPath()));
209			setReadable(file.canRead());
210			setWriteable(file.canWrite());
211			setLength(file.length());
212			setModified(file.lastModified());
213		}
214		
215		public String getSymlinkPath()
216		{
217			fetchAttrs();
218			return super.getSymlinkPath();
219		}
220		
221		public long getLength()
222		{
223			fetchAttrs();
224			return super.getLength();
225		}
226		
227		public boolean isReadable()
228		{
229			fetchAttrs();
230			return super.isReadable();
231		}
232		
233		public boolean isWriteable()
234		{
235			fetchAttrs();
236			return super.isWriteable();
237		}
238
239		public long getModified()
240		{
241			return modified;
242		}
243
244		public void setModified(long modified)
245		{
246			this.modified = modified;
247		}
248	} //}}}
249
250	//{{{ _listFiles() method
251	public VFSFile[] _listFiles(Object session, String path,
252		Component comp)
253	{
254		//{{{ Windows work around
255		/* On Windows, paths of the form X: list the last *working
256		 * directory* on that drive. To list the root of the drive,
257		 * you must use X:\.
258		 *
259		 * However, the VFS browser and friends strip off trailing
260		 * path separators, for various reasons. So to work around
261		 * that, we add a '\' to drive letter paths on Windows.
262		 */
263		if(OperatingSystem.isWindows())
264		{
265			if(path.length() == 2 && path.charAt(1) == ':')
266				path = path.concat(File.separator);
267		} //}}}
268
269		File directory = new File(path);
270		File[] list = null;
271		if(directory.exists())
272			list = fsView.getFiles(directory,false);
273
274		if(list == null)
275		{
276			VFSManager.error(comp,path,"ioerror.directory-error-nomsg",null);
277			return null;
278		}
279
280		VFSFile[] list2 = new VFSFile[list.length];
281		for(int i = 0; i < list.length; i++)
282			list2[i] = new LocalFile(list[i]);
283
284		return list2;
285	} //}}}
286
287	//{{{ _getFile() method
288	public VFSFile _getFile(Object session, String path,
289		Component comp)
290	{
291		if(path.equals("/") && OperatingSystem.isUnix())
292		{
293			return new VFS.DirectoryEntry(path,path,path,
294				VFSFile.DIRECTORY,0L,false);
295		}
296
297		File file = new File(path);
298		if(!file.exists())
299			return null;
300
301		return new LocalFile(file);
302	} //}}}
303
304	//{{{ _delete() method
305	public boolean _delete(Object session, String path, Component comp)
306	{
307		File file = new File(path);
308		// do some platforms throw exceptions if the file does not exist
309		// when we ask for the canonical path?
310		String canonPath;
311		try
312		{
313			canonPath = file.getCanonicalPath();
314		}
315		catch(IOException io)
316		{
317			canonPath = path;
318		}
319
320		boolean retVal = file.delete();
321		if(retVal)
322			VFSManager.sendVFSUpdate(this,canonPath,true);
323		return retVal;
324	} //}}}
325
326	//{{{ _rename() method
327	public boolean _rename(Object session, String from, String to,
328		Component comp)
329	{
330		File _to = new File(to);
331
332		String toCanonPath;
333		try
334		{
335			toCanonPath = _to.getCanonicalPath();
336		}
337		catch(IOException io)
338		{
339			toCanonPath = to;
340		}
341
342		// this is needed because on OS X renaming to a non-existent
343		// directory causes problems
344		File parent = new File(_to.getParent());
345		if(parent.exists())
346		{
347			if(!parent.isDirectory())
348				return false;
349		}
350		else
351		{
352			parent.mkdirs();
353			if(!parent.exists())
354				return false;
355		}
356
357		File _from = new File(from);
358
359		String fromCanonPath;
360		try
361		{
362			fromCanonPath = _from.getCanonicalPath();
363		}
364		catch(IOException io)
365		{
366			fromCanonPath = from;
367		}
368
369		// Case-insensitive fs workaround
370		if(!fromCanonPath.equalsIgnoreCase(toCanonPath))
371			_to.delete();
372
373		boolean retVal = _from.renameTo(_to);
374		VFSManager.sendVFSUpdate(this,fromCanonPath,true);
375		VFSManager.sendVFSUpdate(this,toCanonPath,true);
376		return retVal;
377	} //}}}
378
379	//{{{ _mkdir() method
380	public boolean _mkdir(Object session, String directory, Component comp)
381	{
382		String parent = getParentOfPath(directory);
383		if(!new File(parent).exists())
384		{
385			if(!_mkdir(session,parent,comp))
386				return false;
387		}
388
389		File file = new File(directory);
390
391		boolean retVal = file.mkdir();
392		String canonPath;
393		try
394		{
395			canonPath = file.getCanonicalPath();
396		}
397		catch(IOException io)
398		{
399			canonPath = directory;
400		}
401		VFSManager.sendVFSUpdate(this,canonPath,true);
402		return retVal;
403	} //}}}
404
405	//{{{ _backup() method
406	public void _backup(Object session, String path, Component comp)
407		throws IOException
408	{
409		// Fetch properties
410		int backups = jEdit.getIntegerProperty("backups",1);
411
412		if(backups == 0)
413			return;
414
415		String backupPrefix = jEdit.getProperty("backup.prefix");
416		String backupSuffix = jEdit.getProperty("backup.suffix");
417
418		String backupDirectory = jEdit.getProperty("backup.directory");
419
420		int backupTimeDistance = jEdit.getIntegerProperty("backup.minTime",0);
421		File file = new File(path);
422
423		if (!file.exists())
424			return;
425
426		// Check for backup.directory, and create that
427		// directory if it doesn't exist
428		if(backupDirectory == null || backupDirectory.length() == 0)
429			backupDirectory = file.getParent();
430		else
431		{
432			backupDirectory = MiscUtilities.constructPath(
433				System.getProperty("user.home"),backupDirectory);
434
435			// Perhaps here we would want to guard with
436			// a property for parallel backups or not.
437			backupDirectory = MiscUtilities.concatPath(
438				backupDirectory,file.getParent());
439
440			File dir = new File(backupDirectory);
441
442			if (!dir.exists())
443				dir.mkdirs();
444		}
445
446		MiscUtilities.saveBackup(file,backups,backupPrefix,
447			backupSuffix,backupDirectory,backupTimeDistance);
448	} //}}}
449
450	//{{{ _createInputStream() method
451	public InputStream _createInputStream(Object session, String path,
452		boolean ignoreErrors, Component comp) throws IOException
453	{
454		try
455		{
456			return new FileInputStream(path);
457		}
458		catch(IOException io)
459		{
460			if(ignoreErrors)
461				return null;
462			else
463				throw io;
464		}
465	} //}}}
466
467	//{{{ _createOutputStream() method
468	public OutputStream _createOutputStream(Object session, String path,
469		Component comp) throws IOException
470	{
471		return new FileOutputStream(path);
472	} //}}}
473
474	//{{{ _saveComplete() method
475	public void _saveComplete(Object session, Buffer buffer, String path,
476		Component comp)
477	{
478		int permissions = buffer.getIntegerProperty(PERMISSIONS_PROPERTY,0);
479		setPermissions(path,permissions);
480	} //}}}
481
482	//{{{ Permission preservation code
483
484	/** Code borrowed from j text editor (http://www.armedbear.org) */
485	/** I made some changes to make it support suid, sgid and sticky files */
486
487	//{{{ getPermissions() method
488	/**
489	 * Returns numeric permissions of a file. On non-Unix systems, always
490	 * returns zero.
491	 * @since jEdit 3.2pre9
492	 */
493	public static int getPermissions(String path)
494	{
495		int permissions = 0;
496
497		if(jEdit.getBooleanProperty("chmodDisabled"))
498			return permissions;
499
500		if(OperatingSystem.isUnix())
501		{
502			String[] cmdarray = { "ls", "-ld", path };
503
504			try
505			{
506				Process process = Runtime.getRuntime().exec(cmdarray);
507
508				BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
509
510				String output = reader.readLine();
511
512				if(output != null)
513				{
514					String s = output.substring(1, 10);
515
516					permissions = MiscUtilities
517						.parsePermissions(s);
518				}
519			}
520
521			// Feb 4 2000 5:30 PM
522			// Catch Throwable here rather than Exception.
523			// Kaffe's implementation of Runtime.exec throws java.lang.InternalError.
524			catch (Throwable t)
525			{
526			}
527		}
528
529		return permissions;
530	} //}}}
531
532	//{{{ setPermissions() method
533	/**
534	 * Sets numeric permissions of a file. On non-Unix platforms,
535	 * does nothing.
536	 * @since jEdit 3.2pre9
537	 */
538	public static void setPermissions(String path, int permissions)
539	{
540		if(jEdit.getBooleanProperty("chmodDisabled"))
541			return;
542
543		if(permissions != 0)
544		{
545			if(OperatingSystem.isUnix())
546			{
547				String[] cmdarray = { "chmod", Integer.toString(permissions, 8), path };
548
549				try
550				{
551					Process process = Runtime.getRuntime().exec(cmdarray);
552					process.getInputStream().close();
553					process.getOutputStream().close();
554					process.getErrorStream().close();
555					// Jun 9 2004 12:40 PM
556					// waitFor() hangs on some Java
557					// implementations.
558					/* int exitCode = process.waitFor();
559					if(exitCode != 0)
560						Log.log(Log.NOTICE,FileVFS.class,"chmod exited with code " + exitCode); */
561				}
562
563				// Feb 4 2000 5:30 PM
564				// Catch Throwable here rather than Exception.
565				// Kaffe's implementation of Runtime.exec throws java.lang.InternalError.
566				catch (Throwable t)
567				{
568				}
569			}
570		}
571	} //}}}
572
573	//}}}
574
575	//{{{ Private members
576	private static FileSystemView fsView = FileSystemView.getFileSystemView();
577	//}}}
578}