The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1161 lines
33KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #ifdef _MSC_VER
  19. #pragma warning (disable: 4514)
  20. #pragma warning (push)
  21. #endif
  22. #include "../../core/juce_StandardHeader.h"
  23. #if ! JUCE_WINDOWS
  24. #include <pwd.h>
  25. #endif
  26. BEGIN_JUCE_NAMESPACE
  27. #include "juce_File.h"
  28. #include "juce_FileInputStream.h"
  29. #include "juce_FileOutputStream.h"
  30. #include "../../core/juce_SystemStats.h"
  31. #ifdef _MSC_VER
  32. #pragma warning (pop)
  33. #endif
  34. //==============================================================================
  35. void* juce_fileOpen (const String& path, bool forWriting) throw();
  36. void juce_fileClose (void* handle) throw();
  37. int juce_fileWrite (void* handle, const void* buffer, int size) throw();
  38. int64 juce_fileGetPosition (void* handle) throw();
  39. int64 juce_fileSetPosition (void* handle, int64 pos) throw();
  40. void juce_fileFlush (void* handle) throw();
  41. bool juce_fileExists (const String& fileName, const bool dontCountDirectories) throw();
  42. bool juce_isDirectory (const String& fileName) throw();
  43. int64 juce_getFileSize (const String& fileName) throw();
  44. bool juce_canWriteToFile (const String& fileName) throw();
  45. bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) throw();
  46. void juce_getFileTimes (const String& fileName, int64& modificationTime, int64& accessTime, int64& creationTime) throw();
  47. bool juce_setFileTimes (const String& fileName, int64 modificationTime, int64 accessTime, int64 creationTime) throw();
  48. bool juce_deleteFile (const String& fileName) throw();
  49. bool juce_copyFile (const String& source, const String& dest) throw();
  50. bool juce_moveFile (const String& source, const String& dest) throw();
  51. // this must also create all paths involved in the directory.
  52. void juce_createDirectory (const String& fileName) throw();
  53. bool juce_launchFile (const String& fileName, const String& parameters) throw();
  54. const StringArray juce_getFileSystemRoots() throw();
  55. const String juce_getVolumeLabel (const String& filenameOnVolume, int& volumeSerialNumber) throw();
  56. // starts a directory search operation with a wildcard, returning a handle for
  57. // use in calls to juce_findFileNext.
  58. // juce_firstResultFile gets the name of the file (not the whole pathname) and
  59. // the other pointers, if non-null, are set based on the properties of the file.
  60. void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile,
  61. bool* isDirectory, bool* isHidden, int64* fileSize, Time* modTime,
  62. Time* creationTime, bool* isReadOnly) throw();
  63. // returns false when no more files are found
  64. bool juce_findFileNext (void* handle, String& resultFile,
  65. bool* isDirectory, bool* isHidden, int64* fileSize,
  66. Time* modTime, Time* creationTime, bool* isReadOnly) throw();
  67. void juce_findFileClose (void* handle) throw();
  68. //==============================================================================
  69. static const String juce_addTrailingSeparator (const String& path) throw()
  70. {
  71. return path.endsWithChar (File::separator) ? path
  72. : path + File::separator;
  73. }
  74. //==============================================================================
  75. static const String parseAbsolutePath (String path) throw()
  76. {
  77. if (path.isEmpty())
  78. return String::empty;
  79. #if JUCE_WINDOWS
  80. // Windows..
  81. path = path.replaceCharacter (T('/'), T('\\'));
  82. if (path.startsWithChar (File::separator))
  83. {
  84. if (path[1] != File::separator)
  85. {
  86. jassertfalse // using a filename that starts with a slash is a bit dodgy on
  87. // Windows, because it needs a drive letter, which in this case
  88. // we'll take from the CWD.. but this is a bit of an assumption that
  89. // could be wrong..
  90. path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path;
  91. }
  92. }
  93. else if (path.indexOfChar (T(':')) < 0)
  94. {
  95. if (path.isEmpty())
  96. return String::empty;
  97. jassertfalse // using a partial filename is a bad way to initialise a file, because
  98. // we don't know what directory to put it in.
  99. // Here we'll assume it's in the CWD, but this might not be what was
  100. // intended..
  101. return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
  102. }
  103. #else
  104. // Mac or Linux..
  105. path = path.replaceCharacter (T('\\'), T('/'));
  106. if (path.startsWithChar (T('~')))
  107. {
  108. const char* homeDir = 0;
  109. if (path[1] == File::separator || path[1] == 0)
  110. {
  111. // expand a name of the form "~/abc"
  112. path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName()
  113. + path.substring (1);
  114. }
  115. else
  116. {
  117. // expand a name of type "~dave/abc"
  118. const String userName (path.substring (1)
  119. .upToFirstOccurrenceOf (T("/"), false, false));
  120. struct passwd* const pw = getpwnam (userName);
  121. if (pw != 0)
  122. {
  123. String home (homeDir);
  124. if (home.endsWithChar (File::separator))
  125. home [home.length() - 1] = 0;
  126. path = String (pw->pw_dir)
  127. + path.substring (userName.length());
  128. }
  129. }
  130. }
  131. else if (! path.startsWithChar (File::separator))
  132. {
  133. while (path.startsWith (T("./")))
  134. path = path.substring (2);
  135. if (path.isEmpty())
  136. return String::empty;
  137. jassertfalse // using a partial filename is a bad way to initialise a file, because
  138. // we don't know what directory to put it in.
  139. // Here we'll assume it's in the CWD, but this might not be what was
  140. // intended..
  141. return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
  142. }
  143. #endif
  144. int len = path.length();
  145. while (--len > 0 && path [len] == File::separator)
  146. path [len] = 0;
  147. return path;
  148. }
  149. //==============================================================================
  150. const File File::nonexistent;
  151. //==============================================================================
  152. File::File (const String& fullPathName) throw()
  153. : fullPath (parseAbsolutePath (fullPathName))
  154. {
  155. }
  156. File::File (const String& path, int) throw()
  157. : fullPath (path)
  158. {
  159. }
  160. File::File (const File& other) throw()
  161. : fullPath (other.fullPath)
  162. {
  163. }
  164. const File& File::operator= (const String& newPath) throw()
  165. {
  166. fullPath = parseAbsolutePath (newPath);
  167. return *this;
  168. }
  169. const File& File::operator= (const File& other) throw()
  170. {
  171. fullPath = other.fullPath;
  172. return *this;
  173. }
  174. //==============================================================================
  175. #if JUCE_LINUX
  176. #define NAMES_ARE_CASE_SENSITIVE 1
  177. #endif
  178. bool File::areFileNamesCaseSensitive()
  179. {
  180. #if NAMES_ARE_CASE_SENSITIVE
  181. return true;
  182. #else
  183. return false;
  184. #endif
  185. }
  186. bool File::operator== (const File& other) const throw()
  187. {
  188. // case-insensitive on Windows, but not on linux.
  189. #if NAMES_ARE_CASE_SENSITIVE
  190. return fullPath == other.fullPath;
  191. #else
  192. return fullPath.equalsIgnoreCase (other.fullPath);
  193. #endif
  194. }
  195. bool File::operator!= (const File& other) const throw()
  196. {
  197. return ! operator== (other);
  198. }
  199. //==============================================================================
  200. bool File::exists() const throw()
  201. {
  202. return juce_fileExists (fullPath, false);
  203. }
  204. bool File::existsAsFile() const throw()
  205. {
  206. return juce_fileExists (fullPath, true);
  207. }
  208. bool File::isDirectory() const throw()
  209. {
  210. return juce_isDirectory (fullPath);
  211. }
  212. bool File::hasWriteAccess() const throw()
  213. {
  214. if (exists())
  215. return juce_canWriteToFile (fullPath);
  216. #if ! JUCE_WINDOWS
  217. else if ((! isDirectory()) && fullPath.containsChar (separator))
  218. return getParentDirectory().hasWriteAccess();
  219. else
  220. return false;
  221. #else
  222. // on windows, it seems that even read-only directories can still be written into,
  223. // so checking the parent directory's permissions would return the wrong result..
  224. else
  225. return true;
  226. #endif
  227. }
  228. bool File::setReadOnly (const bool shouldBeReadOnly,
  229. const bool applyRecursively) const throw()
  230. {
  231. bool worked = true;
  232. if (applyRecursively && isDirectory())
  233. {
  234. OwnedArray <File> subFiles;
  235. findChildFiles (subFiles, File::findFilesAndDirectories, false);
  236. for (int i = subFiles.size(); --i >= 0;)
  237. worked = subFiles[i]->setReadOnly (shouldBeReadOnly, true) && worked;
  238. }
  239. return juce_setFileReadOnly (fullPath, shouldBeReadOnly) && worked;
  240. }
  241. bool File::deleteFile() const throw()
  242. {
  243. return (! exists())
  244. || juce_deleteFile (fullPath);
  245. }
  246. bool File::deleteRecursively() const throw()
  247. {
  248. bool worked = true;
  249. if (isDirectory())
  250. {
  251. OwnedArray<File> subFiles;
  252. findChildFiles (subFiles, File::findFilesAndDirectories, false);
  253. for (int i = subFiles.size(); --i >= 0;)
  254. worked = subFiles[i]->deleteRecursively() && worked;
  255. }
  256. return deleteFile() && worked;
  257. }
  258. bool File::moveFileTo (const File& newFile) const throw()
  259. {
  260. if (newFile.fullPath == fullPath)
  261. return true;
  262. #if ! NAMES_ARE_CASE_SENSITIVE
  263. if (*this != newFile)
  264. #endif
  265. if (! newFile.deleteFile())
  266. return false;
  267. return juce_moveFile (fullPath, newFile.fullPath);
  268. }
  269. bool File::copyFileTo (const File& newFile) const throw()
  270. {
  271. if (*this == newFile)
  272. return true;
  273. if (! newFile.deleteFile())
  274. return false;
  275. return juce_copyFile (fullPath, newFile.fullPath);
  276. }
  277. bool File::copyDirectoryTo (const File& newDirectory) const throw()
  278. {
  279. if (isDirectory() && newDirectory.createDirectory())
  280. {
  281. OwnedArray<File> subFiles;
  282. findChildFiles (subFiles, File::findFiles, false);
  283. int i;
  284. for (i = 0; i < subFiles.size(); ++i)
  285. if (! subFiles[i]->copyFileTo (newDirectory.getChildFile (subFiles[i]->getFileName())))
  286. return false;
  287. subFiles.clear();
  288. findChildFiles (subFiles, File::findDirectories, false);
  289. for (i = 0; i < subFiles.size(); ++i)
  290. if (! subFiles[i]->copyDirectoryTo (newDirectory.getChildFile (subFiles[i]->getFileName())))
  291. return false;
  292. return true;
  293. }
  294. return false;
  295. }
  296. //==============================================================================
  297. const String File::getPathUpToLastSlash() const throw()
  298. {
  299. const int lastSlash = fullPath.lastIndexOfChar (separator);
  300. if (lastSlash > 0)
  301. return fullPath.substring (0, lastSlash);
  302. else if (lastSlash == 0)
  303. return separatorString;
  304. else
  305. return fullPath;
  306. }
  307. const File File::getParentDirectory() const throw()
  308. {
  309. return File (getPathUpToLastSlash());
  310. }
  311. //==============================================================================
  312. const String File::getFileName() const throw()
  313. {
  314. return fullPath.substring (fullPath.lastIndexOfChar (separator) + 1);
  315. }
  316. int File::hashCode() const throw()
  317. {
  318. return fullPath.hashCode();
  319. }
  320. int64 File::hashCode64() const throw()
  321. {
  322. return fullPath.hashCode64();
  323. }
  324. const String File::getFileNameWithoutExtension() const throw()
  325. {
  326. const int lastSlash = fullPath.lastIndexOfChar (separator) + 1;
  327. const int lastDot = fullPath.lastIndexOfChar (T('.'));
  328. if (lastDot > lastSlash)
  329. return fullPath.substring (lastSlash, lastDot);
  330. else
  331. return fullPath.substring (lastSlash);
  332. }
  333. bool File::isAChildOf (const File& potentialParent) const throw()
  334. {
  335. const String ourPath (getPathUpToLastSlash());
  336. #if NAMES_ARE_CASE_SENSITIVE
  337. if (potentialParent.fullPath == ourPath)
  338. #else
  339. if (potentialParent.fullPath.equalsIgnoreCase (ourPath))
  340. #endif
  341. {
  342. return true;
  343. }
  344. else if (potentialParent.fullPath.length() >= ourPath.length())
  345. {
  346. return false;
  347. }
  348. else
  349. {
  350. return getParentDirectory().isAChildOf (potentialParent);
  351. }
  352. }
  353. //==============================================================================
  354. bool File::isAbsolutePath (const String& path) throw()
  355. {
  356. return path.startsWithChar (T('/')) || path.startsWithChar (T('\\'))
  357. #if JUCE_WINDOWS
  358. || (path.isNotEmpty() && ((const String&) path)[1] == T(':'));
  359. #else
  360. || path.startsWithChar (T('~'));
  361. #endif
  362. }
  363. const File File::getChildFile (String relativePath) const throw()
  364. {
  365. if (isAbsolutePath (relativePath))
  366. {
  367. // the path is really absolute..
  368. return File (relativePath);
  369. }
  370. else
  371. {
  372. // it's relative, so remove any ../ or ./ bits at the start.
  373. String path (fullPath);
  374. if (relativePath[0] == T('.'))
  375. {
  376. #if JUCE_WINDOWS
  377. relativePath = relativePath.replaceCharacter (T('/'), T('\\')).trimStart();
  378. #else
  379. relativePath = relativePath.replaceCharacter (T('\\'), T('/')).trimStart();
  380. #endif
  381. while (relativePath[0] == T('.'))
  382. {
  383. if (relativePath[1] == T('.'))
  384. {
  385. if (relativePath [2] == 0 || relativePath[2] == separator)
  386. {
  387. const int lastSlash = path.lastIndexOfChar (separator);
  388. if (lastSlash >= 0)
  389. path = path.substring (0, lastSlash);
  390. relativePath = relativePath.substring (3);
  391. }
  392. else
  393. {
  394. break;
  395. }
  396. }
  397. else if (relativePath[1] == separator)
  398. {
  399. relativePath = relativePath.substring (2);
  400. }
  401. else
  402. {
  403. break;
  404. }
  405. }
  406. }
  407. return File (juce_addTrailingSeparator (path) + relativePath);
  408. }
  409. }
  410. const File File::getSiblingFile (const String& fileName) const throw()
  411. {
  412. return getParentDirectory().getChildFile (fileName);
  413. }
  414. //==============================================================================
  415. int64 File::getSize() const throw()
  416. {
  417. return juce_getFileSize (fullPath);
  418. }
  419. const String File::descriptionOfSizeInBytes (const int64 bytes)
  420. {
  421. if (bytes == 1)
  422. {
  423. return "1 byte";
  424. }
  425. else if (bytes < 1024)
  426. {
  427. return String ((int) bytes) + " bytes";
  428. }
  429. else if (bytes < 1024 * 1024)
  430. {
  431. return String (bytes / 1024.0, 1) + " KB";
  432. }
  433. else if (bytes < 1024 * 1024 * 1024)
  434. {
  435. return String (bytes / (1024.0 * 1024.0), 1) + " MB";
  436. }
  437. else
  438. {
  439. return String (bytes / (1024.0 * 1024.0 * 1024.0), 1) + " GB";
  440. }
  441. }
  442. //==============================================================================
  443. bool File::create() const throw()
  444. {
  445. if (! exists())
  446. {
  447. const File parentDir (getParentDirectory());
  448. if (parentDir == *this || ! parentDir.createDirectory())
  449. return false;
  450. void* const fh = juce_fileOpen (fullPath, true);
  451. if (fh == 0)
  452. return false;
  453. juce_fileClose (fh);
  454. }
  455. return true;
  456. }
  457. bool File::createDirectory() const throw()
  458. {
  459. if (! isDirectory())
  460. {
  461. const File parentDir (getParentDirectory());
  462. if (parentDir == *this || ! parentDir.createDirectory())
  463. return false;
  464. String dir (fullPath);
  465. while (dir.endsWithChar (separator))
  466. dir [dir.length() - 1] = 0;
  467. juce_createDirectory (dir);
  468. return isDirectory();
  469. }
  470. return true;
  471. }
  472. //==============================================================================
  473. const Time File::getCreationTime() const throw()
  474. {
  475. int64 m, a, c;
  476. juce_getFileTimes (fullPath, m, a, c);
  477. return Time (c);
  478. }
  479. bool File::setCreationTime (const Time& t) const throw()
  480. {
  481. return juce_setFileTimes (fullPath, 0, 0, t.toMilliseconds());
  482. }
  483. const Time File::getLastModificationTime() const throw()
  484. {
  485. int64 m, a, c;
  486. juce_getFileTimes (fullPath, m, a, c);
  487. return Time (m);
  488. }
  489. bool File::setLastModificationTime (const Time& t) const throw()
  490. {
  491. return juce_setFileTimes (fullPath, t.toMilliseconds(), 0, 0);
  492. }
  493. const Time File::getLastAccessTime() const throw()
  494. {
  495. int64 m, a, c;
  496. juce_getFileTimes (fullPath, m, a, c);
  497. return Time (a);
  498. }
  499. bool File::setLastAccessTime (const Time& t) const throw()
  500. {
  501. return juce_setFileTimes (fullPath, 0, t.toMilliseconds(), 0);
  502. }
  503. //==============================================================================
  504. bool File::loadFileAsData (MemoryBlock& destBlock) const throw()
  505. {
  506. if (! existsAsFile())
  507. return false;
  508. FileInputStream in (*this);
  509. return getSize() == in.readIntoMemoryBlock (destBlock);
  510. }
  511. const String File::loadFileAsString() const throw()
  512. {
  513. if (! existsAsFile())
  514. return String::empty;
  515. FileInputStream in (*this);
  516. return in.readEntireStreamAsString();
  517. }
  518. //==============================================================================
  519. static inline bool fileTypeMatches (const int whatToLookFor,
  520. const bool isDir,
  521. const bool isHidden)
  522. {
  523. return (whatToLookFor & (isDir ? File::findDirectories
  524. : File::findFiles)) != 0
  525. && ((! isHidden)
  526. || (whatToLookFor & File::ignoreHiddenFiles) == 0);
  527. }
  528. int File::findChildFiles (OwnedArray<File>& results,
  529. const int whatToLookFor,
  530. const bool searchRecursively,
  531. const String& wildCardPattern) const throw()
  532. {
  533. // you have to specify the type of files you're looking for!
  534. jassert ((whatToLookFor & (findFiles | findDirectories)) != 0);
  535. int total = 0;
  536. // find child files or directories in this directory first..
  537. if (isDirectory())
  538. {
  539. const String path (juce_addTrailingSeparator (fullPath));
  540. String filename;
  541. bool isDirectory, isHidden;
  542. void* const handle = juce_findFileStart (path,
  543. wildCardPattern,
  544. filename,
  545. &isDirectory, &isHidden,
  546. 0, 0, 0, 0);
  547. if (handle != 0)
  548. {
  549. do
  550. {
  551. if (fileTypeMatches (whatToLookFor, isDirectory, isHidden)
  552. && ! filename.containsOnly (T(".")))
  553. {
  554. results.add (new File (path + filename, 0));
  555. ++total;
  556. }
  557. } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0));
  558. juce_findFileClose (handle);
  559. }
  560. }
  561. else
  562. {
  563. // trying to search for files inside a non-directory?
  564. //jassertfalse
  565. }
  566. // and recurse down if required.
  567. if (searchRecursively)
  568. {
  569. OwnedArray <File> subDirectories;
  570. findChildFiles (subDirectories, File::findDirectories, false);
  571. for (int i = 0; i < subDirectories.size(); ++i)
  572. {
  573. total += subDirectories.getUnchecked(i)
  574. ->findChildFiles (results,
  575. whatToLookFor,
  576. true,
  577. wildCardPattern);
  578. }
  579. }
  580. return total;
  581. }
  582. int File::getNumberOfChildFiles (const int whatToLookFor,
  583. const String& wildCardPattern) const throw()
  584. {
  585. // you have to specify the type of files you're looking for!
  586. jassert (whatToLookFor > 0 && whatToLookFor <= 3);
  587. int count = 0;
  588. if (isDirectory())
  589. {
  590. String filename;
  591. bool isDirectory, isHidden;
  592. void* const handle = juce_findFileStart (fullPath,
  593. wildCardPattern,
  594. filename,
  595. &isDirectory, &isHidden,
  596. 0, 0, 0, 0);
  597. if (handle != 0)
  598. {
  599. do
  600. {
  601. if (fileTypeMatches (whatToLookFor, isDirectory, isHidden)
  602. && ! filename.containsOnly (T(".")))
  603. {
  604. ++count;
  605. }
  606. } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0));
  607. juce_findFileClose (handle);
  608. }
  609. }
  610. else
  611. {
  612. // trying to search for files inside a non-directory?
  613. jassertfalse
  614. }
  615. return count;
  616. }
  617. bool File::containsSubDirectories() const throw()
  618. {
  619. bool result = false;
  620. if (isDirectory())
  621. {
  622. String filename;
  623. bool isDirectory, isHidden;
  624. void* const handle = juce_findFileStart (juce_addTrailingSeparator (fullPath),
  625. T("*"), filename,
  626. &isDirectory, &isHidden, 0, 0, 0, 0);
  627. if (handle != 0)
  628. {
  629. do
  630. {
  631. if (isDirectory)
  632. {
  633. result = true;
  634. break;
  635. }
  636. } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0));
  637. juce_findFileClose (handle);
  638. }
  639. }
  640. return result;
  641. }
  642. //==============================================================================
  643. const File File::getNonexistentChildFile (const String& prefix_,
  644. const String& suffix,
  645. bool putNumbersInBrackets) const throw()
  646. {
  647. File f (getChildFile (prefix_ + suffix));
  648. if (f.exists())
  649. {
  650. int num = 2;
  651. String prefix (prefix_);
  652. // remove any bracketed numbers that may already be on the end..
  653. if (prefix.trim().endsWithChar (T(')')))
  654. {
  655. putNumbersInBrackets = true;
  656. const int openBracks = prefix.lastIndexOfChar (T('('));
  657. const int closeBracks = prefix.lastIndexOfChar (T(')'));
  658. if (openBracks > 0
  659. && closeBracks > openBracks
  660. && prefix.substring (openBracks + 1, closeBracks).containsOnly (T("0123456789")))
  661. {
  662. num = prefix.substring (openBracks + 1, closeBracks).getIntValue() + 1;
  663. prefix = prefix.substring (0, openBracks);
  664. }
  665. }
  666. // also use brackets if it ends in a digit.
  667. putNumbersInBrackets = putNumbersInBrackets
  668. || CharacterFunctions::isDigit (prefix.getLastCharacter());
  669. do
  670. {
  671. if (putNumbersInBrackets)
  672. f = getChildFile (prefix + T('(') + String (num++) + T(')') + suffix);
  673. else
  674. f = getChildFile (prefix + String (num++) + suffix);
  675. } while (f.exists());
  676. }
  677. return f;
  678. }
  679. const File File::getNonexistentSibling (const bool putNumbersInBrackets) const throw()
  680. {
  681. if (exists())
  682. {
  683. return getParentDirectory()
  684. .getNonexistentChildFile (getFileNameWithoutExtension(),
  685. getFileExtension(),
  686. putNumbersInBrackets);
  687. }
  688. else
  689. {
  690. return *this;
  691. }
  692. }
  693. //==============================================================================
  694. const String File::getFileExtension() const throw()
  695. {
  696. String ext;
  697. if (! isDirectory())
  698. {
  699. const int indexOfDot = fullPath.lastIndexOfChar (T('.'));
  700. if (indexOfDot > fullPath.lastIndexOfChar (separator))
  701. ext = fullPath.substring (indexOfDot);
  702. }
  703. return ext;
  704. }
  705. bool File::hasFileExtension (const String& possibleSuffix) const throw()
  706. {
  707. if (possibleSuffix.isEmpty())
  708. return fullPath.lastIndexOfChar (T('.')) <= fullPath.lastIndexOfChar (separator);
  709. if (fullPath.endsWithIgnoreCase (possibleSuffix))
  710. {
  711. if (possibleSuffix.startsWithChar (T('.')))
  712. return true;
  713. const int dotPos = fullPath.length() - possibleSuffix.length() - 1;
  714. if (dotPos >= 0)
  715. return fullPath [dotPos] == T('.');
  716. }
  717. return false;
  718. }
  719. const File File::withFileExtension (const String& newExtension) const throw()
  720. {
  721. if (fullPath.isEmpty())
  722. return File::nonexistent;
  723. String filePart (getFileName());
  724. int i = filePart.lastIndexOfChar (T('.'));
  725. if (i < 0)
  726. i = filePart.length();
  727. String newExt (newExtension);
  728. if (newExt.isNotEmpty() && ! newExt.startsWithChar (T('.')))
  729. newExt = T(".") + newExt;
  730. return getSiblingFile (filePart.substring (0, i) + newExt);
  731. }
  732. //==============================================================================
  733. bool File::startAsProcess (const String& parameters) const throw()
  734. {
  735. return exists()
  736. && juce_launchFile (fullPath, parameters);
  737. }
  738. //==============================================================================
  739. FileInputStream* File::createInputStream() const throw()
  740. {
  741. if (existsAsFile())
  742. return new FileInputStream (*this);
  743. else
  744. return 0;
  745. }
  746. FileOutputStream* File::createOutputStream (const int bufferSize) const throw()
  747. {
  748. FileOutputStream* const out = new FileOutputStream (*this, bufferSize);
  749. if (out->failedToOpen())
  750. {
  751. delete out;
  752. return 0;
  753. }
  754. else
  755. {
  756. return out;
  757. }
  758. }
  759. //==============================================================================
  760. bool File::appendData (const void* const dataToAppend,
  761. const int numberOfBytes) const throw()
  762. {
  763. if (numberOfBytes > 0)
  764. {
  765. FileOutputStream* const out = createOutputStream();
  766. if (out == 0)
  767. return false;
  768. out->write (dataToAppend, numberOfBytes);
  769. delete out;
  770. }
  771. return true;
  772. }
  773. bool File::replaceWithData (const void* const dataToWrite,
  774. const int numberOfBytes) const throw()
  775. {
  776. jassert (numberOfBytes >= 0); // a negative number of bytes??
  777. if (numberOfBytes <= 0)
  778. return deleteFile();
  779. const File tempFile (getSiblingFile (T(".") + getFileName()).getNonexistentSibling (false));
  780. if (tempFile.appendData (dataToWrite, numberOfBytes)
  781. && tempFile.moveFileTo (*this))
  782. {
  783. return true;
  784. }
  785. tempFile.deleteFile();
  786. return false;
  787. }
  788. bool File::appendText (const String& text,
  789. const bool asUnicode,
  790. const bool writeUnicodeHeaderBytes) const throw()
  791. {
  792. FileOutputStream* const out = createOutputStream();
  793. if (out != 0)
  794. {
  795. out->writeText (text, asUnicode, writeUnicodeHeaderBytes);
  796. delete out;
  797. return true;
  798. }
  799. return false;
  800. }
  801. bool File::printf (const tchar* pf, ...) const throw()
  802. {
  803. va_list list;
  804. va_start (list, pf);
  805. String text;
  806. text.vprintf (pf, list);
  807. return appendData ((const char*) text, text.length());
  808. }
  809. bool File::replaceWithText (const String& textToWrite,
  810. const bool asUnicode,
  811. const bool writeUnicodeHeaderBytes) const throw()
  812. {
  813. const File tempFile (getSiblingFile (T(".") + getFileName()).getNonexistentSibling (false));
  814. if (tempFile.appendText (textToWrite, asUnicode, writeUnicodeHeaderBytes)
  815. && tempFile.moveFileTo (*this))
  816. {
  817. return true;
  818. }
  819. tempFile.deleteFile();
  820. return false;
  821. }
  822. //==============================================================================
  823. const String File::createLegalPathName (const String& original) throw()
  824. {
  825. String s (original);
  826. String start;
  827. if (s[1] == T(':'))
  828. {
  829. start = s.substring (0, 2);
  830. s = s.substring (2);
  831. }
  832. return start + s.removeCharacters (T("\"#@,;:<>*^|?"))
  833. .substring (0, 1024);
  834. }
  835. const String File::createLegalFileName (const String& original) throw()
  836. {
  837. String s (original.removeCharacters (T("\"#@,;:<>*^|?\\/")));
  838. const int maxLength = 128; // only the length of the filename, not the whole path
  839. const int len = s.length();
  840. if (len > maxLength)
  841. {
  842. const int lastDot = s.lastIndexOfChar (T('.'));
  843. if (lastDot > jmax (0, len - 12))
  844. {
  845. s = s.substring (0, maxLength - (len - lastDot))
  846. + s.substring (lastDot);
  847. }
  848. else
  849. {
  850. s = s.substring (0, maxLength);
  851. }
  852. }
  853. return s;
  854. }
  855. //==============================================================================
  856. const String File::getRelativePathFrom (const File& dir) const throw()
  857. {
  858. String thisPath (fullPath);
  859. {
  860. int len = thisPath.length();
  861. while (--len >= 0 && thisPath [len] == File::separator)
  862. thisPath [len] = 0;
  863. }
  864. String dirPath (juce_addTrailingSeparator ((dir.existsAsFile()) ? dir.getParentDirectory().getFullPathName()
  865. : dir.fullPath));
  866. const int len = jmin (thisPath.length(), dirPath.length());
  867. int commonBitLength = 0;
  868. for (int i = 0; i < len; ++i)
  869. {
  870. #if NAMES_ARE_CASE_SENSITIVE
  871. if (thisPath[i] != dirPath[i])
  872. #else
  873. if (CharacterFunctions::toLowerCase (thisPath[i])
  874. != CharacterFunctions::toLowerCase (dirPath[i]))
  875. #endif
  876. {
  877. break;
  878. }
  879. ++commonBitLength;
  880. }
  881. while (commonBitLength > 0 && thisPath [commonBitLength - 1] != File::separator)
  882. --commonBitLength;
  883. // if the only common bit is the root, then just return the full path..
  884. if (commonBitLength <= 0
  885. || (commonBitLength == 1 && thisPath [1] == File::separator))
  886. return fullPath;
  887. thisPath = thisPath.substring (commonBitLength);
  888. dirPath = dirPath.substring (commonBitLength);
  889. while (dirPath.isNotEmpty())
  890. {
  891. #if JUCE_WINDOWS
  892. thisPath = T("..\\") + thisPath;
  893. #else
  894. thisPath = T("../") + thisPath;
  895. #endif
  896. const int sep = dirPath.indexOfChar (separator);
  897. if (sep >= 0)
  898. dirPath = dirPath.substring (sep + 1);
  899. else
  900. dirPath = String::empty;
  901. }
  902. return thisPath;
  903. }
  904. //==============================================================================
  905. void File::findFileSystemRoots (OwnedArray<File>& destArray) throw()
  906. {
  907. const StringArray roots (juce_getFileSystemRoots());
  908. for (int i = 0; i < roots.size(); ++i)
  909. destArray.add (new File (roots[i]));
  910. }
  911. const String File::getVolumeLabel() const throw()
  912. {
  913. int serialNum;
  914. return juce_getVolumeLabel (fullPath, serialNum);
  915. }
  916. int File::getVolumeSerialNumber() const throw()
  917. {
  918. int serialNum;
  919. juce_getVolumeLabel (fullPath, serialNum);
  920. return serialNum;
  921. }
  922. //==============================================================================
  923. const File File::createTempFile (const String& fileNameEnding) throw()
  924. {
  925. String tempName (T("temp"));
  926. static int tempNum = 0;
  927. tempName << tempNum++ << fileNameEnding;
  928. const File tempFile (getSpecialLocation (tempDirectory)
  929. .getChildFile (tempName));
  930. if (tempFile.exists())
  931. return createTempFile (fileNameEnding);
  932. else
  933. return tempFile;
  934. }
  935. END_JUCE_NAMESPACE