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.

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