PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms 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
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  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. package org.gjt.sp.jedit.io;
  24. //{{{ Imports
  25. import javax.swing.filechooser.FileSystemView;
  26. import java.awt.Component;
  27. import java.io.*;
  28. import java.text.*;
  29. import java.util.Date;
  30. import org.gjt.sp.jedit.*;
  31. import org.gjt.sp.util.Log;
  32. //}}}
  33. /**
  34. * Local filesystem VFS.
  35. * @author Slava Pestov
  36. * @version $Id: FileVFS.java 5211 2005-03-22 00:36:31Z spestov $
  37. */
  38. public class FileVFS extends VFS
  39. {
  40. public static final String PERMISSIONS_PROPERTY = "FileVFS__perms";
  41. //{{{ FileVFS method
  42. public FileVFS()
  43. {
  44. super("file",READ_CAP | WRITE_CAP | DELETE_CAP
  45. | RENAME_CAP | MKDIR_CAP | LOW_LATENCY_CAP
  46. | ((OperatingSystem.isCaseInsensitiveFS())
  47. ? CASE_INSENSITIVE_CAP : 0),
  48. new String[] { EA_TYPE, EA_SIZE, EA_STATUS,
  49. EA_MODIFIED });
  50. } //}}}
  51. //{{{ getParentOfPath() method
  52. public String getParentOfPath(String path)
  53. {
  54. if(OperatingSystem.isDOSDerived())
  55. {
  56. if(path.length() == 2 && path.charAt(1) == ':')
  57. return FileRootsVFS.PROTOCOL + ":";
  58. else if(path.length() == 3 && path.endsWith(":\\"))
  59. return FileRootsVFS.PROTOCOL + ":";
  60. else if(path.startsWith("\\\\") && path.indexOf('\\',2) == -1)
  61. return path;
  62. }
  63. return super.getParentOfPath(path);
  64. } //}}}
  65. //{{{ constructPath() method
  66. public String constructPath(String parent, String path)
  67. {
  68. if(parent.endsWith(File.separator)
  69. || parent.endsWith("/"))
  70. return parent + path;
  71. else
  72. return parent + File.separator + path;
  73. } //}}}
  74. //{{{ getFileSeparator() method
  75. public char getFileSeparator()
  76. {
  77. return File.separatorChar;
  78. } //}}}
  79. //{{{ getTwoStageSaveName() method
  80. /**
  81. * Returns a temporary file name based on the given path.
  82. *
  83. * <p>If the directory where the file would be created cannot be
  84. * written (i.e., no new files can be created in that directory),
  85. * this method returns <code>null</code>.</p>
  86. *
  87. * @param path The path name
  88. */
  89. public String getTwoStageSaveName(String path)
  90. {
  91. File parent = new File(getParentOfPath(path));
  92. return (parent.canWrite())
  93. ? super.getTwoStageSaveName(path)
  94. : null;
  95. } //}}}
  96. //{{{ save() method
  97. public boolean save(View view, Buffer buffer, String path)
  98. {
  99. if(OperatingSystem.isUnix())
  100. {
  101. int permissions = getPermissions(buffer.getPath());
  102. Log.log(Log.DEBUG,this,buffer.getPath() + " has permissions 0"
  103. + Integer.toString(permissions,8));
  104. buffer.setIntegerProperty(PERMISSIONS_PROPERTY,permissions);
  105. }
  106. return super.save(view,buffer,path);
  107. } //}}}
  108. //{{{ insert() method
  109. public boolean insert(View view, Buffer buffer, String path)
  110. {
  111. File file = new File(path);
  112. //{{{ Check if file is valid
  113. if(!file.exists())
  114. return false;
  115. if(file.isDirectory())
  116. {
  117. VFSManager.error(view,file.getPath(),
  118. "ioerror.open-directory",null);
  119. return false;
  120. }
  121. if(!file.canRead())
  122. {
  123. VFSManager.error(view,file.getPath(),
  124. "ioerror.no-read",null);
  125. return false;
  126. } //}}}
  127. return super.insert(view,buffer,path);
  128. } //}}}
  129. //{{{ _canonPath() method
  130. /**
  131. * Returns the canonical form if the specified path name. For example,
  132. * <code>~</code> might be expanded to the user's home directory.
  133. * @param session The session
  134. * @param path The path
  135. * @param comp The component that will parent error dialog boxes
  136. * @exception IOException if an I/O error occurred
  137. * @since jEdit 4.0pre2
  138. */
  139. public String _canonPath(Object session, String path, Component comp)
  140. throws IOException
  141. {
  142. return MiscUtilities.canonPath(path);
  143. } //}}}
  144. //{{{ LocalFile class
  145. public static class LocalFile extends VFSFile
  146. {
  147. private File file;
  148. // use system default short format
  149. public static DateFormat DATE_FORMAT
  150. = DateFormat.getInstance();
  151. /**
  152. * @deprecated Call getModified() instead.
  153. */
  154. public long modified;
  155. public LocalFile(File file)
  156. {
  157. this.file = file;
  158. /* These attributes are fetched relatively
  159. quickly. The rest are lazily filled in. */
  160. setName(file.getName());
  161. String path = file.getPath();
  162. setPath(path);
  163. setDeletePath(path);
  164. setHidden(file.isHidden());
  165. setType(file.isDirectory()
  166. ? VFSFile.DIRECTORY
  167. : VFSFile.FILE);
  168. }
  169. public String getExtendedAttribute(String name)
  170. {
  171. if(name.equals(EA_MODIFIED))
  172. return DATE_FORMAT.format(new Date(modified));
  173. else
  174. return super.getExtendedAttribute(name);
  175. }
  176. protected void fetchAttrs()
  177. {
  178. if(fetchedAttrs())
  179. return;
  180. super.fetchAttrs();
  181. setSymlinkPath(MiscUtilities.resolveSymlinks(
  182. file.getPath()));
  183. setReadable(file.canRead());
  184. setWriteable(file.canWrite());
  185. setLength(file.length());
  186. setModified(file.lastModified());
  187. }
  188. public String getSymlinkPath()
  189. {
  190. fetchAttrs();
  191. return super.getSymlinkPath();
  192. }
  193. public long getLength()
  194. {
  195. fetchAttrs();
  196. return super.getLength();
  197. }
  198. public boolean isReadable()
  199. {
  200. fetchAttrs();
  201. return super.isReadable();
  202. }
  203. public boolean isWriteable()
  204. {
  205. fetchAttrs();
  206. return super.isWriteable();
  207. }
  208. public long getModified()
  209. {
  210. return modified;
  211. }
  212. public void setModified(long modified)
  213. {
  214. this.modified = modified;
  215. }
  216. } //}}}
  217. //{{{ _listFiles() method
  218. public VFSFile[] _listFiles(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. File directory = new File(path);
  236. File[] list = null;
  237. if(directory.exists())
  238. list = fsView.getFiles(directory,false);
  239. if(list == null)
  240. {
  241. VFSManager.error(comp,path,"ioerror.directory-error-nomsg",null);
  242. return null;
  243. }
  244. VFSFile[] list2 = new VFSFile[list.length];
  245. for(int i = 0; i < list.length; i++)
  246. list2[i] = new LocalFile(list[i]);
  247. return list2;
  248. } //}}}
  249. //{{{ _getFile() method
  250. public VFSFile _getFile(Object session, String path,
  251. Component comp)
  252. {
  253. if(path.equals("/") && OperatingSystem.isUnix())
  254. {
  255. return new VFS.DirectoryEntry(path,path,path,
  256. VFSFile.DIRECTORY,0L,false);
  257. }
  258. File file = new File(path);
  259. if(!file.exists())
  260. return null;
  261. return new LocalFile(file);
  262. } //}}}
  263. //{{{ _delete() method
  264. public boolean _delete(Object session, String path, Component comp)
  265. {
  266. File file = new File(path);
  267. // do some platforms throw exceptions if the file does not exist
  268. // when we ask for the canonical path?
  269. String canonPath;
  270. try
  271. {
  272. canonPath = file.getCanonicalPath();
  273. }
  274. catch(IOException io)
  275. {
  276. canonPath = path;
  277. }
  278. boolean retVal = file.delete();
  279. if(retVal)
  280. VFSManager.sendVFSUpdate(this,canonPath,true);
  281. return retVal;
  282. } //}}}
  283. //{{{ _rename() method
  284. public boolean _rename(Object session, String from, String to,
  285. Component comp)
  286. {
  287. File _to = new File(to);
  288. String toCanonPath;
  289. try
  290. {
  291. toCanonPath = _to.getCanonicalPath();
  292. }
  293. catch(IOException io)
  294. {
  295. toCanonPath = to;
  296. }
  297. // this is needed because on OS X renaming to a non-existent
  298. // directory causes problems
  299. File parent = new File(_to.getParent());
  300. if(parent.exists())
  301. {
  302. if(!parent.isDirectory())
  303. return false;
  304. }
  305. else
  306. {
  307. parent.mkdirs();
  308. if(!parent.exists())
  309. return false;
  310. }
  311. File _from = new File(from);
  312. String fromCanonPath;
  313. try
  314. {
  315. fromCanonPath = _from.getCanonicalPath();
  316. }
  317. catch(IOException io)
  318. {
  319. fromCanonPath = from;
  320. }
  321. // Case-insensitive fs workaround
  322. if(!fromCanonPath.equalsIgnoreCase(toCanonPath))
  323. _to.delete();
  324. boolean retVal = _from.renameTo(_to);
  325. VFSManager.sendVFSUpdate(this,fromCanonPath,true);
  326. VFSManager.sendVFSUpdate(this,toCanonPath,true);
  327. return retVal;
  328. } //}}}
  329. //{{{ _mkdir() method
  330. public boolean _mkdir(Object session, String directory, Component comp)
  331. {
  332. String parent = getParentOfPath(directory);
  333. if(!new File(parent).exists())
  334. {
  335. if(!_mkdir(session,parent,comp))
  336. return false;
  337. }
  338. File file = new File(directory);
  339. boolean retVal = file.mkdir();
  340. String canonPath;
  341. try
  342. {
  343. canonPath = file.getCanonicalPath();
  344. }
  345. catch(IOException io)
  346. {
  347. canonPath = directory;
  348. }
  349. VFSManager.sendVFSUpdate(this,canonPath,true);
  350. return retVal;
  351. } //}}}
  352. //{{{ _backup() method
  353. public void _backup(Object session, String path, Component comp)
  354. throws IOException
  355. {
  356. // Fetch properties
  357. int backups = jEdit.getIntegerProperty("backups",1);
  358. if(backups == 0)
  359. return;
  360. String backupPrefix = jEdit.getProperty("backup.prefix");
  361. String backupSuffix = jEdit.getProperty("backup.suffix");
  362. String backupDirectory = jEdit.getProperty("backup.directory");
  363. int backupTimeDistance = jEdit.getIntegerProperty("backup.minTime",0);
  364. File file = new File(path);
  365. if (!file.exists())
  366. return;
  367. // Check for backup.directory, and create that
  368. // directory if it doesn't exist
  369. if(backupDirectory == null || backupDirectory.length() == 0)
  370. backupDirectory = file.getParent();
  371. else
  372. {
  373. backupDirectory = MiscUtilities.constructPath(
  374. System.getProperty("user.home"),backupDirectory);
  375. // Perhaps here we would want to guard with
  376. // a property for parallel backups or not.
  377. backupDirectory = MiscUtilities.concatPath(
  378. backupDirectory,file.getParent());
  379. File dir = new File(backupDirectory);
  380. if (!dir.exists())
  381. dir.mkdirs();
  382. }
  383. MiscUtilities.saveBackup(file,backups,backupPrefix,
  384. backupSuffix,backupDirectory,backupTimeDistance);
  385. } //}}}
  386. //{{{ _createInputStream() method
  387. public InputStream _createInputStream(Object session, String path,
  388. boolean ignoreErrors, Component comp) throws IOException
  389. {
  390. try
  391. {
  392. return new FileInputStream(path);
  393. }
  394. catch(IOException io)
  395. {
  396. if(ignoreErrors)
  397. return null;
  398. else
  399. throw io;
  400. }
  401. } //}}}
  402. //{{{ _createOutputStream() method
  403. public OutputStream _createOutputStream(Object session, String path,
  404. Component comp) throws IOException
  405. {
  406. return new FileOutputStream(path);
  407. } //}}}
  408. //{{{ _saveComplete() method
  409. public void _saveComplete(Object session, Buffer buffer, String path,
  410. Component comp)
  411. {
  412. int permissions = buffer.getIntegerProperty(PERMISSIONS_PROPERTY,0);
  413. setPermissions(path,permissions);
  414. } //}}}
  415. //{{{ Permission preservation code
  416. /** Code borrowed from j text editor (http://www.armedbear.org) */
  417. /** I made some changes to make it support suid, sgid and sticky files */
  418. //{{{ getPermissions() method
  419. /**
  420. * Returns numeric permissions of a file. On non-Unix systems, always
  421. * returns zero.
  422. * @since jEdit 3.2pre9
  423. */
  424. public static int getPermissions(String path)
  425. {
  426. int permissions = 0;
  427. if(jEdit.getBooleanProperty("chmodDisabled"))
  428. return permissions;
  429. if(OperatingSystem.isUnix())
  430. {
  431. String[] cmdarray = { "ls", "-ld", path };
  432. try
  433. {
  434. Process process = Runtime.getRuntime().exec(cmdarray);
  435. BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
  436. String output = reader.readLine();
  437. if(output != null)
  438. {
  439. String s = output.substring(1, 10);
  440. permissions = MiscUtilities
  441. .parsePermissions(s);
  442. }
  443. }
  444. // Feb 4 2000 5:30 PM
  445. // Catch Throwable here rather than Exception.
  446. // Kaffe's implementation of Runtime.exec throws java.lang.InternalError.
  447. catch (Throwable t)
  448. {
  449. }
  450. }
  451. return permissions;
  452. } //}}}
  453. //{{{ setPermissions() method
  454. /**
  455. * Sets numeric permissions of a file. On non-Unix platforms,
  456. * does nothing.
  457. * @since jEdit 3.2pre9
  458. */
  459. public static void setPermissions(String path, int permissions)
  460. {
  461. if(jEdit.getBooleanProperty("chmodDisabled"))
  462. return;
  463. if(permissions != 0)
  464. {
  465. if(OperatingSystem.isUnix())
  466. {
  467. String[] cmdarray = { "chmod", Integer.toString(permissions, 8), path };
  468. try
  469. {
  470. Process process = Runtime.getRuntime().exec(cmdarray);
  471. process.getInputStream().close();
  472. process.getOutputStream().close();
  473. process.getErrorStream().close();
  474. // Jun 9 2004 12:40 PM
  475. // waitFor() hangs on some Java
  476. // implementations.
  477. /* int exitCode = process.waitFor();
  478. if(exitCode != 0)
  479. Log.log(Log.NOTICE,FileVFS.class,"chmod exited with code " + exitCode); */
  480. }
  481. // Feb 4 2000 5:30 PM
  482. // Catch Throwable here rather than Exception.
  483. // Kaffe's implementation of Runtime.exec throws java.lang.InternalError.
  484. catch (Throwable t)
  485. {
  486. }
  487. }
  488. }
  489. } //}}}
  490. //}}}
  491. //{{{ Private members
  492. private static FileSystemView fsView = FileSystemView.getFileSystemView();
  493. //}}}
  494. }