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.

1097 lines
35KB

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