2 ==============================================================================
4 This file is part of the Water library.
5 Copyright (c) 2016 ROLI Ltd.
6 Copyright (C) 2017-2024 Filipe Coelho <falktx@falktx.com>
8 Permission is granted to use this software under the terms of the ISC license
9 http://www.isc.org/downloads/software-support-policy/isc-license/
11 Permission to use, copy, modify, and/or distribute this software for any
12 purpose with or without fee is hereby granted, provided that the above
13 copyright notice and this permission notice appear in all copies.
15 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
16 TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
18 OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
19 USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
23 ==============================================================================
27 #include "DirectoryIterator.h"
28 #include "FileInputStream.h"
29 #include "FileOutputStream.h"
30 #include "TemporaryFile.h"
31 #include "../maths/Random.h"
32 #include "../misc/Time.h"
33 #include "../text/StringArray.h"
42 # include <sys/stat.h>
44 # include <mach-o/dyld.h>
45 # import <Foundation/NSFileManager.h>
46 # import <Foundation/NSPathUtilities.h>
47 # import <Foundation/NSString.h>
55 File::File () noexcept
60 File::File (const char* const absolutePath
)
61 : fullPath (parseAbsolutePath (absolutePath
))
65 File
File::createFileWithoutCheckingPath (const String
& path
) noexcept
72 File::File (const File
& other
)
73 : fullPath (other
.fullPath
)
77 File
& File::operator= (const char* const newAbsolutePath
)
79 fullPath
= parseAbsolutePath (newAbsolutePath
);
83 File
& File::operator= (const File
& other
)
85 fullPath
= other
.fullPath
;
89 bool File::isNull() const
91 return fullPath
.isEmpty();
94 bool File::isNotNull() const
96 return fullPath
.isNotEmpty();
99 //==============================================================================
100 static String
removeEllipsis (const String
& path
)
102 // This will quickly find both /../ and /./ at the expense of a minor
103 // false-positive performance hit when path elements end in a dot.
105 if (path
.contains (".\\"))
107 if (path
.contains ("./"))
111 toks
.addTokens (path
, CARLA_OS_SEP_STR
, StringRef());
112 bool anythingChanged
= false;
114 for (int i
= 1; i
< toks
.size(); ++i
)
116 const String
& t
= toks
[i
];
118 if (t
== ".." && toks
[i
- 1] != "..")
120 anythingChanged
= true;
121 toks
.removeRange (i
- 1, 2);
126 anythingChanged
= true;
132 return toks
.joinIntoString (CARLA_OS_SEP_STR
);
138 String
File::parseAbsolutePath (const String
& p
)
145 String
path (removeEllipsis (p
.replaceCharacter ('/', '\\')));
147 if (path
.startsWithChar (CARLA_OS_SEP
))
149 if (path
[1] != CARLA_OS_SEP
)
151 // Check if path is valid under Wine
152 String
testpath ("Z:" + path
);
154 if (File(testpath
.toRawUTF8()).exists())
160 /* When you supply a raw string to the File object constructor, it must be an absolute path.
161 If you're trying to parse a string that may be either a relative path or an absolute path,
162 you MUST provide a context against which the partial path can be evaluated - you can do
163 this by simply using File::getChildFile() instead of the File constructor. E.g. saying
164 "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
165 path if that's what was supplied, or would evaluate a partial path relative to the CWD.
167 carla_safe_assert(testpath
.toRawUTF8(), __FILE__
, __LINE__
);
169 path
= File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path
;
173 else if (! path
.containsChar (':'))
175 /* When you supply a raw string to the File object constructor, it must be an absolute path.
176 If you're trying to parse a string that may be either a relative path or an absolute path,
177 you MUST provide a context against which the partial path can be evaluated - you can do
178 this by simply using File::getChildFile() instead of the File constructor. E.g. saying
179 "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute
180 path if that's what was supplied, or would evaluate a partial path relative to the CWD.
182 carla_safe_assert(path
.toRawUTF8(), __FILE__
, __LINE__
);
184 return File::getCurrentWorkingDirectory().getChildFile (path
.toRawUTF8()).getFullPathName();
189 // Yes, I know it's legal for a unix pathname to contain a backslash, but this assertion is here
190 // to catch anyone who's trying to run code that was written on Windows with hard-coded path names.
191 // If that's why you've ended up here, use File::getChildFile() to build your paths instead.
192 wassert ((! p
.containsChar ('\\')) || (p
.indexOfChar ('/') >= 0 && p
.indexOfChar ('/') < p
.indexOfChar ('\\')));
194 String
path (removeEllipsis (p
));
196 if (path
.startsWithChar ('~'))
198 if (path
[1] == CARLA_OS_SEP
|| path
[1] == 0)
200 // expand a name of the form "~/abc"
201 path
= File::getSpecialLocation (File::userHomeDirectory
).getFullPathName()
202 + path
.substring (1);
206 // expand a name of type "~dave/abc"
207 const String
userName (path
.substring (1).upToFirstOccurrenceOf ("/", false, false));
209 if (struct passwd
* const pw
= getpwnam (userName
.toUTF8()))
210 path
= addTrailingSeparator (pw
->pw_dir
) + path
.fromFirstOccurrenceOf ("/", false, false);
213 else if (! path
.startsWithChar (CARLA_OS_SEP
))
215 return File::getCurrentWorkingDirectory().getChildFile (path
.toRawUTF8()).getFullPathName();
219 while (path
.endsWithChar (CARLA_OS_SEP
) && path
!= CARLA_OS_SEP_STR
) // careful not to turn a single "/" into an empty string.
220 path
= path
.dropLastCharacters (1);
225 String
File::addTrailingSeparator (const String
& path
)
227 return path
.endsWithChar (CARLA_OS_SEP
) ? path
228 : path
+ CARLA_OS_SEP
;
231 //==============================================================================
232 #if ! (defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN))
233 #define NAMES_ARE_CASE_SENSITIVE 1
236 bool File::areFileNamesCaseSensitive()
238 #if NAMES_ARE_CASE_SENSITIVE
245 static int compareFilenames (const String
& name1
, const String
& name2
) noexcept
247 #if NAMES_ARE_CASE_SENSITIVE
248 return name1
.compare (name2
);
250 return name1
.compareIgnoreCase (name2
);
254 bool File::operator== (const File
& other
) const { return compareFilenames (fullPath
, other
.fullPath
) == 0; }
255 bool File::operator!= (const File
& other
) const { return compareFilenames (fullPath
, other
.fullPath
) != 0; }
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; }
259 //==============================================================================
260 bool File::deleteRecursively() const
266 std::vector
<File
> subFiles
;
267 findChildFiles (subFiles
, File::findFilesAndDirectories
, false);
269 for (ssize_t i
= subFiles
.size(); --i
>= 0;)
270 worked
= subFiles
[i
].deleteRecursively() && worked
;
273 return deleteFile() && worked
;
276 bool File::moveFileTo (const File
& newFile
) const
278 if (newFile
.fullPath
== fullPath
)
284 #if ! NAMES_ARE_CASE_SENSITIVE
285 if (*this != newFile
)
287 if (! newFile
.deleteFile())
290 return moveInternal (newFile
);
293 bool File::copyFileTo (const File
& newFile
) const
295 return (*this == newFile
)
296 || (exists() && newFile
.deleteFile() && copyInternal (newFile
));
299 bool File::replaceFileIn (const File
& newFile
) const
301 if (newFile
.fullPath
== fullPath
)
304 if (! newFile
.exists())
305 return moveFileTo (newFile
);
307 if (! replaceInternal (newFile
))
314 bool File::copyDirectoryTo (const File
& newDirectory
) const
316 if (isDirectory() && newDirectory
.createDirectory())
318 std::vector
<File
> subFiles
;
319 findChildFiles (subFiles
, File::findFiles
, false);
321 for (size_t i
= 0; i
< subFiles
.size(); ++i
)
323 const File
& src (subFiles
[i
]);
324 const File
& dst (newDirectory
.getChildFile (src
.getFileName().toRawUTF8()));
326 if (src
.isSymbolicLink())
328 if (! src
.getLinkedTarget().createSymbolicLink (dst
, true))
333 if (! src
.copyFileTo (dst
))
339 findChildFiles (subFiles
, File::findDirectories
, false);
341 for (size_t i
= 0; i
< subFiles
.size(); ++i
)
342 if (! subFiles
[i
].copyDirectoryTo (newDirectory
.getChildFile (subFiles
[i
].getFileName().toRawUTF8())))
351 //==============================================================================
352 String
File::getPathUpToLastSlash() const
354 const int lastSlash
= fullPath
.lastIndexOfChar (CARLA_OS_SEP
);
357 return fullPath
.substring (0, lastSlash
);
360 return CARLA_OS_SEP_STR
;
365 File
File::getParentDirectory() const
368 f
.fullPath
= getPathUpToLastSlash();
372 //==============================================================================
373 String
File::getFileName() const
375 return fullPath
.substring (fullPath
.lastIndexOfChar (CARLA_OS_SEP
) + 1);
378 String
File::getFileNameWithoutExtension() const
380 const int lastSlash
= fullPath
.lastIndexOfChar (CARLA_OS_SEP
) + 1;
381 const int lastDot
= fullPath
.lastIndexOfChar ('.');
383 if (lastDot
> lastSlash
)
384 return fullPath
.substring (lastSlash
, lastDot
);
386 return fullPath
.substring (lastSlash
);
389 bool File::isAChildOf (const File
& potentialParent
) const
391 if (potentialParent
.fullPath
.isEmpty())
394 const String
ourPath (getPathUpToLastSlash());
396 if (compareFilenames (potentialParent
.fullPath
, ourPath
) == 0)
399 if (potentialParent
.fullPath
.length() >= ourPath
.length())
402 return getParentDirectory().isAChildOf (potentialParent
);
405 //==============================================================================
406 bool File::isAbsolutePath (const char* const path
)
408 const char firstChar
= *path
;
410 return firstChar
== CARLA_OS_SEP
412 || (firstChar
!= '\0' && path
[1] == ':');
418 File
File::getChildFile (const char* const relativePath
) const
420 if (isAbsolutePath (relativePath
))
421 return File (relativePath
);
423 CharPointer_UTF8
r(relativePath
);
426 if (r
.indexOf ((water_uchar
) '/') >= 0)
427 return getChildFile (String (r
).replaceCharacter ('/', '\\').toRawUTF8());
430 String
path (fullPath
);
434 CharPointer_UTF8 lastPos
= r
;
435 const water_uchar secondChar
= *++r
;
437 if (secondChar
== '.') // remove "../"
439 const water_uchar thirdChar
= *++r
;
441 if (thirdChar
== CARLA_OS_SEP
|| thirdChar
== 0)
443 const int lastSlash
= path
.lastIndexOfChar (CARLA_OS_SEP
);
445 path
= path
.substring (0, lastSlash
);
447 while (*r
== CARLA_OS_SEP
) // ignore duplicate slashes
456 else if (secondChar
== CARLA_OS_SEP
|| secondChar
== 0) // remove "./"
458 while (*r
== CARLA_OS_SEP
) // ignore duplicate slashes
468 path
= addTrailingSeparator (path
);
469 path
.appendCharPointer (r
);
470 return File (path
.toRawUTF8());
473 File
File::getSiblingFile (const char* const fileName
) const
475 return getParentDirectory().getChildFile (fileName
);
478 //==============================================================================
479 Result
File::create() const
484 const File
parentDir (getParentDirectory());
486 if (parentDir
== *this)
487 return Result::fail ("Cannot create parent directory");
489 Result
r (parentDir
.createDirectory());
493 FileOutputStream
fo (*this, 8);
500 Result
File::createDirectory() const
505 const File
parentDir (getParentDirectory());
507 if (parentDir
== *this)
508 return Result::fail ("Cannot create parent directory");
510 Result
r (parentDir
.createDirectory());
513 r
= createDirectoryInternal (fullPath
.trimCharactersAtEnd (CARLA_OS_SEP_STR
));
518 //==============================================================================
519 int64
File::getLastModificationTime() const
522 getFileTimesInternal (m
, _
, _
);
526 int64
File::getLastAccessTime() const
529 getFileTimesInternal (_
, a
, _
);
533 int64
File::getCreationTime() const
536 getFileTimesInternal (_
, _
, c
);
540 //==============================================================================
541 bool File::loadFileAsData (MemoryBlock
& destBlock
) const
543 if (! existsAsFile())
546 FileInputStream
in (*this);
547 return in
.openedOk() && getSize() == (int64
) in
.readIntoMemoryBlock (destBlock
);
550 String
File::loadFileAsString() const
552 if (! existsAsFile())
555 FileInputStream
in (*this);
556 return in
.openedOk() ? in
.readEntireStreamAsString()
560 //==============================================================================
561 uint
File::findChildFiles (std::vector
<File
>& results
,
562 const int whatToLookFor
,
563 const bool searchRecursively
,
564 const char* const wildCardPattern
) const
568 for (DirectoryIterator
di (*this, searchRecursively
, wildCardPattern
, whatToLookFor
); di
.next();)
570 results
.push_back (di
.getFile());
577 uint
File::getNumberOfChildFiles (const int whatToLookFor
, const char* const wildCardPattern
) const
581 for (DirectoryIterator
di (*this, false, wildCardPattern
, whatToLookFor
); di
.next();)
587 bool File::containsSubDirectories() const
592 DirectoryIterator
di (*this, false, "*", findDirectories
);
596 //==============================================================================
597 File
File::getNonexistentChildFile (const String
& suggestedPrefix
,
598 const String
& suffix
,
599 bool putNumbersInBrackets
) const
601 File
f (getChildFile (String(suggestedPrefix
+ suffix
).toRawUTF8()));
606 String
prefix (suggestedPrefix
);
608 // remove any bracketed numbers that may already be on the end..
609 if (prefix
.trim().endsWithChar (')'))
611 putNumbersInBrackets
= true;
613 const int openBracks
= prefix
.lastIndexOfChar ('(');
614 const int closeBracks
= prefix
.lastIndexOfChar (')');
617 && closeBracks
> openBracks
618 && prefix
.substring (openBracks
+ 1, closeBracks
).containsOnly ("0123456789"))
620 number
= prefix
.substring (openBracks
+ 1, closeBracks
).getIntValue();
621 prefix
= prefix
.substring (0, openBracks
);
627 String
newName (prefix
);
629 if (putNumbersInBrackets
)
631 newName
<< '(' << ++number
<< ')';
635 if (CharacterFunctions::isDigit (prefix
.getLastCharacter()))
636 newName
<< '_'; // pad with an underscore if the name already ends in a digit
641 f
= getChildFile (String(newName
+ suffix
).toRawUTF8());
643 } while (f
.exists());
649 File
File::getNonexistentSibling (const bool putNumbersInBrackets
) const
654 return getParentDirectory().getNonexistentChildFile (getFileNameWithoutExtension(),
656 putNumbersInBrackets
);
659 //==============================================================================
660 String
File::getFileExtension() const
662 const int indexOfDot
= fullPath
.lastIndexOfChar ('.');
664 if (indexOfDot
> fullPath
.lastIndexOfChar (CARLA_OS_SEP
))
665 return fullPath
.substring (indexOfDot
);
670 bool File::hasFileExtension (const char* const possibleSuffix_
) const
672 const CharPointer_UTF8
possibleSuffix(possibleSuffix_
);
674 if (possibleSuffix
.isEmpty())
675 return fullPath
.lastIndexOfChar ('.') <= fullPath
.lastIndexOfChar (CARLA_OS_SEP
);
677 const int semicolon
= possibleSuffix
.indexOf ((water_uchar
) ';');
680 return hasFileExtension (String (possibleSuffix
).substring (0, semicolon
).trimEnd().toRawUTF8())
681 || hasFileExtension ((possibleSuffix
+ (semicolon
+ 1)).findEndOfWhitespace());
683 if (fullPath
.endsWithIgnoreCase (possibleSuffix
))
685 if (possibleSuffix
[0] == '.')
688 const int dotPos
= fullPath
.length() - possibleSuffix
.length() - 1;
691 return fullPath
[dotPos
] == '.';
697 File
File::withFileExtension (const char* const newExtension
) const
699 if (fullPath
.isEmpty())
702 String
filePart (getFileName().toRawUTF8());
704 const int i
= filePart
.lastIndexOfChar ('.');
706 filePart
= filePart
.substring (0, i
);
708 if (newExtension
[0] != '\0' && newExtension
[0] != '.')
711 return getSiblingFile (String(filePart
+ newExtension
).toRawUTF8());
714 //==============================================================================
715 FileInputStream
* File::createInputStream() const
717 CarlaScopedPointer
<FileInputStream
> fin (new FileInputStream (*this));
720 return fin
.release();
725 FileOutputStream
* File::createOutputStream (const size_t bufferSize
) const
727 CarlaScopedPointer
<FileOutputStream
> out (new FileOutputStream (*this, bufferSize
));
729 return out
->failedToOpen() ? nullptr
733 //==============================================================================
734 bool File::appendData (const void* const dataToAppend
,
735 const size_t numberOfBytes
) const
737 wassert (((ssize_t
) numberOfBytes
) >= 0);
739 if (numberOfBytes
== 0)
742 FileOutputStream
out (*this, 8192);
743 return out
.openedOk() && out
.write (dataToAppend
, numberOfBytes
);
746 bool File::replaceWithData (const void* const dataToWrite
,
747 const size_t numberOfBytes
) const
749 if (numberOfBytes
== 0)
752 TemporaryFile
tempFile (*this, TemporaryFile::useHiddenFile
);
753 tempFile
.getFile().appendData (dataToWrite
, numberOfBytes
);
754 return tempFile
.overwriteTargetFileWithTemporary();
757 bool File::appendText (const String
& text
,
758 const bool asUnicode
,
759 const bool writeUnicodeHeaderBytes
) const
761 FileOutputStream
out (*this);
762 CARLA_SAFE_ASSERT_RETURN (! out
.failedToOpen(), false);
764 out
.writeText (text
, asUnicode
, writeUnicodeHeaderBytes
);
768 bool File::replaceWithText (const String
& textToWrite
,
769 const bool asUnicode
,
770 const bool writeUnicodeHeaderBytes
) const
772 TemporaryFile
tempFile (*this, TemporaryFile::useHiddenFile
);
773 tempFile
.getFile().appendText (textToWrite
, asUnicode
, writeUnicodeHeaderBytes
);
774 return tempFile
.overwriteTargetFileWithTemporary();
777 bool File::hasIdenticalContentTo (const File
& other
) const
782 if (getSize() == other
.getSize() && existsAsFile() && other
.existsAsFile())
784 FileInputStream
in1 (*this), in2 (other
);
786 if (in1
.openedOk() && in2
.openedOk())
788 const int bufferSize
= 4096;
789 HeapBlock
<char> buffer1
, buffer2
;
791 CARLA_SAFE_ASSERT_RETURN(buffer1
.malloc (bufferSize
), false);
792 CARLA_SAFE_ASSERT_RETURN(buffer2
.malloc (bufferSize
), false);
796 const int num1
= in1
.read (buffer1
, bufferSize
);
797 const int num2
= in2
.read (buffer2
, bufferSize
);
805 if (memcmp (buffer1
, buffer2
, (size_t) num1
) != 0)
814 //==============================================================================
815 String
File::createLegalPathName (const String
& original
)
820 if (s
.isNotEmpty() && s
[1] == ':')
822 start
= s
.substring (0, 2);
826 return start
+ s
.removeCharacters ("\"#@,;:<>*^|?")
827 .substring (0, 1024);
830 String
File::createLegalFileName (const String
& original
)
832 String
s (original
.removeCharacters ("\"#@,;:<>*^|?\\/"));
834 const int maxLength
= 128; // only the length of the filename, not the whole path
835 const int len
= s
.length();
839 const int lastDot
= s
.lastIndexOfChar ('.');
841 if (lastDot
> jmax (0, len
- 12))
843 s
= s
.substring (0, maxLength
- (len
- lastDot
))
844 + s
.substring (lastDot
);
848 s
= s
.substring (0, maxLength
);
855 //==============================================================================
856 static int countNumberOfSeparators (CharPointer_UTF8 s
)
862 const water_uchar c
= s
.getAndAdvance();
867 if (c
== CARLA_OS_SEP
)
874 String
File::getRelativePathFrom (const File
& dir
) const
876 String
thisPath (fullPath
);
878 while (thisPath
.endsWithChar (CARLA_OS_SEP
))
879 thisPath
= thisPath
.dropLastCharacters (1);
881 String
dirPath (addTrailingSeparator (dir
.existsAsFile() ? dir
.getParentDirectory().getFullPathName()
884 int commonBitLength
= 0;
885 CharPointer_UTF8
thisPathAfterCommon (thisPath
.getCharPointer());
886 CharPointer_UTF8
dirPathAfterCommon (dirPath
.getCharPointer());
889 CharPointer_UTF8
thisPathIter (thisPath
.getCharPointer());
890 CharPointer_UTF8
dirPathIter (dirPath
.getCharPointer());
894 const water_uchar c1
= thisPathIter
.getAndAdvance();
895 const water_uchar c2
= dirPathIter
.getAndAdvance();
897 #if NAMES_ARE_CASE_SENSITIVE
900 if ((c1
!= c2
&& CharacterFunctions::toLowerCase (c1
) != CharacterFunctions::toLowerCase (c2
))
907 if (c1
== CARLA_OS_SEP
)
909 thisPathAfterCommon
= thisPathIter
;
910 dirPathAfterCommon
= dirPathIter
;
916 // if the only common bit is the root, then just return the full path..
917 if (commonBitLength
== 0 || (commonBitLength
== 1 && thisPath
[1] == CARLA_OS_SEP
))
920 const int numUpDirectoriesNeeded
= countNumberOfSeparators (dirPathAfterCommon
);
922 if (numUpDirectoriesNeeded
== 0)
923 return thisPathAfterCommon
;
926 String
s (String::repeatedString ("..\\", numUpDirectoriesNeeded
));
928 String
s (String::repeatedString ("../", numUpDirectoriesNeeded
));
930 s
.appendCharPointer (thisPathAfterCommon
);
934 //==============================================================================
935 File
File::createTempFile (const char* const fileNameEnding
)
937 const File
tempFile (getSpecialLocation (tempDirectory
)
938 .getChildFile (String("temp_" + String::toHexString (Random::getSystemRandom().nextInt())).toRawUTF8())
939 .withFileExtension (fileNameEnding
));
941 if (tempFile
.exists())
942 return createTempFile (fileNameEnding
);
947 bool File::createSymbolicLink (const File
& linkFileToCreate
, bool overwriteExisting
) const
949 if (linkFileToCreate
.exists())
951 // user has specified an existing file / directory as the link
952 // this is bad! the user could end up unintentionally destroying data
953 CARLA_SAFE_ASSERT_RETURN(linkFileToCreate
.isSymbolicLink(), false);
955 if (overwriteExisting
)
956 linkFileToCreate
.deleteFile();
960 carla_stderr("File::createSymbolicLink failed, unsupported");
963 // one common reason for getting an error here is that the file already exists
964 return symlink(fullPath
.toRawUTF8(), linkFileToCreate
.getFullPathName().toRawUTF8()) != -1;
968 //=====================================================================================================================
970 namespace WindowsFileHelpers
972 DWORD
getAtts (const String
& path
)
974 return GetFileAttributesW (path
.toUTF16().c_str());
977 int64
fileTimeToTime (const FILETIME
* const ft
)
979 #ifdef CARLA_PROPER_CPP11_SUPPORT
980 static_wassert (sizeof (ULARGE_INTEGER
) == sizeof (FILETIME
)); // tell me if this fails!
983 return (int64
) ((reinterpret_cast<const ULARGE_INTEGER
*> (ft
)->QuadPart
- 116444736000000000LL) / 10000);
986 File
getSpecialFolderPath (int type
)
988 WCHAR wpath
[MAX_PATH
+ 256];
990 if (SHGetSpecialFolderPathW (nullptr, wpath
, type
, FALSE
))
992 CHAR apath
[MAX_PATH
+ 256];
994 if (WideCharToMultiByte (CP_UTF8
, 0, wpath
, -1, apath
, numElementsInArray (apath
), nullptr, nullptr))
1001 File
getModuleFileName (HINSTANCE moduleHandle
)
1003 WCHAR wdest
[MAX_PATH
+ 256];
1004 CHAR adest
[MAX_PATH
+ 256];
1006 GetModuleFileNameW (moduleHandle
, wdest
, (DWORD
) numElementsInArray (wdest
));
1008 if (WideCharToMultiByte (CP_UTF8
, 0, wdest
, -1, adest
, numElementsInArray (adest
), nullptr, nullptr))
1009 return File (adest
);
1015 bool File::isDirectory() const
1017 const DWORD attr
= WindowsFileHelpers::getAtts (fullPath
);
1018 return (attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0 && attr
!= INVALID_FILE_ATTRIBUTES
;
1021 bool File::exists() const
1023 return fullPath
.isNotEmpty()
1024 && WindowsFileHelpers::getAtts (fullPath
) != INVALID_FILE_ATTRIBUTES
;
1027 bool File::existsAsFile() const
1029 return fullPath
.isNotEmpty()
1030 && (WindowsFileHelpers::getAtts (fullPath
) & FILE_ATTRIBUTE_DIRECTORY
) == 0;
1033 bool File::hasWriteAccess() const
1035 if (fullPath
.isEmpty())
1038 const DWORD attr
= WindowsFileHelpers::getAtts (fullPath
);
1040 // NB: According to MS, the FILE_ATTRIBUTE_READONLY attribute doesn't work for
1041 // folders, and can be incorrectly set for some special folders, so we'll just say
1042 // that folders are always writable.
1043 return attr
== INVALID_FILE_ATTRIBUTES
1044 || (attr
& FILE_ATTRIBUTE_DIRECTORY
) != 0
1045 || (attr
& FILE_ATTRIBUTE_READONLY
) == 0;
1048 int64
File::getSize() const
1050 WIN32_FILE_ATTRIBUTE_DATA attributes
;
1052 if (GetFileAttributesExW (fullPath
.toUTF16().c_str(), GetFileExInfoStandard
, &attributes
))
1053 return (((int64
) attributes
.nFileSizeHigh
) << 32) | attributes
.nFileSizeLow
;
1058 void File::getFileTimesInternal (int64
& modificationTime
, int64
& accessTime
, int64
& creationTime
) const
1060 using namespace WindowsFileHelpers
;
1061 WIN32_FILE_ATTRIBUTE_DATA attributes
;
1063 if (GetFileAttributesExW (fullPath
.toUTF16().c_str(), GetFileExInfoStandard
, &attributes
))
1065 modificationTime
= fileTimeToTime (&attributes
.ftLastWriteTime
);
1066 creationTime
= fileTimeToTime (&attributes
.ftCreationTime
);
1067 accessTime
= fileTimeToTime (&attributes
.ftLastAccessTime
);
1071 creationTime
= accessTime
= modificationTime
= 0;
1075 bool File::deleteFile() const
1080 return isDirectory() ? RemoveDirectoryW (fullPath
.toUTF16().c_str()) != 0
1081 : DeleteFileW (fullPath
.toUTF16().c_str()) != 0;
1084 File
File::getLinkedTarget() const
1089 bool File::copyInternal (const File
& dest
) const
1091 return CopyFileW (fullPath
.toUTF16().c_str(), dest
.getFullPathName().toUTF16().c_str(), false) != 0;
1094 bool File::moveInternal (const File
& dest
) const
1096 return MoveFileW (fullPath
.toUTF16().c_str(), dest
.getFullPathName().toUTF16().c_str()) != 0;
1099 bool File::replaceInternal (const File
& dest
) const
1101 void* lpExclude
= 0;
1102 void* lpReserved
= 0;
1104 return ReplaceFileW (dest
.getFullPathName().toUTF16().c_str(),
1105 fullPath
.toUTF16().c_str(),
1106 0, REPLACEFILE_IGNORE_MERGE_ERRORS
, lpExclude
, lpReserved
) != 0;
1109 Result
File::createDirectoryInternal (const String
& fileName
) const
1111 return CreateDirectoryW (fileName
.toUTF16().c_str(), 0) ? Result::ok()
1112 : getResultForLastError();
1115 File
File::getCurrentWorkingDirectory()
1117 WCHAR wdest
[MAX_PATH
+ 256];
1118 CHAR adest
[MAX_PATH
+ 256];
1120 GetCurrentDirectoryW ((DWORD
) numElementsInArray (wdest
), wdest
);
1122 if (WideCharToMultiByte (CP_UTF8
, 0, wdest
, -1, adest
, numElementsInArray (adest
), nullptr, nullptr))
1123 return File (adest
);
1128 bool File::setAsCurrentWorkingDirectory() const
1130 return SetCurrentDirectoryW (getFullPathName().toUTF16().c_str()) != FALSE
;
1133 bool File::isSymbolicLink() const
1135 return (GetFileAttributesW (fullPath
.toUTF16().c_str()) & FILE_ATTRIBUTE_REPARSE_POINT
) != 0;
1138 File
File::getSpecialLocation (const SpecialLocationType type
)
1144 case userHomeDirectory
:
1145 csidlType
= CSIDL_PROFILE
;
1150 WCHAR wdest
[MAX_PATH
+ 256];
1151 CHAR adest
[MAX_PATH
+ 256];
1153 GetTempPathW ((DWORD
) numElementsInArray (wdest
), wdest
);
1155 if (WideCharToMultiByte (CP_UTF8
, 0, wdest
, -1, adest
, numElementsInArray (adest
), nullptr, nullptr))
1156 return File (adest
);
1161 case currentExecutableFile
:
1162 return WindowsFileHelpers::getModuleFileName (water::getCurrentModuleInstanceHandle());
1164 case hostApplicationPath
:
1165 return WindowsFileHelpers::getModuleFileName (nullptr);
1168 csidlType
= CSIDL_APPDATA
;
1171 case winProgramFiles
:
1172 csidlType
= CSIDL_PROGRAM_FILES
;
1175 case winCommonProgramFiles
:
1176 csidlType
= CSIDL_PROGRAM_FILES_COMMON
;
1179 case winMyDocuments
:
1180 csidlType
= CSIDL_MYDOCUMENTS
;
1184 wassertfalse
; // unknown type?
1188 return WindowsFileHelpers::getSpecialFolderPath (csidlType
);
1191 //=====================================================================================================================
1192 class DirectoryIterator::NativeIterator::Pimpl
1195 Pimpl (const File
& directory
, const String
& wildCard
)
1196 : directoryWithWildCard (directory
.getFullPathName().isNotEmpty() ? File::addTrailingSeparator (directory
.getFullPathName()) + wildCard
: String()),
1197 handle (INVALID_HANDLE_VALUE
)
1203 if (handle
!= INVALID_HANDLE_VALUE
)
1207 bool next (String
& filenameFound
, bool* const isDir
, int64
* const fileSize
, bool* const isReadOnly
)
1209 using namespace WindowsFileHelpers
;
1210 WIN32_FIND_DATAW findData
;
1212 if (handle
== INVALID_HANDLE_VALUE
)
1214 handle
= FindFirstFileW (directoryWithWildCard
.toUTF16().c_str(), &findData
);
1216 if (handle
== INVALID_HANDLE_VALUE
)
1221 if (FindNextFileW (handle
, &findData
) == 0)
1225 CHAR apath
[MAX_PATH
+ 256];
1227 if (WideCharToMultiByte (CP_UTF8
, 0, findData
.cFileName
, -1, apath
, numElementsInArray (apath
), nullptr, nullptr))
1228 filenameFound
= apath
;
1230 if (isDir
!= nullptr) *isDir
= ((findData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) != 0);
1231 if (isReadOnly
!= nullptr) *isReadOnly
= ((findData
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
) != 0);
1232 if (fileSize
!= nullptr) *fileSize
= findData
.nFileSizeLow
+ (((int64
) findData
.nFileSizeHigh
) << 32);
1238 const String directoryWithWildCard
;
1241 CARLA_DECLARE_NON_COPYABLE (Pimpl
)
1244 //=====================================================================================================================
1248 typedef struct stat64 water_statStruct
;
1249 #define WATER_STAT stat64
1251 typedef struct stat water_statStruct
;
1252 #define WATER_STAT stat
1255 bool water_stat (const String
& fileName
, water_statStruct
& info
)
1257 return fileName
.isNotEmpty()
1258 && WATER_STAT (fileName
.toUTF8(), &info
) == 0;
1261 void updateStatInfoForFile (const String
& path
, bool* const isDir
, int64
* const fileSize
, bool* const isReadOnly
)
1263 if (isDir
!= nullptr || fileSize
!= nullptr)
1265 water_statStruct info
;
1266 const bool statOk
= water_stat (path
, info
);
1268 if (isDir
!= nullptr) *isDir
= statOk
&& ((info
.st_mode
& S_IFDIR
) != 0);
1269 if (fileSize
!= nullptr) *fileSize
= statOk
? (int64
) info
.st_size
: 0;
1272 if (isReadOnly
!= nullptr)
1273 *isReadOnly
= access (path
.toUTF8(), W_OK
) != 0;
1276 Result
getResultForReturnValue (int value
)
1278 return value
== -1 ? getResultForErrno() : Result::ok();
1282 bool File::isDirectory() const
1284 water_statStruct info
;
1286 return fullPath
.isNotEmpty()
1287 && (water_stat (fullPath
, info
) && ((info
.st_mode
& S_IFDIR
) != 0));
1290 bool File::exists() const
1292 return fullPath
.isNotEmpty()
1293 && access (fullPath
.toUTF8(), F_OK
) == 0;
1296 bool File::existsAsFile() const
1298 return exists() && ! isDirectory();
1301 bool File::hasWriteAccess() const
1304 return access (fullPath
.toUTF8(), W_OK
) == 0;
1306 if ((! isDirectory()) && fullPath
.containsChar (CARLA_OS_SEP
))
1307 return getParentDirectory().hasWriteAccess();
1312 void File::getFileTimesInternal (int64
& modificationTime
, int64
& accessTime
, int64
& creationTime
) const
1314 water_statStruct info
;
1316 if (water_stat (fullPath
, info
))
1318 modificationTime
= (int64
) info
.st_mtime
* 1000;
1319 accessTime
= (int64
) info
.st_atime
* 1000;
1320 creationTime
= (int64
) info
.st_ctime
* 1000;
1324 modificationTime
= accessTime
= creationTime
= 0;
1328 int64
File::getSize() const
1330 water_statStruct info
;
1331 return water_stat (fullPath
, info
) ? info
.st_size
: 0;
1334 bool File::deleteFile() const
1336 if (! exists() && ! isSymbolicLink())
1340 return rmdir (fullPath
.toUTF8()) == 0;
1342 return remove (fullPath
.toUTF8()) == 0;
1345 bool File::moveInternal (const File
& dest
) const
1347 if (rename (fullPath
.toUTF8(), dest
.getFullPathName().toUTF8()) == 0)
1350 if (hasWriteAccess() && copyInternal (dest
))
1361 bool File::replaceInternal (const File
& dest
) const
1363 return moveInternal (dest
);
1366 Result
File::createDirectoryInternal (const String
& fileName
) const
1368 return getResultForReturnValue (mkdir (fileName
.toUTF8(), 0777));
1371 File
File::getCurrentWorkingDirectory()
1373 HeapBlock
<char> heapBuffer
;
1375 char localBuffer
[1024];
1376 char* cwd
= getcwd (localBuffer
, sizeof (localBuffer
) - 1);
1378 size_t bufferSize
= 4096;
1379 while (cwd
== nullptr && errno
== ERANGE
)
1381 CARLA_SAFE_ASSERT_RETURN(heapBuffer
.malloc (bufferSize
), File());
1383 cwd
= getcwd (heapBuffer
, bufferSize
- 1);
1387 return File (CharPointer_UTF8 (cwd
));
1390 bool File::setAsCurrentWorkingDirectory() const
1392 return chdir (getFullPathName().toUTF8()) == 0;
1395 File
water_getExecutableFile();
1396 File
water_getExecutableFile()
1400 static String
getFilename()
1403 void* localSymbol
= (void*) water_getExecutableFile
;
1404 dladdr (localSymbol
, &exeInfo
);
1405 const CharPointer_UTF8
filename (exeInfo
.dli_fname
);
1407 // if the filename is absolute simply return it
1408 if (File::isAbsolutePath (filename
))
1411 // if the filename is relative construct from CWD
1412 if (filename
[0] == '.')
1413 return File::getCurrentWorkingDirectory().getChildFile (filename
).getFullPathName();
1415 // filename is abstract, look up in PATH
1416 if (const char* const envpath
= ::getenv ("PATH"))
1418 StringArray
paths (StringArray::fromTokens (envpath
, ":", ""));
1420 for (int i
=paths
.size(); --i
>=0;)
1422 const File
filepath (File (paths
[i
].toRawUTF8()).getChildFile (filename
));
1424 if (filepath
.existsAsFile())
1425 return filepath
.getFullPathName();
1429 // if we reach this, we failed to find ourselves...
1435 static String
filename (DLAddrReader::getFilename());
1436 return filename
.toRawUTF8();
1440 static NSString
* getFileLink (const String
& path
)
1442 return [[NSFileManager defaultManager
] destinationOfSymbolicLinkAtPath
: waterStringToNS (path
) error
: nil
];
1445 bool File::isSymbolicLink() const
1447 return getFileLink (fullPath
) != nil
;
1450 File
File::getLinkedTarget() const
1452 if (NSString
* dest
= getFileLink (fullPath
))
1453 return getSiblingFile ([dest UTF8String
]);
1458 bool File::copyInternal (const File
& dest
) const
1460 const AutoNSAutoreleasePool arpool
;
1462 NSFileManager
* fm
= [NSFileManager defaultManager
];
1464 return [fm fileExistsAtPath
: waterStringToNS (fullPath
)]
1465 #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
1466 && [fm copyItemAtPath
: waterStringToNS (fullPath
)
1467 toPath
: waterStringToNS (dest
.getFullPathName())
1470 && [fm copyPath
: waterStringToNS (fullPath
)
1471 toPath
: waterStringToNS (dest
.getFullPathName())
1476 File
File::getSpecialLocation (const SpecialLocationType type
)
1478 const AutoNSAutoreleasePool arpool
;
1484 case userHomeDirectory
:
1485 resultPath
= [NSHomeDirectory() UTF8String
];
1490 File
tmp (String("~/Library/Caches/" + water_getExecutableFile().getFileNameWithoutExtension()).toRawUTF8());
1491 tmp
.createDirectory();
1492 return File (tmp
.getFullPathName().toRawUTF8());
1495 case currentExecutableFile
:
1496 return water_getExecutableFile();
1498 case hostApplicationPath
:
1500 unsigned int size
= 8192;
1501 char* const buffer
= new char[size
+ 8];
1502 _NSGetExecutablePath (buffer
, &size
);
1504 return File (buffer
);
1508 wassertfalse
; // unknown type?
1512 if (resultPath
.isNotEmpty())
1513 return File (resultPath
.convertToPrecomposedUnicode().toRawUTF8());
1517 //==============================================================================
1518 class DirectoryIterator::NativeIterator::Pimpl
1521 Pimpl (const File
& directory
, const String
& wildCard_
)
1522 : parentDir (File::addTrailingSeparator (directory
.getFullPathName())),
1523 wildCard (wildCard_
),
1526 const AutoNSAutoreleasePool arpool
;
1528 enumerator
= [[[NSFileManager defaultManager
] enumeratorAtPath
: waterStringToNS (directory
.getFullPathName())] retain
];
1533 [enumerator release
];
1536 bool next (String
& filenameFound
, bool* const isDir
, int64
* const fileSize
,bool* const isReadOnly
)
1538 const AutoNSAutoreleasePool arpool
;
1540 const char* wildcardUTF8
= nullptr;
1545 if (enumerator
== nil
|| (file
= [enumerator nextObject
]) == nil
)
1548 [enumerator skipDescendents
];
1549 filenameFound
= nsStringToWater (file
).convertToPrecomposedUnicode();
1551 if (wildcardUTF8
== nullptr)
1552 wildcardUTF8
= wildCard
.toUTF8();
1554 if (fnmatch (wildcardUTF8
, filenameFound
.toUTF8(), FNM_CASEFOLD
) != 0)
1557 const String
fullPath (parentDir
+ filenameFound
);
1558 updateStatInfoForFile (fullPath
, isDir
, fileSize
, isReadOnly
);
1565 String parentDir
, wildCard
;
1566 NSDirectoryEnumerator
* enumerator
;
1568 CARLA_DECLARE_NON_COPYABLE (Pimpl
)
1571 static String
getLinkedFile (const String
& file
)
1573 HeapBlock
<char> buffer
;
1574 CARLA_SAFE_ASSERT_RETURN(buffer
.malloc(8194), String());
1576 const int numBytes
= (int) readlink (file
.toRawUTF8(), buffer
, 8192);
1577 return String::fromUTF8 (buffer
, jmax (0, numBytes
));
1580 bool File::isSymbolicLink() const
1582 return getLinkedFile (getFullPathName()).isNotEmpty();
1585 File
File::getLinkedTarget() const
1587 String
f (getLinkedFile (getFullPathName()));
1590 return getSiblingFile (f
.toRawUTF8());
1595 bool File::copyInternal (const File
& dest
) const
1597 FileInputStream
in (*this);
1599 if (dest
.deleteFile())
1602 FileOutputStream
out (dest
);
1604 if (out
.failedToOpen())
1607 if (out
.writeFromInputStream (in
, -1) == getSize())
1617 File
File::getSpecialLocation (const SpecialLocationType type
)
1621 case userHomeDirectory
:
1623 if (const char* homeDir
= getenv ("HOME"))
1624 return File (CharPointer_UTF8 (homeDir
));
1626 if (struct passwd
* const pw
= getpwuid (getuid()))
1627 return File (CharPointer_UTF8 (pw
->pw_dir
));
1634 File
tmp ("/var/tmp");
1636 if (! tmp
.isDirectory())
1640 if (! tmp
.isDirectory())
1641 tmp
= File::getCurrentWorkingDirectory();
1647 case currentExecutableFile
:
1648 return water_getExecutableFile();
1650 case hostApplicationPath
:
1652 const File
f ("/proc/self/exe");
1653 return f
.isSymbolicLink() ? f
.getLinkedTarget() : water_getExecutableFile();
1657 wassertfalse
; // unknown type?
1663 //==============================================================================
1664 class DirectoryIterator::NativeIterator::Pimpl
1667 Pimpl (const File
& directory
, const String
& wc
)
1668 : parentDir (File::addTrailingSeparator (directory
.getFullPathName())),
1669 wildCard (wc
), dir (opendir (directory
.getFullPathName().toUTF8()))
1679 bool next (String
& filenameFound
, bool* const isDir
, int64
* const fileSize
,bool* const isReadOnly
)
1683 const char* wildcardUTF8
= nullptr;
1687 struct dirent
* const de
= readdir (dir
);
1692 if (wildcardUTF8
== nullptr)
1693 wildcardUTF8
= wildCard
.toUTF8();
1695 if (fnmatch (wildcardUTF8
, de
->d_name
, FNM_CASEFOLD
) == 0)
1697 filenameFound
= CharPointer_UTF8 (de
->d_name
);
1699 updateStatInfoForFile (parentDir
+ filenameFound
, isDir
, fileSize
, isReadOnly
);
1710 String parentDir
, wildCard
;
1713 CARLA_DECLARE_NON_COPYABLE (Pimpl
)
1718 DirectoryIterator::NativeIterator::NativeIterator (const File
& directory
, const String
& wildCardStr
)
1719 : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory
, wildCardStr
))
1723 DirectoryIterator::NativeIterator::~NativeIterator() {}
1725 bool DirectoryIterator::NativeIterator::next (String
& filenameFound
, bool* isDir
, int64
* fileSize
,bool* isReadOnly
)
1727 return pimpl
->next (filenameFound
, isDir
, fileSize
, isReadOnly
);