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