/jEdit/tags/jedit-4-1-pre6/org/gjt/sp/jedit/io/FileVFS.java

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