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.

927 lines
27KB

  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. int File::findChildFiles (Array<File>& results,
  445. const int whatToLookFor,
  446. const bool searchRecursively,
  447. const String& wildCardPattern) const
  448. {
  449. DirectoryIterator di (*this, searchRecursively, wildCardPattern, whatToLookFor);
  450. int total = 0;
  451. while (di.next())
  452. {
  453. results.add (di.getFile());
  454. ++total;
  455. }
  456. return total;
  457. }
  458. int File::getNumberOfChildFiles (const int whatToLookFor, const String& wildCardPattern) const
  459. {
  460. DirectoryIterator di (*this, false, "*", whatToLookFor);
  461. int total = 0;
  462. while (di.next())
  463. ++total;
  464. return total;
  465. }
  466. bool File::containsSubDirectories() const
  467. {
  468. if (isDirectory())
  469. {
  470. DirectoryIterator di (*this, false, "*", findDirectories);
  471. return di.next();
  472. }
  473. return false;
  474. }
  475. //==============================================================================
  476. const File File::getNonexistentChildFile (const String& prefix_,
  477. const String& suffix,
  478. bool putNumbersInBrackets) const
  479. {
  480. File f (getChildFile (prefix_ + suffix));
  481. if (f.exists())
  482. {
  483. int num = 2;
  484. String prefix (prefix_);
  485. // remove any bracketed numbers that may already be on the end..
  486. if (prefix.trim().endsWithChar (')'))
  487. {
  488. putNumbersInBrackets = true;
  489. const int openBracks = prefix.lastIndexOfChar ('(');
  490. const int closeBracks = prefix.lastIndexOfChar (')');
  491. if (openBracks > 0
  492. && closeBracks > openBracks
  493. && prefix.substring (openBracks + 1, closeBracks).containsOnly ("0123456789"))
  494. {
  495. num = prefix.substring (openBracks + 1, closeBracks).getIntValue() + 1;
  496. prefix = prefix.substring (0, openBracks);
  497. }
  498. }
  499. // also use brackets if it ends in a digit.
  500. putNumbersInBrackets = putNumbersInBrackets
  501. || CharacterFunctions::isDigit (prefix.getLastCharacter());
  502. do
  503. {
  504. if (putNumbersInBrackets)
  505. f = getChildFile (prefix + '(' + String (num++) + ')' + suffix);
  506. else
  507. f = getChildFile (prefix + String (num++) + suffix);
  508. } while (f.exists());
  509. }
  510. return f;
  511. }
  512. const File File::getNonexistentSibling (const bool putNumbersInBrackets) const
  513. {
  514. if (exists())
  515. {
  516. return getParentDirectory()
  517. .getNonexistentChildFile (getFileNameWithoutExtension(),
  518. getFileExtension(),
  519. putNumbersInBrackets);
  520. }
  521. else
  522. {
  523. return *this;
  524. }
  525. }
  526. //==============================================================================
  527. const String File::getFileExtension() const
  528. {
  529. String ext;
  530. if (! isDirectory())
  531. {
  532. const int indexOfDot = fullPath.lastIndexOfChar ('.');
  533. if (indexOfDot > fullPath.lastIndexOfChar (separator))
  534. ext = fullPath.substring (indexOfDot);
  535. }
  536. return ext;
  537. }
  538. bool File::hasFileExtension (const String& possibleSuffix) const
  539. {
  540. if (possibleSuffix.isEmpty())
  541. return fullPath.lastIndexOfChar ('.') <= fullPath.lastIndexOfChar (separator);
  542. const int semicolon = possibleSuffix.indexOfChar (0, ';');
  543. if (semicolon >= 0)
  544. {
  545. return hasFileExtension (possibleSuffix.substring (0, semicolon).trimEnd())
  546. || hasFileExtension (possibleSuffix.substring (semicolon + 1).trimStart());
  547. }
  548. else
  549. {
  550. if (fullPath.endsWithIgnoreCase (possibleSuffix))
  551. {
  552. if (possibleSuffix.startsWithChar ('.'))
  553. return true;
  554. const int dotPos = fullPath.length() - possibleSuffix.length() - 1;
  555. if (dotPos >= 0)
  556. return fullPath [dotPos] == '.';
  557. }
  558. }
  559. return false;
  560. }
  561. const File File::withFileExtension (const String& newExtension) const
  562. {
  563. if (fullPath.isEmpty())
  564. return File::nonexistent;
  565. String filePart (getFileName());
  566. int i = filePart.lastIndexOfChar ('.');
  567. if (i >= 0)
  568. filePart = filePart.substring (0, i);
  569. if (newExtension.isNotEmpty() && ! newExtension.startsWithChar ('.'))
  570. filePart << '.';
  571. return getSiblingFile (filePart + newExtension);
  572. }
  573. //==============================================================================
  574. bool File::startAsProcess (const String& parameters) const
  575. {
  576. return exists() && PlatformUtilities::openDocument (fullPath, parameters);
  577. }
  578. //==============================================================================
  579. FileInputStream* File::createInputStream() const
  580. {
  581. if (existsAsFile())
  582. return new FileInputStream (*this);
  583. return 0;
  584. }
  585. FileOutputStream* File::createOutputStream (const int bufferSize) const
  586. {
  587. ScopedPointer <FileOutputStream> out (new FileOutputStream (*this, bufferSize));
  588. if (out->failedToOpen())
  589. return 0;
  590. return out.release();
  591. }
  592. //==============================================================================
  593. bool File::appendData (const void* const dataToAppend,
  594. const int numberOfBytes) const
  595. {
  596. if (numberOfBytes > 0)
  597. {
  598. const ScopedPointer <FileOutputStream> out (createOutputStream());
  599. if (out == 0)
  600. return false;
  601. out->write (dataToAppend, numberOfBytes);
  602. }
  603. return true;
  604. }
  605. bool File::replaceWithData (const void* const dataToWrite,
  606. const int numberOfBytes) const
  607. {
  608. jassert (numberOfBytes >= 0); // a negative number of bytes??
  609. if (numberOfBytes <= 0)
  610. return deleteFile();
  611. TemporaryFile tempFile (*this, TemporaryFile::useHiddenFile);
  612. tempFile.getFile().appendData (dataToWrite, numberOfBytes);
  613. return tempFile.overwriteTargetFileWithTemporary();
  614. }
  615. bool File::appendText (const String& text,
  616. const bool asUnicode,
  617. const bool writeUnicodeHeaderBytes) const
  618. {
  619. const ScopedPointer <FileOutputStream> out (createOutputStream());
  620. if (out != 0)
  621. {
  622. out->writeText (text, asUnicode, writeUnicodeHeaderBytes);
  623. return true;
  624. }
  625. return false;
  626. }
  627. bool File::replaceWithText (const String& textToWrite,
  628. const bool asUnicode,
  629. const bool writeUnicodeHeaderBytes) const
  630. {
  631. TemporaryFile tempFile (*this, TemporaryFile::useHiddenFile);
  632. tempFile.getFile().appendText (textToWrite, asUnicode, writeUnicodeHeaderBytes);
  633. return tempFile.overwriteTargetFileWithTemporary();
  634. }
  635. bool File::hasIdenticalContentTo (const File& other) const
  636. {
  637. if (other == *this)
  638. return true;
  639. if (getSize() == other.getSize() && existsAsFile() && other.existsAsFile())
  640. {
  641. FileInputStream in1 (*this), in2 (other);
  642. const int bufferSize = 4096;
  643. HeapBlock <char> buffer1, buffer2;
  644. buffer1.malloc (bufferSize);
  645. buffer2.malloc (bufferSize);
  646. for (;;)
  647. {
  648. const int num1 = in1.read (buffer1, bufferSize);
  649. const int num2 = in2.read (buffer2, bufferSize);
  650. if (num1 != num2)
  651. break;
  652. if (num1 <= 0)
  653. return true;
  654. if (memcmp (buffer1, buffer2, num1) != 0)
  655. break;
  656. }
  657. }
  658. return false;
  659. }
  660. //==============================================================================
  661. const String File::createLegalPathName (const String& original)
  662. {
  663. String s (original);
  664. String start;
  665. if (s[1] == ':')
  666. {
  667. start = s.substring (0, 2);
  668. s = s.substring (2);
  669. }
  670. return start + s.removeCharacters ("\"#@,;:<>*^|?")
  671. .substring (0, 1024);
  672. }
  673. const String File::createLegalFileName (const String& original)
  674. {
  675. String s (original.removeCharacters ("\"#@,;:<>*^|?\\/"));
  676. const int maxLength = 128; // only the length of the filename, not the whole path
  677. const int len = s.length();
  678. if (len > maxLength)
  679. {
  680. const int lastDot = s.lastIndexOfChar ('.');
  681. if (lastDot > jmax (0, len - 12))
  682. {
  683. s = s.substring (0, maxLength - (len - lastDot))
  684. + s.substring (lastDot);
  685. }
  686. else
  687. {
  688. s = s.substring (0, maxLength);
  689. }
  690. }
  691. return s;
  692. }
  693. //==============================================================================
  694. const String File::getRelativePathFrom (const File& dir) const
  695. {
  696. String thisPath (fullPath);
  697. {
  698. int len = thisPath.length();
  699. while (--len >= 0 && thisPath [len] == File::separator)
  700. thisPath [len] = 0;
  701. }
  702. String dirPath (addTrailingSeparator (dir.existsAsFile() ? dir.getParentDirectory().getFullPathName()
  703. : dir.fullPath));
  704. const int len = jmin (thisPath.length(), dirPath.length());
  705. int commonBitLength = 0;
  706. for (int i = 0; i < len; ++i)
  707. {
  708. #if NAMES_ARE_CASE_SENSITIVE
  709. if (thisPath[i] != dirPath[i])
  710. #else
  711. if (CharacterFunctions::toLowerCase (thisPath[i])
  712. != CharacterFunctions::toLowerCase (dirPath[i]))
  713. #endif
  714. {
  715. break;
  716. }
  717. ++commonBitLength;
  718. }
  719. while (commonBitLength > 0 && thisPath [commonBitLength - 1] != File::separator)
  720. --commonBitLength;
  721. // if the only common bit is the root, then just return the full path..
  722. if (commonBitLength <= 0
  723. || (commonBitLength == 1 && thisPath [1] == File::separator))
  724. return fullPath;
  725. thisPath = thisPath.substring (commonBitLength);
  726. dirPath = dirPath.substring (commonBitLength);
  727. while (dirPath.isNotEmpty())
  728. {
  729. #if JUCE_WINDOWS
  730. thisPath = "..\\" + thisPath;
  731. #else
  732. thisPath = "../" + thisPath;
  733. #endif
  734. const int sep = dirPath.indexOfChar (separator);
  735. if (sep >= 0)
  736. dirPath = dirPath.substring (sep + 1);
  737. else
  738. dirPath = String::empty;
  739. }
  740. return thisPath;
  741. }
  742. //==============================================================================
  743. const File File::createTempFile (const String& fileNameEnding)
  744. {
  745. const File tempFile (getSpecialLocation (tempDirectory)
  746. .getChildFile ("temp_" + String (Random::getSystemRandom().nextInt()))
  747. .withFileExtension (fileNameEnding));
  748. if (tempFile.exists())
  749. return createTempFile (fileNameEnding);
  750. else
  751. return tempFile;
  752. }
  753. END_JUCE_NAMESPACE