PageRenderTime 223ms CodeModel.GetById 176ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 1ms

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

#
Java | 555 lines | 366 code | 74 blank | 115 comment | 101 complexity | 7640608472d8c6fd5d52a97ee0c8de2e 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 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 3935 2001-12-11 06:32:54Z 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(File.separatorChar == '\\')
 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		if(path.equals("/"))
 68			return FileRootsVFS.PROTOCOL + ":";
 69
 70		return super.getParentOfPath(path);
 71	} //}}}
 72
 73	//{{{ constructPath() method
 74	public String constructPath(String parent, String path)
 75	{
 76		// have to handle these cases specially on windows.
 77		if(File.separatorChar == '\\')
 78		{
 79			if(path.length() == 2 && path.charAt(1) == ':')
 80				return path;
 81			if(path.startsWith("\\"))
 82				parent = parent.substring(0,2);
 83		}
 84
 85		if(parent.endsWith(File.separator))
 86			path = parent + path;
 87		else
 88			path = parent + File.separator + path;
 89
 90		try
 91		{
 92			return new File(path).getCanonicalPath();
 93		}
 94		catch(IOException io)
 95		{
 96			return path;
 97		}
 98	} //}}}
 99
100	//{{{ getFileSeparator() method
101	public char getFileSeparator()
102	{
103		return File.separatorChar;
104	} //}}}
105
106	//{{{ load() method
107	public boolean load(View view, Buffer buffer, String path)
108	{
109		File file = new File(MiscUtilities.canonPath(path));
110
111		//{{{ Check if file is valid
112		if(!file.exists())
113		{
114			buffer.setNewFile(true);
115			return true;
116		}
117		else
118			buffer.setReadOnly(!file.canWrite());
119
120		if(file.isDirectory())
121		{
122			VFSManager.error(view,file.getPath(),
123				"ioerror.open-directory",null);
124			buffer.setNewFile(false);
125			return false;
126		}
127
128		if(!file.canRead())
129		{
130			VFSManager.error(view,file.getPath(),
131				"ioerror.no-read",null);
132			buffer.setNewFile(false);
133			return false;
134		} //}}}
135
136		return super.load(view,buffer,path);
137	} //}}}
138
139	//{{{ save() method
140	public boolean save(View view, Buffer buffer, String path)
141	{
142		// can't call buffer.getFile() here because this
143		// method is called *before* setPath()
144		File file = new File(path);
145
146		//{{{ Check if file is valid
147
148		// Apparently, certain broken OSes (like Micro$oft Windows)
149		// can mess up directories if they are write()'n to
150		if(file.isDirectory())
151		{
152			VFSManager.error(view,file.getPath(),
153				"ioerror.save-directory",null);
154			return false;
155		}
156
157		// Check that we can actually write to the file
158		if((file.exists() && !file.canWrite())
159			|| (!file.exists() && !new File(file.getParent()).canWrite()))
160		{
161			VFSManager.error(view,file.getPath(),
162				"ioerror.no-write",null);
163			return false;
164		} //}}}
165
166		//{{{ On Unix, preserve permissions
167		if(isUnix)
168		{
169			int permissions = getPermissions(buffer.getPath());
170			Log.log(Log.DEBUG,this,buffer.getPath() + " has permissions 0"
171				+ Integer.toString(permissions,8));
172			buffer.setIntegerProperty(PERMISSIONS_PROPERTY,permissions);
173		} //}}}
174
175		return super.save(view,buffer,path);
176	} //}}}
177
178	//{{{ insert() method
179	public boolean insert(View view, Buffer buffer, String path)
180	{
181		File file = new File(path);
182
183		//{{{ Check if file is valid
184		if(!file.exists())
185			return false;
186
187		if(file.isDirectory())
188		{
189			VFSManager.error(view,file.getPath(),
190				"ioerror.open-directory",null);
191			return false;
192		}
193
194		if(!file.canRead())
195		{
196			VFSManager.error(view,file.getPath(),
197				"ioerror.no-read",null);
198			return false;
199		} //}}}
200
201		return super.insert(view,buffer,path);
202	} //}}}
203
204	//{{{ _canonPath() method
205	/**
206	 * Returns the canonical form if the specified path name. For example,
207	 * <code>~</code> might be expanded to the user's home directory.
208	 * @param session The session
209	 * @param path The path
210	 * @param comp The component that will parent error dialog boxes
211	 * @exception IOException if an I/O error occurred
212	 * @since jEdit 4.0pre2
213	 */
214	public String _canonPath(Object session, String path, Component comp)
215		throws IOException
216	{
217		return MiscUtilities.canonPath(path);
218	} //}}}
219
220	//{{{ _listDirectory() method
221	public VFS.DirectoryEntry[] _listDirectory(Object session, String path,
222		Component comp)
223	{
224		//{{{ Windows work around
225		/* On Windows, paths of the form X: list the last *working
226		 * directory* on that drive. To list the root of the drive,
227		 * you must use X:\.
228		 *
229		 * However, the VFS browser and friends strip off trailing
230		 * path separators, for various reasons. So to work around
231		 * that, we add a '\' to drive letter paths on Windows.
232		 */
233		if(File.separatorChar == '\\')
234		{
235			if(path.length() == 2 && path.charAt(1) == ':')
236				path = path.concat(File.separator);
237		} //}}}
238
239		File directory = new File(path);
240		String[] list = directory.list();
241		if(list == null)
242		{
243			VFSManager.error(comp,path,"ioerror.directory-error-nomsg",null);
244			return null;
245		}
246
247		Vector list2 = new Vector();
248		for(int i = 0; i < list.length; i++)
249		{
250			String name = list[i];
251			String _path;
252			if(path.endsWith(File.separator))
253				_path = path + name;
254			else
255				_path = path + File.separatorChar + name;
256
257			VFS.DirectoryEntry entry = _getDirectoryEntry(null,_path,null);
258
259			if(entry != null)
260				list2.addElement(entry);
261		}
262
263		VFS.DirectoryEntry[] retVal = new VFS.DirectoryEntry[list2.size()];
264		list2.copyInto(retVal);
265		return retVal;
266	} //}}}
267
268	//{{{ _getDirectoryEntry() method
269	public DirectoryEntry _getDirectoryEntry(Object session, String path,
270		Component comp)
271	{
272		// workaround for Java bug where paths with trailing / return
273		// null getName()
274		if(path.endsWith("/") || path.endsWith(File.separator))
275			path = path.substring(0,path.length() - 1);
276
277		File file = new File(path);
278		if(!file.exists())
279			return null;
280
281		int type;
282		if(file.isDirectory())
283			type = VFS.DirectoryEntry.DIRECTORY;
284		else
285			type = VFS.DirectoryEntry.FILE;
286
287		return new VFS.DirectoryEntry(file.getName(),path,path,type,
288			file.length(),file.isHidden());
289	} //}}}
290
291	//{{{ _delete() method
292	public boolean _delete(Object session, String path, Component comp)
293	{
294		boolean retVal = new File(path).delete();
295		if(retVal)
296			VFSManager.sendVFSUpdate(this,path,true);
297		return retVal;
298	} //}}}
299
300	//{{{ _rename() method
301	public boolean _rename(Object session, String from, String to,
302		Component comp)
303	{
304		File _to = new File(to);
305
306		// Windows workaround
307		if(!from.equalsIgnoreCase(to))
308			_to.delete();
309
310		boolean retVal = new File(from).renameTo(_to);
311		VFSManager.sendVFSUpdate(this,from,true);
312		VFSManager.sendVFSUpdate(this,to,true);
313		return retVal;
314	} //}}}
315
316	//{{{ _mkdir() method
317	public boolean _mkdir(Object session, String directory, Component comp)
318	{
319		boolean retVal = new File(directory).mkdirs();
320		VFSManager.sendVFSUpdate(this,directory,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 = MiscUtilities.canonPath(
338			jEdit.getProperty("backup.directory"));
339
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);
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		OutputStream retVal = new FileOutputStream(path);
388
389		// commented out for now, because updating VFS browsers
390		// every time file is saved gets annoying
391		//VFSManager.sendVFSUpdate(this,path,true);
392		return retVal;
393	} //}}}
394
395	//{{{ _saveComplete() method
396	public void _saveComplete(Object session, Buffer buffer, Component comp)
397	{
398		int permissions = buffer.getIntegerProperty(PERMISSIONS_PROPERTY,0);
399		setPermissions(buffer.getPath(),permissions);
400	} //}}}
401
402	//{{{ Permission preservation code
403
404	/** Code borrowed from j text editor (http://www.armedbear.org) */
405	/** I made some changes to make it support suid, sgid and sticky files */
406
407	//{{{ getPermissions() method
408	/**
409	 * Returns numeric permissions of a file. On non-Unix systems, always
410	 * returns zero.
411	 * @since jEdit 3.2pre9
412	 */
413	public static int getPermissions(String path)
414	{
415		int permissions = 0;
416
417		if(isUnix)
418		{
419			String[] cmdarray = { "ls", "-ld", path };
420
421			try
422			{
423				Process process = Runtime.getRuntime().exec(cmdarray);
424
425				BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
426
427				String output = reader.readLine();
428
429				if(output != null)
430				{
431					String s = output.substring(1, 10);
432
433					if(s.length() == 9)
434					{
435						if(s.charAt(0) == 'r')
436							permissions += 0400;
437						if(s.charAt(1) == 'w')
438							permissions += 0200;
439						if(s.charAt(2) == 'x')
440							permissions += 0100;
441						else if(s.charAt(2) == 's')
442							permissions += 04100;
443						else if(s.charAt(2) == 'S')
444							permissions += 04000;
445						if(s.charAt(3) == 'r')
446							permissions += 040;
447						if(s.charAt(4) == 'w')
448							permissions += 020;
449						if(s.charAt(5) == 'x')
450							permissions += 010;
451						else if(s.charAt(5) == 's')
452							permissions += 02010;
453						else if(s.charAt(5) == 'S')
454							permissions += 02000;
455						if(s.charAt(6) == 'r')
456							permissions += 04;
457						if(s.charAt(7) == 'w')
458							permissions += 02;
459						if(s.charAt(8) == 'x')
460							permissions += 01;
461						else if(s.charAt(8) == 't')
462							permissions += 01001;
463						else if(s.charAt(8) == 'T')
464							permissions += 01000;
465					}
466				}
467			}
468
469			// Feb 4 2000 5:30 PM
470			// Catch Throwable here rather than Exception.
471			// Kaffe's implementation of Runtime.exec throws java.lang.InternalError.
472			catch (Throwable t)
473			{
474			}
475		}
476
477		return permissions;
478	} //}}}
479
480	//{{{ setPermissions() method
481	/**
482	 * Sets numeric permissions of a file. On non-Unix platforms,
483	 * does nothing.
484	 * @since jEdit 3.2pre9
485	 */
486	public static void setPermissions(String path, int permissions)
487	{
488		if(permissions != 0)
489		{
490			if(isUnix)
491			{
492				String[] cmdarray = { "chmod", Integer.toString(permissions, 8), path };
493
494				try
495				{
496					Process process = Runtime.getRuntime().exec(cmdarray);
497					process.getInputStream().close();
498					process.getOutputStream().close();
499					process.getErrorStream().close();
500					int exitCode = process.waitFor();
501					if(exitCode != 0)
502						Log.log(Log.NOTICE,FileVFS.class,"chmod exited with code " + exitCode);
503				}
504
505				// Feb 4 2000 5:30 PM
506				// Catch Throwable here rather than Exception.
507				// Kaffe's implementation of Runtime.exec throws java.lang.InternalError.
508				catch (Throwable t)
509				{
510				}
511			}
512		}
513	} //}}}
514
515	//}}}
516
517	//{{{ Pivate members
518	private static boolean isUnix;
519
520	//{{{ Class initializer
521	static
522	{
523		//{{{ Determine if we are running on a Unix operating system
524		// If the file separator is '/', the OS is either Unix,
525		// MacOS X, or MacOS.
526		if(File.separatorChar == '/')
527		{
528			String osName = System.getProperty("os.name");
529			if(osName.indexOf("Mac") != -1)
530			{
531				if(osName.indexOf("X") != -1)
532				{
533					// MacOS X is Unix.
534					isUnix = true;
535				}
536				else
537				{
538					// Classic MacOS is definately not Unix.
539					isUnix = false;
540				}
541			}
542			else
543			{
544				// Unix.
545				isUnix = true;
546			}
547		} //}}}
548
549		Log.log(Log.DEBUG,FileVFS.class,"Unix operating system "
550			+ (isUnix ? "detected; will" : "not detected; will not")
551			+ " use permission-preserving code");
552	} //}}}
553
554	//}}}
555}