1#include <Foundation/FoundationPCH.h>
2PL_FOUNDATION_INTERNAL_HEADER
4#include <Foundation/IO/OSFile.h>
5#include <Foundation/Logging/Log.h>
6#include <Foundation/Threading/ThreadUtils.h>
7#include <Foundation/Utilities/CommandLineUtils.h>
12#if PL_ENABLED(PL_PLATFORM_WINDOWS)
14# define PL_USE_OLD_POSIX_FUNCTIONS PL_ON
20# include <sys/types.h>
22# define PL_USE_OLD_POSIX_FUNCTIONS PL_OFF
25#if PL_ENABLED(PL_PLATFORM_OSX)
26# include <CoreFoundation/CoreFoundation.h>
29#if PL_ENABLED(PL_PLATFORM_ANDROID)
30# include <Foundation/Basics/Platform/Android/AndroidJni.h>
31# include <Foundation/Basics/Platform/Android/AndroidUtils.h>
32# include <android_native_app_glue.h>
42 const char* szFile = sFileCopy;
44#if PL_DISABLED(PL_PLATFORM_WINDOWS_UWP)
50 fd = open(szFile, O_RDONLY | O_CLOEXEC);
54 fd = open(szFile, O_CREAT | O_WRONLY | O_CLOEXEC, 0644);
79 plInt32 iRetries = m_bRetryOnSharingViolation ? 20 : 1;
81 while (flock(fd, iSharedMode | LOCK_NB ) != 0)
83 int errorCode = errno;
85 if (iRetries == 0 || errorCode != EWOULDBLOCK)
98 m_FileData.m_pFileHandle = fdopen(fd,
"rb");
101 if (ftruncate(fd, 0) < 0)
106 m_FileData.m_pFileHandle = fdopen(fd,
"wb");
109 m_FileData.m_pFileHandle = fdopen(fd,
"ab");
112 if (m_FileData.m_pFileHandle !=
nullptr)
120 if (m_FileData.m_pFileHandle ==
nullptr)
130 m_FileData.m_pFileHandle = fopen(szFile,
"rb");
133 m_FileData.m_pFileHandle = fopen(szFile,
"wb");
136 m_FileData.m_pFileHandle = fopen(szFile,
"ab");
139 if (m_FileData.m_pFileHandle !=
nullptr)
148 if (m_FileData.m_pFileHandle ==
nullptr)
157void plOSFile::InternalClose()
159 fclose(m_FileData.m_pFileHandle);
162plResult plOSFile::InternalWrite(
const void* pBuffer, plUInt64 uiBytes)
164 const plUInt32 uiBatchBytes = 1024 * 1024 * 1024;
167 while (uiBytes > uiBatchBytes)
169 if (fwrite(pBuffer, 1, uiBatchBytes, m_FileData.m_pFileHandle) != uiBatchBytes)
171 plLog::Error(
"fwrite 1GB failed for '{}'", m_sFileName);
175 uiBytes -= uiBatchBytes;
181 const plUInt32 uiBytes32 =
static_cast<plUInt32
>(uiBytes);
183 if (fwrite(pBuffer, 1, uiBytes32, m_FileData.m_pFileHandle) != uiBytes)
193plUInt64 plOSFile::InternalRead(
void* pBuffer, plUInt64 uiBytes)
195 plUInt64 uiBytesRead = 0;
197 const plUInt32 uiBatchBytes = 1024 * 1024 * 1024;
200 while (uiBytes > uiBatchBytes)
202 const plUInt64 uiReadThisTime = fread(pBuffer, 1, uiBatchBytes, m_FileData.m_pFileHandle);
203 uiBytesRead += uiReadThisTime;
205 if (uiReadThisTime != uiBatchBytes)
208 uiBytes -= uiBatchBytes;
214 const plUInt32 uiBytes32 =
static_cast<plUInt32
>(uiBytes);
216 uiBytesRead += fread(pBuffer, 1, uiBytes32, m_FileData.m_pFileHandle);
222plUInt64 plOSFile::InternalGetFilePosition()
const
224#if PL_ENABLED(PL_USE_OLD_POSIX_FUNCTIONS)
225 return static_cast<plUInt64
>(ftell(m_FileData.m_pFileHandle));
227 return static_cast<plUInt64
>(ftello(m_FileData.m_pFileHandle));
233#if PL_ENABLED(PL_USE_OLD_POSIX_FUNCTIONS)
237 PL_VERIFY(fseek(m_FileData.m_pFileHandle, (
long)iDistance, SEEK_SET) == 0,
"Seek Failed");
240 PL_VERIFY(fseek(m_FileData.m_pFileHandle, (
long)iDistance, SEEK_END) == 0,
"Seek Failed");
243 PL_VERIFY(fseek(m_FileData.m_pFileHandle, (
long)iDistance, SEEK_CUR) == 0,
"Seek Failed");
250 PL_VERIFY(fseeko(m_FileData.m_pFileHandle, iDistance, SEEK_SET) == 0,
"Seek Failed");
253 PL_VERIFY(fseeko(m_FileData.m_pFileHandle, iDistance, SEEK_END) == 0,
"Seek Failed");
256 PL_VERIFY(fseeko(m_FileData.m_pFileHandle, iDistance, SEEK_CUR) == 0,
"Seek Failed");
264# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
270 return (stat(
plString(sFile), &sb) == 0 && !S_ISDIR(sb.st_mode));
273bool plOSFile::InternalExistsDirectory(
plStringView sDirectory)
276 return (stat(
plString(sDirectory), &sb) == 0 && S_ISDIR(sb.st_mode));
281#if PL_ENABLED(PL_PLATFORM_WINDOWS)
282 int iRes = _unlink(
plString(sFile));
287 if (iRes == 0 || (iRes == -1 && errno == ENOENT))
295#if PL_ENABLED(PL_PLATFORM_WINDOWS)
296 int iRes = _rmdir(
plString(sDirectory));
298 int iRes = rmdir(
plString(sDirectory));
301 if (iRes == 0 || (iRes == -1 && errno == ENOENT))
313#if PL_ENABLED(PL_PLATFORM_WINDOWS)
314 int iRes = _mkdir(
plString(sDirectory));
316 int iRes = mkdir(
plString(sDirectory), 0777);
319 if (iRes == 0 || (iRes == -1 && errno == EEXIST))
325 if (errno == EACCES && InternalExistsDirectory(sDirectory))
340#if PL_ENABLED(PL_SUPPORTS_FILE_STATS) && PL_DISABLED(PL_PLATFORM_WINDOWS_UWP)
343 struct stat tempStat;
344 int iRes = stat(
plString(sFileOrFolder), &tempStat);
360#if PL_DISABLED(PL_PLATFORM_WINDOWS_UWP)
364 if (s_sApplicationPath.
IsEmpty())
366# if PL_ENABLED(PL_PLATFORM_OSX)
368 CFBundleRef appBundle = CFBundleGetMainBundle();
369 CFURLRef bundleURL = CFBundleCopyBundleURL(appBundle);
370 CFStringRef bundlePath = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle);
372 if (bundlePath !=
nullptr)
374 CFIndex length = CFStringGetLength(bundlePath);
375 CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
377 plArrayPtr<char> temp = PL_DEFAULT_NEW_ARRAY(
char,
static_cast<plUInt32
>(maxSize));
379 if (CFStringGetCString(bundlePath, temp.
GetPtr(), maxSize, kCFStringEncodingUTF8))
381 s_sApplicationPath = temp.
GetPtr();
384 PL_DEFAULT_DELETE_ARRAY(temp);
387 CFRelease(bundlePath);
388 CFRelease(bundleURL);
389 CFRelease(appBundle);
390# elif PL_ENABLED(PL_PLATFORM_ANDROID)
392 plJniAttachment attachment;
394 plJniString packagePath = attachment.GetActivity().Call<plJniString>(
"getPackageCodePath");
398 sTemp.AppendPath(
"Assets/plDummyBin");
399 s_sApplicationPath = sTemp;
402 char result[PATH_MAX];
403 ssize_t length = readlink(
"/proc/self/exe", result, PATH_MAX);
404 s_sApplicationPath =
plStringView(result, result + length);
408 return s_sApplicationPath;
415# if PL_ENABLED(PL_PLATFORM_ANDROID)
416 android_app* app = plAndroidUtils::GetAndroidApp();
417 s_sUserDataPath = app->activity->internalDataPath;
419 s_sUserDataPath = getenv(
"HOME");
422 s_sUserDataPath = getpwuid(getuid())->pw_dir;
436# if PL_ENABLED(PL_PLATFORM_ANDROID)
437 plJniAttachment attachment;
439 plJniObject cacheDir = attachment.GetActivity().Call<plJniObject>(
"getCacheDir");
440 plJniString path = cacheDir.Call<plJniString>(
"getPath");
441 s_sTempDataPath = path.
GetData();
455 if (s_sUserDocumentsPath.
IsEmpty())
457# if PL_ENABLED(PL_PLATFORM_ANDROID)
458 PL_ASSERT_NOT_IMPLEMENTED;
460 s_sUserDataPath = getenv(
"HOME");
463 s_sUserDataPath = getpwuid(getuid())->pw_dir;
483# if PL_ENABLED(PL_SUPPORTS_FILE_ITERATORS)
485plFileSystemIterator::plFileSystemIterator() =
default;
487plFileSystemIterator::~plFileSystemIterator()
489 while (!m_Data.m_Handles.IsEmpty())
491 closedir((DIR*)m_Data.m_Handles.PeekBack());
492 m_Data.m_Handles.PopBack();
498 return !m_Data.m_Handles.IsEmpty();
505 struct dirent* hCurrentFile = readdir(hSearch);
506 if (hCurrentFile ==
nullptr)
511 while (fnmatch(wildcardSearch.
GetData(), hCurrentFile->d_name, FNM_NOESCAPE) != 0)
513 hCurrentFile = readdir(hSearch);
514 if (hCurrentFile ==
nullptr)
522 struct stat fileStat = {};
523 stat(absFileName.
GetData(), &fileStat);
528 curFile.
m_sName = hCurrentFile->d_name;
537 PL_ASSERT_DEV(m_Data.m_Handles.IsEmpty(),
"Cannot start another search.");
539 m_sSearchTerm = sSearchTerm;
549 sSearch.
Trim(
nullptr,
"/");
553 if (flags.
IsSet(plFileSystemIteratorFlags::Recursive) ==
true && bHasWildcard ==
true)
555 PL_ASSERT_DEV(
false,
"Recursive file iteration does not support wildcards. Either don't use recursion, or filter the filenames manually.");
566 m_Data.m_wildcardSearch.Clear();
567 m_sCurPath = sSearch;
570 PL_ASSERT_DEV(m_sCurPath.
IsAbsolutePath(),
"The path '{0}' is not absolute.", m_sCurPath);
574 DIR* hSearch = opendir(m_sCurPath.
GetData());
576 if (hSearch ==
nullptr)
579 if (UpdateCurrentFile(m_CurFile, m_sCurPath, hSearch, m_Data.m_wildcardSearch).Failed())
584 m_Data.m_Handles.PushBack(hSearch);
594 if (!m_Flags.
IsSet(plFileSystemIteratorFlags::ReportFolders))
602 if (!m_Flags.
IsSet(plFileSystemIteratorFlags::ReportFiles))
610plInt32 plFileSystemIterator::InternalNext()
612 constexpr plInt32 CallInternalNext = 2;
614 if (m_Data.m_Handles.IsEmpty())
621 DIR* hSearch = opendir(m_sCurPath.
GetData());
623 if (hSearch !=
nullptr && UpdateCurrentFile(m_CurFile, m_sCurPath, hSearch, m_Data.m_wildcardSearch).Succeeded())
625 m_Data.m_Handles.PushBack(hSearch);
628 return CallInternalNext;
632 if (!m_Flags.
IsSet(plFileSystemIteratorFlags::ReportFolders))
633 return CallInternalNext;
637 if (!m_Flags.
IsSet(plFileSystemIteratorFlags::ReportFiles))
638 return CallInternalNext;
647 if (UpdateCurrentFile(m_CurFile, m_sCurPath, (DIR*)m_Data.m_Handles.PeekBack(), m_Data.m_wildcardSearch).Failed())
650 closedir((DIR*)m_Data.m_Handles.PeekBack());
651 m_Data.m_Handles.PopBack();
653 if (m_Data.m_Handles.IsEmpty())
662 return CallInternalNext;
666 return CallInternalNext;
670 if (!m_Flags.
IsSet(plFileSystemIteratorFlags::ReportFolders))
671 return CallInternalNext;
675 if (!m_Flags.
IsSet(plFileSystemIteratorFlags::ReportFiles))
676 return CallInternalNext;
This class encapsulates an array and it's size. It is recommended to use this class instead of plain ...
Definition ArrayPtr.h:37
PL_ALWAYS_INLINE PointerType GetPtr() const
Returns the pointer to the array.
Definition ArrayPtr.h:118
bool IsValid() const
Returns true if the iterator currently points to a valid file entry.
void StartSearch(plStringView sSearchTerm, plBitflags< plFileSystemIteratorFlags > flags=plFileSystemIteratorFlags::Default)
Starts a search at the given folder. Use * and ? as wildcards.
void Next()
Advances the iterator to the next file object. Might recurse into sub-folders.
static void Error(plLogInterface *pInterface, const plFormatString &string)
An error that needs to be fixed as soon as possible.
Definition Log.cpp:375
static T * AddByteOffset(T *pPtr, std::ptrdiff_t offset)
Returns the address stored in ptr plus the given byte offset iOffset, cast to type T.
static plStringView GetApplicationPath()
Returns the full path to the application binary.
static plString GetUserDocumentsFolder(plStringView sSubFolder={})
Returns the folder into which the user may want to store documents. Append a sub-folder for your appl...
static plString GetTempDataFolder(plStringView sSubFolder={})
Returns the folder into which temp data may be written.
static const plString GetCurrentWorkingDirectory()
Returns the processes current working directory (CWD).
static plString GetUserDataFolder(plStringView sSubFolder={})
Returns the folder into which user data may be safely written. Append a sub-folder for your applicati...
static plStringView GetFileNameAndExtension(plStringView sPath)
Returns the substring that represents the file name including the file extension.
Definition PathUtils.cpp:85
plStringBuilder is a class that is meant for creating and modifying strings.
Definition StringBuilder.h:35
const char * GetData() const
Returns a char pointer to the internal Utf8 data.
Definition StringBuilder_inl.h:148
plUInt32 GetElementCount() const
Returns the number of bytes that this string takes up.
Definition StringBuilder_inl.h:78
void PathParentDirectory(plUInt32 uiLevelsUp=1)
Modifies this string to point to the parent directory.
Definition StringBuilder.cpp:856
void Shrink(plUInt32 uiShrinkCharsFront, plUInt32 uiShrinkCharsBack)
Removes the first n and last m characters from this string.
Definition StringBuilder.cpp:393
void MakeCleanPath()
Removes "../" where possible, replaces all path separators with /, removes double slashes.
Definition StringBuilder.cpp:751
void Trim(const char *szTrimChars=" \f\n\r\t\v")
Removes all characters from the start and end that appear in the given strings.
Definition StringBuilder.cpp:1179
void AppendPath(plStringView sPath1, plStringView sPath2={}, plStringView sPath3={}, plStringView sPath4={})
Appends several path pieces. Makes sure they are always properly separated by a slash.
Definition StringBuilder.cpp:866
static plUInt32 GetCharacterCount(const char *szUtf8, const char *pStringEnd=plUnicodeUtils::GetMaxStringEnd< char >())
Returns the number of characters (not Bytes!) in a Utf8 string (excluding the zero terminator),...
Definition StringUtils_inl.h:81
plStringView represent a read-only sub-string of a larger string, as it can store a dedicated string ...
Definition StringView.h:34
const char * GetEndPointer() const
Returns the end of the view range. This will point to the byte AFTER the last character.
Definition StringView.h:108
const char * GetStartPointer() const
Returns the start of the view range.
Definition StringView.h:102
static void Sleep(const plTime &duration)
Suspends the execution of the current thread for the given amount of time. (Precision may vary accord...
Definition ThreadUtils_Posix.h:29
static plTimestamp MakeFromInt(plInt64 iTimeValue, plSIUnitOfTime::Enum unitOfTime)
Returns a timestamp initialized from 'iTimeValue' in 'unitOfTime' since Unix epoch.
Definition Timestamp.cpp:36
The plBitflags class allows you to work with type-safe bitflags.
Definition Bitflags.h:82
PL_ALWAYS_INLINE bool IsSet(Enum flag) const
Checks if certain flags are set within the bitfield.
Definition Bitflags.h:127
Enum
Definition OSFile.h:24
@ Append
Open file for appending (writing, but always only at the end, already existing data is preserved).
Definition OSFile.h:28
@ Write
Open file for writing (already existing data is discarded).
Definition OSFile.h:27
@ Read
Open file for reading.
Definition OSFile.h:26
Enum
Definition FileEnums.h:18
@ FromStart
The seek position is relative to the file's beginning.
Definition FileEnums.h:19
@ FromCurrent
The seek position is relative to the file's current seek position.
Definition FileEnums.h:21
@ FromEnd
The seek position is relative to the file's end.
Definition FileEnums.h:20
Enum
Definition FileEnums.h:7
@ Exclusive
No other process is allowed to access the file for reading or writing, while it is open.
Definition FileEnums.h:9
@ Default
Results in 'Exclusive' when requesting write access and 'SharedReads' when requesting read access....
Definition FileEnums.h:8
@ SharedReads
Other processes may read the file concurrently.
Definition FileEnums.h:10
Holds the stats for a file.
Definition OSFile.h:34
bool m_bIsDirectory
Whether the file object is a file or folder.
Definition OSFile.h:56
plString m_sName
The name of the file or folder that the stats are for. Does not include the parent path to it....
Definition OSFile.h:47
plUInt64 m_uiFileSize
The size of the file in bytes.
Definition OSFile.h:53
plStringBuilder m_sParentPath
Path to the parent folder. Append m_sName to m_sParentPath to obtain the full path.
Definition OSFile.h:43
plTimestamp m_LastModificationTime
The last modification time as an UTC timestamp since Unix epoch.
Definition OSFile.h:50
const char * GetData() const
Returns a pointer to the internal Utf8 string.
Definition String_inl.h:56
Default enum for returning failure or success, instead of using a bool.
Definition Types.h:54
@ Second
SI-unit of time (base unit)
Definition Timestamp.h:13
bool IsAbsolutePath() const
Returns true, if the given path represents an absolute path on the current OS.
Definition StringBase_inl.h:360
const char * FindLastSubString(plStringView sStringToFind, const char *szStartSearchAt=nullptr) const
Definition StringBase_inl.h:77
plStringView GetFileNameAndExtension() const
Returns the substring that represents the file name including the file extension.
Definition StringBase_inl.h:372
bool IsEmpty() const
Returns whether the string is an empty string.
Definition StringBase_inl.h:25
bool EndsWith(plStringView sEndsWith) const
Returns true, if this string ends with the given string.
Definition StringBase_inl.h:43
plStringView GetFileDirectory() const
Returns the directory of the given file, which is the substring up to the last path separator.
Definition StringBase_inl.h:366
The time class encapsulates a double value storing the time in seconds.
Definition Time.h:12
PL_ALWAYS_INLINE static constexpr plTime MakeFromMilliseconds(double fMilliseconds)
Creates an instance of plTime that was initialized from milliseconds.
Definition Time.h:26