summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorAndrey Lihatskiy <118752495+marchcat@users.noreply.github.com>2026-01-20 18:49:25 +0200
committerGitHub <noreply@github.com>2026-01-20 18:49:25 +0200
commitb4f75c4cd53192573e8245eb5fe63a65734d0a1b (patch)
treec91ced079bd5b84eb5cd3c7a9c3b3b7bb8367057 /indra/llcommon
parent7d616292ad664472ffa79cc0cd447096a18e72ba (diff)
parent30e7621140a3cbf4c911f596c1325f4b7eae25a6 (diff)
Merge pull request #5303 from secondlife/marchcat/slua-ed-dev-lnx
Merge develop-linux into SLua editor PV branch
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/CMakeLists.txt2
-rw-r--r--indra/llcommon/fsyspath.h4
-rw-r--r--[-rwxr-xr-x]indra/llcommon/hexdump.h0
-rw-r--r--indra/llcommon/llapp.cpp4
-rw-r--r--indra/llcommon/llapr.cpp220
-rw-r--r--indra/llcommon/llapr.h17
-rw-r--r--indra/llcommon/llcrc.cpp2
-rw-r--r--indra/llcommon/llcrc.h2
-rw-r--r--indra/llcommon/llerror.cpp10
-rw-r--r--indra/llcommon/llfile.cpp1096
-rw-r--r--indra/llcommon/llfile.h811
-rwxr-xr-xindra/llcommon/llpointer.cpp43
-rw-r--r--indra/llcommon/llpointer.h291
-rw-r--r--indra/llcommon/llprocessor.cpp4
-rw-r--r--indra/llcommon/llprofilercategories.h9
-rw-r--r--indra/llcommon/llsys.cpp6
-rw-r--r--indra/llcommon/lluuid.cpp46
-rw-r--r--indra/llcommon/lluuid.h2
-rw-r--r--indra/llcommon/tests/llfile_test.cpp299
-rw-r--r--indra/llcommon/tests/llleap_test.cpp20
-rw-r--r--indra/llcommon/tests/llprocess_test.cpp52
-rw-r--r--indra/llcommon/tests/llsdserialize_test.cpp12
-rw-r--r--indra/llcommon/workqueue.cpp22
23 files changed, 2019 insertions, 955 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index b41ffb6dbf..deb3a26ead 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -67,6 +67,7 @@ set(llcommon_SOURCE_FILES
llmetricperformancetester.cpp
llmortician.cpp
llmutex.cpp
+ llpointer.cpp
llptrto.cpp
llpredicate.cpp
llprocess.cpp
@@ -322,6 +323,7 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventfilter "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llevents "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llfile "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llhttpdate "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llheteromap "" "${test_libs}")
diff --git a/indra/llcommon/fsyspath.h b/indra/llcommon/fsyspath.h
index 2c900c02a7..237d99ae86 100644
--- a/indra/llcommon/fsyspath.h
+++ b/indra/llcommon/fsyspath.h
@@ -57,7 +57,7 @@ class fsyspath: public std::filesystem::path
public:
// default
- fsyspath() {}
+ fsyspath() = default;
// construct from UTF-8 encoded string
fsyspath(const std::string& path): fsyspath(std::string_view(path)) {}
fsyspath(const char* path): fsyspath(std::string_view(path)) {}
@@ -66,8 +66,10 @@ public:
{}
// construct from existing path
fsyspath(const super& path): super(path) {}
+ fsyspath(super&& path) : super(std::move(path)) {}
fsyspath& operator=(const super& p) { super::operator=(p); return *this; }
+ fsyspath& operator=(super&& p) { super::operator=(std::move(p)); return *this; }
fsyspath& operator=(const std::string& p) { return (*this) = std::string_view(p); }
fsyspath& operator=(const char* p) { return (*this) = std::string_view(p); }
fsyspath& operator=(std::string_view p)
diff --git a/indra/llcommon/hexdump.h b/indra/llcommon/hexdump.h
index ab5ba2b16d..ab5ba2b16d 100755..100644
--- a/indra/llcommon/hexdump.h
+++ b/indra/llcommon/hexdump.h
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index 73b50d0a50..aa84895fb8 100644
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -37,7 +37,7 @@
#endif
#include "llcommon.h"
-#include "llapr.h"
+
#include "llerrorcontrol.h"
#include "llframetimer.h"
#include "lllivefile.h"
@@ -53,6 +53,8 @@
//
// Signal handling
#ifndef LL_WINDOWS
+#include "apr_signal.h"
+
# include <signal.h>
# include <unistd.h> // for fork()
void setup_signals();
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index 65ee383c6b..9ea7a34bac 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -526,226 +526,6 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
}
}
-//static
-S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
-{
- LL_PROFILE_ZONE_SCOPED;
- //*****************************************
- LLAPRFilePoolScope scope(pool);
- apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), APR_READ|APR_BINARY);
- //*****************************************
- if (!file_handle)
- {
- return 0;
- }
-
- llassert(offset >= 0);
-
- if (offset > 0)
- offset = LLAPRFile::seek(file_handle, APR_SET, offset);
-
- apr_size_t bytes_read;
- if (offset < 0)
- {
- bytes_read = 0;
- }
- else
- {
- bytes_read = nbytes ;
- apr_status_t s = apr_file_read(file_handle, buf, &bytes_read);
- if (s != APR_SUCCESS)
- {
- LL_WARNS("APR") << " Attempting to read filename: " << filename << LL_ENDL;
- ll_apr_warn_status(s);
- bytes_read = 0;
- }
- else
- {
- llassert_always(bytes_read <= 0x7fffffff);
- }
- }
-
- //*****************************************
- close(file_handle) ;
- //*****************************************
- return (S32)bytes_read;
-}
-
-//static
-S32 LLAPRFile::writeEx(const std::string& filename, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
-{
- LL_PROFILE_ZONE_SCOPED;
- apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
- if (offset < 0)
- {
- flags |= APR_APPEND;
- offset = 0;
- }
-
- //*****************************************
- LLAPRFilePoolScope scope(pool);
- apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), flags);
- //*****************************************
- if (!file_handle)
- {
- return 0;
- }
-
- if (offset > 0)
- {
- offset = LLAPRFile::seek(file_handle, APR_SET, offset);
- }
-
- apr_size_t bytes_written;
- if (offset < 0)
- {
- bytes_written = 0;
- }
- else
- {
- bytes_written = nbytes ;
- apr_status_t s = apr_file_write(file_handle, buf, &bytes_written);
- if (s != APR_SUCCESS)
- {
- LL_WARNS("APR") << "Attempting to write filename: " << filename << LL_ENDL;
- if (APR_STATUS_IS_ENOSPC(s))
- {
- LLApp::notifyOutOfDiskSpace();
- }
- ll_apr_warn_status(s);
- bytes_written = 0;
- }
- else
- {
- llassert_always(bytes_written <= 0x7fffffff);
- }
- }
-
- //*****************************************
- LLAPRFile::close(file_handle);
- //*****************************************
-
- return (S32)bytes_written;
-}
-
-//static
-bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool)
-{
- apr_status_t s;
-
- LLAPRFilePoolScope scope(pool);
- s = apr_file_remove(filename.c_str(), scope.getVolatileAPRPool());
-
- if (s != APR_SUCCESS)
- {
- ll_apr_warn_status(s);
- LL_WARNS("APR") << " Attempting to remove filename: " << filename << LL_ENDL;
- return false;
- }
- return true;
-}
-
-//static
-bool LLAPRFile::rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool)
-{
- apr_status_t s;
-
- LLAPRFilePoolScope scope(pool);
- s = apr_file_rename(filename.c_str(), newname.c_str(), scope.getVolatileAPRPool());
-
- if (s != APR_SUCCESS)
- {
- ll_apr_warn_status(s);
- LL_WARNS("APR") << " Attempting to rename filename: " << filename << LL_ENDL;
- return false;
- }
- return true;
-}
-
-//static
-bool LLAPRFile::isExist(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags)
-{
- apr_file_t* apr_file;
- apr_status_t s;
-
- LLAPRFilePoolScope scope(pool);
- s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, scope.getVolatileAPRPool());
-
- if (s != APR_SUCCESS || !apr_file)
- {
- return false;
- }
- else
- {
- apr_file_close(apr_file) ;
- return true;
- }
-}
-
-//static
-S32 LLAPRFile::size(const std::string& filename, LLVolatileAPRPool* pool)
-{
- apr_file_t* apr_file;
- apr_finfo_t info;
- apr_status_t s;
-
- LLAPRFilePoolScope scope(pool);
- s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, scope.getVolatileAPRPool());
-
- if (s != APR_SUCCESS || !apr_file)
- {
- return 0;
- }
- else
- {
- apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file);
-
- apr_file_close(apr_file) ;
-
- if (s == APR_SUCCESS)
- {
- return (S32)info.size;
- }
- else
- {
- return 0;
- }
- }
-}
-
-//static
-bool LLAPRFile::makeDir(const std::string& dirname, LLVolatileAPRPool* pool)
-{
- apr_status_t s;
-
- LLAPRFilePoolScope scope(pool);
- s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, scope.getVolatileAPRPool());
-
- if (s != APR_SUCCESS)
- {
- ll_apr_warn_status(s);
- LL_WARNS("APR") << " Attempting to make directory: " << dirname << LL_ENDL;
- return false;
- }
- return true;
-}
-
-//static
-bool LLAPRFile::removeDir(const std::string& dirname, LLVolatileAPRPool* pool)
-{
- apr_status_t s;
-
- LLAPRFilePoolScope scope(pool);
- s = apr_file_remove(dirname.c_str(), scope.getVolatileAPRPool());
-
- if (s != APR_SUCCESS)
- {
- ll_apr_warn_status(s);
- LL_WARNS("APR") << " Attempting to remove directory: " << dirname << LL_ENDL;
- return false;
- }
- return true;
-}
//
//end of static components of LLAPRFile
//*******************************************************************************************************************************
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
index 0e1e4277e7..8795a0f2c4 100644
--- a/indra/llcommon/llapr.h
+++ b/indra/llcommon/llapr.h
@@ -35,10 +35,6 @@
#include "llwin32headers.h"
#include "apr_thread_proc.h"
-#include "apr_getopt.h"
-#include "apr_signal.h"
-
-#include "llstring.h"
#include <mutex>
@@ -184,20 +180,7 @@ private:
static apr_file_t* open(const std::string& filename, apr_pool_t* apr_pool, apr_int32_t flags);
static apr_status_t close(apr_file_t* file) ;
static S32 seek(apr_file_t* file, apr_seek_where_t where, S32 offset);
-public:
- // returns false if failure:
- static bool remove(const std::string& filename, LLVolatileAPRPool* pool = NULL);
- static bool rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool = NULL);
- static bool isExist(const std::string& filename, LLVolatileAPRPool* pool = NULL, apr_int32_t flags = APR_READ);
- static S32 size(const std::string& filename, LLVolatileAPRPool* pool = NULL);
- static bool makeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
- static bool removeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
-
- // Returns bytes read/written, 0 if read/write fails:
- static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL);
- static S32 writeEx(const std::string& filename, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); // offset<0 means append
//*******************************************************************************************************************************
};
-
#endif // LL_LLAPR_H
diff --git a/indra/llcommon/llcrc.cpp b/indra/llcommon/llcrc.cpp
index d79d06e2a2..33bf01a29b 100644
--- a/indra/llcommon/llcrc.cpp
+++ b/indra/llcommon/llcrc.cpp
@@ -166,7 +166,7 @@ void LLCRC::update(const std::string& filename)
return;
}
- FILE* fp = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */
+ FILE* fp = LLFile::fopen(filename, LLFILE_MODE("rb")); /* Flawfinder: ignore */
if (fp)
{
diff --git a/indra/llcommon/llcrc.h b/indra/llcommon/llcrc.h
index a3bde47780..2ea5fbc3b0 100644
--- a/indra/llcommon/llcrc.h
+++ b/indra/llcommon/llcrc.h
@@ -36,7 +36,7 @@
// example (don't try this at work kids):
//
// LLCRC crc;
-// FILE* fp = LLFile::fopen(filename,"rb");
+// FILE* fp = LLFile::fopen(filename,LLFILE_MODE("rb"));
// while(!feof(fp)) {
// crc.update(fgetc(fp));
// }
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 8ae0fabcc6..5b42c83a14 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -481,13 +481,9 @@ namespace
std::string file = user_dir + "/logcontrol-dev.xml";
- llstat stat_info;
- if (LLFile::stat(file, &stat_info)) {
- // NB: stat returns non-zero if it can't read the file, for example
- // if it doesn't exist. LLFile has no better abstraction for
- // testing for file existence.
-
- file = app_dir + "/logcontrol.xml";
+ if (!LLFile::isfile(file))
+ {
+ file = app_dir + "/logcontrol.xml";
}
return * new LogControlFile(file);
// NB: This instance is never freed
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index a539e4fe28..114df29f6e 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -29,22 +29,17 @@
#include "linden_common.h"
#include "llfile.h"
-#include "llstring.h"
#include "llerror.h"
#include "stringize.h"
#if LL_WINDOWS
-#include "llwin32headers.h"
-#include <vector>
+#include <fcntl.h>
#else
#include <errno.h>
+#include <sys/file.h>
#endif
-using namespace std;
-
-static std::string empty;
-
-// Many of the methods below use OS-level functions that mess with errno. Wrap
+// Some of the methods below use OS-level functions that mess with errno. Wrap
// variants of strerror() to report errors.
#if LL_WINDOWS
@@ -79,6 +74,7 @@ static errentry const errtable[]
{ ERROR_CURRENT_DIRECTORY, EACCES }, // 16
{ ERROR_NOT_SAME_DEVICE, EXDEV }, // 17
{ ERROR_NO_MORE_FILES, ENOENT }, // 18
+ { ERROR_SHARING_VIOLATION, EACCES }, // 32
{ ERROR_LOCK_VIOLATION, EACCES }, // 33
{ ERROR_BAD_NETPATH, ENOENT }, // 53
{ ERROR_NETWORK_ACCESS_DENIED, EACCES }, // 65
@@ -109,22 +105,25 @@ static errentry const errtable[]
{ ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816
};
-static int set_errno_from_oserror(unsigned long oserr)
+static int get_errno_from_oserror(int oserr)
{
if (!oserr)
return 0;
// Check the table for the Windows OS error code
- for (const struct errentry &entry : errtable)
+ for (const struct errentry& entry : errtable)
{
if (oserr == entry.oserr)
{
- _set_errno(entry.errcode);
- return -1;
+ return entry.errcode;
}
}
+ return EINVAL;
+}
- _set_errno(EINVAL);
+static int set_errno_from_oserror(unsigned long oserr)
+{
+ _set_errno(get_errno_from_oserror(oserr));
return -1;
}
@@ -136,69 +135,8 @@ std::string strerr(int errn)
return buffer;
}
-inline bool is_slash(wchar_t const c)
-{
- return c == L'\\' || c == L'/';
-}
-
-static std::wstring utf8path_to_wstring(const std::string& utf8path)
-{
- if (utf8path.size() >= MAX_PATH)
- {
- // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names
- std::wstring utf16path = L"\\\\?\\" + ll_convert<std::wstring>(utf8path);
- // We need to make sure that the path does not contain forward slashes as above
- // prefix does bypass the path normalization that replaces slashes with backslashes
- // before passing the path to kernel mode APIs
- std::replace(utf16path.begin(), utf16path.end(), L'/', L'\\');
- return utf16path;
- }
- return ll_convert<std::wstring>(utf8path);
-}
-
-static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false)
-{
- unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS;
- if (dontFollowSymLink)
- {
- flags |= FILE_FLAG_OPEN_REPARSE_POINT;
- }
- HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES,
- FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- nullptr, OPEN_EXISTING, flags, nullptr);
- if (file_handle != INVALID_HANDLE_VALUE)
- {
- FILE_ATTRIBUTE_TAG_INFO attribute_info;
- if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info)))
- {
- // A volume path alone (only drive letter) is not recognized as directory while it technically is
- bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
- (iswalpha(utf16path[0]) && utf16path[1] == ':' &&
- (!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3])));
- unsigned short st_mode = is_directory ? S_IFDIR :
- (attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG);
- st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;
- // we do not try to guess executable flag
-
- // propagate user bits to group/other fields:
- st_mode |= (st_mode & 0700) >> 3;
- st_mode |= (st_mode & 0700) >> 6;
-
- CloseHandle(file_handle);
- return st_mode;
- }
- }
- // Retrieve last error and set errno before calling CloseHandle()
- set_errno_from_oserror(GetLastError());
-
- if (file_handle != INVALID_HANDLE_VALUE)
- {
- CloseHandle(file_handle);
- }
- return 0;
-}
-
#else
+
// On Posix we want to call strerror_r(), but alarmingly, there are two
// different variants. The one that returns int always populates the passed
// buffer (except in case of error), whereas the other one always returns a
@@ -245,9 +183,50 @@ std::string strerr(int errn)
return message_from(errn, buffer, sizeof(buffer),
strerror_r(errn, buffer, sizeof(buffer)));
}
+
#endif // ! LL_WINDOWS
-static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0)
+#if LL_WINDOWS && 0 // turn on to debug file-locking problems
+#define PROCESS_LOCKING_CHECK 1
+static void find_locking_process(const std::string& filename)
+{
+ // Only do any of this stuff (before LL_ENDL) if it will be logged.
+ LL_DEBUGS("LLFile") << "";
+ // wrong way
+ std::string TEMP = LLFile::tmpdir();
+ if (TEMP.empty())
+ {
+ LL_CONT << "No $TEMP, not running 'handle'";
+ }
+ else
+ {
+ std::string tf(TEMP);
+ tf += "handle.tmp";
+ // http://technet.microsoft.com/en-us/sysinternals/bb896655
+ std::string cmd(STRINGIZE("handle \"" << filename
+ // "openfiles /query /v | fgrep -i \"" << filename
+ << "\" > \"" << tf << '"'));
+ LL_CONT << cmd;
+ if (system(cmd.c_str()) != 0)
+ {
+ LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655";
+ }
+ else
+ {
+ std::ifstream inf(tf);
+ std::string line;
+ while (std::getline(inf, line))
+ {
+ LL_CONT << '\n' << line;
+ }
+ }
+ LLFile::remove(tf);
+ }
+ LL_CONT << LL_ENDL;
+}
+#endif // LL_WINDOWS hack to identify processes holding file open
+
+static int warnif(std::string_view desc, const std::filesystem::path& filename, int rc, int suppress_warning = 0)
{
if (rc < 0)
{
@@ -256,322 +235,840 @@ static int warnif(const std::string& desc, const std::string& filename, int rc,
// For certain operations, a particular errno value might be
// acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit
- // EEXIST. Don't warn if caller explicitly says this errno is okay.
- if (errn != accept)
+ // EEXIST. Don't log a warning if caller explicitly says this errno is okay.
+ if (errn != suppress_warning)
{
- LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename
- << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL;
+ LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL;
}
-#if 0 && LL_WINDOWS // turn on to debug file-locking problems
+#if PROCESS_LOCKING_CHECK
// If the problem is "Permission denied," maybe it's because another
// process has the file open. Try to find out.
- if (errn == EACCES) // *not* EPERM
+ if (errn == EACCES) // *not* EPERM
{
- // Only do any of this stuff (before LL_ENDL) if it will be logged.
- LL_DEBUGS("LLFile") << empty;
- // would be nice to use LLDir for this, but dependency goes the
- // wrong way
- const char* TEMP = LLFile::tmpdir();
- if (! (TEMP && *TEMP))
- {
- LL_CONT << "No $TEMP, not running 'handle'";
- }
- else
- {
- std::string tf(TEMP);
- tf += "\\handle.tmp";
- // http://technet.microsoft.com/en-us/sysinternals/bb896655
- std::string cmd(STRINGIZE("handle \"" << filename
- // "openfiles /query /v | fgrep -i \"" << filename
- << "\" > \"" << tf << '"'));
- LL_CONT << cmd;
- if (system(cmd.c_str()) != 0)
- {
- LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655";
- }
- else
- {
- std::ifstream inf(tf);
- std::string line;
- while (std::getline(inf, line))
- {
- LL_CONT << '\n' << line;
- }
- }
- LLFile::remove(tf);
- }
- LL_CONT << LL_ENDL;
+ find_locking_process(filename);
}
-#endif // LL_WINDOWS hack to identify processes holding file open
+#endif
}
return rc;
}
-// static
-int LLFile::mkdir(const std::string& dirname, int perms)
+static int warnif(std::string_view desc, const std::filesystem::path& filename, const std::error_code& ec, int suppress_warning = 0)
{
- // We often use mkdir() to ensure the existence of a directory that might
- // already exist. There is no known case in which we want to call out as
- // an error the requested directory already existing.
+ if (ec)
+ {
+ // get Posix errno from the std::error_code so we can compare it to the suppress_warning parameter
+ // to see when a caller wants us to not generate a warning for a particular error code
#if LL_WINDOWS
- // permissions are ignored on Windows
- int rc = 0;
- std::wstring utf16dirname = utf8path_to_wstring(dirname);
- if (!CreateDirectoryW(utf16dirname.c_str(), nullptr))
+ int errn = get_errno_from_oserror(ec.value());
+#else
+ int errn = ec.value();
+#endif
+ // For certain operations, a particular errno value might be acceptable
+ // Don't warn if caller explicitly says this errno is okay.
+ if (errn != suppress_warning)
+ {
+ LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << ec.message() << LL_ENDL;
+ }
+#if PROCESS_LOCKING_CHECK
+ // Try to detect locked files by other processes
+ if (ec.value() == ERROR_SHARING_VIOLATION || ec.value() == ERROR_LOCK_VIOLATION)
+ {
+ find_locking_process(filename);
+ }
+#endif
+ return -1;
+ }
+ return 0;
+}
+
+#if LL_WINDOWS
+
+inline int set_ec_from_system_error(std::error_code& ec, DWORD error)
+{
+ ec.assign(error, std::system_category());
+ return -1;
+}
+
+static int set_ec_from_system_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, GetLastError());
+}
+
+inline int set_ec_to_parameter_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, ERROR_INVALID_PARAMETER);
+}
+
+inline int set_ec_to_outofmemory_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, ERROR_NOT_ENOUGH_MEMORY);
+}
+
+inline DWORD decode_access_mode(std::ios_base::openmode omode)
+{
+ switch (omode & (LLFile::in | LLFile::out))
{
- // Only treat other errors than an already existing file as a real error
- unsigned long oserr = GetLastError();
- if (oserr != ERROR_ALREADY_EXISTS)
+ case LLFile::in:
+ return GENERIC_READ;
+ case LLFile::out:
+ return GENERIC_WRITE;
+ case static_cast<std::ios_base::openmode>(LLFile::in | LLFile::out):
+ return GENERIC_READ | GENERIC_WRITE;
+ }
+ if (omode & LLFile::app)
+ {
+ return GENERIC_WRITE;
+ }
+ return 0;
+}
+
+inline DWORD decode_open_create_flags(std::ios_base::openmode omode)
+{
+ if (omode & LLFile::noreplace)
+ {
+ return CREATE_NEW; // create if it does not exist, otherwise fail
+ }
+ if (omode & LLFile::trunc)
+ {
+ if (!(omode & LLFile::out))
{
- rc = set_errno_from_oserror(oserr);
+ return TRUNCATE_EXISTING; // open and truncate if it exists, otherwise fail
}
+ return CREATE_ALWAYS; // open and truncate if it exists, otherwise create it
}
+ if (!(omode & LLFile::out))
+ {
+ return OPEN_EXISTING; // open if it exists, otherwise fail
+ }
+ // LLFile::app or (LLFile::out and (!LLFile::trunc or !LLFile::noreplace))
+ return OPEN_ALWAYS; // open if it exists, otherwise create it
+}
+
+inline DWORD decode_share_mode(int omode)
+{
+ if (omode & LLFile::exclusive)
+ {
+ return 0; // allow no other access
+ }
+ if (omode & LLFile::shared)
+ {
+ return FILE_SHARE_READ; // allow read access
+ }
+ return FILE_SHARE_READ | FILE_SHARE_WRITE; // allow read and write access to others
+}
+
+inline DWORD decode_attributes(std::ios_base::openmode omode, int perm)
+{
+ return (perm & S_IWRITE) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_READONLY;
+}
+
+// Under Windows the values for the std::ios_base::seekdir constants match the according FILE_BEGIN
+// and other constants but we do a programmatic translation for now to be sure
+static DWORD seek_mode_from_dir(std::ios_base::seekdir seekdir)
+{
+ switch (seekdir)
+ {
+ case LLFile::beg:
+ return FILE_BEGIN;
+ case LLFile::cur:
+ return FILE_CURRENT;
+ case LLFile::end:
+ return FILE_END;
+ }
+ return FILE_BEGIN;
+}
+
#else
- int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
- if (rc < 0 && errno == EEXIST)
+
+inline int set_ec_from_system_error(std::error_code& ec, int error)
+{
+ ec.assign(error, std::system_category());
+ return -1;
+}
+
+static int set_ec_from_system_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, errno);
+}
+
+inline int set_ec_to_parameter_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, EINVAL);
+}
+
+inline int set_ec_to_outofmemory_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, ENOMEM);
+}
+
+inline int decode_access_mode(std::ios_base::openmode omode)
+{
+ if (omode & LLFile::out)
{
- // this is not the error you want, move along
- return 0;
+ if (omode & LLFile::in)
+ {
+ return O_RDWR;
+ }
+ return O_WRONLY;
+ }
+ return O_RDONLY;
+
+ /*switch (omode & (LLFile::in | LLFile::out))
+ {
+ case LLFile::out:
+ return O_WRONLY;
+ case static_cast<std::ios_base::openmode>(LLFile::in | LLFile::out):
+ return O_RDWR;
}
+ return O_RDONLY;*/
+}
+
+inline int decode_open_mode(std::ios_base::openmode omode)
+{
+ int flags = O_CREAT | decode_access_mode(omode);
+ if (omode & LLFile::app)
+ {
+ flags |= O_APPEND;
+ }
+ if (omode & LLFile::trunc)
+ {
+ flags |= O_TRUNC;
+ }
+ if (omode & LLFile::binary)
+ {
+ // Not a thing under *nix
+ }
+ if (omode & LLFile::noreplace)
+ {
+ flags |= O_EXCL;
+ }
+ return flags;
+}
+
+inline int decode_lock_mode(std::ios_base::openmode omode)
+{
+ int lmode = omode & LLFile::noblock ? LOCK_NB : 0;
+ if (omode & LLFile::lock_mask)
+ {
+ if (omode & LLFile::exclusive)
+ {
+ return lmode | LOCK_EX;
+ }
+ return lmode | LOCK_SH;
+ }
+ return lmode | LOCK_UN;
+}
+
+// Under Linux and Mac the values for the std::ios_base::seekdir constants match the according SEEK_SET
+// and other constants but we do a programmatic translation for now to be sure
+inline int seek_mode_from_dir(std::ios_base::seekdir seekdir)
+{
+ switch (seekdir)
+ {
+ case LLFile::beg:
+ return SEEK_SET;
+ case LLFile::cur:
+ return SEEK_CUR;
+ case LLFile::end:
+ return SEEK_END;
+ }
+ return SEEK_SET;
+}
+
#endif
- // anything else might be a problem
- return warnif("mkdir", dirname, rc);
+
+inline int clear_error(std::error_code& ec)
+{
+ ec.clear();
+ return 0;
}
-// static
-int LLFile::rmdir(const std::string& dirname, int suppress_error)
+inline bool are_open_mode_flags_invalid(std::ios_base::openmode omode)
+{
+ // at least one of input or output needs to be specified
+ if (!(omode & (LLFile::in | LLFile::out)))
+ {
+ return true;
+ }
+ // output must be possible for any of the extra options
+ if (!(omode & LLFile::out) && (omode & (LLFile::trunc | LLFile::app | LLFile::noreplace)))
+ {
+ return true;
+ }
+ // invalid combination, mutually exclusive
+ if ((omode & LLFile::app) && (omode & (LLFile::trunc | LLFile::noreplace)))
+ {
+ return true;
+ }
+ return false;
+}
+
+//----------------------------------------------------------------------------------------
+// class member functions
+//----------------------------------------------------------------------------------------
+int LLFile::open(const std::filesystem::path& file_path, std::ios_base::openmode omode, std::error_code& ec, int perm)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ close(ec);
+ if (are_open_mode_flags_invalid(omode))
+ {
+ return set_ec_to_parameter_error(ec);
+ }
#if LL_WINDOWS
- std::wstring utf16dirname = utf8path_to_wstring(dirname);
- int rc = _wrmdir(utf16dirname.c_str());
+ DWORD access = decode_access_mode(omode),
+ share = decode_share_mode(omode),
+ create = decode_open_create_flags(omode),
+ attributes = decode_attributes(omode, perm);
+
+ mHandle = CreateFileW(file_path.native().c_str(), access, share, nullptr, create, attributes, nullptr);
+ // The dwShareMode = share parameter takes care of locking the file for other processes if indicated,
+ // no need to do anything else for file locking here
#else
- int rc = ::rmdir(dirname.c_str());
+ int oflags = decode_open_mode(omode);
+ int lmode = omode & LLFile::lock_mask;
+ mHandle = ::open(file_path.native().c_str(), oflags, perm);
+ if (mHandle != InvalidHandle && lmode && lock(lmode | LLFile::noblock, ec) != 0)
+ {
+ close();
+ return -1;
+ }
#endif
- return warnif("rmdir", dirname, rc, suppress_error);
+ if (mHandle == InvalidHandle)
+ {
+ return set_ec_from_system_error(ec);
+ }
+
+ if (omode & LLFile::ate && seek(0, LLFile::end, ec) != 0)
+ {
+ close();
+ return -1;
+ }
+ mOpen = omode;
+ return clear_error(ec);
}
-// static
-LLFILE* LLFile::fopen(const std::string& filename, const char* mode)
+S64 LLFile::size(std::error_code& ec)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
#if LL_WINDOWS
- std::wstring utf16filename = utf8path_to_wstring(filename);
- std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
- return _wfopen(utf16filename.c_str(), utf16mode.c_str());
+ LARGE_INTEGER value = { 0 };
+ if (GetFileSizeEx(mHandle, &value))
+ {
+ clear_error(ec);
+ return value.QuadPart;
+ }
#else
- return ::fopen(filename.c_str(),mode);
+ struct stat statval;
+ if (fstat(mHandle, &statval) == 0)
+ {
+ clear_error(ec);
+ return statval.st_size;
+ }
#endif
+ set_ec_from_system_error(ec);
+ return 0;
}
-// static
-int LLFile::close(LLFILE * file)
+S64 LLFile::tell(std::error_code& ec)
{
- int ret_value = 0;
- if (file)
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+#if LL_WINDOWS
+ LARGE_INTEGER value = { 0 };
+ if (SetFilePointerEx(mHandle, value, &value, FILE_CURRENT))
{
- ret_value = fclose(file);
+ clear_error(ec);
+ return value.QuadPart;
}
- return ret_value;
+#else
+ off_t offset = lseek(mHandle, 0, SEEK_CUR);
+ if (offset != -1)
+ {
+ clear_error(ec);
+ return offset;
+ }
+#endif
+ return set_ec_from_system_error(ec);
}
-// static
-std::string LLFile::getContents(const std::string& filename)
+int LLFile::seek(S64 pos, std::error_code& ec)
{
- LLFILE* fp = LLFile::fopen(filename, "rb");
- if (fp)
+ return seek(pos, LLFile::beg, ec);
+}
+
+int LLFile::seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ S64 newOffset = 0;
+#if LL_WINDOWS
+ DWORD seekdir = seek_mode_from_dir(dir);
+ LARGE_INTEGER value;
+ value.QuadPart = offset;
+ if (SetFilePointerEx(mHandle, value, (PLARGE_INTEGER)&newOffset, seekdir))
+#else
+ newOffset = lseek(mHandle, offset, seek_mode_from_dir(dir));
+ if (newOffset != -1)
+#endif
{
- fseek(fp, 0, SEEK_END);
- U32 length = ftell(fp);
- fseek(fp, 0, SEEK_SET);
+ return clear_error(ec);
+ }
+ return set_ec_from_system_error(ec);
+}
- std::vector<char> buffer(length);
- size_t nread = fread(buffer.data(), 1, length, fp);
- fclose(fp);
+#if LL_WINDOWS
+inline DWORD next_buffer_size(S64 nbytes)
+{
+ return nbytes > 0x80000000 ? 0x80000000 : (DWORD)nbytes;
+}
+#endif
- return std::string(buffer.data(), nread);
+S64 LLFile::read(void* buffer, S64 nbytes, std::error_code& ec)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ if (nbytes == 0)
+ {
+ // Nothing to do
+ return clear_error(ec);
}
+ else if (!buffer || nbytes < 0)
+ {
+ return set_ec_to_parameter_error(ec);
+ }
+#if LL_WINDOWS
+ S64 totalBytes = 0;
+ char *ptr = (char*)buffer;
+ DWORD bytesRead, bytesToRead = next_buffer_size(nbytes);
- return LLStringUtil::null;
+ // Read in chunks to support >4GB which the S64 nbytes value makes possible
+ while (ReadFile(mHandle, ptr, bytesToRead, &bytesRead, nullptr))
+ {
+ totalBytes += bytesRead;
+ if (nbytes <= totalBytes || // requested amount read
+ bytesRead < bytesToRead) // ReadFile encountered eof
+ {
+ clear_error(ec);
+ return totalBytes;
+ }
+ ptr += bytesRead;
+ bytesToRead = next_buffer_size(nbytes - totalBytes);
+ }
+#else
+ ssize_t bytesRead = ::read(mHandle, buffer, nbytes);
+ if (bytesRead != -1)
+ {
+ clear_error(ec);
+ return bytesRead;
+ }
+#endif
+ return set_ec_from_system_error(ec);
}
-// static
-int LLFile::remove(const std::string& filename, int suppress_error)
+S64 LLFile::write(const void* buffer, S64 nbytes, std::error_code& ec)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ if (nbytes == 0)
+ {
+ // Nothing to do here
+ return clear_error(ec);
+ }
+ else if (!buffer || nbytes < 0)
+ {
+ return set_ec_to_parameter_error(ec);
+ }
#if LL_WINDOWS
- // Posix remove() works on both files and directories although on Windows
- // remove() and its wide char variant _wremove() only removes files just
- // as its siblings unlink() and _wunlink().
- // If we really only want to support files we should instead use
- // unlink() in the non-Windows part below too
- int rc = -1;
- std::wstring utf16filename = utf8path_to_wstring(filename);
- unsigned short st_mode = get_fileattr(utf16filename);
- if (S_ISDIR(st_mode))
+ // If this was opened in append mode, we emulate it on Windows
+ if (mOpen & LLFile::app && seek(0, LLFile::end, ec) != 0)
{
- rc = _wrmdir(utf16filename.c_str());
+ return -1;
}
- else if (S_ISREG(st_mode))
+
+ S64 totalBytes = 0;
+ char* ptr = (char*)buffer;
+ DWORD bytesWritten, bytesToWrite = next_buffer_size(nbytes);
+
+ // Write in chunks to support >4GB which the S64 nbytes value makes possible
+ while (WriteFile(mHandle, ptr, bytesToWrite, &bytesWritten, nullptr))
{
- rc = _wunlink(utf16filename.c_str());
+ totalBytes += bytesWritten;
+ if (nbytes <= totalBytes)
+ {
+ clear_error(ec);
+ return totalBytes;
+ }
+ ptr += bytesWritten;
+ bytesToWrite = next_buffer_size(nbytes - totalBytes);
}
- else if (st_mode)
+#else
+ ssize_t bytesWritten = ::write(mHandle, buffer, nbytes);
+ if (bytesWritten != -1)
{
- // it is something else than a file or directory
- // this should not really happen as long as we do not allow for symlink
- // detection in the optional parameter to get_fileattr()
- rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER);
+ clear_error(ec);
+ return bytesWritten;
+ }
+#endif
+ return set_ec_from_system_error(ec);
+}
+
+S64 LLFile::printf(const char* fmt, ...)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ va_list args1;
+ va_start(args1, fmt);
+ va_list args2;
+ va_copy(args2, args1);
+ int length = vsnprintf(nullptr, 0, fmt, args1);
+ va_end(args1);
+ if (length < 0)
+ {
+ va_end(args2);
+ return -1;
+ }
+ void* buffer = malloc(length + 1);
+ if (!buffer)
+ {
+ va_end(args2);
+ return -1;
+ }
+ length = vsnprintf((char*)buffer, length + 1, fmt, args2);
+ va_end(args2);
+ std::error_code ec;
+ S64 written = write(buffer, length, ec);
+ free(buffer);
+ return written;
+}
+
+int LLFile::lock(int mode, std::error_code& ec)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+#if LL_WINDOWS
+ if (!(mode & LLFile::lock_mask))
+ {
+ if (UnlockFile(mHandle, 0, 0, MAXDWORD, MAXDWORD))
+ {
+ return clear_error(ec);
+ }
}
else
{
- // get_fileattr() failed and already set errno, preserve it for correct error reporting
+ OVERLAPPED overlapped = { 0 };
+ DWORD flags = (mode & LLFile::noblock) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
+ if (mode & LLFile::exclusive)
+ {
+ flags |= LOCKFILE_EXCLUSIVE_LOCK;
+ }
+ // We lock the maximum range, since flock only supports locking the entire file too
+ if (LockFileEx(mHandle, flags, 0, MAXDWORD, MAXDWORD, &overlapped))
+ {
+ return clear_error(ec);
+ }
}
#else
- int rc = ::remove(filename.c_str());
+ if (flock(mHandle, decode_lock_mode(static_cast<std::ios_base::openmode>(mode))) == 0)
+ {
+ return clear_error(ec);
+ }
#endif
- return warnif("remove", filename, rc, suppress_error);
+ return set_ec_from_system_error(ec);
}
+int LLFile::close(std::error_code& ec)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ if (mHandle != InvalidHandle)
+ {
+ llfile_handle_t handle = InvalidHandle;
+ std::swap(handle, mHandle);
+#if LL_WINDOWS
+ if (!CloseHandle(handle))
+#else
+ if (::close(handle))
+#endif
+ {
+ return set_ec_from_system_error(ec);
+ }
+ }
+ return clear_error(ec);
+}
+
+int LLFile::close()
+{
+ std::error_code ec;
+ return close(ec);
+}
+
+//----------------------------------------------------------------------------------------
+// static member functions
+//----------------------------------------------------------------------------------------
+
// static
-int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error)
+LLFILE* LLFile::fopen(const std::filesystem::path& file_path, const fopen_flags_t* mode, int lmode)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ LLFILE* file;
#if LL_WINDOWS
- // Posix rename() will gladly overwrite a file at newname if it exists, the Windows
- // rename(), respectively _wrename(), will bark on that. Instead call directly the Windows
- // API MoveFileEx() and use its flags to specify that overwrite is allowed.
- std::wstring utf16filename = utf8path_to_wstring(filename);
- std::wstring utf16newname = utf8path_to_wstring(newname);
- int rc = 0;
- if (!MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
+ int shflag = _SH_DENYNO;
+ switch (lmode)
{
- rc = set_errno_from_oserror(GetLastError());
+ case LLFile::exclusive:
+ shflag = _SH_DENYRW;
+ break;
+ case LLFile::shared:
+ shflag = _SH_DENYWR;
+ break;
}
+ file = _wfsopen(file_path.native().c_str(), mode, shflag);
#else
- int rc = ::rename(filename.c_str(),newname.c_str());
+ file = ::fopen(file_path.native().c_str(), mode);
+ if (file && (lmode & (LLFile::lock_mask)))
+ {
+ // Rather fail on a sharing conflict than block
+ if (flock(fileno(file), decode_lock_mode(static_cast<std::ios_base::openmode>(lmode | LLFile::noblock))))
+ {
+ ::fclose(file);
+ file = nullptr;
+ }
+ }
#endif
- return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error);
+ return file;
}
-// Make this a define rather than using magic numbers multiple times in the code
-#define LLFILE_COPY_BUFFER_SIZE 16384
+// static
+int LLFile::close(LLFILE* file)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ int ret_value = 0;
+ if (file)
+ {
+ ret_value = ::fclose(file);
+ }
+ return ret_value;
+}
// static
-bool LLFile::copy(const std::string& from, const std::string& to)
+std::string LLFile::getContents(const std::filesystem::path& file_path, std::error_code& ec)
{
- bool copied = false;
- LLFILE* in = LLFile::fopen(from, "rb");
- if (in)
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::string buffer;
+ LLFile file(file_path, LLFile::in | LLFile::binary, ec);
+ if (file)
{
- LLFILE* out = LLFile::fopen(to, "wb");
- if (out)
+ S64 length = file.size(ec);
+ if (!ec && length > 0)
{
- char buf[LLFILE_COPY_BUFFER_SIZE];
- size_t readbytes;
- bool write_ok = true;
- while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in)))
- {
- if (fwrite(buf, 1, readbytes, out) != readbytes)
- {
- LL_WARNS("LLFile") << "Short write" << LL_ENDL;
- write_ok = false;
- }
- }
- if ( write_ok )
+ buffer = std::string(length, 0);
+ file.read(&buffer[0], length, ec);
+ if (ec)
{
- copied = true;
+ buffer.clear();
}
- fclose(out);
}
- fclose(in);
}
- return copied;
+ return buffer;
}
// static
-int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error)
+int LLFile::mkdir(const std::filesystem::path& file_path)
{
-#if LL_WINDOWS
- std::wstring utf16filename = utf8path_to_wstring(filename);
- int rc = _wstat64(utf16filename.c_str(), filestatus);
-#else
- int rc = ::stat(filename.c_str(), filestatus);
-#endif
- return warnif("stat", filename, rc, suppress_error);
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::error_code ec;
+ // We often use mkdir() to ensure the existence of a directory that might
+ // already exist. There is no known case in which we want to call out as
+ // an error the requested directory already existing.
+ std::filesystem::create_directory(file_path, ec);
+ // The return value is only true if the directory was actually created.
+ // But if it already existed, ec still indicates success.
+ return warnif("mkdir", file_path, ec);
}
// static
-unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error)
+int LLFile::remove(const std::filesystem::path& file_path, int suppress_warning)
{
-#if LL_WINDOWS
- // _wstat64() is a bit heavyweight on Windows, use a more lightweight API
- // to just get the attributes
- int rc = -1;
- std::wstring utf16filename = utf8path_to_wstring(filename);
- unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink);
- if (st_mode)
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::error_code ec;
+ std::filesystem::remove(file_path, ec);
+ return warnif("remove", file_path, ec, suppress_warning);
+}
+
+// static
+int LLFile::rename(const std::filesystem::path& file_path, const std::filesystem::path& new_path, int suppress_warning)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::error_code ec;
+ std::filesystem::rename(file_path, new_path, ec);
+ return warnif("rename", file_path, ec, suppress_warning);
+}
+
+// static
+S64 LLFile::read(const std::filesystem::path& file_path, void* buf, S64 offset, S64 nbytes, std::error_code& ec)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ // if number of bytes is 0 or less there is nothing to do here
+ if (nbytes <= 0)
{
- return st_mode;
+ clear_error(ec);
+ return 0;
}
-#else
- llstat filestatus;
- int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus);
- if (rc == 0)
+
+ if (!buf || offset < 0)
{
- return filestatus.st_mode;
+ set_ec_to_parameter_error(ec);
}
-#endif
- warnif("getattr", filename, rc, suppress_error);
- return 0;
+ else
+ {
+ std::ios_base::openmode omode = LLFile::in | LLFile::binary;
+
+ LLFile file(file_path, omode, ec);
+ if (!ec && (bool)file)
+ {
+ if (offset > 0)
+ {
+ file.seek(offset, ec);
+ }
+ // else (offset == 0) file was just opened and should already be at 0.
+ if (!ec)
+ {
+ S64 bytes_read = file.read(buf, nbytes, ec);
+ if (!ec)
+ {
+ return bytes_read;
+ }
+ }
+ }
+ }
+ return warnif("read from file failed", file_path, ec);
}
// static
-bool LLFile::isdir(const std::string& filename)
+S64 LLFile::write(const std::filesystem::path& file_path, const void* buf, S64 offset, S64 nbytes, std::error_code& ec)
{
- return S_ISDIR(getattr(filename));
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ // if number of bytes is 0 or less there is nothing to do here
+ if (nbytes <= 0)
+ {
+ clear_error(ec);
+ return 0;
+ }
+
+ if (!buf)
+ {
+ set_ec_to_parameter_error(ec);
+ }
+ else
+ {
+ std::ios_base::openmode omode = LLFile::out | LLFile::binary;
+ if (offset < 0)
+ {
+ omode |= LLFile::app;
+ }
+
+ LLFile file(file_path, omode, ec);
+ if (!ec && (bool)file)
+ {
+ if (offset > 0)
+ {
+ file.seek(offset, ec);
+ }
+ // else (offset == 0) we are not appending, file was just opened and should already be at 0.
+ if (!ec)
+ {
+ S64 bytes_written = file.write(buf, nbytes, ec);
+ if (!ec)
+ {
+ return bytes_written;
+ }
+ }
+ }
+ }
+ return warnif("write to file failed", file_path, ec);
}
// static
-bool LLFile::isfile(const std::string& filename)
+bool LLFile::copy(const std::filesystem::path& source_path, const std::filesystem::path& target_path, std::filesystem::copy_options options, std::error_code& ec)
{
- return S_ISREG(getattr(filename));
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ bool copied = std::filesystem::copy_file(source_path, target_path, options, ec);
+ if (!copied)
+ {
+ warnif(STRINGIZE("copy failed, to '" << target_path << "' from"), source_path, ec);
+ }
+ return copied;
}
// static
-bool LLFile::islink(const std::string& filename)
+int LLFile::stat(const std::filesystem::path& file_path, llstat* filestatus, const char *fname, int suppress_warning)
{
- return S_ISLNK(getattr(filename, true));
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+#if LL_WINDOWS
+ int rc = _wstat64(file_path.native().c_str(), filestatus);
+#else
+ int rc = ::stat(file_path.native().c_str(), filestatus);
+#endif
+ return warnif(fname ? fname : "stat", file_path, rc, suppress_warning);
}
// static
-const char *LLFile::tmpdir()
+S64 LLFile::size(const std::filesystem::path& file_path, int suppress_warning)
{
- static std::string utf8path;
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::error_code ec;
+ std::intmax_t size = static_cast<std::intmax_t>(std::filesystem::file_size(file_path, ec));
+ if (ec)
+ {
+ warnif("size", file_path, ec, suppress_warning);
+ return 0;
+ }
+ return size;
+}
- if (utf8path.empty())
+// static
+std::filesystem::file_status LLFile::getStatus(const std::filesystem::path& file_path, bool dontFollowSymLink, int suppress_warning)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::error_code ec;
+ std::filesystem::file_status status;
+ if (dontFollowSymLink)
{
- char sep;
-#if LL_WINDOWS
- sep = '\\';
+ status = std::filesystem::symlink_status(file_path, ec);
+ }
+ else
+ {
+ status = std::filesystem::status(file_path, ec);
+ }
+ warnif("getStatus()", file_path, ec, suppress_warning);
+ return status;
+}
- std::vector<wchar_t> utf16path(MAX_PATH + 1);
- GetTempPathW(static_cast<DWORD>(utf16path.size()), &utf16path[0]);
- utf8path = ll_convert_wide_to_string(&utf16path[0]);
+// static
+const std::string& LLFile::tmpdir()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ static std::string temppath;
+ if (temppath.empty())
+ {
+#if LL_WINDOWS
+ temppath = ll_convert<std::string>(std::filesystem::temp_directory_path().native());
+ char sep = '\\';
#else
- sep = '/';
-
- utf8path = LLStringUtil::getenv("TMPDIR", "/tmp/");
+ temppath = std::filesystem::temp_directory_path().string();
+ char sep = '/';
#endif
- if (utf8path[utf8path.size() - 1] != sep)
+ if (temppath[temppath.size() - 1] != sep)
{
- utf8path += sep;
+ temppath += sep;
}
}
- return utf8path.c_str();
+
+ return temppath;
}
#if LL_WINDOWS
/************** input file stream ********************************/
-llifstream::llifstream() {}
+// explicit
+llifstream::llifstream(const char* _Filename, ios_base::openmode _Mode) :
+ std::ifstream(ll_convert<std::wstring>(_Filename).c_str(), _Mode | ios_base::in)
+{
+}
// explicit
llifstream::llifstream(const std::string& _Filename, ios_base::openmode _Mode):
@@ -580,17 +1077,37 @@ llifstream::llifstream(const std::string& _Filename, ios_base::openmode _Mode):
{
}
+// explicit
+llifstream::llifstream(const std::filesystem::path& _Filepath, ios_base::openmode _Mode) :
+ std::ifstream(_Filepath, _Mode | ios_base::in)
+{
+}
+
+void llifstream::open(const char* _Filename, ios_base::openmode _Mode)
+{
+ std::ifstream::open(ll_convert<std::wstring>(_Filename).c_str(),
+ _Mode | ios_base::in);
+}
+
void llifstream::open(const std::string& _Filename, ios_base::openmode _Mode)
{
std::ifstream::open(ll_convert<std::wstring>(_Filename).c_str(),
_Mode | ios_base::in);
}
+void llifstream::open(const std::filesystem::path& _Filepath, ios_base::openmode _Mode)
+{
+ std::ifstream::open(_Filepath,
+ _Mode | ios_base::in);
+}
/************** output file stream ********************************/
-
-llofstream::llofstream() {}
+// explicit
+llofstream::llofstream(const char* _Filename, ios_base::openmode _Mode) :
+ std::ofstream(ll_convert<std::wstring>(_Filename).c_str(), _Mode | ios_base::out)
+{
+}
// explicit
llofstream::llofstream(const std::string& _Filename, ios_base::openmode _Mode):
@@ -599,36 +1116,27 @@ llofstream::llofstream(const std::string& _Filename, ios_base::openmode _Mode):
{
}
-void llofstream::open(const std::string& _Filename, ios_base::openmode _Mode)
+// explicit
+llofstream::llofstream(const std::filesystem::path& _Filepath, ios_base::openmode _Mode) :
+ std::ofstream(_Filepath, _Mode | ios_base::out)
{
- std::ofstream::open(ll_convert<std::wstring>( _Filename ).c_str(),
- _Mode | ios_base::out);
}
-/************** helper functions ********************************/
+void llofstream::open(const char* _Filename, ios_base::openmode _Mode)
+{
+ std::ofstream::open(ll_convert<std::wstring>(_Filename).c_str(), _Mode | ios_base::out);
+}
-std::streamsize llifstream_size(llifstream& ifstr)
+void llofstream::open(const std::string& _Filename, ios_base::openmode _Mode)
{
- if(!ifstr.is_open()) return 0;
- std::streampos pos_old = ifstr.tellg();
- ifstr.seekg(0, ios_base::beg);
- std::streampos pos_beg = ifstr.tellg();
- ifstr.seekg(0, ios_base::end);
- std::streampos pos_end = ifstr.tellg();
- ifstr.seekg(pos_old, ios_base::beg);
- return pos_end - pos_beg;
+ std::ofstream::open(ll_convert<std::wstring>( _Filename ).c_str(),
+ _Mode | ios_base::out);
}
-std::streamsize llofstream_size(llofstream& ofstr)
+void llofstream::open(const std::filesystem::path& _Filepath, ios_base::openmode _Mode)
{
- if(!ofstr.is_open()) return 0;
- std::streampos pos_old = ofstr.tellp();
- ofstr.seekp(0, ios_base::beg);
- std::streampos pos_beg = ofstr.tellp();
- ofstr.seekp(0, ios_base::end);
- std::streampos pos_end = ofstr.tellp();
- ofstr.seekp(pos_old, ios_base::beg);
- return pos_end - pos_beg;
+ std::ofstream::open(_Filepath,
+ _Mode | ios_base::out);
}
#endif // LL_WINDOWS
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index 04a2946ac4..8de3f5c9b4 100644
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -35,133 +35,712 @@
* Attempts to mostly mirror the POSIX style IO functions.
*/
-typedef FILE LLFILE;
+#include "stdtypes.h"
#include <fstream>
+#include <filesystem>
#include <sys/stat.h>
#if LL_WINDOWS
+#include <windows.h>
// The Windows version of stat function and stat data structure are called _stat64
// We use _stat64 here to support 64-bit st_size and time_t values
-typedef struct _stat64 llstat;
+typedef struct _stat64 llstat;
#else
-typedef struct stat llstat;
#include <sys/types.h>
+typedef struct stat llstat;
#endif
-#ifndef S_ISREG
-# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
-#endif
-
-#ifndef S_ISDIR
-# define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
-#endif
+typedef FILE LLFILE;
-// Windows C runtime library does not define this and does not support symlink detection in the
-// stat functions but we do in our getattr() function
-#ifndef S_IFLNK
-#define S_IFLNK 0xA000 /* symlink */
-#endif
+#include "fsyspath.h"
+#include "llstring.h" // safe char* -> std::string conversion
-#ifndef S_ISLNK
-#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
+#if LL_WINDOWS
+#define LLFILE_MODE(flags) L##flags
+#else
+#define LLFILE_MODE(flags) flags
#endif
-#include "llstring.h" // safe char* -> std::string conversion
-
-/// LLFile is a class of static functions operating on paths
+/// This class provides a selection of functions to operate on files through names and
+/// a class implementation to represent a file for reading and writing to it
/// All the functions with a path string input take UTF8 path/filenames
-class LL_COMMON_API LLFile
+///
+/// @nosubgrouping
+///
+class LLFile
{
public:
+ // ================================================================================
+ /// @name Constants
+ ///
+ ///@{
+ /** These can be passed to the omode parameter of LLFile::open() and its constructor
+
+ This is similar to the openmode flags for std:fstream but not exactly the same
+ std::fstream open() does not allow to open a file for writing without either
+ forcing the file to be truncated on open or all write operations being always
+ appended to the end of the file or failing to open when the file does not exist.
+ But to allow implementing the LLAPRFile::writeEx() functionality we need to be
+ able to write at a random position to the file without truncating it on open.
+
+ any other combinations than listed here are not allowed and will cause an error
+
+ bin in out trunc app noreplace File exists File doesn't exist
+ ----------------------------------------------------------------------------------
+ - + - - - - Open at begin Failure to open
+ + + - - - - " "
+ - - + - - - " Create new
+ + - + - - - " "
+ - + + - - - " "
+ + + + - - - " "
+ ----------------------------------------------------------------------------------
+ - - + + - - Destroy contents Create new
+ + - + + - - " "
+ - + + + - - " "
+ + + + + - - " "
+ ----------------------------------------------------------------------------------
+ - - + x - + Failure to open Create new
+ + - + x - + " "
+ - + + x - + " "
+ + + + x - + " "
+ ----------------------------------------------------------------------------------
+ - - + - + - Write to end Create new
+ + - + - + - " "
+ - + + - + - " "
+ + + + - + - " "
+ ----------------------------------------------------------------------------------
+ */
+ static const std::ios_base::openmode app = static_cast<std::ios_base::openmode>(1 << 1); // append to end
+ static const std::ios_base::openmode ate = static_cast<std::ios_base::openmode>(1 << 2); // initialize to end
+ static const std::ios_base::openmode binary = static_cast<std::ios_base::openmode>(1 << 3); // binary mode
+ static const std::ios_base::openmode in = static_cast<std::ios_base::openmode>(1 << 4); // for reading
+ static const std::ios_base::openmode out = static_cast<std::ios_base::openmode>(1 << 5); // for writing // for writing and reading
+ static const std::ios_base::openmode trunc = static_cast<std::ios_base::openmode>(1 << 6); // truncate on open
+ static const std::ios_base::openmode noreplace = static_cast<std::ios_base::openmode>(1 << 7); // no replace if it exists
+
+ /// Additional optional flags to omode in open() and lmode in fopen() or lock()
+ /// to indicate which sort of lock if any to attempt to get
+ ///
+ /// NOTE: there is a fundamental difference between platforms.
+ /// On Windows this lock is mandatory as it is part of the API to open a file handle and other
+ /// processes can not avoid it. If a file was opened denying other processes read and/or write
+ /// access, trying to open the same file in another process with that access will fail.
+ /// On Mac and Linux it is only an advisory lock implemented through the flock() system call.
+ /// This means that any other application needs to also attempt to at least acquire a shared
+ /// lock on the file in order to notice that the file is actually already locked. It can
+ /// therefore not be used to prevent random other applications from accessing the file, but it
+ /// works for other viewer processes when they use either the LLFile::open() or LLFile::fopen()
+ /// functions with the appropriate lock flags to open a file.
+ static const std::ios_base::openmode exclusive = static_cast<std::ios_base::openmode>(1 << 16);
+ static const std::ios_base::openmode shared = static_cast<std::ios_base::openmode>(1 << 17);
+
+ /// Additional lmode flag to indicate to rather fail instead of blocking when trying
+ /// to acquire a lock with LLFile::lock()
+ static const std::ios_base::openmode noblock = static_cast<std::ios_base::openmode>(1 << 18);
+
+ /// The mask value for the lock mask bits
+ static const std::ios_base::openmode lock_mask = static_cast<std::ios_base::openmode>(exclusive | shared);
+
+ /// One of these can be passed to the dir parameter of LLFile::seek()
+ static const std::ios_base::seekdir beg = std::ios_base::beg;
+ static const std::ios_base::seekdir cur = std::ios_base::cur;
+ static const std::ios_base::seekdir end = std::ios_base::end;
+ ///@}
+
+ // ================================================================================
+ /// @name constructor/deconstructor
+ ///
+ ///@{
+ /// default constructor
+ LLFile() : mHandle(InvalidHandle) {}
+
+ /// no copy constructor
+ LLFile(const LLFile&) = delete;
+
+ /// move constructor
+ LLFile(LLFile&& other) noexcept
+ {
+ mHandle = other.mHandle;
+ other.mHandle = InvalidHandle;
+ }
+
+ /// constructor opening the file
+ explicit LLFile(const char* filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666) :
+ mHandle(InvalidHandle)
+ {
+ open(filename, omode, ec, perm);
+ }
+
+ explicit LLFile(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666) :
+ mHandle(InvalidHandle)
+ {
+ open(filename, omode, ec, perm);
+ }
+
+ explicit LLFile(const std::filesystem::path& file_path, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666) :
+ mHandle(InvalidHandle)
+ {
+ open(file_path, omode, ec, perm);
+ }
+
+ /// destructor always attempts to close the file
+ ~LLFile() { close(); }
+ ///@}
+
+ // ================================================================================
+ /// @name operators
+ ///
+ ///@{
+ /// copy assignment deleted
+ LLFile& operator=(const LLFile&) = delete;
+
+ /// move assignment
+ LLFile& operator=(LLFile&& other) noexcept
+ {
+ close();
+ std::swap(mHandle, other.mHandle);
+ return *this;
+ }
+
+ // detect whether the wrapped file descriptor/handle is open or not
+ explicit operator bool() const { return (mHandle != InvalidHandle); }
+ bool operator!() { return (mHandle == InvalidHandle); }
+ ///@}
+
+ /// ================================================================================
+ /// @name class member methods
+ ///
+ /// These methods provide read and write support as well as additional functionality to query the size of
+ /// the file, change the position of the current file pointer or query it.
+ ///
+ /// Most of these functions take as one of their parameters a std::error_code object which can be used to
+ /// determine in more detail what error occurred if required
+ ///@{
+
+ /// Open a file with the specific open mode flags
+ inline int open(const char* filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return open(file_path, omode, ec, perm);
+ }
+ inline int open(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return open(file_path, omode, ec, perm);
+ }
+ int open(const std::filesystem::path& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Determine the size of the opened file
+ S64 size(std::error_code& ec);
+ ///< @returns the number of bytes in the file or 0 on failure
+
+ /// Query the position of the current file pointer in the file
+ S64 tell(std::error_code& ec);
+ ///< @returns the absolute offset of the file pointer in bytes relative to the start of the file or -1 on failure
+
+ /// Move the file pointer to the specified absolute position relative to the start of the file
+ int seek(S64 pos, std::error_code& ec);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Move the file pointer to the specified position relative to dir
+ int seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Read the specified number of bytes into the buffer starting at the current file pointer
+ S64 read(void* buffer, S64 nbytes, std::error_code& ec);
+ ///< If the file ends before the requested amount of bytes could be read, the function succeeds and
+ /// returns the bytes up to the end of the file. The return value indicates the number of actually
+ /// read bytes and can be therefore smaller than the requested amount.
+ /// @returns the number of bytes read from the file or -1 on failure
+
+ /// Write the specified number of bytes to the file starting at the current file pointer
+ S64 write(const void* buffer, S64 nbytes, std::error_code& ec);
+ ///< @returns the number of bytes written to the file or -1 on failure
+
+ /// Write into the file starting at the current file pointer using printf style format and
+ /// additional optional parameters as specified in the fmt string
+ S64 printf(const char* fmt, ...);
+ ///< @returns the number of bytes written to the file or -1 on failure
+
+ /// Attempt to acquire or release a lock on the file
+ int lock(int lmode, std::error_code& ec);
+ ///< lmode can be one of LLFile::exclusive or LLFile::shared to acquire the according lock
+ /// or 0 to give up an earlier acquired lock. Adding LLFile::noblock together with one of
+ /// the lock requests will cause the function to fail if the lock can not be acquired,
+ /// otherwise the function will block until the lock can be acquired.
+ /// @returns 0 on success, -1 on failure
+
+ /// close the file explicitly
+ int close(std::error_code& ec);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Convenience function to close the file without additional parameters
+ int close();
+ ///< @returns 0 on success, -1 on failure
+ ///@}
+
+ /// ================================================================================
+ /// @name static member functions
+ ///
+ /// These functions are static and operate with UTF8 filenames as one of their parameters.
+ ///
+ ///@{
/// open a file with the specified access mode
- static LLFILE* fopen(const std::string& filename, const char* accessmode); /* Flawfinder: ignore */
+ ///
+#if LL_WINDOWS
+ using fopen_flags_t = wchar_t;
+#else
+ using fopen_flags_t = char;
+#endif
+ inline static LLFILE* fopen(const char* filename, const fopen_flags_t* accessmode, int lmode = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return LLFile::fopen(file_path, accessmode, lmode);
+ }
+ inline static LLFILE* fopen(const std::string& filename, const fopen_flags_t* accessmode, int lmode = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return LLFile::fopen(file_path, accessmode, lmode);
+ }
+ static LLFILE* fopen(const std::filesystem::path& file_path, const fopen_flags_t* accessmode, int lmode = 0);
///< 'accessmode' follows the rules of the Posix fopen() mode parameter
- /// "r" open the file for reading only and positions the stream at the beginning
- /// "r+" open the file for reading and writing and positions the stream at the beginning
- /// "w" open the file for reading and writing and truncate it to zero length
- /// "w+" open or create the file for reading and writing and truncate to zero length if it existed
- /// "a" open the file for reading and writing and position the stream at the end of the file
- /// "a+" open or create the file for reading and writing and position the stream at the end of the file
+ /// "r" open the file for reading only and positions the stream at the beginning
+ /// "r+" open the file for reading and writing and positions the stream at the beginning
+ /// "w" open the file for reading and writing and truncate it to zero length
+ /// "w+" open or create the file for reading and writing and truncate to zero length if it existed
+ /// "a" open the file for reading and writing and before every write position the stream at the end of the file
+ /// "a+" open or create the file for reading and writing and before every write position the stream at the end of the file
+ ///
+ /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac
+ /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not
+ /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither
+ /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT.
+ /// This means that it is always a good idea to append "b" specifically for binary file access to
+ /// avoid corruption of the binary consistency of the data stream when reading or writing
+ /// Other characters in 'accessmode', while possible on some platforms (Windows), will usually
+ /// cause an error on other platforms as fopen will verify this parameter
///
- /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac
- /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not
- /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither
- /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT.
- /// This means that it is always a good idea to append "b" specifically for binary file access to
- /// avoid corruption of the binary consistency of the data stream when reading or writing
- /// Other characters in 'accessmode' will usually cause an error as fopen will verify this parameter
- /// @returns a valid LLFILE* pointer on success or NULL on failure
+ /// lmode is optional and allows to lock the file for other processes either as a shared lock or an
+ /// exclusive lock. If the requested lock conflicts with an already existing lock, the open fails.
+ /// Pass either LLFIle::exclusive or LLFile::shared to this parameter if you want to prevent other
+ /// processes from reading (exclusive lock) or writing (shared lock) to the file. It will always use
+ /// LLFile::noblock, meaning the open will immediately fail if it conflicts with an existing lock on the
+ /// file.
+ ///
+ /// @returns a valid LLFILE* pointer on success that can be passed to the fread() and fwrite() functions
+ /// and some other f<something> functions in the Standard C library that accept a FILE* as parameter
+ /// or NULL on failure
+ /// Close a file handle opened with fopen() above
static int close(LLFILE * file);
-
- /// retrieve the content of a file into a string
- static std::string getContents(const std::string& filename);
- ///< @returns the content of the file or an empty string on failure
+ ///< @returns 0 on success and -1 on failure.
/// create a directory
- static int mkdir(const std::string& filename, int perms = 0700);
- ///< perms is a permissions mask like 0777 or 0700. In most cases it will be
- /// overridden by the user's umask. It is ignored on Windows.
- /// mkdir() considers "directory already exists" to be not an error.
- /// @returns 0 on success and -1 on failure.
-
- //// remove a directory
- static int rmdir(const std::string& filename, int suppress_error = 0);
- ///< pass ENOENT in the optional 'suppress_error' parameter
- /// if you don't want a warning in the log when the directory does not exist
+ inline static int mkdir(const char* dirname)
+ {
+ std::filesystem::path dir_path = fsyspath(dirname);
+ return mkdir(dir_path);
+ }
+ inline static int mkdir(const std::string& dirname)
+ {
+ std::filesystem::path dir_path = fsyspath(dirname);
+ return mkdir(dir_path);
+ }
+ static int mkdir(const std::filesystem::path& dirname);
+ ///< mkdir() considers "directory already exists" to be not an error.
/// @returns 0 on success and -1 on failure.
/// remove a file or directory
- static int remove(const std::string& filename, int suppress_error = 0);
- ///< pass ENOENT in the optional 'suppress_error' parameter
- /// if you don't want a warning in the log when the directory does not exist
+ inline static int remove(const char* filename, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return remove(file_path, suppress_warning);
+ }
+ inline static int remove(const std::string& filename, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return remove(file_path, suppress_warning);
+ }
+ static int remove(const std::filesystem::path& file_path, int suppress_warning = 0);
+ ///< pass an errno value (e.g., ENOENT) in the optional 'suppress_warning' parameter if you want to
+ /// suppress a warning in the log when the failure matches that errno (e.g., suppress warning if
+ /// the file or directory does not exist)
/// @returns 0 on success and -1 on failure.
/// rename a file
- static int rename(const std::string& filename, const std::string& newname, int suppress_error = 0);
+ inline static int rename(const char* filename, const char* newname, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ std::filesystem::path new_path = fsyspath(newname);
+ return rename(file_path, new_path, suppress_warning);
+ }
+ inline static int rename(const std::string& filename, const char* newname, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ std::filesystem::path new_path = fsyspath(newname);
+ return rename(file_path, new_path, suppress_warning);
+ }
+ inline static int rename(const char* filename, const std::string& newname, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ std::filesystem::path new_path = fsyspath(newname);
+ return rename(file_path, new_path, suppress_warning);
+ }
+ inline static int rename(const std::string& filename, const std::string& newname, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ std::filesystem::path new_path = fsyspath(newname);
+ return rename(file_path, new_path, suppress_warning);
+ }
+ static int rename(const std::filesystem::path& file_path, const std::filesystem::path& new_path, int suppress_warning = 0);
///< it will silently overwrite newname if it exists without returning an error
/// Posix guarantees that if newname already exists, then there will be no moment
/// in which for other processes newname does not exist. There is no such guarantee
- /// under Windows at this time. It may do it in the same way but the used Windows API
- /// does not make such guarantees.
+ /// under Windows at this time. It may do it in the same way but the used Windows
+ /// APIs do not make such guarantees.
/// @returns 0 on success and -1 on failure.
+ /// copy the contents of the file from 'source' to 'target'
+ inline static bool copy(const char* source, const char* target)
+ {
+ std::error_code ec;
+ return copy(source, target, std::filesystem::copy_options::overwrite_existing, ec);
+ }
+ inline static bool copy(const char* source, const std::string& target)
+ {
+ std::error_code ec;
+ return copy(source, target, std::filesystem::copy_options::overwrite_existing, ec);
+ }
+ inline static bool copy(const std::string& source, const char* target)
+ {
+ std::error_code ec;
+ return copy(source, target, std::filesystem::copy_options::overwrite_existing, ec);
+ }
+ inline static bool copy(const std::string& source, const std::string& target)
+ {
+ std::error_code ec;
+ return copy(source, target, std::filesystem::copy_options::overwrite_existing, ec);
+ }
+ inline static bool copy(const std::filesystem::path& source_path, const std::filesystem::path& target_path)
+ {
+ std::error_code ec;
+ return copy(source_path, target_path, std::filesystem::copy_options::overwrite_existing, ec);
+ }
+ ///< Copies the contents of the file 'source' to the file 'target', overwriting 'target' if it already
+ /// existed.
+ /// This is a convenience function that implements the previous behavior of silently overwriting an
+ /// already existing target file. Consider using the function below if you desire a different
+ /// behavior when the target file already exists
+ /// @returns true on success and false on failure.
+
+ /// copy the contents of the file from 'from' to 'to'
+ inline static bool copy(const char* source, const char* target, std::filesystem::copy_options options, std::error_code& ec)
+ {
+ std::filesystem::path source_path = fsyspath(source);
+ std::filesystem::path target_path = fsyspath(target);
+ return copy(source_path, target_path, options, ec);
+ }
+ inline static bool copy(const char* source, const std::string& target, std::filesystem::copy_options options, std::error_code& ec)
+ {
+ std::filesystem::path source_path = fsyspath(source);
+ std::filesystem::path target_path = fsyspath(target);
+ return copy(source_path, target_path, options, ec);
+ }
+ inline static bool copy(const std::string& source, const char* target, std::filesystem::copy_options options, std::error_code& ec)
+ {
+ std::filesystem::path source_path = fsyspath(source);
+ std::filesystem::path target_path = fsyspath(target);
+ return copy(source_path, target_path, options, ec);
+ }
+ inline static bool copy(const std::string& source, const std::string& target, std::filesystem::copy_options options, std::error_code& ec)
+ {
+ std::filesystem::path source_path = fsyspath(source);
+ std::filesystem::path target_path = fsyspath(target);
+ return copy(source_path, target_path, options, ec);
+ }
+ static bool copy(const std::filesystem::path& source_path, const std::filesystem::path& target_path, std::filesystem::copy_options options, std::error_code& ec);
+ ///< Copies the contents of the file 'source' to the file 'target'. The options parameter allows to
+ /// specify what should happen if the "target" file already exists:
+ /// std::filesystem::copy_options::none - return an error in ec and fail
+ /// std::filesystem::copy_options::skip_existing - skip the operation and do not overwrite file
+ /// std::filesystem::copy_options::overwrite_existing - overwrite the file
+ /// std::filesystem::copy_options::update_existing - overwrite the file only if it is older than the file being copied
+ /// @returns true on success and false on failure.
+
+ /// retrieve the content of a file into a string
+ inline static std::string getContents(const char* filename)
+ {
+ std::error_code ec;
+ return getContents(filename, ec);
+ }
+ inline static std::string getContents(const std::string& filename)
+ {
+ std::error_code ec;
+ return getContents(filename, ec);
+ }
+ inline static std::string getContents(const std::filesystem::path& file_path)
+ {
+ std::error_code ec;
+ return getContents(file_path, ec);
+ }
+ inline static std::string getContents(const char* filename, std::error_code& ec)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return getContents(file_path, ec);
+ }
+ inline static std::string getContents(const std::string& filename, std::error_code& ec)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return getContents(file_path, ec);
+ }
+ static std::string getContents(const std::filesystem::path& file_path, std::error_code& ec);
+ ///< @returns the entire content of the file as std::string or an empty string on failure
+
+ /// read nBytes from the file into the buffer, starting at offset in the file
+ inline static S64 read(const char* filename, void* buf, S64 offset, S64 nbytes)
+ {
+ std::error_code ec;
+ return read(filename, buf, offset, nbytes, ec);
+ }
+ inline static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes)
+ {
+ std::error_code ec;
+ return read(filename, buf, offset, nbytes, ec);
+ }
+ inline static S64 read(const std::filesystem::path& file_path, void* buf, S64 offset, S64 nbytes)
+ {
+ std::error_code ec;
+ return read(file_path, buf, offset, nbytes, ec);
+ }
+ inline static S64 read(const char* filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return read(file_path, buf, offset, nbytes, ec);
+ }
+ inline static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return read(file_path, buf, offset, nbytes, ec);
+ }
+ static S64 read(const std::filesystem::path& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec);
+ ///< @returns bytes read on success, or -1 on failure
- /// copy the contents of file from 'from' to 'to' filename
- static bool copy(const std::string& from, const std::string& to);
- ///< @returns true on success and false on failure.
+ /// write nBytes from the buffer into the file, starting at offset in the file
+ inline static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes)
+ {
+ std::error_code ec;
+ std::filesystem::path file_path = fsyspath(filename);
+ return write(file_path, buf, offset, nbytes, ec);
+ }
+ inline static S64 write(const std::filesystem::path& filename, const void* buf, S64 offset, S64 nbytes)
+ {
+ std::error_code ec;
+ std::filesystem::path file_path = fsyspath(filename);
+ return write(file_path, buf, offset, nbytes, ec);
+ }
+ inline static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return write(file_path, buf, offset, nbytes, ec);
+ }
+ static S64 write(const std::filesystem::path& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec);
+ ///< If a negative offset is provided, the file is opened in append mode and the
+ /// write will be appended to the end of the file.
+ /// @returns bytes written on success, or -1 on failure
/// return the file stat structure for filename
- static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT);
+ inline static int stat(const char* filename, llstat* file_status, const char* operation = nullptr, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return stat(file_path, file_status, operation, suppress_warning);
+ }
+ inline static int stat(const std::string& filename, llstat* file_status, const char* operation = nullptr, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return stat(file_path, file_status, operation, suppress_warning);
+ }
+ static int stat(const std::filesystem::path& file_path, llstat* file_status, const char *operation = nullptr, int suppress_warning = ENOENT);
///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the
- /// optional 'suppress_error' parameter to avoid spamming the log with warnings when the API
+ /// optional 'suppress_warning' parameter to avoid spamming the log with warnings when the API
/// is used to detect if a file exists
/// @returns 0 on success and -1 on failure.
- /// get the file or directory attributes for filename
- static unsigned short getattr(const std::string& filename, bool dontFollowSymLink = false, int suppress_error = ENOENT);
- ///< a more lightweight function on Windows to stat, that just returns the file attribute flags
- /// dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it
- /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with
- /// warnings when the file or directory does not exist
- /// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise
- /// together with the three access bits which under Windows only the write bit is relevant.
+ /// get the std::filesystem::file_status for filename
+ inline static std::filesystem::file_status getStatus(const char* filename, bool dontFollowSymLink = false, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return getStatus(file_path, dontFollowSymLink, suppress_warning);
+ }
+ inline static std::filesystem::file_status getStatus(const std::string& filename, bool dontFollowSymLink = false, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return getStatus(file_path, dontFollowSymLink, suppress_warning);
+ }
+ static std::filesystem::file_status getStatus(const std::filesystem::path& file_path, bool dontFollowSymLink = false, int suppress_warning = ENOENT);
+ ///< dontFollowSymLinks set to true returns the std::filesystem::file_status of the symlink if it
+ /// is one, rather than resolving it. We pass by default ENOENT in the optional 'suppress_warning'
+ /// parameter to not spam the log with warnings when the file or directory does not exist
+ /// @returns a std::filesystem::file_status value that can be passed to the appropriate std::filesystem::exists()
+ /// and other APIs accepting a file_status.
+
+ /// get the size of a file in bytes
+ inline static S64 size(const char* filename, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return size(file_path, suppress_warning);
+ }
+ inline static S64 size(const std::string& filename, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return size(file_path, suppress_warning);
+ }
+ static S64 size(const std::filesystem::path& file_path, int suppress_warning = ENOENT);
+ ///< we pass by default ENOENT in the optional 'suppress_warning' parameter to not spam
+ /// the log with warnings when the file does not exist
+ /// @returns the file size on success or 0 on failure.
+
+ /// check if filename is an existing file or directory
+ inline static bool exists(const char* filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::exists(status);
+ }
+ inline static bool exists(const std::string& filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::exists(status);
+ }
+ inline static bool exists(const std::filesystem::path& file_path)
+ {
+ std::filesystem::file_status status = getStatus(file_path);
+ return std::filesystem::exists(status);
+ }
+ ///< @returns true if the path is for an existing file or directory
/// check if filename is an existing directory
- static bool isdir(const std::string& filename);
+ inline static bool isdir(const char* filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::is_directory(status);
+ }
+ inline static bool isdir(const std::string& filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::is_directory(status);
+ }
+ inline static bool isdir(const std::filesystem::path& file_path)
+ {
+ std::filesystem::file_status status = getStatus(file_path);
+ return std::filesystem::is_directory(status);
+ }
///< @returns true if the path is for an existing directory
/// check if filename is an existing file
- static bool isfile(const std::string& filename);
+ inline static bool isfile(const char* filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::is_regular_file(status);
+ }
+ inline static bool isfile(const std::string& filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::is_regular_file(status);
+ }
+ inline static bool isfile(const std::filesystem::path& file_path)
+ {
+ std::filesystem::file_status status = getStatus(file_path);
+ return std::filesystem::is_regular_file(status);
+ }
///< @returns true if the path is for an existing file
/// check if filename is a symlink
- static bool islink(const std::string& filename);
+ inline static bool islink(const char* filename)
+ {
+ std::filesystem::file_status status = getStatus(filename, true);
+ return std::filesystem::is_symlink(status);
+ }
+ inline static bool islink(const std::string& filename)
+ {
+ std::filesystem::file_status status = getStatus(filename, true);
+ return std::filesystem::is_symlink(status);
+ }
+ inline static bool islink(const std::filesystem::path& file_path)
+ {
+ std::filesystem::file_status status = getStatus(file_path, true);
+ return std::filesystem::is_symlink(status);
+ }
///< @returns true if the path is pointing at a symlink
/// return a path to the temporary directory on the system
- static const char * tmpdir();
+ static const std::string& tmpdir();
+
+private:
+#if LL_WINDOWS
+ typedef HANDLE llfile_handle_t;
+ const llfile_handle_t InvalidHandle = INVALID_HANDLE_VALUE;
+ llfile_handle_t mHandle = INVALID_HANDLE_VALUE; // The file handle/descriptor
+#else
+ typedef int llfile_handle_t;
+ const llfile_handle_t InvalidHandle = -1;
+ llfile_handle_t mHandle = -1; // The file handle/descriptor
+#endif
+
+ std::ios_base::openmode mOpen{}; // Used to emulate std::ios_base::app under Windows
+};
+
+#if LL_WINDOWS
+/**
+ * @brief Controlling input for files.
+ *
+ * This class supports reading from named files, using the inherited
+ * functions from std::ifstream. The only added value is that our constructor
+ * Does The Right Thing when passed a non-ASCII pathname. Sadly, that isn't
+ * true of Microsoft's std::ifstream.
+ */
+class llifstream : public std::ifstream
+{
+ // input stream associated with a C stream
+ public:
+ // Constructors:
+ /**
+ * @brief Default constructor.
+ *
+ * Initializes @c sb using its default constructor, and passes
+ * @c &sb to the base class initializer. Does not open any files
+ * (you haven't given it a filename to open).
+ */
+ llifstream() = default;
+
+ /**
+ * @brief Create an input file stream.
+ * @param Filename String specifying the filename.
+ * @param Mode Open file in specified mode (see std::ios_base).
+ *
+ * @c ios_base::in is automatically included in @a mode.
+ */
+ explicit llifstream(const char* _Filename,
+ ios_base::openmode _Mode = ios_base::in);
+ explicit llifstream(const std::string& _Filename,
+ ios_base::openmode _Mode = ios_base::in);
+ explicit llifstream(const std::filesystem::path& _Filepath,
+ ios_base::openmode _Mode = ios_base::in);
+
+ /**
+ * @brief Opens an external file.
+ * @param Filename The name of the file.
+ * @param Node The open mode flags.
+ *
+ * Calls @c llstdio_filebuf::open(s,mode|in). If that function
+ * fails, @c failbit is set in the stream's error state.
+ */
+ void open(const char* _Filename,
+ ios_base::openmode _Mode = ios_base::in);
+ void open(const std::string& _Filename,
+ ios_base::openmode _Mode = ios_base::in);
+ void open(const std::filesystem::path& _Filepath,
+ ios_base::openmode _Mode = ios_base::in);
};
/// RAII class
@@ -169,22 +748,19 @@ class LLUniqueFile
{
public:
// empty
- LLUniqueFile(): mFileHandle(nullptr) {}
+ LLUniqueFile() = default;
// wrap (e.g.) result of LLFile::fopen()
- LLUniqueFile(LLFILE* f): mFileHandle(f) {}
+ LLUniqueFile(LLFILE* f) : mFileHandle(f) {}
// no copy
LLUniqueFile(const LLUniqueFile&) = delete;
// move construction
LLUniqueFile(LLUniqueFile&& other) noexcept
{
- mFileHandle = other.mFileHandle;
+ mFileHandle = other.mFileHandle;
other.mFileHandle = nullptr;
}
// The point of LLUniqueFile is to close on destruction.
- ~LLUniqueFile()
- {
- close();
- }
+ ~LLUniqueFile() { close(); }
// simple assignment
LLUniqueFile& operator=(LLFILE* f)
@@ -209,7 +785,7 @@ public:
if (mFileHandle)
{
// in case close() throws, set mFileHandle null FIRST
- LLFILE* h{nullptr};
+ LLFILE* h{ nullptr };
std::swap(h, mFileHandle);
LLFile::close(h);
}
@@ -217,62 +793,16 @@ public:
// detect whether the wrapped LLFILE is open or not
explicit operator bool() const { return bool(mFileHandle); }
- bool operator!() { return ! mFileHandle; }
+ bool operator!() { return !mFileHandle; }
// LLUniqueFile should be usable for any operation that accepts LLFILE*
// (or FILE* for that matter)
operator LLFILE*() const { return mFileHandle; }
private:
- LLFILE* mFileHandle;
-};
-
-#if LL_WINDOWS
-/**
- * @brief Controlling input for files.
- *
- * This class supports reading from named files, using the inherited
- * functions from std::ifstream. The only added value is that our constructor
- * Does The Right Thing when passed a non-ASCII pathname. Sadly, that isn't
- * true of Microsoft's std::ifstream.
- */
-class LL_COMMON_API llifstream : public std::ifstream
-{
- // input stream associated with a C stream
- public:
- // Constructors:
- /**
- * @brief Default constructor.
- *
- * Initializes @c sb using its default constructor, and passes
- * @c &sb to the base class initializer. Does not open any files
- * (you haven't given it a filename to open).
- */
- llifstream();
-
- /**
- * @brief Create an input file stream.
- * @param Filename String specifying the filename.
- * @param Mode Open file in specified mode (see std::ios_base).
- *
- * @c ios_base::in is automatically included in @a mode.
- */
- explicit llifstream(const std::string& _Filename,
- ios_base::openmode _Mode = ios_base::in);
-
- /**
- * @brief Opens an external file.
- * @param Filename The name of the file.
- * @param Node The open mode flags.
- *
- * Calls @c llstdio_filebuf::open(s,mode|in). If that function
- * fails, @c failbit is set in the stream's error state.
- */
- void open(const std::string& _Filename,
- ios_base::openmode _Mode = ios_base::in);
+ LLFILE* mFileHandle = nullptr;
};
-
/**
* @brief Controlling output for files.
*
@@ -281,7 +811,7 @@ class LL_COMMON_API llifstream : public std::ifstream
* Right Thing when passed a non-ASCII pathname. Sadly, that isn't true of
* Microsoft's std::ofstream.
*/
-class LL_COMMON_API llofstream : public std::ofstream
+class llofstream : public std::ofstream
{
public:
// Constructors:
@@ -292,7 +822,7 @@ class LL_COMMON_API llofstream : public std::ofstream
* @c &sb to the base class initializer. Does not open any files
* (you haven't given it a filename to open).
*/
- llofstream();
+ llofstream() = default;
/**
* @brief Create an output file stream.
@@ -301,8 +831,12 @@ class LL_COMMON_API llofstream : public std::ofstream
*
* @c ios_base::out is automatically included in @a mode.
*/
+ explicit llofstream(const char* _Filename,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
explicit llofstream(const std::string& _Filename,
ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
+ explicit llofstream(const std::filesystem::path& _Filepath,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
/**
* @brief Opens an external file.
@@ -311,21 +845,14 @@ class LL_COMMON_API llofstream : public std::ofstream
*
* @c ios_base::out is automatically included in @a mode.
*/
+ void open(const char* _Filename,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
void open(const std::string& _Filename,
ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
+ void open(const std::filesystem::path& _Filepath,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
};
-
-/**
- * @brief filesize helpers.
- *
- * The file size helpers are not considered particularly efficient,
- * and should only be used for config files and the like -- not in a
- * loop.
- */
-std::streamsize LL_COMMON_API llifstream_size(llifstream& fstr);
-std::streamsize LL_COMMON_API llofstream_size(llofstream& fstr);
-
#else // ! LL_WINDOWS
// on non-windows, llifstream and llofstream are just mapped directly to the std:: equivalents
diff --git a/indra/llcommon/llpointer.cpp b/indra/llcommon/llpointer.cpp
new file mode 100755
index 0000000000..1bb7055b3a
--- /dev/null
+++ b/indra/llcommon/llpointer.cpp
@@ -0,0 +1,43 @@
+/**
+ * @file llpointer.cpp
+ * @author Nat Goodspeed
+ * @date 2024-09-26
+ * @brief Implementation for llpointer.
+ *
+ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2024, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llpointer.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "llerror.h"
+
+void LLPointerBase::wild_dtor(std::string_view msg)
+{
+// LL_WARNS() << msg << LL_ENDL;
+ llassert_msg(false, msg);
+}
diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h
index 048547e4cc..d2dcf530e5 100644
--- a/indra/llcommon/llpointer.h
+++ b/indra/llcommon/llpointer.h
@@ -26,8 +26,9 @@
#ifndef LLPOINTER_H
#define LLPOINTER_H
-#include "llerror.h" // *TODO: consider eliminating this
-#include "llmutex.h"
+#include <functional>
+#include <string_view>
+#include <utility> // std::swap()
//----------------------------------------------------------------------------
// RefCount objects should generally only be accessed by way of LLPointer<>'s
@@ -42,8 +43,18 @@
//----------------------------------------------------------------------------
+class LLPointerBase
+{
+protected:
+ // alert the coder that a referenced type's destructor did something very
+ // strange -- this is in a non-template base class so we can hide the
+ // implementation in llpointer.cpp
+ static void wild_dtor(std::string_view msg);
+};
+
// Note: relies on Type having ref() and unref() methods
-template <class Type> class LLPointer
+template <class Type>
+class LLPointer: public LLPointerBase
{
public:
template<typename Subclass>
@@ -60,6 +71,13 @@ public:
ref();
}
+ // Even though the template constructors below accepting
+ // (const LLPointer<Subclass>&) and (LLPointer<Subclass>&&) appear to
+ // subsume these specific (const LLPointer<Type>&) and (LLPointer<Type>&&)
+ // constructors, the compiler recognizes these as The Copy Constructor and
+ // The Move Constructor, respectively. In other words, even in the
+ // presence of the LLPointer<Subclass> constructors, we still must specify
+ // the LLPointer<Type> constructors.
LLPointer(const LLPointer<Type>& ptr) :
mPointer(ptr.mPointer)
{
@@ -98,39 +116,52 @@ public:
const Type& operator*() const { return *mPointer; }
Type& operator*() { return *mPointer; }
- operator BOOL() const { return (mPointer != nullptr); }
operator bool() const { return (mPointer != nullptr); }
bool operator!() const { return (mPointer == nullptr); }
bool isNull() const { return (mPointer == nullptr); }
bool notNull() const { return (mPointer != nullptr); }
operator Type*() const { return mPointer; }
- bool operator !=(Type* ptr) const { return (mPointer != ptr); }
- bool operator ==(Type* ptr) const { return (mPointer == ptr); }
- bool operator ==(const LLPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); }
- bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); }
- bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); }
+ template <typename Type1>
+ bool operator !=(Type1* ptr) const { return (mPointer != ptr); }
+ template <typename Type1>
+ bool operator ==(Type1* ptr) const { return (mPointer == ptr); }
+ template <typename Type1>
+ bool operator !=(const LLPointer<Type1>& ptr) const { return (mPointer != ptr.mPointer); }
+ template <typename Type1>
+ bool operator ==(const LLPointer<Type1>& ptr) const { return (mPointer == ptr.mPointer); }
+ bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); }
+ bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); }
LLPointer<Type>& operator =(Type* ptr)
{
- assign(ptr);
+ // copy-and-swap idiom, see http://gotw.ca/gotw/059.htm
+ LLPointer temp(ptr);
+ using std::swap; // per Swappable convention
+ swap(*this, temp);
return *this;
}
+ // Even though the template assignment operators below accepting
+ // (const LLPointer<Subclass>&) and (LLPointer<Subclass>&&) appear to
+ // subsume these specific (const LLPointer<Type>&) and (LLPointer<Type>&&)
+ // assignment operators, the compiler recognizes these as Copy Assignment
+ // and Move Assignment, respectively. In other words, even in the presence
+ // of the LLPointer<Subclass> assignment operators, we still must specify
+ // the LLPointer<Type> operators.
LLPointer<Type>& operator =(const LLPointer<Type>& ptr)
{
- assign(ptr);
+ LLPointer temp(ptr);
+ using std::swap; // per Swappable convention
+ swap(*this, temp);
return *this;
}
LLPointer<Type>& operator =(LLPointer<Type>&& ptr)
{
- if (mPointer != ptr.mPointer)
- {
- unref();
- mPointer = ptr.mPointer;
- ptr.mPointer = nullptr;
- }
+ LLPointer temp(std::move(ptr));
+ using std::swap; // per Swappable convention
+ swap(*this, temp);
return *this;
}
@@ -138,28 +169,32 @@ public:
template<typename Subclass>
LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr)
{
- assign(ptr.get());
+ LLPointer temp(ptr);
+ using std::swap; // per Swappable convention
+ swap(*this, temp);
return *this;
}
template<typename Subclass>
LLPointer<Type>& operator =(LLPointer<Subclass>&& ptr)
{
- if (mPointer != ptr.mPointer)
- {
- unref();
- mPointer = ptr.mPointer;
- ptr.mPointer = nullptr;
- }
+ LLPointer temp(std::move(ptr));
+ using std::swap; // per Swappable convention
+ swap(*this, temp);
return *this;
}
// Just exchange the pointers, which will not change the reference counts.
static void swap(LLPointer<Type>& a, LLPointer<Type>& b)
{
- Type* temp = a.mPointer;
- a.mPointer = b.mPointer;
- b.mPointer = temp;
+ using std::swap; // per Swappable convention
+ swap(a.mPointer, b.mPointer);
+ }
+
+ // Put swap() overload in the global namespace, per Swappable convention
+ friend void swap(LLPointer<Type>& a, LLPointer<Type>& b)
+ {
+ LLPointer<Type>::swap(a, b);
}
protected:
@@ -184,191 +219,19 @@ protected:
temp->unref();
if (mPointer != nullptr)
{
- LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL;
+ wild_dtor("Unreference did assignment to non-NULL because of destructor");
unref();
}
}
}
#endif // LL_LIBRARY_INCLUDE
- void assign(const LLPointer<Type>& ptr)
- {
- if (mPointer != ptr.mPointer)
- {
- unref();
- mPointer = ptr.mPointer;
- ref();
- }
- }
-
protected:
Type* mPointer;
};
-template <class Type> class LLConstPointer
-{
- template<typename Subclass>
- friend class LLConstPointer;
-public:
- LLConstPointer() :
- mPointer(nullptr)
- {
- }
-
- LLConstPointer(const Type* ptr) :
- mPointer(ptr)
- {
- ref();
- }
-
- LLConstPointer(const LLConstPointer<Type>& ptr) :
- mPointer(ptr.mPointer)
- {
- ref();
- }
-
- LLConstPointer(LLConstPointer<Type>&& ptr) noexcept
- {
- mPointer = ptr.mPointer;
- ptr.mPointer = nullptr;
- }
-
- // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed.
- template<typename Subclass>
- LLConstPointer(const LLConstPointer<Subclass>& ptr) :
- mPointer(ptr.get())
- {
- ref();
- }
-
- template<typename Subclass>
- LLConstPointer(LLConstPointer<Subclass>&& ptr) noexcept :
- mPointer(ptr.get())
- {
- ptr.mPointer = nullptr;
- }
-
- ~LLConstPointer()
- {
- unref();
- }
-
- const Type* get() const { return mPointer; }
- const Type* operator->() const { return mPointer; }
- const Type& operator*() const { return *mPointer; }
-
- operator BOOL() const { return (mPointer != nullptr); }
- operator bool() const { return (mPointer != nullptr); }
- bool operator!() const { return (mPointer == nullptr); }
- bool isNull() const { return (mPointer == nullptr); }
- bool notNull() const { return (mPointer != nullptr); }
-
- operator const Type*() const { return mPointer; }
- bool operator !=(const Type* ptr) const { return (mPointer != ptr); }
- bool operator ==(const Type* ptr) const { return (mPointer == ptr); }
- bool operator ==(const LLConstPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); }
- bool operator < (const LLConstPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); }
- bool operator > (const LLConstPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); }
-
- LLConstPointer<Type>& operator =(const Type* ptr)
- {
- if( mPointer != ptr )
- {
- unref();
- mPointer = ptr;
- ref();
- }
-
- return *this;
- }
-
- LLConstPointer<Type>& operator =(const LLConstPointer<Type>& ptr)
- {
- if( mPointer != ptr.mPointer )
- {
- unref();
- mPointer = ptr.mPointer;
- ref();
- }
- return *this;
- }
-
- LLConstPointer<Type>& operator =(LLConstPointer<Type>&& ptr)
- {
- if (mPointer != ptr.mPointer)
- {
- unref();
- mPointer = ptr.mPointer;
- ptr.mPointer = nullptr;
- }
- return *this;
- }
-
- // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed.
- template<typename Subclass>
- LLConstPointer<Type>& operator =(const LLConstPointer<Subclass>& ptr)
- {
- if( mPointer != ptr.get() )
- {
- unref();
- mPointer = ptr.get();
- ref();
- }
- return *this;
- }
-
- template<typename Subclass>
- LLConstPointer<Type>& operator =(LLConstPointer<Subclass>&& ptr)
- {
- if (mPointer != ptr.mPointer)
- {
- unref();
- mPointer = ptr.mPointer;
- ptr.mPointer = nullptr;
- }
- return *this;
- }
-
- // Just exchange the pointers, which will not change the reference counts.
- static void swap(LLConstPointer<Type>& a, LLConstPointer<Type>& b)
- {
- const Type* temp = a.mPointer;
- a.mPointer = b.mPointer;
- b.mPointer = temp;
- }
-
-protected:
-#ifdef LL_LIBRARY_INCLUDE
- void ref();
- void unref();
-#else // LL_LIBRARY_INCLUDE
- void ref()
- {
- if (mPointer)
- {
- mPointer->ref();
- }
- }
-
- void unref()
- {
- if (mPointer)
- {
- const Type *temp = mPointer;
- mPointer = nullptr;
- temp->unref();
- if (mPointer != nullptr)
- {
- LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL;
- unref();
- }
- }
- }
-#endif // LL_LIBRARY_INCLUDE
-
-protected:
- const Type* mPointer;
-};
+template <typename Type>
+using LLConstPointer = LLPointer<const Type>;
template<typename Type>
class LLCopyOnWritePointer : public LLPointer<Type>
@@ -418,38 +281,26 @@ private:
bool mStayUnique;
};
-template<typename Type>
-bool operator!=(Type* lhs, const LLPointer<Type>& rhs)
+template<typename Type0, typename Type1>
+bool operator!=(Type0* lhs, const LLPointer<Type1>& rhs)
{
return (lhs != rhs.get());
}
-template<typename Type>
-bool operator==(Type* lhs, const LLPointer<Type>& rhs)
+template<typename Type0, typename Type1>
+bool operator==(Type0* lhs, const LLPointer<Type1>& rhs)
{
return (lhs == rhs.get());
}
-// boost hash adapter
-template <class Type>
-struct boost::hash<LLPointer<Type>>
-{
- typedef LLPointer<Type> argument_type;
- typedef std::size_t result_type;
- result_type operator()(argument_type const& s) const
- {
- return (std::size_t) s.get();
- }
-};
-
-// Adapt boost hash to std hash
+// Specialize for std::hash
namespace std
{
template<class Type> struct hash<LLPointer<Type>>
{
std::size_t operator()(LLPointer<Type> const& s) const noexcept
{
- return boost::hash<LLPointer<Type>>()(s);
+ return std::hash<Type*>()(s.get());
}
};
}
diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp
index 37d1c60f91..9d0ef45bee 100644
--- a/indra/llcommon/llprocessor.cpp
+++ b/indra/llcommon/llprocessor.cpp
@@ -837,7 +837,7 @@ private:
void get_proc_cpuinfo()
{
std::map< std::string, std::string > cpuinfo;
- LLFILE* cpuinfo_fp = LLFile::fopen(CPUINFO_FILE, "rb");
+ LLFILE* cpuinfo_fp = LLFile::fopen(CPUINFO_FILE, LLFILE_MODE("rb"));
if(cpuinfo_fp)
{
char line[MAX_STRING];
@@ -954,7 +954,7 @@ private:
std::ostringstream s;
// *NOTE:Mani - This is for linux only.
- LLFILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, "rb");
+ LLFILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, LLFILE_MODE("rb"));
if(cpuinfo)
{
char line[MAX_STRING];
diff --git a/indra/llcommon/llprofilercategories.h b/indra/llcommon/llprofilercategories.h
index 1c4f0f5624..261fdf14b7 100644
--- a/indra/llcommon/llprofilercategories.h
+++ b/indra/llcommon/llprofilercategories.h
@@ -48,6 +48,7 @@
#define LL_PROFILER_CATEGORY_ENABLE_DRAWPOOL 1
#define LL_PROFILER_CATEGORY_ENABLE_ENVIRONMENT 1
#define LL_PROFILER_CATEGORY_ENABLE_FACE 1
+//#define LL_PROFILER_CATEGORY_ENABLE_FILE 1
#define LL_PROFILER_CATEGORY_ENABLE_LLSD 1
#define LL_PROFILER_CATEGORY_ENABLE_LOGGING 1
#define LL_PROFILER_CATEGORY_ENABLE_MATERIAL 1
@@ -126,6 +127,14 @@
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_FACE
#endif
+#if LL_PROFILER_CATEGORY_ENABLE_FILE
+#define LL_PROFILE_ZONE_NAMED_CATEGORY_FILE LL_PROFILE_ZONE_NAMED
+#define LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE LL_PROFILE_ZONE_SCOPED
+#else
+#define LL_PROFILE_ZONE_NAMED_CATEGORY_FILE(name)
+#define LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE
+#endif
+
#if LL_PROFILER_CATEGORY_ENABLE_LLSD
#define LL_PROFILE_ZONE_NAMED_CATEGORY_LLSD LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD LL_PROFILE_ZONE_SCOPED
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index 9b5dd2df39..5ef042b5a7 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -514,7 +514,7 @@ namespace {
#if LL_LINUX
constexpr U32 STATUS_SIZE = 2048;
- LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb");
+ LLFILE* status_filep = LLFile::fopen("/proc/self/status", LLFILE_MODE("rb"));
if (status_filep)
{
char buff[STATUS_SIZE]; /* Flawfinder: ignore */
@@ -1273,7 +1273,7 @@ bool gunzip_file(const std::string& srcfile, const std::string& dstfile)
src = gzopen(srcfile.c_str(), "rb");
#endif
if (! src) goto err;
- dst = LLFile::fopen(tmpfile, "wb"); /* Flawfinder: ignore */
+ dst = LLFile::fopen(tmpfile, LLFILE_MODE("wb")); /* Flawfinder: ignore */
if (! dst) goto err;
do
{
@@ -1314,7 +1314,7 @@ bool gzip_file(const std::string& srcfile, const std::string& dstfile)
#endif
if (! dst) goto err;
- src = LLFile::fopen(srcfile, "rb"); /* Flawfinder: ignore */
+ src = LLFile::fopen(srcfile, LLFILE_MODE("rb")); /* Flawfinder: ignore */
if (! src) goto err;
while ((bytes = (S32)fread(buffer, sizeof(U8), COMPRESS_BUFFER_SIZE, src)) > 0)
diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp
index 4aae90626e..867744a29d 100644
--- a/indra/llcommon/lluuid.cpp
+++ b/indra/llcommon/lluuid.cpp
@@ -151,6 +151,48 @@ U32 janky_fast_random_seeded_bytes(U32 seed, U32 val)
}
#endif
+void LLUUID::to_chars(char* out) const
+{
+ snprintf(out, UUID_STR_LENGTH, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ (U8)(mData[0]),
+ (U8)(mData[1]),
+ (U8)(mData[2]),
+ (U8)(mData[3]),
+ (U8)(mData[4]),
+ (U8)(mData[5]),
+ (U8)(mData[6]),
+ (U8)(mData[7]),
+ (U8)(mData[8]),
+ (U8)(mData[9]),
+ (U8)(mData[10]),
+ (U8)(mData[11]),
+ (U8)(mData[12]),
+ (U8)(mData[13]),
+ (U8)(mData[14]),
+ (U8)(mData[15]));
+}
+
+void LLUUID::to_wchars(wchar_t* out) const
+{
+ swprintf(out, UUID_STR_LENGTH, L"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ (U8)(mData[0]),
+ (U8)(mData[1]),
+ (U8)(mData[2]),
+ (U8)(mData[3]),
+ (U8)(mData[4]),
+ (U8)(mData[5]),
+ (U8)(mData[6]),
+ (U8)(mData[7]),
+ (U8)(mData[8]),
+ (U8)(mData[9]),
+ (U8)(mData[10]),
+ (U8)(mData[11]),
+ (U8)(mData[12]),
+ (U8)(mData[13]),
+ (U8)(mData[14]),
+ (U8)(mData[15]));
+}
+
// Common to all UUID implementations
void LLUUID::toString(std::string& out) const
{
@@ -405,8 +447,8 @@ LLUUID LLUUID::combine(const LLUUID& other) const
std::ostream& operator<<(std::ostream& s, const LLUUID& uuid)
{
- std::string uuid_str;
- uuid.toString(uuid_str);
+ char uuid_str[UUID_STR_LENGTH];
+ uuid.to_chars(uuid_str);
s << uuid_str;
return s;
}
diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h
index ca1cf03c4d..e6b0eea7a1 100644
--- a/indra/llcommon/lluuid.h
+++ b/indra/llcommon/lluuid.h
@@ -103,6 +103,8 @@ public:
friend LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLUUID &uuid);
friend LL_COMMON_API std::istream& operator>>(std::istream& s, LLUUID &uuid);
+ void to_chars(char* out) const;
+ void to_wchars(wchar_t* out) const;
void toString(std::string& out) const;
void toCompressedString(std::string& out) const;
diff --git a/indra/llcommon/tests/llfile_test.cpp b/indra/llcommon/tests/llfile_test.cpp
new file mode 100644
index 0000000000..ab4616cfb2
--- /dev/null
+++ b/indra/llcommon/tests/llfile_test.cpp
@@ -0,0 +1,299 @@
+/**
+ * @file llfile_test.cpp
+ * @author Frederick Martian
+ * @date 2025-11
+ * @brief LLFile test cases.
+ *
+ * $LicenseInfo:firstyear=2025&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2025, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include <tut/tut.hpp>
+#include "lltut.h"
+#include "linden_common.h"
+#include "llfile.h"
+
+namespace tut
+{
+ static void clear_entire_dir(const std::filesystem::path& dir_path)
+ {
+ std::error_code ec;
+ std::filesystem::remove_all(dir_path, ec);
+ }
+
+ static std::filesystem::path append_filename(const std::filesystem::path& dir, const std::u8string& element)
+ {
+ std::filesystem::path path = dir;
+ return path.append(element);
+ }
+
+ static std::filesystem::path get_testdir(const std::filesystem::path& tempdir)
+ {
+ return append_filename(tempdir, std::u8string(u8"test_dir"));
+ }
+
+ static std::filesystem::path get_testdir_unicode(const std::filesystem::path& tempdir)
+ {
+ // Example Unicode directory name: "test_ユニコード_dir"
+ return append_filename(tempdir, std::u8string(u8"test_\xE3\x83\xA6\xE3\x83\x8B\xE3\x82\xB3\xE3\x83\xBC\xE3\x83\x89_dir"));
+ }
+
+ struct llfile_test
+ {
+ std::filesystem::path tempdir = LLFile::tmpdir();
+ std::filesystem::path testdir = get_testdir(tempdir);
+ };
+ typedef test_group<llfile_test> llfile_test_t;
+ typedef llfile_test_t::object llfile_test_object_t;
+ tut::llfile_test_t tut_llfile_test("llfile_test");
+
+ template<> template<>
+ void llfile_test_object_t::test<1>()
+ {
+ // Test creating directories and files and deleting them and checking if the
+ // relevant status functions work as expected
+ ensure("LLFile::tmpdir() empty", !tempdir.empty());
+ ensure("LLFile::tmpdir() doesn't exist", LLFile::exists(tempdir));
+ ensure("LLFile::tmpdir() is not a directory", LLFile::isdir(tempdir));
+ ensure("LLFile::tmpdir() should not be a file", !LLFile::isfile(tempdir));
+
+ // Make sure there is nothing left from a previous test run
+ clear_entire_dir(testdir);
+ ensure("llfile_test should not exist anymore", !LLFile::exists(testdir));
+
+ int rc = LLFile::mkdir(testdir);
+ ensure("LLFile::mkdir() failed", rc == 0);
+ ensure("llfile_test should be a directory", LLFile::isdir(testdir));
+ rc = LLFile::mkdir(testdir);
+ ensure("LLFile::mkdir() should not fail when the directory already exists", rc == 0);
+
+ std::filesystem::path testfile1 = testdir;
+ testfile1.append("llfile_test.dat");
+ ensure("llfile_test1.dat should not yet exist", !LLFile::exists(testfile1));
+
+ const char* testdata = "testdata";
+ S64 bytes = LLFile::write(testfile1, testdata, 0, sizeof(testdata));
+ ensure("LLFile::write() did not write correctly", bytes == sizeof(testdata));
+
+ rc = LLFile::remove(testfile1);
+ ensure("LLFile::remove() for file test_file.dat", rc == 0);
+ ensure("llfile_test.dat should not exist anymore", !LLFile::exists(testfile1));
+ ensure("llfile_test.dat should not be a file", !LLFile::isfile(testfile1));
+ ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1));
+ ensure("llfile_test.dat should not be a symlink", !LLFile::islink(testfile1));
+
+ rc = LLFile::remove(testdir);
+ ensure("LLFile::remove() for directory llfile_test failed", rc == 0);
+ ensure("llfile_test should not exist anymore", !LLFile::exists(testdir));
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<2>()
+ {
+ // High level static file IO functions to read and write data files
+ LLFile::mkdir(testdir);
+ ensure("llfile_test should exist", LLFile::isdir(testdir));
+
+ std::filesystem::path testfile1 = testdir;
+ testfile1.append("llfile_test.dat");
+
+ std::string testdata1("testdata");
+ std::string testdata2("datateststuff");
+ std::time_t current = time(nullptr);
+ S64 bytes = LLFile::write(testfile1, testdata1.c_str(), 0, testdata1.length());
+ ensure("LLFile::write() did not write correctly", bytes == testdata1.length());
+ ensure("llfile_test.dat should exist", LLFile::exists(testfile1));
+ ensure("llfile_test.dat should be a file", LLFile::isfile(testfile1));
+ ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1));
+
+ bytes = LLFile::size(testfile1);
+ ensure("LLFile::size() did not return the correct size", bytes == testdata1.length());
+
+ std::string data = LLFile::getContents(testfile1);
+ ensure("LLFile::getContents() did not return the correct size data", data.length() == testdata1.length());
+ ensure_memory_matches("LLFile::getContents() did not read correct data", testdata1.c_str(), (U32)testdata1.length(), data.c_str(), (U32)data.length());
+
+ char buffer[1024];
+ bytes = LLFile::read(testfile1, buffer, 0, testdata1.length());
+ ensure("LLFile:read() did not return the correct size", bytes == testdata1.length());
+ ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes);
+
+ // What if we try to read more data than there is in the file?
+ bytes = LLFile::read(testfile1, buffer, 0, bytes + 10);
+ ensure("LLFile:read() did not correctly stop on eof", bytes == testdata1.length());
+ ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes);
+
+ // Let's append more data
+ bytes = LLFile::write(testfile1, testdata2.c_str(), -1, testdata2.length());
+ ensure("LLFile::write() did not write correctly", bytes == testdata2.length());
+
+ bytes = LLFile::size(testfile1);
+ ensure("LLFile::size() did not return the correct size", bytes == testdata1.length() + testdata2.length());
+ bytes = LLFile::read(testfile1, buffer, 0, bytes);
+ ensure("LLFile:read() did not read correct number of bytes", bytes == testdata1.length() + testdata2.length());
+ ensure_memory_matches("LLFile:read() did not read correct testdata1", testdata1.c_str(), (U32)testdata1.length(), buffer, (U32)testdata1.length());
+ ensure_memory_matches("LLFile:read() did not read correct testdata2", testdata2.c_str(), (U32)testdata2.length(), buffer + testdata1.length(), (U32)testdata2.length());
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<3>()
+ {
+ const size_t numints = 1024;
+
+ // Testing the LLFile class implementation
+ std::filesystem::path testfile = testdir;
+ testfile.append("llfile_test.bin");
+
+ int data[numints];
+ for (int &t : data)
+ {
+ t = rand();
+ }
+
+ std::error_code ec;
+ LLFile fileout(testfile, LLFile::out, ec);
+ ensure("LLFile constructor did not open correctly", (bool)fileout);
+ ensure("error_code from LLFile constructor should not indicate an error", !ec);
+ if (fileout)
+ {
+ S64 length = fileout.size(ec);
+ ensure("freshly created file should be empty", length == 0);
+ ensure("error_code from LLFile::size() should not indicate an error", !ec);
+ S64 bytes = fileout.write(data, sizeof(data), ec);
+ ensure("LLFile::write() did not write correctly", bytes == sizeof(data));
+ ensure("error_code from LLFile::write() should not indicate an error", !ec);
+ bytes = fileout.write(data, sizeof(data), ec);
+ ensure("LLFile::write() did not write correctly", bytes == sizeof(data));
+ ensure("error_code from LLFile::write() should not indicate an error", !ec);
+ bytes = fileout.size(ec);
+ ensure("LLFile::size() returned wrong size", bytes == 2 * sizeof(data));
+ ensure("error_code from LLFile::size() should not indicate an error", !ec);
+ fileout.close();
+ }
+
+ LLFile filein(testfile, LLFile::in, ec);
+ ensure("LLFile constructor did not open correctly", (bool)filein);
+ ensure("error_code from LLFile constructor should not indicate an error", !ec);
+ if (filein)
+ {
+ S64 length = filein.size(ec);
+ ensure("LLFile::size() returned wrong size", length == 2 * sizeof(data));
+ ensure("error_code from LLFile::size() should not indicate an error", !ec);
+ char* buffer = (char*)malloc(length);
+ S64 bytes = filein.read(buffer, length, ec);
+ ensure("LLFile::read() did not read correctly", bytes == length);
+ ensure("error_code from LLFile::read() should not indicate an error", !ec);
+ ensure_memory_matches("LLFile:read() did not read correct data1", data, (U32)sizeof(data), buffer, (U32)sizeof(data));
+ ensure_memory_matches("LLFile:read() did not read correct data2", data, (U32)sizeof(data), buffer + sizeof(data), (U32)sizeof(data));
+ S64 offset = filein.tell(ec);
+ ensure("LLFile::tell() returned a bad offset", offset == length);
+ ensure("error_code from LLFile::read() should not indicate an error", !ec);
+ offset = sizeof(data) / 2;
+ int rc = filein.seek(offset, ec);
+ ensure("LLFile::seek() indicated an error", rc == 0);
+ ensure("error_code from LLFile::seek() should not indicate an error", !ec);
+ bytes = filein.read(buffer, 2 * sizeof(data), ec);
+ ensure("LLFile::read() did not read correctly", bytes == sizeof(data) + offset);
+ ensure("error_code from LLFile::read() should not indicate an error", !ec);
+ ensure_memory_matches("LLFile:read() did not read correct data3", (char*)data + offset, (U32)offset, buffer, (U32)offset);
+ ensure_memory_matches("LLFile:read() did not read correct data4", (char*)data, (U32)sizeof(data), buffer + offset, (U32)sizeof(data));
+ filein.close();
+
+ free(buffer);
+ }
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<4>()
+ {
+ // Testing the LLFile class implementation with wrong paths and parameters
+ std::filesystem::path testfile = testdir;
+ testfile.append("llfile_test.bin");
+
+ std::error_code ec;
+ LLFile file(testfile, LLFile::out | LLFile::noreplace, ec);
+ ensure("LLFile constructor should not have opened the already existing file", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+
+ LLFile::remove(testfile);
+ file = LLFile(testfile, LLFile::out | LLFile::app | LLFile::trunc, ec);
+ ensure("LLFile constructor should not have opened the file with conflicting flags", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+
+ file = LLFile(testfile, LLFile::out | LLFile::app | LLFile::noreplace, ec);
+ ensure("LLFile constructor should not have opened the file with conflicting flags", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+
+ testfile = testdir;
+ testfile.append("llfile_test");
+ testfile.append("llfile_test.bin");
+
+ file = LLFile(testfile, LLFile::in, ec);
+ ensure("LLFile constructor should not have been able to open the file in the non-existing directory", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<5>()
+ {
+ // Test file and directory operations with Unicode paths and filenames
+ std::filesystem::path testdir_unicode = get_testdir_unicode(tempdir);
+
+ // Unicode filename: "ファイル_テスト.bin" (means "file_test.bin" in Japanese)
+ std::string unicode_filename = "\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB_\xE3\x83\x86\xE3\x82\xB9\xE3\x83\x88.bin";
+ std::filesystem::path testfile_unicode = testdir_unicode;
+ testfile_unicode.append(unicode_filename);
+
+ // Clean up any previous test artifacts
+ clear_entire_dir(testdir_unicode);
+ ensure("Unicode test directory should not exist", !LLFile::exists(testdir_unicode));
+
+ // Create the Unicode directory
+ int rc = LLFile::mkdir(testdir_unicode);
+ ensure("LLFile::mkdir() failed for Unicode directory", rc == 0);
+ ensure("Unicode test directory should exist", LLFile::isdir(testdir_unicode));
+
+
+ ensure("Unicode test file should not exist", !LLFile::exists(testfile_unicode));
+
+ // Write to the Unicode file
+ const char* testdata = "unicode_testdata";
+ S64 bytes = LLFile::write(testfile_unicode, testdata, 0, strlen(testdata));
+ ensure("Unicode test file should exist", LLFile::exists(testfile_unicode));
+ ensure("Unicode test file should be a file", LLFile::isfile(testfile_unicode));
+ ensure("LLFile::write() did not write correctly to Unicode file", bytes == (S64)strlen(testdata));
+
+ // Read back the data
+ char buffer[64] = {};
+ bytes = LLFile::read(testfile_unicode, buffer, 0, sizeof(buffer));
+ ensure("LLFile::read() did not read correctly from Unicode file", bytes == (S64)strlen(testdata));
+ ensure_memory_matches("LLFile::read() did not read correct Unicode data", testdata, (U32)bytes, buffer, (U32)bytes);
+
+ // Remove the file and directory
+ rc = LLFile::remove(testfile_unicode);
+ ensure("LLFile::remove() failed for Unicode file", rc == 0);
+ ensure("Unicode test file should not exist after removal", !LLFile::exists(testfile_unicode));
+
+ rc = LLFile::remove(testdir_unicode);
+ ensure("LLFile::remove() failed for Unicode directory", rc == 0);
+ ensure("Unicode test directory should not exist after removal", !LLFile::exists(testdir_unicode));
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
index fa48bcdefd..ae20e7ed27 100644
--- a/indra/llcommon/tests/llleap_test.cpp
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -190,7 +190,7 @@ namespace tut
// computation, so I don't mind calling it twice.) Then take the
// basename.
reader_module(LLProcess::basename(
- reader.getName().substr(0, reader.getName().length()-3))),
+ reader.getPath().string().substr(0, reader.getPath().string().length()-3))),
PYTHON(LLStringUtil::getenv("PYTHON"))
{
ensure("Set PYTHON to interpreter pathname", !PYTHON.empty());
@@ -212,9 +212,9 @@ namespace tut
"time.sleep(1)\n");
LLLeapVector instances;
instances.push_back(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()})->getWeak());
+ StringVec{PYTHON, script.getPath().string()})->getWeak());
instances.push_back(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()})->getWeak());
+ StringVec{PYTHON, script.getPath().string()})->getWeak());
// In this case we're simply establishing that two LLLeap instances
// can coexist without throwing exceptions or bombing in any other
// way. Wait for them to terminate.
@@ -229,7 +229,7 @@ namespace tut
"import sys\n"
"sys.stderr.write('''Hello from Python!\n"
"note partial line''')\n");
- StringVec vcommand{ PYTHON, script.getName() };
+ StringVec vcommand{ PYTHON, script.getPath().string() };
CaptureLog log(LLError::LEVEL_INFO);
waitfor(LLLeap::create(get_test_name(), vcommand));
log.messageWith("Hello from Python!");
@@ -244,7 +244,7 @@ namespace tut
"print('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "Hello from Python!");
}
@@ -259,7 +259,7 @@ namespace tut
"sys.stdout.write('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
ensure_contains("error log line",
log.messageWith("Discarding"), "Hello from Python!");
}
@@ -273,7 +273,7 @@ namespace tut
"sys.stdout.write('5a2:something')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "5a2:");
}
@@ -386,7 +386,7 @@ namespace tut
" else 'bad: ' + str(resp)\n"
"send(pump='" << result.getName() << "', data=result)\n";});
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
result.ensure();
}
@@ -445,7 +445,7 @@ namespace tut
" result = 'expected reqid=%s in %s' % (i, resp)\n"
" break\n"
"send(pump='" << result.getName() << "', data=result)\n";});
- waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getName()}),
+ waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getPath().string()}),
300); // needs more realtime than most tests
result.ensure();
}
@@ -512,7 +512,7 @@ namespace tut
" (start, large[start:end], echoed[start:end]))\n"
"sys.exit(1)\n";});
waitfor(LLLeap::create(test_name,
- StringVec{PYTHON, script.getName(), stringize(size)}),
+ StringVec{PYTHON, script.getPath().string(), stringize(size)}),
180); // try a longer timeout
result.ensure();
}
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index cdf9f70b6e..5e653d0ba0 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -87,12 +87,12 @@ static void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR
* @param desc Optional description of the file for error message;
* defaults to "in <pathname>"
*/
-static std::string readfile(const std::string& pathname, const std::string& desc="")
+static std::string readfile(const std::filesystem::path& pathname, const std::string& desc="")
{
std::string use_desc(desc);
if (use_desc.empty())
{
- use_desc = "in " + pathname;
+ use_desc = "in " + pathname.string();
}
llifstream inf(pathname.c_str());
std::string output;
@@ -165,7 +165,7 @@ struct PythonProcessLauncher
mParams.desc = desc + " script";
mParams.executable = PYTHON;
- mParams.args.add(mScript.getName());
+ mParams.args.add(mScript.getPath().string());
}
/// Launch Python script; verify that it launched
@@ -233,11 +233,11 @@ struct PythonProcessLauncher
{
NamedTempFile out("out", ""); // placeholder
// pass name of this temporary file to the script
- mParams.args.add(out.getName());
+ mParams.args.add(out.getPath().string());
run();
// assuming the script wrote to that file, read it
std::string desc = "from " + mDesc + " script";
- return readfile(out.getName(), desc);
+ return readfile(out.getPath(), desc);
}
LLProcess::Params mParams;
@@ -271,23 +271,23 @@ public:
NamedTempDir():
mPath(NamedTempFile::temp_path()),
- mCreated(boost::filesystem::create_directories(mPath))
+ mCreated(std::filesystem::create_directories(mPath))
{
- mPath = boost::filesystem::canonical(mPath);
+ mPath = std::filesystem::canonical(mPath);
}
~NamedTempDir()
{
if (mCreated)
{
- boost::filesystem::remove_all(mPath);
+ std::filesystem::remove_all(mPath);
}
}
std::string getName() const { return mPath.string(); }
private:
- boost::filesystem::path mPath;
+ std::filesystem::path mPath;
bool mCreated;
};
@@ -440,7 +440,7 @@ namespace tut
#endif
// Have to have a named copy of this std::string so its c_str() value
// will persist.
- std::string scriptname(script.getName());
+ std::string scriptname(script.getPath().string());
argv.push_back(scriptname.c_str());
argv.push_back(NULL);
@@ -719,14 +719,14 @@ namespace tut
"with open(sys.argv[1], 'w') as f:\n"
" f.write('bad')\n");
NamedTempFile out("out", "not started");
- py.mParams.args.add(out.getName());
+ py.mParams.args.add(out.getPath().string());
py.launch();
// Wait for the script to wake up and do its first write
int i = 0, timeout = 60;
for ( ; i < timeout; ++i)
{
yield();
- if (readfile(out.getName(), "from kill() script") == "ok")
+ if (readfile(out.getPath(), "from kill() script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -745,7 +745,7 @@ namespace tut
// If kill() failed, the script would have woken up on its own and
// overwritten the file with 'bad'. But if kill() succeeded, it should
// not have had that chance.
- ensure_equals(get_test_name() + " script output", readfile(out.getName()), "ok");
+ ensure_equals(get_test_name() + " script output", readfile(out.getPath()), "ok");
}
template<> template<>
@@ -765,7 +765,7 @@ namespace tut
"# if caller hasn't managed to kill by now, bad\n"
"with open(sys.argv[1], 'w') as f:\n"
" f.write('bad')\n");
- py.mParams.args.add(out.getName());
+ py.mParams.args.add(out.getPath().string());
py.launch();
// Capture handle for later
phandle = py.mPy->getProcessHandle();
@@ -774,7 +774,7 @@ namespace tut
for ( ; i < timeout; ++i)
{
yield();
- if (readfile(out.getName(), "from kill() script") == "ok")
+ if (readfile(out.getPath(), "from kill() script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -787,7 +787,7 @@ namespace tut
// If kill() failed, the script would have woken up on its own and
// overwritten the file with 'bad'. But if kill() succeeded, it should
// not have had that chance.
- ensure_equals(get_test_name() + " script output", readfile(out.getName()), "ok");
+ ensure_equals(get_test_name() + " script output", readfile(out.getPath()), "ok");
}
template<> template<>
@@ -817,8 +817,8 @@ namespace tut
"# okay, saw 'go', write 'ack'\n"
"with open(sys.argv[1], 'w') as f:\n"
" f.write('ack')\n");
- py.mParams.args.add(from.getName());
- py.mParams.args.add(to.getName());
+ py.mParams.args.add(from.getPath().string());
+ py.mParams.args.add(to.getPath().string());
py.mParams.autokill = false;
py.launch();
// Capture handle for later
@@ -828,7 +828,7 @@ namespace tut
for ( ; i < timeout; ++i)
{
yield();
- if (readfile(from.getName(), "from autokill script") == "ok")
+ if (readfile(from.getPath(), "from autokill script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -840,14 +840,14 @@ namespace tut
// How do we know it's not terminated? By making it respond to
// a specific stimulus in a specific way.
{
- llofstream outf(to.getName().c_str());
+ llofstream outf(to.getPath());
outf << "go";
} // flush and close.
// now wait for the script to terminate... one way or another.
waitfor(phandle, "autokill script");
// If the LLProcess destructor implicitly called kill(), the
// script could not have written 'ack' as we expect.
- ensure_equals(get_test_name() + " script output", readfile(from.getName()), "ack");
+ ensure_equals(get_test_name() + " script output", readfile(from.getPath()), "ack");
}
template<> template<>
@@ -879,8 +879,8 @@ namespace tut
"# okay, saw 'go', write 'ack'\n"
"with open(sys.argv[1], 'w') as f:\n"
" f.write('ack')\n");
- py.mParams.args.add(from.getName());
- py.mParams.args.add(to.getName());
+ py.mParams.args.add(from.getPath().string());
+ py.mParams.args.add(to.getPath().string());
py.mParams.autokill = true;
py.mParams.attached = false;
py.launch();
@@ -891,7 +891,7 @@ namespace tut
for ( ; i < timeout; ++i)
{
yield();
- if (readfile(from.getName(), "from autokill script") == "ok")
+ if (readfile(from.getPath(), "from autokill script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -903,14 +903,14 @@ namespace tut
// How do we know it's not terminated? By making it respond to
// a specific stimulus in a specific way.
{
- llofstream outf(to.getName().c_str());
+ llofstream outf(to.getPath());
outf << "go";
} // flush and close.
// now wait for the script to terminate... one way or another.
waitfor(phandle, "autokill script");
// If the LLProcess destructor implicitly called kill(), the
// script could not have written 'ack' as we expect.
- ensure_equals(get_test_name() + " script output", readfile(from.getName()), "ack");
+ ensure_equals(get_test_name() + " script output", readfile(from.getPath()), "ack");
}
template<> template<>
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index 272eb55521..a7abfda099 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -1808,7 +1808,7 @@ namespace tut
#if LL_WINDOWS
std::string q("\"");
std::string qPYTHON(q + PYTHON + q);
- std::string qscript(q + scriptfile.getName() + q);
+ std::string qscript(q + scriptfile.getPath().string() + q);
int rc = (int)_spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(),
std::forward<ARGS>(args)..., NULL);
if (rc == -1)
@@ -1825,7 +1825,7 @@ namespace tut
#else // LL_DARWIN, LL_LINUX
LLProcess::Params params;
params.executable = PYTHON;
- params.args.add(scriptfile.getName());
+ params.args.add(scriptfile.getPath().string());
for (const std::string& arg : StringVec{ std::forward<ARGS>(args)... })
{
params.args.add(arg);
@@ -2002,8 +2002,8 @@ namespace tut
" yield frombytes\n"
<< pydata <<
// Don't forget raw-string syntax for Windows pathnames.
- "debug = open(r'" << debug.getName() << "', 'w')\n"
- "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n";});
+ "debug = open(r'" << debug.getPath().string() << "', 'w')\n"
+ "verify(parse_each(open(r'" << file.getPath().string() << "', 'rb')))\n";});
}
catch (const failure&)
{
@@ -2111,13 +2111,13 @@ namespace tut
"]\n"
// Don't forget raw-string syntax for Windows pathnames.
// N.B. Using 'print' implicitly adds newlines.
- "with open(r'" << file.getName() << "', 'wb') as f:\n"
+ "with open(r'" << (const char*)file.getPath().u8string().c_str() << "', 'wb') as f:\n"
" for item in DATA:\n"
" serialized = llsd." << pyformatter << "(item)\n"
" f.write(lenformat.pack(len(serialized)))\n"
" f.write(serialized)\n";});
- llifstream inf(file.getName().c_str());
+ llifstream inf(file.getPath());
LLSD item;
try
{
diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp
index 7efaebd569..4db44ff2a6 100644
--- a/indra/llcommon/workqueue.cpp
+++ b/indra/llcommon/workqueue.cpp
@@ -276,12 +276,30 @@ bool LL::WorkQueue::done()
bool LL::WorkQueue::post(const Work& callable)
{
- return mQueue.pushIfOpen(callable);
+ try
+ {
+ return mQueue.pushIfOpen(callable);
+ }
+ catch (std::bad_alloc&)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS("LLCoros") << "Bad memory allocation in WorkQueue::post" << LL_ENDL;
+ return false;
+ }
}
bool LL::WorkQueue::tryPost(const Work& callable)
{
- return mQueue.tryPush(callable);
+ try
+ {
+ return mQueue.tryPush(callable);
+ }
+ catch (std::bad_alloc&)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS("LLCoros") << "Bad memory allocation in WorkQueue::tryPost" << LL_ENDL;
+ return false;
+ }
}
LL::WorkQueue::Work LL::WorkQueue::pop_()