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

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