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.

1070 lines
34KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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. #if ! JUCE_WINDOWS
  19. #include <pwd.h>
  20. #endif
  21. BEGIN_JUCE_NAMESPACE
  22. //==============================================================================
  23. File::File (const String& fullPathName)
  24. : fullPath (parseAbsolutePath (fullPathName))
  25. {
  26. }
  27. File File::createFileWithoutCheckingPath (const String& path)
  28. {
  29. File f;
  30. f.fullPath = path;
  31. return f;
  32. }
  33. File::File (const File& other)
  34. : fullPath (other.fullPath)
  35. {
  36. }
  37. File& File::operator= (const String& newPath)
  38. {
  39. fullPath = parseAbsolutePath (newPath);
  40. return *this;
  41. }
  42. File& File::operator= (const File& other)
  43. {
  44. fullPath = other.fullPath;
  45. return *this;
  46. }
  47. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  48. File::File (File&& other) noexcept
  49. : fullPath (static_cast <String&&> (other.fullPath))
  50. {
  51. }
  52. File& File::operator= (File&& other) noexcept
  53. {
  54. fullPath = static_cast <String&&> (other.fullPath);
  55. return *this;
  56. }
  57. #endif
  58. const File File::nonexistent;
  59. //==============================================================================
  60. String File::parseAbsolutePath (const String& p)
  61. {
  62. if (p.isEmpty())
  63. return String::empty;
  64. #if JUCE_WINDOWS
  65. // Windows..
  66. String path (p.replaceCharacter ('/', '\\'));
  67. if (path.startsWithChar (File::separator))
  68. {
  69. if (path[1] != File::separator)
  70. {
  71. /* When you supply a raw string to the File object constructor, it must be an absolute path.
  72. If you're trying to parse a string that may be either a relative path or an absolute path,
  73. you MUST provide a context against which the partial path can be evaluated - you can do
  74. this by simply using File::getChildFile() instead of the File constructor. E.g. saying
  75. "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
  76. path if that's what was supplied, or would evaluate a partial path relative to the CWD.
  77. */
  78. jassertfalse;
  79. path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path;
  80. }
  81. }
  82. else if (! path.containsChar (':'))
  83. {
  84. /* When you supply a raw string to the File object constructor, it must be an absolute path.
  85. If you're trying to parse a string that may be either a relative path or an absolute path,
  86. you MUST provide a context against which the partial path can be evaluated - you can do
  87. this by simply using File::getChildFile() instead of the File constructor. E.g. saying
  88. "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
  89. path if that's what was supplied, or would evaluate a partial path relative to the CWD.
  90. */
  91. jassertfalse;
  92. return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
  93. }
  94. #else
  95. // Mac or Linux..
  96. // Yes, I know it's legal for a unix pathname to contain a backslash, but this assertion is here
  97. // to catch anyone who's trying to run code that was written on Windows with hard-coded path names.
  98. // If that's why you've ended up here, use File::getChildFile() to build your paths instead.
  99. jassert ((! p.containsChar ('\\')) || (p.indexOfChar ('/') >= 0 && p.indexOfChar ('/') < p.indexOfChar ('\\')));
  100. String path (p);
  101. if (path.startsWithChar ('~'))
  102. {
  103. if (path[1] == File::separator || path[1] == 0)
  104. {
  105. // expand a name of the form "~/abc"
  106. path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName()
  107. + path.substring (1);
  108. }
  109. else
  110. {
  111. // expand a name of type "~dave/abc"
  112. const String userName (path.substring (1).upToFirstOccurrenceOf ("/", false, false));
  113. struct passwd* const pw = getpwnam (userName.toUTF8());
  114. if (pw != nullptr)
  115. path = addTrailingSeparator (pw->pw_dir) + path.fromFirstOccurrenceOf ("/", false, false);
  116. }
  117. }
  118. else if (! path.startsWithChar (File::separator))
  119. {
  120. /* When you supply a raw string to the File object constructor, it must be an absolute path.
  121. If you're trying to parse a string that may be either a relative path or an absolute path,
  122. you MUST provide a context against which the partial path can be evaluated - you can do
  123. this by simply using File::getChildFile() instead of the File constructor. E.g. saying
  124. "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
  125. path if that's what was supplied, or would evaluate a partial path relative to the CWD.
  126. */
  127. jassert (path.startsWith ("./") || path.startsWith ("../")); // (assume that a path "./xyz" is deliberately intended to be relative to the CWD)
  128. return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
  129. }
  130. #endif
  131. while (path.endsWithChar (separator) && path != separatorString) // careful not to turn a single "/" into an empty string.
  132. path = path.dropLastCharacters (1);
  133. return path;
  134. }
  135. String File::addTrailingSeparator (const String& path)
  136. {
  137. return path.endsWithChar (File::separator) ? path
  138. : path + File::separator;
  139. }
  140. //==============================================================================
  141. #if JUCE_LINUX
  142. #define NAMES_ARE_CASE_SENSITIVE 1
  143. #endif
  144. bool File::areFileNamesCaseSensitive()
  145. {
  146. #if NAMES_ARE_CASE_SENSITIVE
  147. return true;
  148. #else
  149. return false;
  150. #endif
  151. }
  152. bool File::operator== (const File& other) const
  153. {
  154. #if NAMES_ARE_CASE_SENSITIVE
  155. return fullPath == other.fullPath;
  156. #else
  157. return fullPath.equalsIgnoreCase (other.fullPath);
  158. #endif
  159. }
  160. bool File::operator!= (const File& other) const
  161. {
  162. return ! operator== (other);
  163. }
  164. bool File::operator< (const File& other) const
  165. {
  166. #if NAMES_ARE_CASE_SENSITIVE
  167. return fullPath < other.fullPath;
  168. #else
  169. return fullPath.compareIgnoreCase (other.fullPath) < 0;
  170. #endif
  171. }
  172. bool File::operator> (const File& other) const
  173. {
  174. #if NAMES_ARE_CASE_SENSITIVE
  175. return fullPath > other.fullPath;
  176. #else
  177. return fullPath.compareIgnoreCase (other.fullPath) > 0;
  178. #endif
  179. }
  180. //==============================================================================
  181. bool File::setReadOnly (const bool shouldBeReadOnly,
  182. const bool applyRecursively) const
  183. {
  184. bool worked = true;
  185. if (applyRecursively && isDirectory())
  186. {
  187. Array <File> subFiles;
  188. findChildFiles (subFiles, File::findFilesAndDirectories, false);
  189. for (int i = subFiles.size(); --i >= 0;)
  190. worked = subFiles.getReference(i).setReadOnly (shouldBeReadOnly, true) && worked;
  191. }
  192. return setFileReadOnlyInternal (shouldBeReadOnly) && worked;
  193. }
  194. bool File::deleteRecursively() const
  195. {
  196. bool worked = true;
  197. if (isDirectory())
  198. {
  199. Array<File> subFiles;
  200. findChildFiles (subFiles, File::findFilesAndDirectories, false);
  201. for (int i = subFiles.size(); --i >= 0;)
  202. worked = subFiles.getReference(i).deleteRecursively() && worked;
  203. }
  204. return deleteFile() && worked;
  205. }
  206. bool File::moveFileTo (const File& newFile) const
  207. {
  208. if (newFile.fullPath == fullPath)
  209. return true;
  210. #if ! NAMES_ARE_CASE_SENSITIVE
  211. if (*this != newFile)
  212. #endif
  213. if (! newFile.deleteFile())
  214. return false;
  215. return moveInternal (newFile);
  216. }
  217. bool File::copyFileTo (const File& newFile) const
  218. {
  219. return (*this == newFile)
  220. || (exists() && newFile.deleteFile() && copyInternal (newFile));
  221. }
  222. bool File::copyDirectoryTo (const File& newDirectory) const
  223. {
  224. if (isDirectory() && newDirectory.createDirectory())
  225. {
  226. Array<File> subFiles;
  227. findChildFiles (subFiles, File::findFiles, false);
  228. int i;
  229. for (i = 0; i < subFiles.size(); ++i)
  230. if (! subFiles.getReference(i).copyFileTo (newDirectory.getChildFile (subFiles.getReference(i).getFileName())))
  231. return false;
  232. subFiles.clear();
  233. findChildFiles (subFiles, File::findDirectories, false);
  234. for (i = 0; i < subFiles.size(); ++i)
  235. if (! subFiles.getReference(i).copyDirectoryTo (newDirectory.getChildFile (subFiles.getReference(i).getFileName())))
  236. return false;
  237. return true;
  238. }
  239. return false;
  240. }
  241. //==============================================================================
  242. String File::getPathUpToLastSlash() const
  243. {
  244. const int lastSlash = fullPath.lastIndexOfChar (separator);
  245. if (lastSlash > 0)
  246. return fullPath.substring (0, lastSlash);
  247. else if (lastSlash == 0)
  248. return separatorString;
  249. else
  250. return fullPath;
  251. }
  252. File File::getParentDirectory() const
  253. {
  254. File f;
  255. f.fullPath = getPathUpToLastSlash();
  256. return f;
  257. }
  258. //==============================================================================
  259. String File::getFileName() const
  260. {
  261. return fullPath.substring (fullPath.lastIndexOfChar (separator) + 1);
  262. }
  263. int File::hashCode() const
  264. {
  265. return fullPath.hashCode();
  266. }
  267. int64 File::hashCode64() const
  268. {
  269. return fullPath.hashCode64();
  270. }
  271. String File::getFileNameWithoutExtension() const
  272. {
  273. const int lastSlash = fullPath.lastIndexOfChar (separator) + 1;
  274. const int lastDot = fullPath.lastIndexOfChar ('.');
  275. if (lastDot > lastSlash)
  276. return fullPath.substring (lastSlash, lastDot);
  277. else
  278. return fullPath.substring (lastSlash);
  279. }
  280. bool File::isAChildOf (const File& potentialParent) const
  281. {
  282. if (potentialParent == File::nonexistent)
  283. return false;
  284. const String ourPath (getPathUpToLastSlash());
  285. #if NAMES_ARE_CASE_SENSITIVE
  286. if (potentialParent.fullPath == ourPath)
  287. #else
  288. if (potentialParent.fullPath.equalsIgnoreCase (ourPath))
  289. #endif
  290. {
  291. return true;
  292. }
  293. else if (potentialParent.fullPath.length() >= ourPath.length())
  294. {
  295. return false;
  296. }
  297. else
  298. {
  299. return getParentDirectory().isAChildOf (potentialParent);
  300. }
  301. }
  302. //==============================================================================
  303. bool File::isAbsolutePath (const String& path)
  304. {
  305. return path.startsWithChar (separator)
  306. #if JUCE_WINDOWS
  307. || (path.isNotEmpty() && path[1] == ':');
  308. #else
  309. || path.startsWithChar ('~');
  310. #endif
  311. }
  312. File File::getChildFile (String relativePath) const
  313. {
  314. if (isAbsolutePath (relativePath))
  315. return File (relativePath);
  316. String path (fullPath);
  317. // It's relative, so remove any ../ or ./ bits at the start..
  318. if (relativePath[0] == '.')
  319. {
  320. #if JUCE_WINDOWS
  321. relativePath = relativePath.replaceCharacter ('/', '\\').trimStart();
  322. #else
  323. relativePath = relativePath.trimStart();
  324. #endif
  325. while (relativePath[0] == '.')
  326. {
  327. if (relativePath[1] == '.')
  328. {
  329. if (relativePath [2] == 0 || relativePath[2] == separator)
  330. {
  331. const int lastSlash = path.lastIndexOfChar (separator);
  332. if (lastSlash >= 0)
  333. path = path.substring (0, lastSlash);
  334. relativePath = relativePath.substring (3);
  335. }
  336. else
  337. {
  338. break;
  339. }
  340. }
  341. else if (relativePath[1] == separator)
  342. {
  343. relativePath = relativePath.substring (2);
  344. }
  345. else
  346. {
  347. break;
  348. }
  349. }
  350. }
  351. return File (addTrailingSeparator (path) + relativePath);
  352. }
  353. File File::getSiblingFile (const String& fileName) const
  354. {
  355. return getParentDirectory().getChildFile (fileName);
  356. }
  357. //==============================================================================
  358. String File::descriptionOfSizeInBytes (const int64 bytes)
  359. {
  360. if (bytes == 1) return "1 byte";
  361. else if (bytes < 1024) return String (bytes) + " bytes";
  362. else if (bytes < 1024 * 1024) return String (bytes / 1024.0, 1) + " KB";
  363. else if (bytes < 1024 * 1024 * 1024) return String (bytes / (1024.0 * 1024.0), 1) + " MB";
  364. else return String (bytes / (1024.0 * 1024.0 * 1024.0), 1) + " GB";
  365. }
  366. //==============================================================================
  367. Result File::create() const
  368. {
  369. if (exists())
  370. return Result::ok();
  371. const File parentDir (getParentDirectory());
  372. if (parentDir == *this)
  373. return Result::fail ("Cannot create parent directory");
  374. Result r (parentDir.createDirectory());
  375. if (r.wasOk())
  376. {
  377. FileOutputStream fo (*this, 8);
  378. r = fo.getStatus();
  379. }
  380. return r;
  381. }
  382. Result File::createDirectory() const
  383. {
  384. if (isDirectory())
  385. return Result::ok();
  386. const File parentDir (getParentDirectory());
  387. if (parentDir == *this)
  388. return Result::fail ("Cannot create parent directory");
  389. Result r (parentDir.createDirectory());
  390. if (r.wasOk())
  391. r = createDirectoryInternal (fullPath.trimCharactersAtEnd (separatorString));
  392. return r;
  393. }
  394. //==============================================================================
  395. Time File::getLastModificationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (m); }
  396. Time File::getLastAccessTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (a); }
  397. Time File::getCreationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (c); }
  398. bool File::setLastModificationTime (const Time& t) const { return setFileTimesInternal (t.toMilliseconds(), 0, 0); }
  399. bool File::setLastAccessTime (const Time& t) const { return setFileTimesInternal (0, t.toMilliseconds(), 0); }
  400. bool File::setCreationTime (const Time& t) const { return setFileTimesInternal (0, 0, t.toMilliseconds()); }
  401. //==============================================================================
  402. bool File::loadFileAsData (MemoryBlock& destBlock) const
  403. {
  404. if (! existsAsFile())
  405. return false;
  406. FileInputStream in (*this);
  407. return getSize() == in.readIntoMemoryBlock (destBlock);
  408. }
  409. String File::loadFileAsString() const
  410. {
  411. if (! existsAsFile())
  412. return String::empty;
  413. FileInputStream in (*this);
  414. return in.readEntireStreamAsString();
  415. }
  416. void File::readLines (StringArray& destLines) const
  417. {
  418. destLines.addLines (loadFileAsString());
  419. }
  420. //==============================================================================
  421. int File::findChildFiles (Array<File>& results,
  422. const int whatToLookFor,
  423. const bool searchRecursively,
  424. const String& wildCardPattern) const
  425. {
  426. DirectoryIterator di (*this, searchRecursively, wildCardPattern, whatToLookFor);
  427. int total = 0;
  428. while (di.next())
  429. {
  430. results.add (di.getFile());
  431. ++total;
  432. }
  433. return total;
  434. }
  435. int File::getNumberOfChildFiles (const int whatToLookFor, const String& wildCardPattern) const
  436. {
  437. DirectoryIterator di (*this, false, wildCardPattern, whatToLookFor);
  438. int total = 0;
  439. while (di.next())
  440. ++total;
  441. return total;
  442. }
  443. bool File::containsSubDirectories() const
  444. {
  445. if (isDirectory())
  446. {
  447. DirectoryIterator di (*this, false, "*", findDirectories);
  448. return di.next();
  449. }
  450. return false;
  451. }
  452. //==============================================================================
  453. File File::getNonexistentChildFile (const String& prefix_,
  454. const String& suffix,
  455. bool putNumbersInBrackets) const
  456. {
  457. File f (getChildFile (prefix_ + suffix));
  458. if (f.exists())
  459. {
  460. int num = 2;
  461. String prefix (prefix_);
  462. // remove any bracketed numbers that may already be on the end..
  463. if (prefix.trim().endsWithChar (')'))
  464. {
  465. putNumbersInBrackets = true;
  466. const int openBracks = prefix.lastIndexOfChar ('(');
  467. const int closeBracks = prefix.lastIndexOfChar (')');
  468. if (openBracks > 0
  469. && closeBracks > openBracks
  470. && prefix.substring (openBracks + 1, closeBracks).containsOnly ("0123456789"))
  471. {
  472. num = prefix.substring (openBracks + 1, closeBracks).getIntValue() + 1;
  473. prefix = prefix.substring (0, openBracks);
  474. }
  475. }
  476. // also use brackets if it ends in a digit.
  477. putNumbersInBrackets = putNumbersInBrackets
  478. || CharacterFunctions::isDigit (prefix.getLastCharacter());
  479. do
  480. {
  481. String newName (prefix);
  482. if (putNumbersInBrackets)
  483. newName << '(' << num++ << ')';
  484. else
  485. newName << num++;
  486. f = getChildFile (newName + suffix);
  487. } while (f.exists());
  488. }
  489. return f;
  490. }
  491. File File::getNonexistentSibling (const bool putNumbersInBrackets) const
  492. {
  493. if (exists())
  494. return getParentDirectory()
  495. .getNonexistentChildFile (getFileNameWithoutExtension(),
  496. getFileExtension(),
  497. putNumbersInBrackets);
  498. return *this;
  499. }
  500. //==============================================================================
  501. String File::getFileExtension() const
  502. {
  503. const int indexOfDot = fullPath.lastIndexOfChar ('.');
  504. if (indexOfDot > fullPath.lastIndexOfChar (separator))
  505. return fullPath.substring (indexOfDot);
  506. return String::empty;
  507. }
  508. bool File::hasFileExtension (const String& possibleSuffix) const
  509. {
  510. if (possibleSuffix.isEmpty())
  511. return fullPath.lastIndexOfChar ('.') <= fullPath.lastIndexOfChar (separator);
  512. const int semicolon = possibleSuffix.indexOfChar (0, ';');
  513. if (semicolon >= 0)
  514. {
  515. return hasFileExtension (possibleSuffix.substring (0, semicolon).trimEnd())
  516. || hasFileExtension (possibleSuffix.substring (semicolon + 1).trimStart());
  517. }
  518. else
  519. {
  520. if (fullPath.endsWithIgnoreCase (possibleSuffix))
  521. {
  522. if (possibleSuffix.startsWithChar ('.'))
  523. return true;
  524. const int dotPos = fullPath.length() - possibleSuffix.length() - 1;
  525. if (dotPos >= 0)
  526. return fullPath [dotPos] == '.';
  527. }
  528. }
  529. return false;
  530. }
  531. File File::withFileExtension (const String& newExtension) const
  532. {
  533. if (fullPath.isEmpty())
  534. return File::nonexistent;
  535. String filePart (getFileName());
  536. int i = filePart.lastIndexOfChar ('.');
  537. if (i >= 0)
  538. filePart = filePart.substring (0, i);
  539. if (newExtension.isNotEmpty() && ! newExtension.startsWithChar ('.'))
  540. filePart << '.';
  541. return getSiblingFile (filePart + newExtension);
  542. }
  543. //==============================================================================
  544. bool File::startAsProcess (const String& parameters) const
  545. {
  546. return exists() && Process::openDocument (fullPath, parameters);
  547. }
  548. //==============================================================================
  549. FileInputStream* File::createInputStream() const
  550. {
  551. if (existsAsFile())
  552. return new FileInputStream (*this);
  553. return nullptr;
  554. }
  555. FileOutputStream* File::createOutputStream (const int bufferSize) const
  556. {
  557. ScopedPointer<FileOutputStream> out (new FileOutputStream (*this, bufferSize));
  558. return out->failedToOpen() ? nullptr
  559. : out.release();
  560. }
  561. //==============================================================================
  562. bool File::appendData (const void* const dataToAppend,
  563. const int numberOfBytes) const
  564. {
  565. if (numberOfBytes <= 0)
  566. return true;
  567. FileOutputStream out (*this, 8192);
  568. return out.openedOk() && out.write (dataToAppend, numberOfBytes);
  569. }
  570. bool File::replaceWithData (const void* const dataToWrite,
  571. const int numberOfBytes) const
  572. {
  573. jassert (numberOfBytes >= 0); // a negative number of bytes??
  574. if (numberOfBytes <= 0)
  575. return deleteFile();
  576. TemporaryFile tempFile (*this, TemporaryFile::useHiddenFile);
  577. tempFile.getFile().appendData (dataToWrite, numberOfBytes);
  578. return tempFile.overwriteTargetFileWithTemporary();
  579. }
  580. bool File::appendText (const String& text,
  581. const bool asUnicode,
  582. const bool writeUnicodeHeaderBytes) const
  583. {
  584. FileOutputStream out (*this);
  585. if (out.failedToOpen())
  586. return false;
  587. out.writeText (text, asUnicode, writeUnicodeHeaderBytes);
  588. return true;
  589. }
  590. bool File::replaceWithText (const String& textToWrite,
  591. const bool asUnicode,
  592. const bool writeUnicodeHeaderBytes) const
  593. {
  594. TemporaryFile tempFile (*this, TemporaryFile::useHiddenFile);
  595. tempFile.getFile().appendText (textToWrite, asUnicode, writeUnicodeHeaderBytes);
  596. return tempFile.overwriteTargetFileWithTemporary();
  597. }
  598. bool File::hasIdenticalContentTo (const File& other) const
  599. {
  600. if (other == *this)
  601. return true;
  602. if (getSize() == other.getSize() && existsAsFile() && other.existsAsFile())
  603. {
  604. FileInputStream in1 (*this), in2 (other);
  605. const int bufferSize = 4096;
  606. HeapBlock <char> buffer1 (bufferSize), buffer2 (bufferSize);
  607. for (;;)
  608. {
  609. const int num1 = in1.read (buffer1, bufferSize);
  610. const int num2 = in2.read (buffer2, bufferSize);
  611. if (num1 != num2)
  612. break;
  613. if (num1 <= 0)
  614. return true;
  615. if (memcmp (buffer1, buffer2, (size_t) num1) != 0)
  616. break;
  617. }
  618. }
  619. return false;
  620. }
  621. //==============================================================================
  622. String File::createLegalPathName (const String& original)
  623. {
  624. String s (original);
  625. String start;
  626. if (s[1] == ':')
  627. {
  628. start = s.substring (0, 2);
  629. s = s.substring (2);
  630. }
  631. return start + s.removeCharacters ("\"#@,;:<>*^|?")
  632. .substring (0, 1024);
  633. }
  634. String File::createLegalFileName (const String& original)
  635. {
  636. String s (original.removeCharacters ("\"#@,;:<>*^|?\\/"));
  637. const int maxLength = 128; // only the length of the filename, not the whole path
  638. const int len = s.length();
  639. if (len > maxLength)
  640. {
  641. const int lastDot = s.lastIndexOfChar ('.');
  642. if (lastDot > jmax (0, len - 12))
  643. {
  644. s = s.substring (0, maxLength - (len - lastDot))
  645. + s.substring (lastDot);
  646. }
  647. else
  648. {
  649. s = s.substring (0, maxLength);
  650. }
  651. }
  652. return s;
  653. }
  654. //==============================================================================
  655. String File::getRelativePathFrom (const File& dir) const
  656. {
  657. String thisPath (fullPath);
  658. while (thisPath.endsWithChar (separator))
  659. thisPath = thisPath.dropLastCharacters (1);
  660. String dirPath (addTrailingSeparator (dir.existsAsFile() ? dir.getParentDirectory().getFullPathName()
  661. : dir.fullPath));
  662. const int len = jmin (thisPath.length(), dirPath.length());
  663. int commonBitLength = 0;
  664. {
  665. String::CharPointerType thisPathIter (thisPath.getCharPointer());
  666. String::CharPointerType dirPathIter (dirPath.getCharPointer());
  667. for (int i = 0; i < len; ++i)
  668. {
  669. const juce_wchar c1 = thisPathIter.getAndAdvance();
  670. const juce_wchar c2 = dirPathIter.getAndAdvance();
  671. #if NAMES_ARE_CASE_SENSITIVE
  672. if (c1 != c2)
  673. #else
  674. if (c1 != c2 && CharacterFunctions::toLowerCase (c1) != CharacterFunctions::toLowerCase (c2))
  675. #endif
  676. break;
  677. ++commonBitLength;
  678. }
  679. }
  680. while (commonBitLength > 0 && thisPath [commonBitLength - 1] != File::separator)
  681. --commonBitLength;
  682. // if the only common bit is the root, then just return the full path..
  683. if (commonBitLength <= 0
  684. || (commonBitLength == 1 && thisPath [1] == File::separator))
  685. return fullPath;
  686. thisPath = thisPath.substring (commonBitLength);
  687. dirPath = dirPath.substring (commonBitLength);
  688. while (dirPath.isNotEmpty())
  689. {
  690. #if JUCE_WINDOWS
  691. thisPath = "..\\" + thisPath;
  692. #else
  693. thisPath = "../" + thisPath;
  694. #endif
  695. const int sep = dirPath.indexOfChar (separator);
  696. if (sep >= 0)
  697. dirPath = dirPath.substring (sep + 1);
  698. else
  699. dirPath = String::empty;
  700. }
  701. return thisPath;
  702. }
  703. //==============================================================================
  704. File File::createTempFile (const String& fileNameEnding)
  705. {
  706. const File tempFile (getSpecialLocation (tempDirectory)
  707. .getChildFile ("temp_" + String::toHexString (Random::getSystemRandom().nextInt()))
  708. .withFileExtension (fileNameEnding));
  709. if (tempFile.exists())
  710. return createTempFile (fileNameEnding);
  711. return tempFile;
  712. }
  713. //==============================================================================
  714. #if JUCE_UNIT_TESTS
  715. class FileTests : public UnitTest
  716. {
  717. public:
  718. FileTests() : UnitTest ("Files") {}
  719. void runTest()
  720. {
  721. beginTest ("Reading");
  722. const File home (File::getSpecialLocation (File::userHomeDirectory));
  723. const File temp (File::getSpecialLocation (File::tempDirectory));
  724. expect (! File::nonexistent.exists());
  725. expect (home.isDirectory());
  726. expect (home.exists());
  727. expect (! home.existsAsFile());
  728. expect (File::getSpecialLocation (File::userDocumentsDirectory).isDirectory());
  729. expect (File::getSpecialLocation (File::userApplicationDataDirectory).isDirectory());
  730. expect (File::getSpecialLocation (File::currentExecutableFile).exists());
  731. expect (File::getSpecialLocation (File::currentApplicationFile).exists());
  732. expect (File::getSpecialLocation (File::invokedExecutableFile).exists());
  733. expect (home.getVolumeTotalSize() > 1024 * 1024);
  734. expect (home.getBytesFreeOnVolume() > 0);
  735. expect (! home.isHidden());
  736. expect (home.isOnHardDisk());
  737. expect (! home.isOnCDRomDrive());
  738. expect (File::getCurrentWorkingDirectory().exists());
  739. expect (home.setAsCurrentWorkingDirectory());
  740. expect (File::getCurrentWorkingDirectory() == home);
  741. {
  742. Array<File> roots;
  743. File::findFileSystemRoots (roots);
  744. expect (roots.size() > 0);
  745. int numRootsExisting = 0;
  746. for (int i = 0; i < roots.size(); ++i)
  747. if (roots[i].exists())
  748. ++numRootsExisting;
  749. // (on windows, some of the drives may not contain media, so as long as at least one is ok..)
  750. expect (numRootsExisting > 0);
  751. }
  752. beginTest ("Writing");
  753. File demoFolder (temp.getChildFile ("Juce UnitTests Temp Folder.folder"));
  754. expect (demoFolder.deleteRecursively());
  755. expect (demoFolder.createDirectory());
  756. expect (demoFolder.isDirectory());
  757. expect (demoFolder.getParentDirectory() == temp);
  758. expect (temp.isDirectory());
  759. {
  760. Array<File> files;
  761. temp.findChildFiles (files, File::findFilesAndDirectories, false, "*");
  762. expect (files.contains (demoFolder));
  763. }
  764. {
  765. Array<File> files;
  766. temp.findChildFiles (files, File::findDirectories, true, "*.folder");
  767. expect (files.contains (demoFolder));
  768. }
  769. File tempFile (demoFolder.getNonexistentChildFile ("test", ".txt", false));
  770. expect (tempFile.getFileExtension() == ".txt");
  771. expect (tempFile.hasFileExtension (".txt"));
  772. expect (tempFile.hasFileExtension ("txt"));
  773. expect (tempFile.withFileExtension ("xyz").hasFileExtension (".xyz"));
  774. expect (tempFile.withFileExtension ("xyz").hasFileExtension ("abc;xyz;foo"));
  775. expect (tempFile.withFileExtension ("xyz").hasFileExtension ("xyz;foo"));
  776. expect (! tempFile.withFileExtension ("h").hasFileExtension ("bar;foo;xx"));
  777. expect (tempFile.getSiblingFile ("foo").isAChildOf (temp));
  778. expect (tempFile.hasWriteAccess());
  779. {
  780. FileOutputStream fo (tempFile);
  781. fo.write ("0123456789", 10);
  782. }
  783. expect (tempFile.exists());
  784. expect (tempFile.getSize() == 10);
  785. expect (std::abs ((int) (tempFile.getLastModificationTime().toMilliseconds() - Time::getCurrentTime().toMilliseconds())) < 3000);
  786. expectEquals (tempFile.loadFileAsString(), String ("0123456789"));
  787. expect (! demoFolder.containsSubDirectories());
  788. expectEquals (tempFile.getRelativePathFrom (demoFolder.getParentDirectory()), demoFolder.getFileName() + File::separatorString + tempFile.getFileName());
  789. expectEquals (demoFolder.getParentDirectory().getRelativePathFrom (tempFile), ".." + File::separatorString + ".." + File::separatorString + demoFolder.getParentDirectory().getFileName());
  790. expect (demoFolder.getNumberOfChildFiles (File::findFiles) == 1);
  791. expect (demoFolder.getNumberOfChildFiles (File::findFilesAndDirectories) == 1);
  792. expect (demoFolder.getNumberOfChildFiles (File::findDirectories) == 0);
  793. demoFolder.getNonexistentChildFile ("tempFolder", "", false).createDirectory();
  794. expect (demoFolder.getNumberOfChildFiles (File::findDirectories) == 1);
  795. expect (demoFolder.getNumberOfChildFiles (File::findFilesAndDirectories) == 2);
  796. expect (demoFolder.containsSubDirectories());
  797. expect (tempFile.hasWriteAccess());
  798. tempFile.setReadOnly (true);
  799. expect (! tempFile.hasWriteAccess());
  800. tempFile.setReadOnly (false);
  801. expect (tempFile.hasWriteAccess());
  802. Time t (Time::getCurrentTime());
  803. tempFile.setLastModificationTime (t);
  804. Time t2 = tempFile.getLastModificationTime();
  805. expect (std::abs ((int) (t2.toMilliseconds() - t.toMilliseconds())) <= 1000);
  806. {
  807. MemoryBlock mb;
  808. tempFile.loadFileAsData (mb);
  809. expect (mb.getSize() == 10);
  810. expect (mb[0] == '0');
  811. }
  812. beginTest ("Memory-mapped files");
  813. {
  814. MemoryMappedFile mmf (tempFile, MemoryMappedFile::readOnly);
  815. expect (mmf.getSize() == 10);
  816. expect (mmf.getData() != nullptr);
  817. expect (memcmp (mmf.getData(), "0123456789", 10) == 0);
  818. }
  819. {
  820. const File tempFile2 (tempFile.getNonexistentSibling (false));
  821. expect (tempFile2.create());
  822. expect (tempFile2.appendData ("xxxxxxxxxx", 10));
  823. {
  824. MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite);
  825. expect (mmf.getSize() == 10);
  826. expect (mmf.getData() != nullptr);
  827. memcpy (mmf.getData(), "abcdefghij", 10);
  828. }
  829. {
  830. MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite);
  831. expect (mmf.getSize() == 10);
  832. expect (mmf.getData() != nullptr);
  833. expect (memcmp (mmf.getData(), "abcdefghij", 10) == 0);
  834. }
  835. expect (tempFile2.deleteFile());
  836. }
  837. beginTest ("More writing");
  838. expect (tempFile.appendData ("abcdefghij", 10));
  839. expect (tempFile.getSize() == 20);
  840. expect (tempFile.replaceWithData ("abcdefghij", 10));
  841. expect (tempFile.getSize() == 10);
  842. File tempFile2 (tempFile.getNonexistentSibling (false));
  843. expect (tempFile.copyFileTo (tempFile2));
  844. expect (tempFile2.exists());
  845. expect (tempFile2.hasIdenticalContentTo (tempFile));
  846. expect (tempFile.deleteFile());
  847. expect (! tempFile.exists());
  848. expect (tempFile2.moveFileTo (tempFile));
  849. expect (tempFile.exists());
  850. expect (! tempFile2.exists());
  851. expect (demoFolder.deleteRecursively());
  852. expect (! demoFolder.exists());
  853. }
  854. };
  855. static FileTests fileUnitTests;
  856. #endif
  857. END_JUCE_NAMESPACE