PageRenderTime 113ms CodeModel.GetById 94ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 513 lines | 337 code | 70 blank | 106 comment | 91 complexity | 36c229475920aa0e5a7fa8b8bf30fdc3 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.util.Vector;
 30import org.gjt.sp.jedit.*;
 31import org.gjt.sp.util.Log;
 32//}}}
 33
 34/**
 35 * Local filesystem VFS.
 36 * @author Slava Pestov
 37 * @version $Id: FileVFS.java 4000 2002-01-30 08:06:41Z spestov $
 38 */
 39public class FileVFS extends VFS
 40{
 41	public static final String PERMISSIONS_PROPERTY = "FileVFS__perms";
 42
 43	//{{{ FileVFS method
 44	public FileVFS()
 45	{
 46		super("file");
 47	} //}}}
 48
 49	//{{{ getCapabilities() method
 50	public int getCapabilities()
 51	{
 52		return READ_CAP | WRITE_CAP | BROWSE_CAP | DELETE_CAP
 53			| RENAME_CAP | MKDIR_CAP;
 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		}
 66
 67		return super.getParentOfPath(path);
 68	} //}}}
 69
 70	//{{{ constructPath() method
 71	public String constructPath(String parent, String path)
 72	{
 73		// have to handle these cases specially on windows.
 74		if(OperatingSystem.isDOSDerived())
 75		{
 76			if(path.length() == 2 && path.charAt(1) == ':')
 77				return path;
 78			if(path.startsWith("\\"))
 79				parent = parent.substring(0,2);
 80		}
 81
 82		if(parent.endsWith(File.separator))
 83			path = parent + path;
 84		else
 85			path = parent + File.separator + path;
 86
 87		try
 88		{
 89			return new File(path).getCanonicalPath();
 90		}
 91		catch(IOException io)
 92		{
 93			return path;
 94		}
 95	} //}}}
 96
 97	//{{{ getFileSeparator() method
 98	public char getFileSeparator()
 99	{
100		return File.separatorChar;
101	} //}}}
102
103	//{{{ load() method
104	public boolean load(View view, Buffer buffer, String path)
105	{
106		File file = new File(MiscUtilities.canonPath(path));
107
108		//{{{ Check if file is valid
109		if(!file.exists())
110		{
111			buffer.setNewFile(true);
112			return true;
113		}
114		else
115			buffer.setReadOnly(!file.canWrite());
116
117		if(file.isDirectory())
118		{
119			VFSManager.error(view,file.getPath(),
120				"ioerror.open-directory",null);
121			buffer.setNewFile(false);
122			return false;
123		}
124
125		if(!file.canRead())
126		{
127			VFSManager.error(view,file.getPath(),
128				"ioerror.no-read",null);
129			buffer.setNewFile(false);
130			return false;
131		} //}}}
132
133		return super.load(view,buffer,path);
134	} //}}}
135
136	//{{{ save() method
137	public boolean save(View view, Buffer buffer, String path)
138	{
139		// can't call buffer.getFile() here because this
140		// method is called *before* setPath()
141		File file = new File(path);
142
143		//{{{ Check if file is valid
144
145		// Apparently, certain broken OSes (like Micro$oft Windows)
146		// can mess up directories if they are write()'n to
147		if(file.isDirectory())
148		{
149			VFSManager.error(view,file.getPath(),
150				"ioerror.save-directory",null);
151			return false;
152		}
153
154		// Check that we can actually write to the file
155		if((file.exists() && !file.canWrite())
156			|| (!file.exists() && !new File(file.getParent()).canWrite()))
157		{
158			VFSManager.error(view,file.getPath(),
159				"ioerror.no-write",null);
160			return false;
161		} //}}}
162
163		//{{{ On Unix, preserve permissions
164		if(OperatingSystem.isUnix())
165		{
166			int permissions = getPermissions(buffer.getPath());
167			Log.log(Log.DEBUG,this,buffer.getPath() + " has permissions 0"
168				+ Integer.toString(permissions,8));
169			buffer.setIntegerProperty(PERMISSIONS_PROPERTY,permissions);
170		} //}}}
171
172		return super.save(view,buffer,path);
173	} //}}}
174
175	//{{{ insert() method
176	public boolean insert(View view, Buffer buffer, String path)
177	{
178		File file = new File(path);
179
180		//{{{ Check if file is valid
181		if(!file.exists())
182			return false;
183
184		if(file.isDirectory())
185		{
186			VFSManager.error(view,file.getPath(),
187				"ioerror.open-directory",null);
188			return false;
189		}
190
191		if(!file.canRead())
192		{
193			VFSManager.error(view,file.getPath(),
194				"ioerror.no-read",null);
195			return false;
196		} //}}}
197
198		return super.insert(view,buffer,path);
199	} //}}}
200
201	//{{{ _canonPath() method
202	/**
203	 * Returns the canonical form if the specified path name. For example,
204	 * <code>~</code> might be expanded to the user's home directory.
205	 * @param session The session
206	 * @param path The path
207	 * @param comp The component that will parent error dialog boxes
208	 * @exception IOException if an I/O error occurred
209	 * @since jEdit 4.0pre2
210	 */
211	public String _canonPath(Object session, String path, Component comp)
212		throws IOException
213	{
214		return MiscUtilities.canonPath(path);
215	} //}}}
216
217	//{{{ _listDirectory() method
218	public VFS.DirectoryEntry[] _listDirectory(Object session, String path,
219		Component comp)
220	{
221		//{{{ Windows work around
222		/* On Windows, paths of the form X: list the last *working
223		 * directory* on that drive. To list the root of the drive,
224		 * you must use X:\.
225		 *
226		 * However, the VFS browser and friends strip off trailing
227		 * path separators, for various reasons. So to work around
228		 * that, we add a '\' to drive letter paths on Windows.
229		 */
230		if(OperatingSystem.isWindows())
231		{
232			if(path.length() == 2 && path.charAt(1) == ':')
233				path = path.concat(File.separator);
234		} //}}}
235
236		File directory = new File(path);
237		File[] list = directory.listFiles();
238		if(list == null)
239		{
240			VFSManager.error(comp,path,"ioerror.directory-error-nomsg",null);
241			return null;
242		}
243
244		Vector list2 = new Vector();
245		for(int i = 0; i < list.length; i++)
246		{
247			File file = list[i];
248
249			int type;
250			if(file.isDirectory())
251				type = VFS.DirectoryEntry.DIRECTORY;
252			else
253				type = VFS.DirectoryEntry.FILE;
254
255			list2.add(new VFS.DirectoryEntry(file.getName(),
256				file.getPath(),file.getPath(),type,
257				file.length(),file.isHidden()));
258		}
259
260		VFS.DirectoryEntry[] retVal = new VFS.DirectoryEntry[list2.size()];
261		list2.copyInto(retVal);
262		return retVal;
263	} //}}}
264
265	//{{{ _getDirectoryEntry() method
266	public DirectoryEntry _getDirectoryEntry(Object session, String path,
267		Component comp)
268	{
269		// workaround for Java bug where paths with trailing / return
270		// null getName()
271		if(path.endsWith("/") || path.endsWith(File.separator))
272			path = path.substring(0,path.length() - 1);
273
274		File file = new File(path);
275		if(!file.exists())
276			return null;
277
278		int type;
279		if(file.isDirectory())
280			type = VFS.DirectoryEntry.DIRECTORY;
281		else
282			type = VFS.DirectoryEntry.FILE;
283
284		return new VFS.DirectoryEntry(file.getName(),path,path,type,
285			file.length(),file.isHidden());
286	} //}}}
287
288	//{{{ _delete() method
289	public boolean _delete(Object session, String path, Component comp)
290	{
291		boolean retVal = new File(path).delete();
292		if(retVal)
293			VFSManager.sendVFSUpdate(this,path,true);
294		return retVal;
295	} //}}}
296
297	//{{{ _rename() method
298	public boolean _rename(Object session, String from, String to,
299		Component comp)
300	{
301		File _to = new File(to);
302
303		// Case-insensitive fs workaround
304		if(!from.equalsIgnoreCase(to))
305			_to.delete();
306
307		boolean retVal = new File(from).renameTo(_to);
308		VFSManager.sendVFSUpdate(this,from,true);
309		VFSManager.sendVFSUpdate(this,to,true);
310		return retVal;
311	} //}}}
312
313	//{{{ _mkdir() method
314	public boolean _mkdir(Object session, String directory, Component comp)
315	{
316		boolean retVal = new File(directory).mkdirs();
317		VFSManager.sendVFSUpdate(this,directory,true);
318		return retVal;
319	} //}}}
320
321	//{{{ _backup() method
322	public void _backup(Object session, String path, Component comp)
323		throws IOException
324	{
325		// Fetch properties
326		int backups = jEdit.getIntegerProperty("backups",1);
327
328		if(backups == 0)
329			return;
330
331		String backupPrefix = jEdit.getProperty("backup.prefix");
332		String backupSuffix = jEdit.getProperty("backup.suffix");
333
334		String backupDirectory = MiscUtilities.canonPath(
335			jEdit.getProperty("backup.directory"));
336
337		File file = new File(path);
338
339		// Check for backup.directory, and create that
340		// directory if it doesn't exist
341		if(backupDirectory == null || backupDirectory.length() == 0)
342			backupDirectory = file.getParent();
343		else
344		{
345			backupDirectory = MiscUtilities.constructPath(
346				System.getProperty("user.home"),backupDirectory);
347
348			// Perhaps here we would want to guard with
349			// a property for parallel backups or not.
350			backupDirectory = MiscUtilities.concatPath(
351				backupDirectory,file.getParent());
352
353			File dir = new File(backupDirectory);
354
355			if (!dir.exists())
356				dir.mkdirs();
357		}
358
359		MiscUtilities.saveBackup(file,backups,backupPrefix,
360			backupSuffix,backupDirectory);
361	} //}}}
362
363	//{{{ _createInputStream() method
364	public InputStream _createInputStream(Object session, String path,
365		boolean ignoreErrors, Component comp) throws IOException
366	{
367		try
368		{
369			return new FileInputStream(path);
370		}
371		catch(IOException io)
372		{
373			if(ignoreErrors)
374				return null;
375			else
376				throw io;
377		}
378	} //}}}
379
380	//{{{ _createOutputStream() method
381	public OutputStream _createOutputStream(Object session, String path,
382		Component comp) throws IOException
383	{
384		OutputStream retVal = new FileOutputStream(path);
385
386		// commented out for now, because updating VFS browsers
387		// every time file is saved gets annoying
388		//VFSManager.sendVFSUpdate(this,path,true);
389		return retVal;
390	} //}}}
391
392	//{{{ _saveComplete() method
393	public void _saveComplete(Object session, Buffer buffer, Component comp)
394	{
395		int permissions = buffer.getIntegerProperty(PERMISSIONS_PROPERTY,0);
396		setPermissions(buffer.getPath(),permissions);
397	} //}}}
398
399	//{{{ Permission preservation code
400
401	/** Code borrowed from j text editor (http://www.armedbear.org) */
402	/** I made some changes to make it support suid, sgid and sticky files */
403
404	//{{{ getPermissions() method
405	/**
406	 * Returns numeric permissions of a file. On non-Unix systems, always
407	 * returns zero.
408	 * @since jEdit 3.2pre9
409	 */
410	public static int getPermissions(String path)
411	{
412		int permissions = 0;
413
414		if(OperatingSystem.isUnix())
415		{
416			String[] cmdarray = { "ls", "-ld", path };
417
418			try
419			{
420				Process process = Runtime.getRuntime().exec(cmdarray);
421
422				BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
423
424				String output = reader.readLine();
425
426				if(output != null)
427				{
428					String s = output.substring(1, 10);
429
430					if(s.length() == 9)
431					{
432						if(s.charAt(0) == 'r')
433							permissions += 0400;
434						if(s.charAt(1) == 'w')
435							permissions += 0200;
436						if(s.charAt(2) == 'x')
437							permissions += 0100;
438						else if(s.charAt(2) == 's')
439							permissions += 04100;
440						else if(s.charAt(2) == 'S')
441							permissions += 04000;
442						if(s.charAt(3) == 'r')
443							permissions += 040;
444						if(s.charAt(4) == 'w')
445							permissions += 020;
446						if(s.charAt(5) == 'x')
447							permissions += 010;
448						else if(s.charAt(5) == 's')
449							permissions += 02010;
450						else if(s.charAt(5) == 'S')
451							permissions += 02000;
452						if(s.charAt(6) == 'r')
453							permissions += 04;
454						if(s.charAt(7) == 'w')
455							permissions += 02;
456						if(s.charAt(8) == 'x')
457							permissions += 01;
458						else if(s.charAt(8) == 't')
459							permissions += 01001;
460						else if(s.charAt(8) == 'T')
461							permissions += 01000;
462					}
463				}
464			}
465
466			// Feb 4 2000 5:30 PM
467			// Catch Throwable here rather than Exception.
468			// Kaffe's implementation of Runtime.exec throws java.lang.InternalError.
469			catch (Throwable t)
470			{
471			}
472		}
473
474		return permissions;
475	} //}}}
476
477	//{{{ setPermissions() method
478	/**
479	 * Sets numeric permissions of a file. On non-Unix platforms,
480	 * does nothing.
481	 * @since jEdit 3.2pre9
482	 */
483	public static void setPermissions(String path, int permissions)
484	{
485		if(permissions != 0)
486		{
487			if(OperatingSystem.isUnix())
488			{
489				String[] cmdarray = { "chmod", Integer.toString(permissions, 8), path };
490
491				try
492				{
493					Process process = Runtime.getRuntime().exec(cmdarray);
494					process.getInputStream().close();
495					process.getOutputStream().close();
496					process.getErrorStream().close();
497					int exitCode = process.waitFor();
498					if(exitCode != 0)
499						Log.log(Log.NOTICE,FileVFS.class,"chmod exited with code " + exitCode);
500				}
501
502				// Feb 4 2000 5:30 PM
503				// Catch Throwable here rather than Exception.
504				// Kaffe's implementation of Runtime.exec throws java.lang.InternalError.
505				catch (Throwable t)
506				{
507				}
508			}
509		}
510	} //}}}
511
512	//}}}
513}