OpenShot Audio Library | OpenShotAudio 0.3.2
Loading...
Searching...
No Matches
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
23namespace juce
24{
25
27 : fullPath (parseAbsolutePath (fullPathName))
28{
29}
30
32{
33 File f;
34 f.fullPath = path;
35 return f;
36}
37
39 : fullPath (other.fullPath)
40{
41}
42
44{
45 fullPath = parseAbsolutePath (newPath);
46 return *this;
47}
48
50{
51 fullPath = other.fullPath;
52 return *this;
53}
54
56 : fullPath (std::move (other.fullPath))
57{
58}
59
61{
62 fullPath = std::move (other.fullPath);
63 return *this;
64}
65
66JUCE_DECLARE_DEPRECATED_STATIC (const File File::nonexistent{};)
67
68//==============================================================================
69static 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
107static 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
127bool File::isRoot() const
128{
129 return fullPath.isNotEmpty() && *this == getParentDirectory();
130}
131
132String 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
154 path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path;
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
168 return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
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"
185 path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName()
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
217 return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
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
247static 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
256bool File::operator== (const File& other) const { return compareFilenames (fullPath, other.fullPath) == 0; }
257bool File::operator!= (const File& other) const { return compareFilenames (fullPath, other.fullPath) != 0; }
258bool File::operator< (const File& other) const { return compareFilenames (fullPath, other.fullPath) < 0; }
259bool File::operator> (const File& other) const { return compareFilenames (fullPath, other.fullPath) > 0; }
260
261//==============================================================================
263 const bool applyRecursively) const
264{
265 bool worked = true;
266
268 for (auto& f : findChildFiles (File::findFilesAndDirectories, false))
269 worked = f.setReadOnly (shouldBeReadOnly, true) && worked;
270
271 return setFileReadOnlyInternal (shouldBeReadOnly) && worked;
272}
273
275{
276 return setFileExecutableInternal (shouldBeExecutable);
277}
278
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
290bool 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
307bool File::copyFileTo (const File& newFile) const
308{
309 return (*this == newFile)
310 || (exists() && newFile.deleteFile() && copyInternal (newFile));
311}
312
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
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//==============================================================================
347String 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
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
396}
397
398int File::hashCode() const { return fullPath.hashCode(); }
399int64 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
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;
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
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
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
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//==============================================================================
532Time File::getLastModificationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (m); }
533Time File::getLastAccessTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (a); }
534Time File::getCreationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (c); }
535
536bool File::setLastModificationTime (Time t) const { return setFileTimesInternal (t.toMilliseconds(), 0, 0); }
537bool File::setLastAccessTime (Time t) const { return setFileTimesInternal (0, t.toMilliseconds(), 0); }
538bool File::setCreationTime (Time t) const { return setFileTimesInternal (0, 0, t.toMilliseconds()); }
539
540//==============================================================================
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
561{
562 destLines.addLines (loadFileAsString());
563}
564
565//==============================================================================
566Array<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
573int 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
586int 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//==============================================================================
607 const String& suffix,
608 bool putNumbersInBrackets) const
609{
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
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
658File 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
674 return fullPath.substring (indexOfDot);
675
676 return {};
677}
678
680{
682 return fullPath.lastIndexOfChar ('.') <= fullPath.lastIndexOfChar (getSeparatorChar());
683
684 auto semicolon = possibleSuffix.text.indexOf ((juce_wchar) ';');
685
686 if (semicolon >= 0)
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
720}
721
722//==============================================================================
723bool 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
740{
741 std::unique_ptr<FileOutputStream> out (new FileOutputStream (*this, bufferSize));
742
743 return out->failedToOpen() ? nullptr
744 : out.release();
745}
746
747//==============================================================================
748bool 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
760bool 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
771bool 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
781bool 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
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//==============================================================================
864static 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
892 auto dirPath = addTrailingSeparator (dir.existsAsFile() ? dir.getParentDirectory().getFullPathName()
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 {
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
938 #else
940 #endif
941 s.appendCharPointer (thisPathAfterCommon);
942 return s;
943}
944
945//==============================================================================
947{
949 .getChildFile ("temp_" + String::toHexString (Random::getSystemRandom().nextInt()))
950 .withFileExtension (fileNameEnding);
951
952 if (tempFile.exists())
954
955 return tempFile;
956}
957
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
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
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
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
1031class FileTests : public UnitTest
1032{
1033public:
1034 FileTests()
1035 : UnitTest ("Files", UnitTestCategories::files)
1036 {}
1037
1038 void runTest() override
1039 {
1040 beginTest ("Reading");
1041
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());
1066
1067 {
1070 expect (roots.size() > 0);
1071
1072 int numRootsExisting = 0;
1073 for (int i = 0; i < roots.size(); ++i)
1074 if (roots[i].exists())
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
1245static FileTests fileUnitTests;
1246
1247#endif
1248
1249} // namespace juce
bool isEmpty() const noexcept
Definition juce_Array.h:222
void removeRange(int startIndex, int numberToRemove)
Definition juce_Array.h:916
Array()=default
int indexOf(ParameterType elementToLookFor) const
Definition juce_Array.h:382
void add(const ElementType &newElement)
Definition juce_Array.h:418
static juce_wchar toLowerCase(juce_wchar character) noexcept
static bool isDigit(char character) noexcept
bool replaceWithText(const String &textToWrite, bool asUnicode=false, bool writeUnicodeHeaderBytes=false, const char *lineEndings="\r\n") const
bool isSymbolicLink() const
int getNumberOfChildFiles(int whatToLookFor, const String &wildCardPattern="*") const
bool moveFileTo(const File &targetLocation) const
bool operator==(const File &) const
int64 hashCode64() const
bool containsSubDirectories() const
bool isDirectory() const
static void findFileSystemRoots(Array< File > &results)
bool hasIdenticalContentTo(const File &other) const
FileOutputStream * createOutputStream(size_t bufferSize=0x8000) const
static String createLegalPathName(const String &pathNameToFix)
static String addTrailingSeparator(const String &path)
String getFileExtension() const
Time getLastModificationTime() const
bool existsAsFile() const
bool copyFileTo(const File &targetLocation) const
int64 getSize() const
bool deleteRecursively(bool followSymlinks=false) const
static File JUCE_CALLTYPE getSpecialLocation(const SpecialLocationType type)
const String & getFullPathName() const noexcept
Definition juce_File.h:149
String getFileName() const
bool replaceWithData(const void *dataToWrite, size_t numberOfBytes) const
bool setLastAccessTime(Time newTime) const
File getChildFile(StringRef relativeOrAbsolutePath) const
void readLines(StringArray &destLines) const
static bool isAbsolutePath(StringRef path)
File getSiblingFile(StringRef siblingFileName) const
bool createSymbolicLink(const File &linkFileToCreate, bool overwriteExisting) const
String getFileNameWithoutExtension() const
Array< File > findChildFiles(int whatToLookFor, bool searchRecursively, const String &wildCardPattern="*") const
File getNonexistentSibling(bool putNumbersInBrackets=true) const
@ 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
bool appendText(const String &textToAppend, bool asUnicode=false, bool writeUnicodeHeaderBytes=false, const char *lineEndings="\r\n") const
int hashCode() const
Result create() const
@ findDirectories
Definition juce_File.h:549
@ findFilesAndDirectories
Definition juce_File.h:551
File getNonexistentChildFile(const String &prefix, const String &suffix, bool putNumbersInBrackets=true) const
bool operator!=(const File &) const
bool setCreationTime(Time newTime) const
static String descriptionOfSizeInBytes(int64 bytes)
bool setReadOnly(bool shouldBeReadOnly, bool applyRecursively=false) const
static File createTempFile(StringRef fileNameEnding)
static juce_wchar getSeparatorChar()
static bool areFileNamesCaseSensitive()
bool isRoot() const
String loadFileAsString() const
FileInputStream * createInputStream() const
bool operator>(const File &) const
File getLinkedTarget() const
File getParentDirectory() const
bool appendData(const void *dataToAppend, size_t numberOfBytes) const
bool operator<(const File &) const
Time getCreationTime() const
bool setExecutePermission(bool shouldBeExecutable) const
File withFileExtension(StringRef newExtension) const
static String createLegalFileName(const String &fileNameToFix)
File()=default
bool deleteFile() const
bool replaceFileIn(const File &targetLocation) const
bool isAChildOf(const File &potentialParentDirectory) const
static File createFileWithoutCheckingPath(const String &absolutePath) noexcept
Definition juce_File.cpp:31
bool startAsProcess(const String &parameters=String()) const
String getNativeLinkedTarget() const
bool hasFileExtension(StringRef extensionToTest) const
bool exists() const
bool copyDirectoryTo(const File &newDirectory) const
bool loadFileAsData(MemoryBlock &result) const
bool setLastModificationTime(Time newTime) const
Time getLastAccessTime() const
Result createDirectory() const
static File getCurrentWorkingDirectory()
static StringRef getSeparatorString()
File & operator=(const String &newAbsolutePath)
Definition juce_File.cpp:43
static void JUCE_CALLTYPE writeToLog(const String &message)
MemoryMappedFile(const File &file, AccessMode mode, bool exclusive=false)
static bool JUCE_CALLTYPE openDocument(const String &documentURL, const String &parameters)
static Random & getSystemRandom() noexcept
static Result fail(const String &errorMessage) noexcept
static Result ok() noexcept
Definition juce_Result.h:61
String::CharPointerType text
static String repeatedString(StringRef stringToRepeat, int numberOfTimesToRepeat)
int indexOfChar(juce_wchar characterToLookFor) const noexcept
int length() const noexcept
bool endsWithChar(juce_wchar character) const noexcept
bool isEmpty() const noexcept
int64 hashCode64() const noexcept
bool containsChar(juce_wchar character) const noexcept
String removeCharacters(StringRef charactersToRemove) const
bool endsWithIgnoreCase(StringRef text) const noexcept
String trimEnd() const
static String toHexString(IntegerType number)
int lastIndexOfChar(juce_wchar character) const noexcept
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
static Time JUCE_CALLTYPE getCurrentTime() noexcept