PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

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