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.

984 lines
29KB

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