OpenShot Audio Library | OpenShotAudio  0.3.2
juce_File.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 File::File (const String& fullPathName)
27  : fullPath (parseAbsolutePath (fullPathName))
28 {
29 }
30 
32 {
33  File f;
34  f.fullPath = path;
35  return f;
36 }
37 
38 File::File (const File& other)
39  : fullPath (other.fullPath)
40 {
41 }
42 
43 File& File::operator= (const String& newPath)
44 {
45  fullPath = parseAbsolutePath (newPath);
46  return *this;
47 }
48 
49 File& File::operator= (const File& other)
50 {
51  fullPath = other.fullPath;
52  return *this;
53 }
54 
55 File::File (File&& other) noexcept
56  : fullPath (std::move (other.fullPath))
57 {
58 }
59 
60 File& File::operator= (File&& other) noexcept
61 {
62  fullPath = std::move (other.fullPath);
63  return *this;
64 }
65 
66 JUCE_DECLARE_DEPRECATED_STATIC (const File File::nonexistent{};)
67 
68 //==============================================================================
69 static String removeEllipsis (const String& path)
70 {
71  // This will quickly find both /../ and /./ at the expense of a minor
72  // false-positive performance hit when path elements end in a dot.
73  #if JUCE_WINDOWS
74  if (path.contains (".\\"))
75  #else
76  if (path.contains ("./"))
77  #endif
78  {
79  StringArray toks;
80  toks.addTokens (path, File::getSeparatorString(), {});
81  bool anythingChanged = false;
82 
83  for (int i = 1; i < toks.size(); ++i)
84  {
85  auto& t = toks[i];
86 
87  if (t == ".." && toks[i - 1] != "..")
88  {
89  anythingChanged = true;
90  toks.removeRange (i - 1, 2);
91  i = jmax (0, i - 2);
92  }
93  else if (t == ".")
94  {
95  anythingChanged = true;
96  toks.remove (i--);
97  }
98  }
99 
100  if (anythingChanged)
101  return toks.joinIntoString (File::getSeparatorString());
102  }
103 
104  return path;
105 }
106 
107 static String normaliseSeparators (const String& path)
108 {
109  auto normalisedPath = path;
110 
111  String separator (File::getSeparatorString());
112  String doubleSeparator (separator + separator);
113 
114  auto uncPath = normalisedPath.startsWith (doubleSeparator)
115  && ! normalisedPath.fromFirstOccurrenceOf (doubleSeparator, false, false).startsWith (separator);
116 
117  if (uncPath)
118  normalisedPath = normalisedPath.fromFirstOccurrenceOf (doubleSeparator, false, false);
119 
120  while (normalisedPath.contains (doubleSeparator))
121  normalisedPath = normalisedPath.replace (doubleSeparator, separator);
122 
123  return uncPath ? doubleSeparator + normalisedPath
124  : normalisedPath;
125 }
126 
127 bool File::isRoot() const
128 {
129  return fullPath.isNotEmpty() && *this == getParentDirectory();
130 }
131 
132 String File::parseAbsolutePath (const String& p)
133 {
134  if (p.isEmpty())
135  return {};
136 
137  #if JUCE_WINDOWS
138  // Windows..
139  auto path = normaliseSeparators (removeEllipsis (p.replaceCharacter ('/', '\\')));
140 
141  if (path.startsWithChar (getSeparatorChar()))
142  {
143  if (path[1] != getSeparatorChar())
144  {
145  /* When you supply a raw string to the File object constructor, it must be an absolute path.
146  If you're trying to parse a string that may be either a relative path or an absolute path,
147  you MUST provide a context against which the partial path can be evaluated - you can do
148  this by simply using File::getChildFile() instead of the File constructor. E.g. saying
149  "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
150  path if that's what was supplied, or would evaluate a partial path relative to the CWD.
151  */
152  jassertfalse;
153 
155  }
156  }
157  else if (! path.containsChar (':'))
158  {
159  /* When you supply a raw string to the File object constructor, it must be an absolute path.
160  If you're trying to parse a string that may be either a relative path or an absolute path,
161  you MUST provide a context against which the partial path can be evaluated - you can do
162  this by simply using File::getChildFile() instead of the File constructor. E.g. saying
163  "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
164  path if that's what was supplied, or would evaluate a partial path relative to the CWD.
165  */
166  jassertfalse;
167 
169  }
170  #else
171  // Mac or Linux..
172 
173  // Yes, I know it's legal for a unix pathname to contain a backslash, but this assertion is here
174  // to catch anyone who's trying to run code that was written on Windows with hard-coded path names.
175  // If that's why you've ended up here, use File::getChildFile() to build your paths instead.
176  jassert ((! p.containsChar ('\\')) || (p.indexOfChar ('/') >= 0 && p.indexOfChar ('/') < p.indexOfChar ('\\')));
177 
178  auto path = normaliseSeparators (removeEllipsis (p));
179 
180  if (path.startsWithChar ('~'))
181  {
182  if (path[1] == getSeparatorChar() || path[1] == 0)
183  {
184  // expand a name of the form "~/abc"
186  + path.substring (1);
187  }
188  else
189  {
190  // expand a name of type "~dave/abc"
191  auto userName = path.substring (1).upToFirstOccurrenceOf ("/", false, false);
192 
193  if (auto* pw = getpwnam (userName.toUTF8()))
194  path = addTrailingSeparator (pw->pw_dir) + path.fromFirstOccurrenceOf ("/", false, false);
195  }
196  }
197  else if (! path.startsWithChar (getSeparatorChar()))
198  {
199  #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
200  if (! (path.startsWith ("./") || path.startsWith ("../")))
201  {
202  /* When you supply a raw string to the File object constructor, it must be an absolute path.
203  If you're trying to parse a string that may be either a relative path or an absolute path,
204  you MUST provide a context against which the partial path can be evaluated - you can do
205  this by simply using File::getChildFile() instead of the File constructor. E.g. saying
206  "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
207  path if that's what was supplied, or would evaluate a partial path relative to the CWD.
208  */
209  jassertfalse;
210 
211  #if JUCE_LOG_ASSERTIONS
212  Logger::writeToLog ("Illegal absolute path: " + path);
213  #endif
214  }
215  #endif
216 
218  }
219  #endif
220 
221  while (path.endsWithChar (getSeparatorChar()) && path != getSeparatorString()) // careful not to turn a single "/" into an empty string.
222  path = path.dropLastCharacters (1);
223 
224  return path;
225 }
226 
228 {
229  return path.endsWithChar (getSeparatorChar()) ? path
230  : path + getSeparatorChar();
231 }
232 
233 //==============================================================================
234 #if JUCE_LINUX
235  #define NAMES_ARE_CASE_SENSITIVE 1
236 #endif
237 
239 {
240  #if NAMES_ARE_CASE_SENSITIVE
241  return true;
242  #else
243  return false;
244  #endif
245 }
246 
247 static int compareFilenames (const String& name1, const String& name2) noexcept
248 {
249  #if NAMES_ARE_CASE_SENSITIVE
250  return name1.compare (name2);
251  #else
252  return name1.compareIgnoreCase (name2);
253  #endif
254 }
255 
256 bool File::operator== (const File& other) const { return compareFilenames (fullPath, other.fullPath) == 0; }
257 bool File::operator!= (const File& other) const { return compareFilenames (fullPath, other.fullPath) != 0; }
258 bool File::operator< (const File& other) const { return compareFilenames (fullPath, other.fullPath) < 0; }
259 bool File::operator> (const File& other) const { return compareFilenames (fullPath, other.fullPath) > 0; }
260 
261 //==============================================================================
262 bool File::setReadOnly (const bool shouldBeReadOnly,
263  const bool applyRecursively) const
264 {
265  bool worked = true;
266 
267  if (applyRecursively && isDirectory())
268  for (auto& f : findChildFiles (File::findFilesAndDirectories, false))
269  worked = f.setReadOnly (shouldBeReadOnly, true) && worked;
270 
271  return setFileReadOnlyInternal (shouldBeReadOnly) && worked;
272 }
273 
274 bool File::setExecutePermission (bool shouldBeExecutable) const
275 {
276  return setFileExecutableInternal (shouldBeExecutable);
277 }
278 
279 bool File::deleteRecursively (bool followSymlinks) const
280 {
281  bool worked = true;
282 
283  if (isDirectory() && (followSymlinks || ! isSymbolicLink()))
284  for (auto& f : findChildFiles (File::findFilesAndDirectories, false))
285  worked = f.deleteRecursively (followSymlinks) && worked;
286 
287  return deleteFile() && worked;
288 }
289 
290 bool File::moveFileTo (const File& newFile) const
291 {
292  if (newFile.fullPath == fullPath)
293  return true;
294 
295  if (! exists())
296  return false;
297 
298  #if ! NAMES_ARE_CASE_SENSITIVE
299  if (*this != newFile)
300  #endif
301  if (! newFile.deleteFile())
302  return false;
303 
304  return moveInternal (newFile);
305 }
306 
307 bool File::copyFileTo (const File& newFile) const
308 {
309  return (*this == newFile)
310  || (exists() && newFile.deleteFile() && copyInternal (newFile));
311 }
312 
313 bool File::replaceFileIn (const File& newFile) const
314 {
315  if (newFile.fullPath == fullPath)
316  return true;
317 
318  if (! newFile.exists())
319  return moveFileTo (newFile);
320 
321  if (! replaceInternal (newFile))
322  return false;
323 
324  deleteFile();
325  return true;
326 }
327 
328 bool File::copyDirectoryTo (const File& newDirectory) const
329 {
330  if (isDirectory() && newDirectory.createDirectory())
331  {
332  for (auto& f : findChildFiles (File::findFiles, false))
333  if (! f.copyFileTo (newDirectory.getChildFile (f.getFileName())))
334  return false;
335 
336  for (auto& f : findChildFiles (File::findDirectories, false))
337  if (! f.copyDirectoryTo (newDirectory.getChildFile (f.getFileName())))
338  return false;
339 
340  return true;
341  }
342 
343  return false;
344 }
345 
346 //==============================================================================
347 String File::getPathUpToLastSlash() const
348 {
349  auto lastSlash = fullPath.lastIndexOfChar (getSeparatorChar());
350 
351  if (lastSlash > 0)
352  return fullPath.substring (0, lastSlash);
353 
354  if (lastSlash == 0)
355  return getSeparatorString();
356 
357  return fullPath;
358 }
359 
361 {
362  return createFileWithoutCheckingPath (getPathUpToLastSlash());
363 }
364 
365 //==============================================================================
367 {
368  return fullPath.substring (fullPath.lastIndexOfChar (getSeparatorChar()) + 1);
369 }
370 
372 {
373  auto lastSlash = fullPath.lastIndexOfChar (getSeparatorChar()) + 1;
374  auto lastDot = fullPath.lastIndexOfChar ('.');
375 
376  if (lastDot > lastSlash)
377  return fullPath.substring (lastSlash, lastDot);
378 
379  return fullPath.substring (lastSlash);
380 }
381 
382 bool File::isAChildOf (const File& potentialParent) const
383 {
384  if (potentialParent.fullPath.isEmpty())
385  return false;
386 
387  auto ourPath = getPathUpToLastSlash();
388 
389  if (compareFilenames (potentialParent.fullPath, ourPath) == 0)
390  return true;
391 
392  if (potentialParent.fullPath.length() >= ourPath.length())
393  return false;
394 
395  return getParentDirectory().isAChildOf (potentialParent);
396 }
397 
398 int File::hashCode() const { return fullPath.hashCode(); }
399 int64 File::hashCode64() const { return fullPath.hashCode64(); }
400 
401 //==============================================================================
403 {
404  auto firstChar = *(path.text);
405 
406  return firstChar == getSeparatorChar()
407  #if JUCE_WINDOWS
408  || (firstChar != 0 && path.text[1] == ':');
409  #else
410  || firstChar == '~';
411  #endif
412 }
413 
414 File File::getChildFile (StringRef relativePath) const
415 {
416  auto r = relativePath.text;
417 
418  if (isAbsolutePath (r))
419  return File (String (r));
420 
421  #if JUCE_WINDOWS
422  if (r.indexOf ((juce_wchar) '/') >= 0)
423  return getChildFile (String (r).replaceCharacter ('/', '\\'));
424  #endif
425 
426  auto path = fullPath;
427  auto separatorChar = getSeparatorChar();
428 
429  while (*r == '.')
430  {
431  auto lastPos = r;
432  auto secondChar = *++r;
433 
434  if (secondChar == '.') // remove "../"
435  {
436  auto thirdChar = *++r;
437 
438  if (thirdChar == separatorChar || thirdChar == 0)
439  {
440  auto lastSlash = path.lastIndexOfChar (separatorChar);
441 
442  if (lastSlash >= 0)
443  path = path.substring (0, lastSlash);
444 
445  while (*r == separatorChar) // ignore duplicate slashes
446  ++r;
447  }
448  else
449  {
450  r = lastPos;
451  break;
452  }
453  }
454  else if (secondChar == separatorChar || secondChar == 0) // remove "./"
455  {
456  while (*r == separatorChar) // ignore duplicate slashes
457  ++r;
458  }
459  else
460  {
461  r = lastPos;
462  break;
463  }
464  }
465 
466  path = addTrailingSeparator (path);
467  path.appendCharPointer (r);
468  return File (path);
469 }
470 
472 {
473  return getParentDirectory().getChildFile (fileName);
474 }
475 
476 //==============================================================================
478 {
479  const char* suffix;
480  double divisor = 0;
481 
482  if (bytes == 1) { suffix = " byte"; }
483  else if (bytes < 1024) { suffix = " bytes"; }
484  else if (bytes < 1024 * 1024) { suffix = " KB"; divisor = 1024.0; }
485  else if (bytes < 1024 * 1024 * 1024) { suffix = " MB"; divisor = 1024.0 * 1024.0; }
486  else { suffix = " GB"; divisor = 1024.0 * 1024.0 * 1024.0; }
487 
488  return (divisor > 0 ? String (bytes / divisor, 1) : String (bytes)) + suffix;
489 }
490 
491 //==============================================================================
493 {
494  if (exists())
495  return Result::ok();
496 
497  auto parentDir = getParentDirectory();
498 
499  if (parentDir == *this)
500  return Result::fail ("Cannot create parent directory");
501 
502  auto r = parentDir.createDirectory();
503 
504  if (r.wasOk())
505  {
506  FileOutputStream fo (*this, 8);
507  r = fo.getStatus();
508  }
509 
510  return r;
511 }
512 
514 {
515  if (isDirectory())
516  return Result::ok();
517 
518  auto parentDir = getParentDirectory();
519 
520  if (parentDir == *this)
521  return Result::fail ("Cannot create parent directory");
522 
523  auto r = parentDir.createDirectory();
524 
525  if (r.wasOk())
526  r = createDirectoryInternal (fullPath.trimCharactersAtEnd (getSeparatorString()));
527 
528  return r;
529 }
530 
531 //==============================================================================
532 Time File::getLastModificationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (m); }
533 Time File::getLastAccessTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (a); }
534 Time File::getCreationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (c); }
535 
536 bool File::setLastModificationTime (Time t) const { return setFileTimesInternal (t.toMilliseconds(), 0, 0); }
537 bool File::setLastAccessTime (Time t) const { return setFileTimesInternal (0, t.toMilliseconds(), 0); }
538 bool File::setCreationTime (Time t) const { return setFileTimesInternal (0, 0, t.toMilliseconds()); }
539 
540 //==============================================================================
541 bool File::loadFileAsData (MemoryBlock& destBlock) const
542 {
543  if (! existsAsFile())
544  return false;
545 
546  FileInputStream in (*this);
547  return in.openedOk() && getSize() == (int64) in.readIntoMemoryBlock (destBlock);
548 }
549 
551 {
552  if (! existsAsFile())
553  return {};
554 
555  FileInputStream in (*this);
556  return in.openedOk() ? in.readEntireStreamAsString()
557  : String();
558 }
559 
560 void File::readLines (StringArray& destLines) const
561 {
562  destLines.addLines (loadFileAsString());
563 }
564 
565 //==============================================================================
566 Array<File> File::findChildFiles (int whatToLookFor, bool searchRecursively, const String& wildcard) const
567 {
568  Array<File> results;
569  findChildFiles (results, whatToLookFor, searchRecursively, wildcard);
570  return results;
571 }
572 
573 int File::findChildFiles (Array<File>& results, int whatToLookFor, bool searchRecursively, const String& wildcard) const
574 {
575  int total = 0;
576 
577  for (DirectoryIterator di (*this, searchRecursively, wildcard, whatToLookFor); di.next();)
578  {
579  results.add (di.getFile());
580  ++total;
581  }
582 
583  return total;
584 }
585 
586 int File::getNumberOfChildFiles (const int whatToLookFor, const String& wildCardPattern) const
587 {
588  int total = 0;
589 
590  for (DirectoryIterator di (*this, false, wildCardPattern, whatToLookFor); di.next();)
591  ++total;
592 
593  return total;
594 }
595 
597 {
598  if (! isDirectory())
599  return false;
600 
601  DirectoryIterator di (*this, false, "*", findDirectories);
602  return di.next();
603 }
604 
605 //==============================================================================
606 File File::getNonexistentChildFile (const String& suggestedPrefix,
607  const String& suffix,
608  bool putNumbersInBrackets) const
609 {
610  auto f = getChildFile (suggestedPrefix + suffix);
611 
612  if (f.exists())
613  {
614  int number = 1;
615  auto prefix = suggestedPrefix;
616 
617  // remove any bracketed numbers that may already be on the end..
618  if (prefix.trim().endsWithChar (')'))
619  {
620  putNumbersInBrackets = true;
621 
622  auto openBracks = prefix.lastIndexOfChar ('(');
623  auto closeBracks = prefix.lastIndexOfChar (')');
624 
625  if (openBracks > 0
626  && closeBracks > openBracks
627  && prefix.substring (openBracks + 1, closeBracks).containsOnly ("0123456789"))
628  {
629  number = prefix.substring (openBracks + 1, closeBracks).getIntValue();
630  prefix = prefix.substring (0, openBracks);
631  }
632  }
633 
634  do
635  {
636  auto newName = prefix;
637 
638  if (putNumbersInBrackets)
639  {
640  newName << '(' << ++number << ')';
641  }
642  else
643  {
644  if (CharacterFunctions::isDigit (prefix.getLastCharacter()))
645  newName << '_'; // pad with an underscore if the name already ends in a digit
646 
647  newName << ++number;
648  }
649 
650  f = getChildFile (newName + suffix);
651 
652  } while (f.exists());
653  }
654 
655  return f;
656 }
657 
658 File File::getNonexistentSibling (const bool putNumbersInBrackets) const
659 {
660  if (! exists())
661  return *this;
662 
665  putNumbersInBrackets);
666 }
667 
668 //==============================================================================
670 {
671  auto indexOfDot = fullPath.lastIndexOfChar ('.');
672 
673  if (indexOfDot > fullPath.lastIndexOfChar (getSeparatorChar()))
674  return fullPath.substring (indexOfDot);
675 
676  return {};
677 }
678 
679 bool File::hasFileExtension (StringRef possibleSuffix) const
680 {
681  if (possibleSuffix.isEmpty())
682  return fullPath.lastIndexOfChar ('.') <= fullPath.lastIndexOfChar (getSeparatorChar());
683 
684  auto semicolon = possibleSuffix.text.indexOf ((juce_wchar) ';');
685 
686  if (semicolon >= 0)
687  return hasFileExtension (String (possibleSuffix.text).substring (0, semicolon).trimEnd())
688  || hasFileExtension ((possibleSuffix.text + (semicolon + 1)).findEndOfWhitespace());
689 
690  if (fullPath.endsWithIgnoreCase (possibleSuffix))
691  {
692  if (possibleSuffix.text[0] == '.')
693  return true;
694 
695  auto dotPos = fullPath.length() - possibleSuffix.length() - 1;
696 
697  if (dotPos >= 0)
698  return fullPath[dotPos] == '.';
699  }
700 
701  return false;
702 }
703 
705 {
706  if (fullPath.isEmpty())
707  return {};
708 
709  auto filePart = getFileName();
710 
711  auto lastDot = filePart.lastIndexOfChar ('.');
712 
713  if (lastDot >= 0)
714  filePart = filePart.substring (0, lastDot);
715 
716  if (newExtension.isNotEmpty() && newExtension.text[0] != '.')
717  filePart << '.';
718 
719  return getSiblingFile (filePart + newExtension);
720 }
721 
722 //==============================================================================
723 bool File::startAsProcess (const String& parameters) const
724 {
725  return exists() && Process::openDocument (fullPath, parameters);
726 }
727 
728 //==============================================================================
730 {
731  std::unique_ptr<FileInputStream> fin (new FileInputStream (*this));
732 
733  if (fin->openedOk())
734  return fin.release();
735 
736  return nullptr;
737 }
738 
739 FileOutputStream* File::createOutputStream (size_t bufferSize) const
740 {
741  std::unique_ptr<FileOutputStream> out (new FileOutputStream (*this, bufferSize));
742 
743  return out->failedToOpen() ? nullptr
744  : out.release();
745 }
746 
747 //==============================================================================
748 bool File::appendData (const void* const dataToAppend,
749  const size_t numberOfBytes) const
750 {
751  jassert (((ssize_t) numberOfBytes) >= 0);
752 
753  if (numberOfBytes == 0)
754  return true;
755 
756  FileOutputStream out (*this, 8192);
757  return out.openedOk() && out.write (dataToAppend, numberOfBytes);
758 }
759 
760 bool File::replaceWithData (const void* const dataToWrite,
761  const size_t numberOfBytes) const
762 {
763  if (numberOfBytes == 0)
764  return deleteFile();
765 
767  tempFile.getFile().appendData (dataToWrite, numberOfBytes);
768  return tempFile.overwriteTargetFileWithTemporary();
769 }
770 
771 bool File::appendText (const String& text, bool asUnicode, bool writeHeaderBytes, const char* lineFeed) const
772 {
773  FileOutputStream out (*this);
774 
775  if (out.failedToOpen())
776  return false;
777 
778  return out.writeText (text, asUnicode, writeHeaderBytes, lineFeed);
779 }
780 
781 bool File::replaceWithText (const String& textToWrite, bool asUnicode, bool writeHeaderBytes, const char* lineFeed) const
782 {
784  tempFile.getFile().appendText (textToWrite, asUnicode, writeHeaderBytes, lineFeed);
785  return tempFile.overwriteTargetFileWithTemporary();
786 }
787 
788 bool File::hasIdenticalContentTo (const File& other) const
789 {
790  if (other == *this)
791  return true;
792 
793  if (getSize() == other.getSize() && existsAsFile() && other.existsAsFile())
794  {
795  FileInputStream in1 (*this), in2 (other);
796 
797  if (in1.openedOk() && in2.openedOk())
798  {
799  const int bufferSize = 4096;
800  HeapBlock<char> buffer1 (bufferSize), buffer2 (bufferSize);
801 
802  for (;;)
803  {
804  auto num1 = in1.read (buffer1, bufferSize);
805  auto num2 = in2.read (buffer2, bufferSize);
806 
807  if (num1 != num2)
808  break;
809 
810  if (num1 <= 0)
811  return true;
812 
813  if (memcmp (buffer1, buffer2, (size_t) num1) != 0)
814  break;
815  }
816  }
817  }
818 
819  return false;
820 }
821 
822 //==============================================================================
824 {
825  auto s = original;
826  String start;
827 
828  if (s.isNotEmpty() && s[1] == ':')
829  {
830  start = s.substring (0, 2);
831  s = s.substring (2);
832  }
833 
834  return start + s.removeCharacters ("\"#@,;:<>*^|?")
835  .substring (0, 1024);
836 }
837 
839 {
840  auto s = original.removeCharacters ("\"#@,;:<>*^|?\\/");
841 
842  const int maxLength = 128; // only the length of the filename, not the whole path
843  auto len = s.length();
844 
845  if (len > maxLength)
846  {
847  auto lastDot = s.lastIndexOfChar ('.');
848 
849  if (lastDot > jmax (0, len - 12))
850  {
851  s = s.substring (0, maxLength - (len - lastDot))
852  + s.substring (lastDot);
853  }
854  else
855  {
856  s = s.substring (0, maxLength);
857  }
858  }
859 
860  return s;
861 }
862 
863 //==============================================================================
864 static int countNumberOfSeparators (String::CharPointerType s)
865 {
866  int num = 0;
867 
868  for (;;)
869  {
870  auto c = s.getAndAdvance();
871 
872  if (c == 0)
873  break;
874 
875  if (c == File::getSeparatorChar())
876  ++num;
877  }
878 
879  return num;
880 }
881 
883 {
884  if (dir == *this)
885  return ".";
886 
887  auto thisPath = fullPath;
888 
889  while (thisPath.endsWithChar (getSeparatorChar()))
890  thisPath = thisPath.dropLastCharacters (1);
891 
893  : dir.fullPath);
894 
895  int commonBitLength = 0;
896  auto thisPathAfterCommon = thisPath.getCharPointer();
897  auto dirPathAfterCommon = dirPath.getCharPointer();
898 
899  {
900  auto thisPathIter = thisPath.getCharPointer();
901  auto dirPathIter = dirPath.getCharPointer();
902 
903  for (int i = 0;;)
904  {
905  auto c1 = thisPathIter.getAndAdvance();
906  auto c2 = dirPathIter.getAndAdvance();
907 
908  #if NAMES_ARE_CASE_SENSITIVE
909  if (c1 != c2
910  #else
912  #endif
913  || c1 == 0)
914  break;
915 
916  ++i;
917 
918  if (c1 == getSeparatorChar())
919  {
920  thisPathAfterCommon = thisPathIter;
921  dirPathAfterCommon = dirPathIter;
922  commonBitLength = i;
923  }
924  }
925  }
926 
927  // if the only common bit is the root, then just return the full path..
928  if (commonBitLength == 0 || (commonBitLength == 1 && thisPath[1] == getSeparatorChar()))
929  return fullPath;
930 
931  auto numUpDirectoriesNeeded = countNumberOfSeparators (dirPathAfterCommon);
932 
933  if (numUpDirectoriesNeeded == 0)
934  return thisPathAfterCommon;
935 
936  #if JUCE_WINDOWS
937  auto s = String::repeatedString ("..\\", numUpDirectoriesNeeded);
938  #else
939  auto s = String::repeatedString ("../", numUpDirectoriesNeeded);
940  #endif
941  s.appendCharPointer (thisPathAfterCommon);
942  return s;
943 }
944 
945 //==============================================================================
947 {
948  auto tempFile = getSpecialLocation (tempDirectory)
949  .getChildFile ("temp_" + String::toHexString (Random::getSystemRandom().nextInt()))
950  .withFileExtension (fileNameEnding);
951 
952  if (tempFile.exists())
953  return createTempFile (fileNameEnding);
954 
955  return tempFile;
956 }
957 
958 bool File::createSymbolicLink (const File& linkFileToCreate,
959  const String& nativePathOfTarget,
960  bool overwriteExisting)
961 {
962  if (linkFileToCreate.exists())
963  {
964  if (! linkFileToCreate.isSymbolicLink())
965  {
966  // user has specified an existing file / directory as the link
967  // this is bad! the user could end up unintentionally destroying data
968  jassertfalse;
969  return false;
970  }
971 
972  if (overwriteExisting)
973  linkFileToCreate.deleteFile();
974  }
975 
976  #if JUCE_MAC || JUCE_LINUX
977  // one common reason for getting an error here is that the file already exists
978  if (symlink (nativePathOfTarget.toRawUTF8(), linkFileToCreate.getFullPathName().toRawUTF8()) == -1)
979  {
980  jassertfalse;
981  return false;
982  }
983 
984  return true;
985  #elif JUCE_MSVC
986  File targetFile (linkFileToCreate.getSiblingFile (nativePathOfTarget));
987 
988  return CreateSymbolicLink (linkFileToCreate.getFullPathName().toWideCharPointer(),
989  nativePathOfTarget.toWideCharPointer(),
990  targetFile.isDirectory() ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) != FALSE;
991  #else
992  ignoreUnused (nativePathOfTarget);
993  jassertfalse; // symbolic links not supported on this platform!
994  return false;
995  #endif
996 }
997 
998 bool File::createSymbolicLink (const File& linkFileToCreate, bool overwriteExisting) const
999 {
1000  return createSymbolicLink (linkFileToCreate, getFullPathName(), overwriteExisting);
1001 }
1002 
1003 #if ! JUCE_WINDOWS
1005 {
1006  if (isSymbolicLink())
1008 
1009  return *this;
1010 }
1011 #endif
1012 
1013 //==============================================================================
1015  : range (0, file.getSize())
1016 {
1017  openInternal (file, mode, exclusive);
1018 }
1019 
1020 MemoryMappedFile::MemoryMappedFile (const File& file, const Range<int64>& fileRange, AccessMode mode, bool exclusive)
1021  : range (fileRange.getIntersectionWith (Range<int64> (0, file.getSize())))
1022 {
1023  openInternal (file, mode, exclusive);
1024 }
1025 
1026 
1027 //==============================================================================
1028 //==============================================================================
1029 #if JUCE_UNIT_TESTS
1030 
1031 class FileTests : public UnitTest
1032 {
1033 public:
1034  FileTests()
1035  : UnitTest ("Files", UnitTestCategories::files)
1036  {}
1037 
1038  void runTest() override
1039  {
1040  beginTest ("Reading");
1041 
1043  const File temp (File::getSpecialLocation (File::tempDirectory));
1044 
1045  expect (! File().exists());
1046  expect (! File().existsAsFile());
1047  expect (! File().isDirectory());
1048  #if ! JUCE_WINDOWS
1049  expect (File("/").isDirectory());
1050  #endif
1051  expect (home.isDirectory());
1052  expect (home.exists());
1053  expect (! home.existsAsFile());
1058  expect (home.getVolumeTotalSize() > 1024 * 1024);
1059  expect (home.getBytesFreeOnVolume() > 0);
1060  expect (! home.isHidden());
1061  expect (home.isOnHardDisk());
1062  expect (! home.isOnCDRomDrive());
1063  expect (File::getCurrentWorkingDirectory().exists());
1064  expect (home.setAsCurrentWorkingDirectory());
1065  expect (File::getCurrentWorkingDirectory() == home);
1066 
1067  {
1068  Array<File> roots;
1069  File::findFileSystemRoots (roots);
1070  expect (roots.size() > 0);
1071 
1072  int numRootsExisting = 0;
1073  for (int i = 0; i < roots.size(); ++i)
1074  if (roots[i].exists())
1075  ++numRootsExisting;
1076 
1077  // (on windows, some of the drives may not contain media, so as long as at least one is ok..)
1078  expect (numRootsExisting > 0);
1079  }
1080 
1081  beginTest ("Writing");
1082 
1083  File demoFolder (temp.getChildFile ("JUCE UnitTests Temp Folder.folder"));
1084  expect (demoFolder.deleteRecursively());
1085  expect (demoFolder.createDirectory());
1086  expect (demoFolder.isDirectory());
1087  expect (demoFolder.getParentDirectory() == temp);
1088  expect (temp.isDirectory());
1089  expect (temp.findChildFiles (File::findFilesAndDirectories, false, "*").contains (demoFolder));
1090  expect (temp.findChildFiles (File::findDirectories, true, "*.folder").contains (demoFolder));
1091 
1092  File tempFile (demoFolder.getNonexistentChildFile ("test", ".txt", false));
1093 
1094  expect (tempFile.getFileExtension() == ".txt");
1095  expect (tempFile.hasFileExtension (".txt"));
1096  expect (tempFile.hasFileExtension ("txt"));
1097  expect (tempFile.withFileExtension ("xyz").hasFileExtension (".xyz"));
1098  expect (tempFile.withFileExtension ("xyz").hasFileExtension ("abc;xyz;foo"));
1099  expect (tempFile.withFileExtension ("xyz").hasFileExtension ("xyz;foo"));
1100  expect (! tempFile.withFileExtension ("h").hasFileExtension ("bar;foo;xx"));
1101  expect (tempFile.getSiblingFile ("foo").isAChildOf (temp));
1102  expect (tempFile.hasWriteAccess());
1103 
1104  expect (home.getChildFile (".") == home);
1105  expect (home.getChildFile ("..") == home.getParentDirectory());
1106  expect (home.getChildFile (".xyz").getFileName() == ".xyz");
1107  expect (home.getChildFile ("..xyz").getFileName() == "..xyz");
1108  expect (home.getChildFile ("...xyz").getFileName() == "...xyz");
1109  expect (home.getChildFile ("./xyz") == home.getChildFile ("xyz"));
1110  expect (home.getChildFile ("././xyz") == home.getChildFile ("xyz"));
1111  expect (home.getChildFile ("../xyz") == home.getParentDirectory().getChildFile ("xyz"));
1112  expect (home.getChildFile (".././xyz") == home.getParentDirectory().getChildFile ("xyz"));
1113  expect (home.getChildFile (".././xyz/./abc") == home.getParentDirectory().getChildFile ("xyz/abc"));
1114  expect (home.getChildFile ("./../xyz") == home.getParentDirectory().getChildFile ("xyz"));
1115  expect (home.getChildFile ("a1/a2/a3/./../../a4") == home.getChildFile ("a1/a4"));
1116 
1117  {
1118  FileOutputStream fo (tempFile);
1119  fo.write ("0123456789", 10);
1120  }
1121 
1122  expect (tempFile.exists());
1123  expect (tempFile.getSize() == 10);
1124  expect (std::abs ((int) (tempFile.getLastModificationTime().toMilliseconds() - Time::getCurrentTime().toMilliseconds())) < 3000);
1125  expectEquals (tempFile.loadFileAsString(), String ("0123456789"));
1126  expect (! demoFolder.containsSubDirectories());
1127 
1128  expectEquals (tempFile.getRelativePathFrom (demoFolder.getParentDirectory()), demoFolder.getFileName() + File::getSeparatorString() + tempFile.getFileName());
1129  expectEquals (demoFolder.getParentDirectory().getRelativePathFrom (tempFile), ".." + File::getSeparatorString() + ".." + File::getSeparatorString() + demoFolder.getParentDirectory().getFileName());
1130 
1131  expect (demoFolder.getNumberOfChildFiles (File::findFiles) == 1);
1132  expect (demoFolder.getNumberOfChildFiles (File::findFilesAndDirectories) == 1);
1133  expect (demoFolder.getNumberOfChildFiles (File::findDirectories) == 0);
1134  demoFolder.getNonexistentChildFile ("tempFolder", "", false).createDirectory();
1135  expect (demoFolder.getNumberOfChildFiles (File::findDirectories) == 1);
1136  expect (demoFolder.getNumberOfChildFiles (File::findFilesAndDirectories) == 2);
1137  expect (demoFolder.containsSubDirectories());
1138 
1139  expect (tempFile.hasWriteAccess());
1140  tempFile.setReadOnly (true);
1141  expect (! tempFile.hasWriteAccess());
1142  tempFile.setReadOnly (false);
1143  expect (tempFile.hasWriteAccess());
1144 
1145  Time t (Time::getCurrentTime());
1146  tempFile.setLastModificationTime (t);
1147  Time t2 = tempFile.getLastModificationTime();
1148  expect (std::abs ((int) (t2.toMilliseconds() - t.toMilliseconds())) <= 1000);
1149 
1150  {
1151  MemoryBlock mb;
1152  tempFile.loadFileAsData (mb);
1153  expect (mb.getSize() == 10);
1154  expect (mb[0] == '0');
1155  }
1156 
1157  {
1158  expect (tempFile.getSize() == 10);
1159  FileOutputStream fo (tempFile);
1160  expect (fo.openedOk());
1161 
1162  expect (fo.setPosition (7));
1163  expect (fo.truncate().wasOk());
1164  expect (tempFile.getSize() == 7);
1165  fo.write ("789", 3);
1166  fo.flush();
1167  expect (tempFile.getSize() == 10);
1168  }
1169 
1170  beginTest ("Memory-mapped files");
1171 
1172  {
1173  MemoryMappedFile mmf (tempFile, MemoryMappedFile::readOnly);
1174  expect (mmf.getSize() == 10);
1175  expect (mmf.getData() != nullptr);
1176  expect (memcmp (mmf.getData(), "0123456789", 10) == 0);
1177  }
1178 
1179  {
1180  const File tempFile2 (tempFile.getNonexistentSibling (false));
1181  expect (tempFile2.create());
1182  expect (tempFile2.appendData ("xxxxxxxxxx", 10));
1183 
1184  {
1185  MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite);
1186  expect (mmf.getSize() == 10);
1187  expect (mmf.getData() != nullptr);
1188  memcpy (mmf.getData(), "abcdefghij", 10);
1189  }
1190 
1191  {
1192  MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite);
1193  expect (mmf.getSize() == 10);
1194  expect (mmf.getData() != nullptr);
1195  expect (memcmp (mmf.getData(), "abcdefghij", 10) == 0);
1196  }
1197 
1198  expect (tempFile2.deleteFile());
1199  }
1200 
1201  beginTest ("More writing");
1202 
1203  expect (tempFile.appendData ("abcdefghij", 10));
1204  expect (tempFile.getSize() == 20);
1205  expect (tempFile.replaceWithData ("abcdefghij", 10));
1206  expect (tempFile.getSize() == 10);
1207 
1208  File tempFile2 (tempFile.getNonexistentSibling (false));
1209  expect (tempFile.copyFileTo (tempFile2));
1210  expect (tempFile2.exists());
1211  expect (tempFile2.hasIdenticalContentTo (tempFile));
1212  expect (tempFile.deleteFile());
1213  expect (! tempFile.exists());
1214  expect (tempFile2.moveFileTo (tempFile));
1215  expect (tempFile.exists());
1216  expect (! tempFile2.exists());
1217 
1218  expect (demoFolder.deleteRecursively());
1219  expect (! demoFolder.exists());
1220 
1221  {
1222  URL url ("https://audio.dev/foo/bar/");
1223  expectEquals (url.toString (false), String ("https://audio.dev/foo/bar/"));
1224  expectEquals (url.getChildURL ("x").toString (false), String ("https://audio.dev/foo/bar/x"));
1225  expectEquals (url.getParentURL().toString (false), String ("https://audio.dev/foo"));
1226  expectEquals (url.getParentURL().getParentURL().toString (false), String ("https://audio.dev/"));
1227  expectEquals (url.getParentURL().getParentURL().getParentURL().toString (false), String ("https://audio.dev/"));
1228  expectEquals (url.getParentURL().getChildURL ("x").toString (false), String ("https://audio.dev/foo/x"));
1229  expectEquals (url.getParentURL().getParentURL().getParentURL().getChildURL ("x").toString (false), String ("https://audio.dev/x"));
1230  }
1231 
1232  {
1233  URL url ("https://audio.dev/foo/bar");
1234  expectEquals (url.toString (false), String ("https://audio.dev/foo/bar"));
1235  expectEquals (url.getChildURL ("x").toString (false), String ("https://audio.dev/foo/bar/x"));
1236  expectEquals (url.getParentURL().toString (false), String ("https://audio.dev/foo"));
1237  expectEquals (url.getParentURL().getParentURL().toString (false), String ("https://audio.dev/"));
1238  expectEquals (url.getParentURL().getParentURL().getParentURL().toString (false), String ("https://audio.dev/"));
1239  expectEquals (url.getParentURL().getChildURL ("x").toString (false), String ("https://audio.dev/foo/x"));
1240  expectEquals (url.getParentURL().getParentURL().getParentURL().getChildURL ("x").toString (false), String ("https://audio.dev/x"));
1241  }
1242  }
1243 };
1244 
1245 static FileTests fileUnitTests;
1246 
1247 #endif
1248 
1249 } // namespace juce
void add(const ElementType &newElement)
Definition: juce_Array.h:418
static juce_wchar toLowerCase(juce_wchar character) noexcept
static bool isDigit(char character) noexcept
int read(void *, int) override
bool openedOk() const noexcept
bool write(const void *, size_t) override
bool failedToOpen() const noexcept
const Result & getStatus() const noexcept
bool openedOk() const noexcept
bool replaceWithText(const String &textToWrite, bool asUnicode=false, bool writeUnicodeHeaderBytes=false, const char *lineEndings="\r\n") const
Definition: juce_File.cpp:781
bool isSymbolicLink() const
int getNumberOfChildFiles(int whatToLookFor, const String &wildCardPattern="*") const
Definition: juce_File.cpp:586
bool moveFileTo(const File &targetLocation) const
Definition: juce_File.cpp:290
bool operator==(const File &) const
Definition: juce_File.cpp:256
int64 hashCode64() const
Definition: juce_File.cpp:399
bool containsSubDirectories() const
Definition: juce_File.cpp:596
bool isDirectory() const
static void findFileSystemRoots(Array< File > &results)
bool hasIdenticalContentTo(const File &other) const
Definition: juce_File.cpp:788
FileOutputStream * createOutputStream(size_t bufferSize=0x8000) const
Definition: juce_File.cpp:739
static String createLegalPathName(const String &pathNameToFix)
Definition: juce_File.cpp:823
static String addTrailingSeparator(const String &path)
Definition: juce_File.cpp:227
String getFileExtension() const
Definition: juce_File.cpp:669
Time getLastModificationTime() const
Definition: juce_File.cpp:532
const String & getFullPathName() const noexcept
Definition: juce_File.h:149
bool existsAsFile() const
bool copyFileTo(const File &targetLocation) const
Definition: juce_File.cpp:307
int64 getSize() const
bool deleteRecursively(bool followSymlinks=false) const
Definition: juce_File.cpp:279
static File JUCE_CALLTYPE getSpecialLocation(const SpecialLocationType type)
String getFileName() const
Definition: juce_File.cpp:366
bool replaceWithData(const void *dataToWrite, size_t numberOfBytes) const
Definition: juce_File.cpp:760
bool setLastAccessTime(Time newTime) const
Definition: juce_File.cpp:537
File getChildFile(StringRef relativeOrAbsolutePath) const
Definition: juce_File.cpp:414
void readLines(StringArray &destLines) const
Definition: juce_File.cpp:560
static bool isAbsolutePath(StringRef path)
Definition: juce_File.cpp:402
File getSiblingFile(StringRef siblingFileName) const
Definition: juce_File.cpp:471
bool createSymbolicLink(const File &linkFileToCreate, bool overwriteExisting) const
Definition: juce_File.cpp:998
String getFileNameWithoutExtension() const
Definition: juce_File.cpp:371
Array< File > findChildFiles(int whatToLookFor, bool searchRecursively, const String &wildCardPattern="*") const
Definition: juce_File.cpp:566
File getNonexistentSibling(bool putNumbersInBrackets=true) const
Definition: juce_File.cpp:658
@ tempDirectory
Definition: juce_File.h:882
@ currentApplicationFile
Definition: juce_File.h:905
@ invokedExecutableFile
Definition: juce_File.h:912
@ userApplicationDataDirectory
Definition: juce_File.h:858
@ currentExecutableFile
Definition: juce_File.h:895
@ userHomeDirectory
Definition: juce_File.h:832
String getRelativePathFrom(const File &directoryToBeRelativeTo) const
Definition: juce_File.cpp:882
bool appendText(const String &textToAppend, bool asUnicode=false, bool writeUnicodeHeaderBytes=false, const char *lineEndings="\r\n") const
Definition: juce_File.cpp:771
int hashCode() const
Definition: juce_File.cpp:398
Result create() const
Definition: juce_File.cpp:492
@ findDirectories
Definition: juce_File.h:549
@ findFilesAndDirectories
Definition: juce_File.h:551
File getNonexistentChildFile(const String &prefix, const String &suffix, bool putNumbersInBrackets=true) const
Definition: juce_File.cpp:606
bool operator!=(const File &) const
Definition: juce_File.cpp:257
bool setCreationTime(Time newTime) const
Definition: juce_File.cpp:538
static String descriptionOfSizeInBytes(int64 bytes)
Definition: juce_File.cpp:477
bool setReadOnly(bool shouldBeReadOnly, bool applyRecursively=false) const
Definition: juce_File.cpp:262
static File createTempFile(StringRef fileNameEnding)
Definition: juce_File.cpp:946
static juce_wchar getSeparatorChar()
static bool areFileNamesCaseSensitive()
Definition: juce_File.cpp:238
bool isRoot() const
Definition: juce_File.cpp:127
String loadFileAsString() const
Definition: juce_File.cpp:550
FileInputStream * createInputStream() const
Definition: juce_File.cpp:729
bool operator>(const File &) const
Definition: juce_File.cpp:259
File getLinkedTarget() const
Definition: juce_File.cpp:1004
File getParentDirectory() const
Definition: juce_File.cpp:360
bool appendData(const void *dataToAppend, size_t numberOfBytes) const
Definition: juce_File.cpp:748
bool operator<(const File &) const
Definition: juce_File.cpp:258
Time getCreationTime() const
Definition: juce_File.cpp:534
bool setExecutePermission(bool shouldBeExecutable) const
Definition: juce_File.cpp:274
File withFileExtension(StringRef newExtension) const
Definition: juce_File.cpp:704
static String createLegalFileName(const String &fileNameToFix)
Definition: juce_File.cpp:838
File()=default
bool deleteFile() const
bool replaceFileIn(const File &targetLocation) const
Definition: juce_File.cpp:313
bool isAChildOf(const File &potentialParentDirectory) const
Definition: juce_File.cpp:382
static File createFileWithoutCheckingPath(const String &absolutePath) noexcept
Definition: juce_File.cpp:31
bool startAsProcess(const String &parameters=String()) const
Definition: juce_File.cpp:723
String getNativeLinkedTarget() const
bool hasFileExtension(StringRef extensionToTest) const
Definition: juce_File.cpp:679
bool exists() const
bool copyDirectoryTo(const File &newDirectory) const
Definition: juce_File.cpp:328
bool loadFileAsData(MemoryBlock &result) const
Definition: juce_File.cpp:541
bool setLastModificationTime(Time newTime) const
Definition: juce_File.cpp:536
Time getLastAccessTime() const
Definition: juce_File.cpp:533
Result createDirectory() const
Definition: juce_File.cpp:513
static File getCurrentWorkingDirectory()
static StringRef getSeparatorString()
File & operator=(const String &newAbsolutePath)
Definition: juce_File.cpp:43
virtual size_t readIntoMemoryBlock(MemoryBlock &destBlock, ssize_t maxNumBytesToRead=-1)
virtual String readEntireStreamAsString()
static void JUCE_CALLTYPE writeToLog(const String &message)
Definition: juce_Logger.cpp:40
MemoryMappedFile(const File &file, AccessMode mode, bool exclusive=false)
Definition: juce_File.cpp:1014
virtual bool writeText(const String &text, bool asUTF16, bool writeUTF16ByteOrderMark, const char *lineEndings)
static bool JUCE_CALLTYPE openDocument(const String &documentURL, const String &parameters)
static Random & getSystemRandom() noexcept
Definition: juce_Random.cpp:71
static Result fail(const String &errorMessage) noexcept
Definition: juce_Result.cpp:65
static Result ok() noexcept
Definition: juce_Result.h:61
int addLines(StringRef stringToBreakUp)
bool isNotEmpty() const noexcept
int length() const noexcept
String::CharPointerType text
bool isEmpty() const noexcept
static String repeatedString(StringRef stringToRepeat, int numberOfTimesToRepeat)
int indexOfChar(juce_wchar characterToLookFor) const noexcept
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
int length() const noexcept
bool endsWithChar(juce_wchar character) const noexcept
bool isEmpty() const noexcept
Definition: juce_String.h:296
const char * toRawUTF8() const
int64 hashCode64() const noexcept
bool containsChar(juce_wchar character) const noexcept
String removeCharacters(StringRef charactersToRemove) const
bool endsWithIgnoreCase(StringRef text) const noexcept
String dropLastCharacters(int numberToDrop) const
String trimEnd() const
static String toHexString(IntegerType number)
Definition: juce_String.h:1053
int lastIndexOfChar(juce_wchar character) const noexcept
const wchar_t * toWideCharPointer() const
String trimCharactersAtEnd(StringRef charactersToTrim) const
String replaceCharacter(juce_wchar characterToReplace, juce_wchar characterToInsertInstead) const
String substring(int startIndex, int endIndex) const
int hashCode() const noexcept
bool isNotEmpty() const noexcept
Definition: juce_String.h:302
String fromFirstOccurrenceOf(StringRef substringToStartFrom, bool includeSubStringInResult, bool ignoreCase) const
bool overwriteTargetFileWithTemporary() const
const File & getFile() const noexcept
static Time JUCE_CALLTYPE getCurrentTime() noexcept
Definition: juce_Time.cpp:218
int64 toMilliseconds() const noexcept
Definition: juce_Time.h:98