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.

1148 lines
32KB

  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);
  36. void juce_fileClose (void* handle);
  37. int juce_fileWrite (void* handle, const void* buffer, int size);
  38. int64 juce_fileGetPosition (void* handle);
  39. int64 juce_fileSetPosition (void* handle, int64 pos);
  40. void juce_fileFlush (void* handle);
  41. bool juce_fileExists (const String& fileName, const bool dontCountDirectories);
  42. bool juce_isDirectory (const String& fileName);
  43. int64 juce_getFileSize (const String& fileName);
  44. bool juce_canWriteToFile (const String& fileName);
  45. bool juce_setFileReadOnly (const String& fileName, bool isReadOnly);
  46. void juce_getFileTimes (const String& fileName, int64& modificationTime, int64& accessTime, int64& creationTime);
  47. bool juce_setFileTimes (const String& fileName, int64 modificationTime, int64 accessTime, int64 creationTime);
  48. bool juce_deleteFile (const String& fileName);
  49. bool juce_copyFile (const String& source, const String& dest);
  50. bool juce_moveFile (const String& source, const String& dest);
  51. // this must also create all paths involved in the directory.
  52. void juce_createDirectory (const String& fileName);
  53. bool juce_launchFile (const String& fileName, const String& parameters);
  54. const StringArray juce_getFileSystemRoots();
  55. const String juce_getVolumeLabel (const String& filenameOnVolume, int& volumeSerialNumber);
  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);
  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);
  67. void juce_findFileClose (void* handle);
  68. //==============================================================================
  69. static const String juce_addTrailingSeparator (const String& path)
  70. {
  71. return path.endsWithChar (File::separator) ? path
  72. : path + File::separator;
  73. }
  74. //==============================================================================
  75. static const String parseAbsolutePath (String path)
  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)
  153. : fullPath (parseAbsolutePath (fullPathName))
  154. {
  155. }
  156. File::File (const String& path, int)
  157. : fullPath (path)
  158. {
  159. }
  160. File::File (const File& other)
  161. : fullPath (other.fullPath)
  162. {
  163. }
  164. const File& File::operator= (const String& newPath)
  165. {
  166. fullPath = parseAbsolutePath (newPath);
  167. return *this;
  168. }
  169. const File& File::operator= (const File& other)
  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
  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
  196. {
  197. return ! operator== (other);
  198. }
  199. //==============================================================================
  200. bool File::exists() const
  201. {
  202. return juce_fileExists (fullPath, false);
  203. }
  204. bool File::existsAsFile() const
  205. {
  206. return juce_fileExists (fullPath, true);
  207. }
  208. bool File::isDirectory() const
  209. {
  210. return juce_isDirectory (fullPath);
  211. }
  212. bool File::hasWriteAccess() const
  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
  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
  242. {
  243. return (! exists())
  244. || juce_deleteFile (fullPath);
  245. }
  246. bool File::deleteRecursively() const
  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
  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
  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
  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
  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
  308. {
  309. return File (getPathUpToLastSlash());
  310. }
  311. //==============================================================================
  312. const String File::getFileName() const
  313. {
  314. return fullPath.substring (fullPath.lastIndexOfChar (separator) + 1);
  315. }
  316. int File::hashCode() const
  317. {
  318. return fullPath.hashCode();
  319. }
  320. int64 File::hashCode64() const
  321. {
  322. return fullPath.hashCode64();
  323. }
  324. const String File::getFileNameWithoutExtension() const
  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
  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)
  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
  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
  411. {
  412. return getParentDirectory().getChildFile (fileName);
  413. }
  414. //==============================================================================
  415. int64 File::getSize() const
  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
  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
  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
  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
  480. {
  481. return juce_setFileTimes (fullPath, 0, 0, t.toMilliseconds());
  482. }
  483. const Time File::getLastModificationTime() const
  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
  490. {
  491. return juce_setFileTimes (fullPath, t.toMilliseconds(), 0, 0);
  492. }
  493. const Time File::getLastAccessTime() const
  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
  500. {
  501. return juce_setFileTimes (fullPath, 0, t.toMilliseconds(), 0);
  502. }
  503. //==============================================================================
  504. bool File::loadFileAsData (MemoryBlock& destBlock) const
  505. {
  506. if (! existsAsFile())
  507. return false;
  508. FileInputStream in (*this);
  509. return getSize() == in.readIntoMemoryBlock (destBlock);
  510. }
  511. const String File::loadFileAsString() const
  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
  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 itemIsDirectory, itemIsHidden;
  542. void* const handle = juce_findFileStart (path, wildCardPattern, filename,
  543. &itemIsDirectory, &itemIsHidden,
  544. 0, 0, 0, 0);
  545. if (handle != 0)
  546. {
  547. do
  548. {
  549. if (fileTypeMatches (whatToLookFor, itemIsDirectory, itemIsHidden)
  550. && ! filename.containsOnly (T(".")))
  551. {
  552. results.add (new File (path + filename, 0));
  553. ++total;
  554. }
  555. } while (juce_findFileNext (handle, filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0));
  556. juce_findFileClose (handle);
  557. }
  558. }
  559. else
  560. {
  561. // trying to search for files inside a non-directory?
  562. //jassertfalse
  563. }
  564. // and recurse down if required.
  565. if (searchRecursively)
  566. {
  567. OwnedArray <File> subDirectories;
  568. findChildFiles (subDirectories, File::findDirectories, false);
  569. for (int i = 0; i < subDirectories.size(); ++i)
  570. {
  571. total += subDirectories.getUnchecked(i)
  572. ->findChildFiles (results,
  573. whatToLookFor,
  574. true,
  575. wildCardPattern);
  576. }
  577. }
  578. return total;
  579. }
  580. int File::getNumberOfChildFiles (const int whatToLookFor,
  581. const String& wildCardPattern) const
  582. {
  583. // you have to specify the type of files you're looking for!
  584. jassert (whatToLookFor > 0 && whatToLookFor <= 3);
  585. int count = 0;
  586. if (isDirectory())
  587. {
  588. String filename;
  589. bool itemIsDirectory, itemIsHidden;
  590. void* const handle = juce_findFileStart (fullPath, wildCardPattern, filename,
  591. &itemIsDirectory, &itemIsHidden,
  592. 0, 0, 0, 0);
  593. if (handle != 0)
  594. {
  595. do
  596. {
  597. if (fileTypeMatches (whatToLookFor, itemIsDirectory, itemIsHidden)
  598. && ! filename.containsOnly (T(".")))
  599. {
  600. ++count;
  601. }
  602. } while (juce_findFileNext (handle, filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0));
  603. juce_findFileClose (handle);
  604. }
  605. }
  606. else
  607. {
  608. // trying to search for files inside a non-directory?
  609. jassertfalse
  610. }
  611. return count;
  612. }
  613. bool File::containsSubDirectories() const
  614. {
  615. bool result = false;
  616. if (isDirectory())
  617. {
  618. String filename;
  619. bool itemIsDirectory, itemIsHidden;
  620. void* const handle = juce_findFileStart (juce_addTrailingSeparator (fullPath),
  621. T("*"), filename,
  622. &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0);
  623. if (handle != 0)
  624. {
  625. do
  626. {
  627. if (itemIsDirectory)
  628. {
  629. result = true;
  630. break;
  631. }
  632. } while (juce_findFileNext (handle, filename, &itemIsDirectory, &itemIsHidden, 0, 0, 0, 0));
  633. juce_findFileClose (handle);
  634. }
  635. }
  636. return result;
  637. }
  638. //==============================================================================
  639. const File File::getNonexistentChildFile (const String& prefix_,
  640. const String& suffix,
  641. bool putNumbersInBrackets) const
  642. {
  643. File f (getChildFile (prefix_ + suffix));
  644. if (f.exists())
  645. {
  646. int num = 2;
  647. String prefix (prefix_);
  648. // remove any bracketed numbers that may already be on the end..
  649. if (prefix.trim().endsWithChar (T(')')))
  650. {
  651. putNumbersInBrackets = true;
  652. const int openBracks = prefix.lastIndexOfChar (T('('));
  653. const int closeBracks = prefix.lastIndexOfChar (T(')'));
  654. if (openBracks > 0
  655. && closeBracks > openBracks
  656. && prefix.substring (openBracks + 1, closeBracks).containsOnly (T("0123456789")))
  657. {
  658. num = prefix.substring (openBracks + 1, closeBracks).getIntValue() + 1;
  659. prefix = prefix.substring (0, openBracks);
  660. }
  661. }
  662. // also use brackets if it ends in a digit.
  663. putNumbersInBrackets = putNumbersInBrackets
  664. || CharacterFunctions::isDigit (prefix.getLastCharacter());
  665. do
  666. {
  667. if (putNumbersInBrackets)
  668. f = getChildFile (prefix + T('(') + String (num++) + T(')') + suffix);
  669. else
  670. f = getChildFile (prefix + String (num++) + suffix);
  671. } while (f.exists());
  672. }
  673. return f;
  674. }
  675. const File File::getNonexistentSibling (const bool putNumbersInBrackets) const
  676. {
  677. if (exists())
  678. {
  679. return getParentDirectory()
  680. .getNonexistentChildFile (getFileNameWithoutExtension(),
  681. getFileExtension(),
  682. putNumbersInBrackets);
  683. }
  684. else
  685. {
  686. return *this;
  687. }
  688. }
  689. //==============================================================================
  690. const String File::getFileExtension() const
  691. {
  692. String ext;
  693. if (! isDirectory())
  694. {
  695. const int indexOfDot = fullPath.lastIndexOfChar (T('.'));
  696. if (indexOfDot > fullPath.lastIndexOfChar (separator))
  697. ext = fullPath.substring (indexOfDot);
  698. }
  699. return ext;
  700. }
  701. bool File::hasFileExtension (const String& possibleSuffix) const
  702. {
  703. if (possibleSuffix.isEmpty())
  704. return fullPath.lastIndexOfChar (T('.')) <= fullPath.lastIndexOfChar (separator);
  705. const int semicolon = possibleSuffix.indexOfChar (0, T(';'));
  706. if (semicolon >= 0)
  707. {
  708. return hasFileExtension (possibleSuffix.substring (0, semicolon).trimEnd())
  709. || hasFileExtension (possibleSuffix.substring (semicolon + 1).trimStart());
  710. }
  711. else
  712. {
  713. if (fullPath.endsWithIgnoreCase (possibleSuffix))
  714. {
  715. if (possibleSuffix.startsWithChar (T('.')))
  716. return true;
  717. const int dotPos = fullPath.length() - possibleSuffix.length() - 1;
  718. if (dotPos >= 0)
  719. return fullPath [dotPos] == T('.');
  720. }
  721. }
  722. return false;
  723. }
  724. const File File::withFileExtension (const String& newExtension) const
  725. {
  726. if (fullPath.isEmpty())
  727. return File::nonexistent;
  728. String filePart (getFileName());
  729. int i = filePart.lastIndexOfChar (T('.'));
  730. if (i < 0)
  731. i = filePart.length();
  732. String newExt (newExtension);
  733. if (newExt.isNotEmpty() && ! newExt.startsWithChar (T('.')))
  734. newExt = T(".") + newExt;
  735. return getSiblingFile (filePart.substring (0, i) + newExt);
  736. }
  737. //==============================================================================
  738. bool File::startAsProcess (const String& parameters) const
  739. {
  740. return exists()
  741. && juce_launchFile (fullPath, parameters);
  742. }
  743. //==============================================================================
  744. FileInputStream* File::createInputStream() const
  745. {
  746. if (existsAsFile())
  747. return new FileInputStream (*this);
  748. else
  749. return 0;
  750. }
  751. FileOutputStream* File::createOutputStream (const int bufferSize) const
  752. {
  753. ScopedPointer <FileOutputStream> out (new FileOutputStream (*this, bufferSize));
  754. if (out->failedToOpen())
  755. return 0;
  756. return out.release();
  757. }
  758. //==============================================================================
  759. bool File::appendData (const void* const dataToAppend,
  760. const int numberOfBytes) const
  761. {
  762. if (numberOfBytes > 0)
  763. {
  764. const ScopedPointer <FileOutputStream> out (createOutputStream());
  765. if (out == 0)
  766. return false;
  767. out->write (dataToAppend, numberOfBytes);
  768. }
  769. return true;
  770. }
  771. bool File::replaceWithData (const void* const dataToWrite,
  772. const int numberOfBytes) const
  773. {
  774. jassert (numberOfBytes >= 0); // a negative number of bytes??
  775. if (numberOfBytes <= 0)
  776. return deleteFile();
  777. const File tempFile (getSiblingFile (T(".") + getFileName()).getNonexistentSibling (false));
  778. if (tempFile.appendData (dataToWrite, numberOfBytes)
  779. && tempFile.moveFileTo (*this))
  780. {
  781. return true;
  782. }
  783. tempFile.deleteFile();
  784. return false;
  785. }
  786. bool File::appendText (const String& text,
  787. const bool asUnicode,
  788. const bool writeUnicodeHeaderBytes) const
  789. {
  790. const ScopedPointer <FileOutputStream> out (createOutputStream());
  791. if (out != 0)
  792. {
  793. out->writeText (text, asUnicode, writeUnicodeHeaderBytes);
  794. return true;
  795. }
  796. return false;
  797. }
  798. bool File::replaceWithText (const String& textToWrite,
  799. const bool asUnicode,
  800. const bool writeUnicodeHeaderBytes) const
  801. {
  802. const File tempFile (getSiblingFile (T(".") + getFileName()).getNonexistentSibling (false));
  803. if (tempFile.appendText (textToWrite, asUnicode, writeUnicodeHeaderBytes)
  804. && tempFile.moveFileTo (*this))
  805. {
  806. return true;
  807. }
  808. tempFile.deleteFile();
  809. return false;
  810. }
  811. //==============================================================================
  812. const String File::createLegalPathName (const String& original)
  813. {
  814. String s (original);
  815. String start;
  816. if (s[1] == T(':'))
  817. {
  818. start = s.substring (0, 2);
  819. s = s.substring (2);
  820. }
  821. return start + s.removeCharacters (T("\"#@,;:<>*^|?"))
  822. .substring (0, 1024);
  823. }
  824. const String File::createLegalFileName (const String& original)
  825. {
  826. String s (original.removeCharacters (T("\"#@,;:<>*^|?\\/")));
  827. const int maxLength = 128; // only the length of the filename, not the whole path
  828. const int len = s.length();
  829. if (len > maxLength)
  830. {
  831. const int lastDot = s.lastIndexOfChar (T('.'));
  832. if (lastDot > jmax (0, len - 12))
  833. {
  834. s = s.substring (0, maxLength - (len - lastDot))
  835. + s.substring (lastDot);
  836. }
  837. else
  838. {
  839. s = s.substring (0, maxLength);
  840. }
  841. }
  842. return s;
  843. }
  844. //==============================================================================
  845. const String File::getRelativePathFrom (const File& dir) const
  846. {
  847. String thisPath (fullPath);
  848. {
  849. int len = thisPath.length();
  850. while (--len >= 0 && thisPath [len] == File::separator)
  851. thisPath [len] = 0;
  852. }
  853. String dirPath (juce_addTrailingSeparator ((dir.existsAsFile()) ? dir.getParentDirectory().getFullPathName()
  854. : dir.fullPath));
  855. const int len = jmin (thisPath.length(), dirPath.length());
  856. int commonBitLength = 0;
  857. for (int i = 0; i < len; ++i)
  858. {
  859. #if NAMES_ARE_CASE_SENSITIVE
  860. if (thisPath[i] != dirPath[i])
  861. #else
  862. if (CharacterFunctions::toLowerCase (thisPath[i])
  863. != CharacterFunctions::toLowerCase (dirPath[i]))
  864. #endif
  865. {
  866. break;
  867. }
  868. ++commonBitLength;
  869. }
  870. while (commonBitLength > 0 && thisPath [commonBitLength - 1] != File::separator)
  871. --commonBitLength;
  872. // if the only common bit is the root, then just return the full path..
  873. if (commonBitLength <= 0
  874. || (commonBitLength == 1 && thisPath [1] == File::separator))
  875. return fullPath;
  876. thisPath = thisPath.substring (commonBitLength);
  877. dirPath = dirPath.substring (commonBitLength);
  878. while (dirPath.isNotEmpty())
  879. {
  880. #if JUCE_WINDOWS
  881. thisPath = T("..\\") + thisPath;
  882. #else
  883. thisPath = T("../") + thisPath;
  884. #endif
  885. const int sep = dirPath.indexOfChar (separator);
  886. if (sep >= 0)
  887. dirPath = dirPath.substring (sep + 1);
  888. else
  889. dirPath = String::empty;
  890. }
  891. return thisPath;
  892. }
  893. //==============================================================================
  894. void File::findFileSystemRoots (OwnedArray<File>& destArray)
  895. {
  896. const StringArray roots (juce_getFileSystemRoots());
  897. for (int i = 0; i < roots.size(); ++i)
  898. destArray.add (new File (roots[i]));
  899. }
  900. const String File::getVolumeLabel() const
  901. {
  902. int serialNum;
  903. return juce_getVolumeLabel (fullPath, serialNum);
  904. }
  905. int File::getVolumeSerialNumber() const
  906. {
  907. int serialNum;
  908. juce_getVolumeLabel (fullPath, serialNum);
  909. return serialNum;
  910. }
  911. //==============================================================================
  912. const File File::createTempFile (const String& fileNameEnding)
  913. {
  914. String tempName (T("temp"));
  915. static int tempNum = 0;
  916. tempName << tempNum++ << fileNameEnding;
  917. const File tempFile (getSpecialLocation (tempDirectory)
  918. .getChildFile (tempName));
  919. if (tempFile.exists())
  920. return createTempFile (fileNameEnding);
  921. else
  922. return tempFile;
  923. }
  924. END_JUCE_NAMESPACE