summaryrefslogtreecommitdiffstats
path: root/sal/osl
diff options
context:
space:
mode:
Diffstat (limited to 'sal/osl')
-rw-r--r--sal/osl/all/compat.cxx197
-rw-r--r--sal/osl/all/debugbase.cxx145
-rw-r--r--sal/osl/all/filepath.cxx113
-rw-r--r--sal/osl/all/loadmodulerelative.cxx60
-rw-r--r--sal/osl/all/log.cxx477
-rw-r--r--sal/osl/all/mutexshared.cxx20
-rw-r--r--sal/osl/all/signalshared.cxx141
-rw-r--r--sal/osl/all/utility.cxx49
-rw-r--r--sal/osl/unx/asm/interlck_sparc.s79
-rw-r--r--sal/osl/unx/backtrace.c246
-rw-r--r--sal/osl/unx/backtrace.h51
-rw-r--r--sal/osl/unx/backtraceapi.cxx279
-rw-r--r--sal/osl/unx/conditn.cxx270
-rw-r--r--sal/osl/unx/createfilehandlefromfd.hxx24
-rw-r--r--sal/osl/unx/file.cxx1606
-rw-r--r--sal/osl/unx/file_error_transl.cxx181
-rw-r--r--sal/osl/unx/file_error_transl.hxx37
-rw-r--r--sal/osl/unx/file_impl.hxx55
-rw-r--r--sal/osl/unx/file_misc.cxx1049
-rw-r--r--sal/osl/unx/file_path_helper.cxx262
-rw-r--r--sal/osl/unx/file_path_helper.hxx251
-rw-r--r--sal/osl/unx/file_stat.cxx453
-rw-r--r--sal/osl/unx/file_url.cxx972
-rw-r--r--sal/osl/unx/file_url.hxx50
-rw-r--r--sal/osl/unx/file_volume.cxx336
-rw-r--r--sal/osl/unx/interlck.cxx95
-rw-r--r--sal/osl/unx/memory.cxx35
-rw-r--r--sal/osl/unx/module.cxx344
-rw-r--r--sal/osl/unx/mutex.cxx163
-rw-r--r--sal/osl/unx/nlsupport.cxx872
-rw-r--r--sal/osl/unx/nlsupport.hxx40
-rw-r--r--sal/osl/unx/osxlocale.cxx104
-rw-r--r--sal/osl/unx/pipe.cxx523
-rw-r--r--sal/osl/unx/process.cxx1194
-rw-r--r--sal/osl/unx/process_impl.cxx473
-rw-r--r--sal/osl/unx/profile.cxx1866
-rw-r--r--sal/osl/unx/random.cxx46
-rw-r--r--sal/osl/unx/readwrite_helper.cxx79
-rw-r--r--sal/osl/unx/readwrite_helper.hxx26
-rw-r--r--sal/osl/unx/salinit.cxx98
-rw-r--r--sal/osl/unx/saltime.hxx29
-rw-r--r--sal/osl/unx/secimpl.hxx35
-rw-r--r--sal/osl/unx/security.cxx537
-rw-r--r--sal/osl/unx/signal.cxx452
-rw-r--r--sal/osl/unx/socket.cxx2046
-rw-r--r--sal/osl/unx/sockimpl.hxx61
-rw-r--r--sal/osl/unx/soffice.cxx25
-rw-r--r--sal/osl/unx/soffice.hxx26
-rw-r--r--sal/osl/unx/system.cxx167
-rw-r--r--sal/osl/unx/system.hxx406
-rw-r--r--sal/osl/unx/system.mm1
-rw-r--r--sal/osl/unx/tempfile.cxx340
-rw-r--r--sal/osl/unx/thread.cxx1102
-rw-r--r--sal/osl/unx/time.cxx310
-rw-r--r--sal/osl/unx/unixerrnostring.hxx27
-rw-r--r--sal/osl/unx/uunxapi.cxx905
-rw-r--r--sal/osl/unx/uunxapi.hxx83
-rw-r--r--sal/osl/unx/uunxapi.mm1
-rw-r--r--sal/osl/w32/backtrace.cxx92
-rw-r--r--sal/osl/w32/conditn.cxx114
-rw-r--r--sal/osl/w32/dllentry.cxx235
-rw-r--r--sal/osl/w32/file-impl.hxx25
-rw-r--r--sal/osl/w32/file.cxx1121
-rw-r--r--sal/osl/w32/file_dirvol.cxx1691
-rw-r--r--sal/osl/w32/file_error.cxx125
-rw-r--r--sal/osl/w32/file_error.hxx29
-rw-r--r--sal/osl/w32/file_url.cxx990
-rw-r--r--sal/osl/w32/file_url.hxx63
-rw-r--r--sal/osl/w32/filetime.hxx40
-rw-r--r--sal/osl/w32/interlck.cxx34
-rw-r--r--sal/osl/w32/memory.cxx24
-rw-r--r--sal/osl/w32/module.cxx202
-rw-r--r--sal/osl/w32/mutex.cxx88
-rw-r--r--sal/osl/w32/nlsupport.cxx202
-rw-r--r--sal/osl/w32/nlsupport.hxx21
-rw-r--r--sal/osl/w32/path_helper.cxx89
-rw-r--r--sal/osl/w32/path_helper.hxx125
-rw-r--r--sal/osl/w32/pipe.cxx489
-rw-r--r--sal/osl/w32/process.cxx525
-rw-r--r--sal/osl/w32/procimpl.cxx596
-rw-r--r--sal/osl/w32/procimpl.hxx33
-rw-r--r--sal/osl/w32/profile.cxx2341
-rw-r--r--sal/osl/w32/random.cxx63
-rw-r--r--sal/osl/w32/salinit.cxx83
-rw-r--r--sal/osl/w32/secimpl.hxx40
-rw-r--r--sal/osl/w32/security.cxx662
-rw-r--r--sal/osl/w32/signal.cxx133
-rw-r--r--sal/osl/w32/socket.cxx1611
-rw-r--r--sal/osl/w32/sockimpl.hxx42
-rw-r--r--sal/osl/w32/system.h54
-rw-r--r--sal/osl/w32/tempfile.cxx238
-rw-r--r--sal/osl/w32/thread.cxx540
-rw-r--r--sal/osl/w32/thread.hxx18
-rw-r--r--sal/osl/w32/time.cxx200
-rw-r--r--sal/osl/w32/time.hxx19
95 files changed, 32911 insertions, 0 deletions
diff --git a/sal/osl/all/compat.cxx b/sal/osl/all/compat.cxx
new file mode 100644
index 000000000..8fafc628b
--- /dev/null
+++ b/sal/osl/all/compat.cxx
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <cstdlib>
+
+#include <osl/module.h>
+#include <osl/pipe.h>
+#include <osl/socket.h>
+#include <osl/time.h>
+#include <sal/types.h>
+
+// Stubs for removed functionality, to be killed when we bump sal SONAME
+
+extern "C" {
+
+SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL osl_acquireSemaphore(void *) {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC void SAL_CALL osl_addToSocketSet(void *, oslSocket) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT int SAL_CALL osl_areCommandArgsSet() {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL osl_assertFailedLine(
+ char const *, sal_Int32, char const *)
+{
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL osl_breakDebug() {
+ std::abort();
+}
+
+SAL_DLLPUBLIC void SAL_CALL osl_clearSocketSet(void *) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT void * SAL_CALL osl_createSemaphore(sal_uInt32) {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC void * SAL_CALL osl_createSocketSet() {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC sal_Int32 SAL_CALL osl_demultiplexSocketEvents(
+ void *, void *, void *, TimeValue const *)
+{
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL osl_destroySemaphore(void *) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC void SAL_CALL osl_destroySocketSet(void *) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL osl_getEthernetAddress(sal_uInt8 *) {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC sal_Bool SAL_CALL osl_isInSocketSet(void *, oslSocket) {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT oslSocket SAL_CALL osl_receiveResourcePipe(oslPipe) {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL osl_releaseSemaphore(void *) {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT sal_Int32 SAL_CALL osl_reportError(
+ sal_uInt32, char const *)
+{
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC void SAL_CALL osl_removeFromSocketSet(void *, oslSocket) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL osl_sendResourcePipe(oslPipe, oslSocket)
+{
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+namespace {
+typedef void (* pfunc_osl_printDebugMessage)(char const *);
+}
+SAL_DLLPUBLIC_EXPORT pfunc_osl_printDebugMessage SAL_CALL
+osl_setDebugMessageFunc(pfunc_osl_printDebugMessage) {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+namespace {
+typedef void (* pfunc_osl_printDetailedDebugMessage)(
+ char const *, sal_Int32, char const *);
+}
+SAL_DLLPUBLIC_EXPORT pfunc_osl_printDetailedDebugMessage SAL_CALL
+osl_setDetailedDebugMessageFunc(pfunc_osl_printDetailedDebugMessage) {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL osl_trace(char const *, ...) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL osl_tryToAcquireSemaphore(void *) {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT sal_Int32 SAL_CALL rtl_addUnloadingListener(
+ void (SAL_CALL *)(void *), void *)
+{
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT sal_Int32 SAL_CALL rtl_compareMemory(
+ void const *, void const *, sal_Size)
+{
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL rtl_copyMemory(
+ void *, void const *, sal_Size)
+{
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL rtl_fillMemory(void *, sal_Size, sal_uInt8) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT void * SAL_CALL rtl_findInMemory(
+ void const *, sal_uInt8, sal_Size)
+{
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL rtl_moveMemory(
+ void *, void const *, sal_Size)
+{
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL rtl_registerModuleForUnloading(oslModule)
+{
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL rtl_removeUnloadingListener(sal_Int32) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL rtl_unloadUnusedModules(TimeValue *) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL rtl_unregisterModuleForUnloading(oslModule) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL rtl_zeroMemory(void *, sal_Size) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL rtl_logfile_trace( const char*, ... ) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT void SAL_CALL rtl_logfile_longTrace(char const *, ...) {
+ std::abort();
+}
+
+SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL rtl_logfile_hasLogFile() {
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/all/debugbase.cxx b/sal/osl/all/debugbase.cxx
new file mode 100644
index 000000000..59848a883
--- /dev/null
+++ b/sal/osl/all/debugbase.cxx
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/strbuf.hxx>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <osl/process.h>
+#include <osl/diagnose.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <algorithm>
+#include <vector>
+
+namespace {
+
+struct StaticDebugBaseAddressFilter
+ : rtl::StaticWithInit<std::vector<OString>, StaticDebugBaseAddressFilter> {
+ std::vector<OString> operator()() const {
+ std::vector<OString> vec;
+ rtl_uString * pStr = nullptr;
+ OUString const name(
+ "OSL_DEBUGBASE_STORE_ADDRESSES" );
+ if (osl_getEnvironment( name.pData, &pStr ) == osl_Process_E_None) {
+ OUString const str(pStr);
+ rtl_uString_release(pStr);
+ sal_Int32 nIndex = 0;
+ do {
+ vec.push_back( OUStringToOString(
+ o3tl::getToken(str, 0, ';', nIndex ),
+ RTL_TEXTENCODING_ASCII_US ) );
+ }
+ while (nIndex >= 0);
+ }
+ return vec;
+ }
+};
+
+bool isSubStr( char const* pStr, OString const& subStr )
+{
+ return rtl_str_indexOfStr( pStr, subStr.getStr() ) >= 0;
+}
+
+} // anon namespace
+
+extern "C" {
+
+// These functions presumably should not be extern "C", but changing
+// that would break binary compatibility.
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
+#endif
+
+osl::Mutex & SAL_CALL osl_detail_ObjectRegistry_getMutex()
+ SAL_THROW_EXTERN_C()
+{
+ static osl::Mutex aMutex;
+ return aMutex;
+}
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+bool SAL_CALL osl_detail_ObjectRegistry_storeAddresses( char const* pName )
+ SAL_THROW_EXTERN_C()
+{
+ std::vector<OString> const& rVec = StaticDebugBaseAddressFilter::get();
+ if (rVec.empty())
+ return false;
+ // check for "all":
+ OString const& rFirst = rVec[0];
+ if ( rFirst == "all" )
+ return true;
+ auto const iEnd( rVec.cend() );
+ return std::any_of( rVec.begin(), iEnd,
+ [pName] (OString const& it) { return isSubStr(pName, it); });
+}
+
+bool SAL_CALL osl_detail_ObjectRegistry_checkObjectCount(
+ osl::detail::ObjectRegistryData const& rData, std::size_t nExpected )
+ SAL_THROW_EXTERN_C()
+{
+ std::size_t nSize;
+ if (rData.m_bStoreAddresses)
+ nSize = rData.m_addresses.size();
+ else
+ nSize = static_cast<std::size_t>(rData.m_nCount);
+
+ bool const bRet = (nSize == nExpected);
+ SAL_WARN_IF(
+ !bRet, "sal.osl",
+ "unexpected number of " << rData.m_pName << ": " << nSize
+ << "; Expected: " << nExpected);
+ return bRet;
+}
+
+void SAL_CALL osl_detail_ObjectRegistry_registerObject(
+ osl::detail::ObjectRegistryData & rData, void const* pObj )
+ SAL_THROW_EXTERN_C()
+{
+ if (rData.m_bStoreAddresses) {
+ osl::MutexGuard const guard( osl_detail_ObjectRegistry_getMutex() );
+ std::pair<osl::detail::VoidPointerSet::iterator, bool> const insertion(
+ rData.m_addresses.insert(pObj) );
+ SAL_WARN_IF(!insertion.second, "sal.osl", "insertion failed!?");
+ }
+ else {
+ osl_atomic_increment(&rData.m_nCount);
+ }
+}
+
+void SAL_CALL osl_detail_ObjectRegistry_revokeObject(
+ osl::detail::ObjectRegistryData & rData, void const* pObj )
+ SAL_THROW_EXTERN_C()
+{
+ if (rData.m_bStoreAddresses) {
+ osl::MutexGuard const guard( osl_detail_ObjectRegistry_getMutex() );
+ std::size_t const n = rData.m_addresses.erase(pObj);
+ SAL_WARN_IF(n != 1, "sal.osl", "erased more than 1 entry!?");
+ }
+ else {
+ osl_atomic_decrement(&rData.m_nCount);
+ }
+}
+
+} // extern "C"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/all/filepath.cxx b/sal/osl/all/filepath.cxx
new file mode 100644
index 000000000..afba01739
--- /dev/null
+++ b/sal/osl/all/filepath.cxx
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/file.h>
+#include <rtl/ustring.h>
+#include <cassert>
+
+static sal_uInt32 osl_defCalcTextWidth( rtl_uString *ustrText )
+{
+ return ustrText ? ustrText->length : 0;
+}
+
+oslFileError SAL_CALL osl_abbreviateSystemPath( rtl_uString *ustrSystemPath, rtl_uString **pustrCompacted, sal_uInt32 uMaxWidth, oslCalcTextWidthFunc pfnCalcWidth )
+{
+ rtl_uString *ustrPath = nullptr;
+ rtl_uString *ustrFile = nullptr;
+ sal_uInt32 uPathWidth, uFileWidth;
+
+ if ( !pfnCalcWidth )
+ pfnCalcWidth = osl_defCalcTextWidth;
+
+ {
+ sal_Int32 iLastSlash = rtl_ustr_lastIndexOfChar_WithLength( ustrSystemPath->buffer, ustrSystemPath->length, SAL_PATHDELIMITER );
+
+ if ( iLastSlash >= 0 )
+ {
+ rtl_uString_newFromStr_WithLength( &ustrPath, ustrSystemPath->buffer, iLastSlash );
+ rtl_uString_newFromStr_WithLength( &ustrFile, &ustrSystemPath->buffer[iLastSlash], ustrSystemPath->length - iLastSlash );
+ }
+ else
+ {
+ rtl_uString_new( &ustrPath );
+ rtl_uString_newFromString( &ustrFile, ustrSystemPath );
+ }
+ }
+
+ assert(ustrPath && ustrFile);
+
+ uPathWidth = pfnCalcWidth( ustrPath );
+ uFileWidth = pfnCalcWidth( ustrFile );
+
+ /* First abbreviate the directory component of the path */
+
+ while ( uPathWidth + uFileWidth > uMaxWidth )
+ {
+ if ( ustrPath->length > 3 )
+ {
+ ustrPath->length--;
+ ustrPath->buffer[ustrPath->length-3] = '.';
+ ustrPath->buffer[ustrPath->length-2] = '.';
+ ustrPath->buffer[ustrPath->length-1] = '.';
+ ustrPath->buffer[ustrPath->length] = 0;
+
+ uPathWidth = pfnCalcWidth( ustrPath );
+ }
+ else
+ break;
+ }
+
+ /* Now abbreviate file component */
+
+ while ( uPathWidth + uFileWidth > uMaxWidth )
+ {
+ if ( ustrFile->length > 4 )
+ {
+ ustrFile->length--;
+ ustrFile->buffer[ustrFile->length-3] = '.';
+ ustrFile->buffer[ustrFile->length-2] = '.';
+ ustrFile->buffer[ustrFile->length-1] = '.';
+ ustrFile->buffer[ustrFile->length] = 0;
+
+ uFileWidth = pfnCalcWidth( ustrFile );
+ }
+ else
+ break;
+ }
+
+ rtl_uString_newConcat( pustrCompacted, ustrPath, ustrFile );
+
+ /* Event now if path was compacted to ".../..." it can be too large */
+
+ uPathWidth += uFileWidth;
+
+ while ( uPathWidth > uMaxWidth )
+ {
+ (*pustrCompacted)->length--;
+ (*pustrCompacted)->buffer[(*pustrCompacted)->length] = 0;
+ uPathWidth = pfnCalcWidth( *pustrCompacted );
+ }
+
+ rtl_uString_release(ustrPath);
+ rtl_uString_release(ustrFile);
+
+ return osl_File_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/all/loadmodulerelative.cxx b/sal/osl/all/loadmodulerelative.cxx
new file mode 100644
index 000000000..242bb6210
--- /dev/null
+++ b/sal/osl/all/loadmodulerelative.cxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cstddef>
+
+#include <sal/log.hxx>
+#include <osl/module.h>
+#include <osl/module.hxx>
+#include <rtl/malformeduriexception.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+extern "C" {
+
+#ifndef DISABLE_DYNLOADING
+
+oslModule SAL_CALL osl_loadModuleRelative(
+ oslGenericFunction const baseModule, rtl_uString * const relativePath,
+ sal_Int32 const mode)
+{
+ OUString base;
+ if (!osl::Module::getUrlFromAddress(baseModule, base)) {
+ SAL_INFO("sal.osl","osl::Module::getUrlFromAddress failed");
+ return nullptr;
+ }
+ OUString abs;
+ try {
+ abs = rtl::Uri::convertRelToAbs(base, relativePath);
+ } catch (const rtl::MalformedUriException & e) {
+ SAL_INFO("sal.osl", "rtl::MalformedUriException <" << e.getMessage() << ">");
+ return nullptr;
+ }
+ return osl_loadModule(abs.pData, mode);
+}
+
+#endif // !DISABLE_DYNLOADING
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/all/log.cxx b/sal/osl/all/log.cxx
new file mode 100644
index 000000000..405d5599c
--- /dev/null
+++ b/sal/osl/all/log.cxx
@@ -0,0 +1,477 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <sstream>
+
+#include <stdio.h>
+#include <string.h>
+#include <fstream>
+
+#include <config_global.h>
+#include <osl/thread.hxx>
+#include <rtl/string.h>
+#include <sal/detail/log.h>
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <backtraceasstring.hxx>
+#include <salusesyslog.hxx>
+
+#if defined ANDROID
+#include <android/log.h>
+#elif defined _WIN32
+#include <process.h>
+#include <windows.h>
+#define OSL_DETAIL_GETPID _getpid()
+#else
+#include <unistd.h>
+#define OSL_DETAIL_GETPID getpid()
+#endif
+
+#if HAVE_SYSLOG_H
+#include <syslog.h>
+// sal/osl/unx/salinit.cxx::sal_detail_initialize updates this:
+bool sal_use_syslog;
+#else
+bool const sal_use_syslog = false;
+#endif
+
+// Avoid the use of other sal code in this file as much as possible, so that
+// this code can be called from other sal code without causing endless
+// recursion.
+
+namespace {
+
+struct TimeContainer
+{
+ TimeValue aTime;
+ TimeContainer()
+ {
+ osl_getSystemTime(&aTime);
+ }
+};
+
+TimeContainer aStartTime;
+
+bool equalStrings(
+ char const * string1, std::size_t length1, char const * string2,
+ std::size_t length2)
+{
+ return length1 == length2 && std::memcmp(string1, string2, length1) == 0;
+}
+
+#if !defined ANDROID
+char const * toString(sal_detail_LogLevel level) {
+ switch (level) {
+ case SAL_DETAIL_LOG_LEVEL_INFO:
+ return "info";
+ case SAL_DETAIL_LOG_LEVEL_WARN:
+ return "warn";
+ case SAL_DETAIL_LOG_LEVEL_DEBUG:
+ return "debug";
+ default:
+ assert(false); // this cannot happen
+ return "broken";
+ }
+}
+#endif
+
+#ifdef _WIN32
+
+char const* setEnvFromLoggingIniFile(const char* env, const char* key)
+{
+ char const* sResult = nullptr;
+ wchar_t buffer[MAX_PATH];
+ GetModuleFileNameW(nullptr, buffer, MAX_PATH);
+ std::wstring sProgramDirectory(buffer);
+ std::wstring::size_type pos = sProgramDirectory.find_last_of(L"\\/");
+ sProgramDirectory = sProgramDirectory.substr(0, pos+1);
+ sProgramDirectory += L"logging.ini";
+
+ std::ifstream logFileStream(sProgramDirectory);
+ if (!logFileStream.good())
+ return sResult;
+
+ std::size_t n;
+ std::string aKey;
+ std::string sWantedKey(key);
+ std::string sLine;
+ while (std::getline(logFileStream, sLine)) {
+ if (sLine.find('#') == 0)
+ continue;
+ if ( ( n = sLine.find('=') ) != std::string::npos) {
+ aKey = sLine.substr(0, n);
+ if (aKey != sWantedKey)
+ continue;
+ _putenv_s(env, sLine.substr(n+1, sLine.length()).c_str());
+ sResult = std::getenv(env);
+ break;
+ }
+ }
+ return sResult;
+}
+#endif
+
+char const* pLogSelector = nullptr;
+
+char const* getLogLevelEnvVar() {
+ static char const* const pLevel = [] {
+ char const* pResult = nullptr;
+
+ // First check the environment variable, then the setting in logging.ini
+ char const* env = std::getenv("SAL_LOG");
+
+#ifdef _WIN32
+ if (!env)
+ env = setEnvFromLoggingIniFile("SAL_LOG", "LogLevel");
+#endif
+
+ if (env)
+ {
+ // Make a copy from the string in environment block
+ static std::string sLevel(env);
+ pResult = sLevel.c_str();
+ }
+ return pResult;
+ }();
+
+ return pLevel;
+}
+
+#if !defined ANDROID
+
+std::ofstream * getLogFile() {
+ static std::ofstream* const pFile = [] {
+ std::ofstream* pResult = nullptr;
+
+ // First check the environment variable, then the setting in logging.ini
+ char const* logFile = std::getenv("SAL_LOG_FILE");
+
+#ifdef _WIN32
+ if (!logFile)
+ logFile = setEnvFromLoggingIniFile("SAL_LOG_FILE", "LogFilePath");
+#endif
+
+ if (logFile)
+ {
+ // stays until process exits
+ static std::ofstream file(logFile, std::ios::app | std::ios::out);
+ pResult = &file;
+ }
+
+ return pResult;
+ }();
+
+ return pFile;
+}
+
+
+std::pair<bool, bool> getTimestampFlags(char const *selector)
+{
+ bool outputTimestamp = false;
+ bool outputRelativeTimer = false;
+ for (char const* p = selector; p && *p;)
+ {
+ if (*p++ == '+')
+ {
+ char const * p1 = p;
+ while (*p1 != '.' && *p1 != '+' && *p1 != '-' && *p1 != '\0') {
+ ++p1;
+ }
+ if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("TIMESTAMP")))
+ outputTimestamp = true;
+ else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("RELATIVETIMER")))
+ outputRelativeTimer = true;
+ char const * p2 = p1;
+ while (*p2 != '+' && *p2 != '-' && *p2 != '\0') {
+ ++p2;
+ }
+ p = p2;
+ }
+ }
+ return std::pair(outputTimestamp, outputRelativeTimer);
+}
+
+void maybeOutputTimestamp(std::ostringstream &s) {
+ static const std::pair<bool, bool> aEnvFlags = getTimestampFlags(getLogLevelEnvVar());
+ const auto& [outputTimestamp, outputRelativeTimer] = (pLogSelector == nullptr ? aEnvFlags : getTimestampFlags(pLogSelector));
+
+ if (outputTimestamp)
+ {
+ char ts[100];
+ TimeValue systemTime;
+ osl_getSystemTime(&systemTime);
+ TimeValue localTime;
+ osl_getLocalTimeFromSystemTime(&systemTime, &localTime);
+ oslDateTime dateTime;
+ osl_getDateTimeFromTimeValue(&localTime, &dateTime);
+ struct tm tm;
+ tm.tm_sec = dateTime.Seconds;
+ tm.tm_min = dateTime.Minutes;
+ tm.tm_hour = dateTime.Hours;
+ tm.tm_mday = dateTime.Day;
+ tm.tm_wday = dateTime.DayOfWeek;
+ tm.tm_mon = dateTime.Month - 1;
+ tm.tm_year = dateTime.Year - 1900;
+ tm.tm_yday = 0;
+ strftime(ts, sizeof(ts), "%Y-%m-%d:%H:%M:%S", &tm);
+ char milliSecs[11];
+ snprintf(milliSecs, sizeof(milliSecs), "%03u",
+ static_cast<unsigned>(dateTime.NanoSeconds / 1000000));
+ s << ts << '.' << milliSecs << ':';
+ }
+ if (!outputRelativeTimer)
+ return;
+
+ TimeValue now;
+ osl_getSystemTime(&now);
+ int seconds = now.Seconds - aStartTime.aTime.Seconds;
+ int milliSeconds;
+ if (now.Nanosec < aStartTime.aTime.Nanosec)
+ {
+ seconds--;
+ milliSeconds = 1000 - (aStartTime.aTime.Nanosec - now.Nanosec) / 1000000;
+ }
+ else
+ milliSeconds = (now.Nanosec - aStartTime.aTime.Nanosec) / 1000000;
+ char relativeTimestamp[100];
+ snprintf(relativeTimestamp, sizeof(relativeTimestamp), "%d.%03d", seconds, milliSeconds);
+ s << relativeTimestamp << ':';
+}
+
+#endif
+
+}
+
+void sal_detail_log(
+ sal_detail_LogLevel level, char const * area, char const * where,
+ char const * message, sal_uInt32 backtraceDepth)
+{
+ std::ostringstream s;
+#if !defined ANDROID
+ // On Android, the area will be used as the "tag," and log info already
+ // contains timestamp and PID.
+ if (!sal_use_syslog) {
+ maybeOutputTimestamp(s);
+ s << toString(level) << ':';
+ }
+ if (level != SAL_DETAIL_LOG_LEVEL_DEBUG) {
+ s << area << ':';
+ }
+ s << OSL_DETAIL_GETPID << ':';
+#endif
+ s << osl::Thread::getCurrentIdentifier() << ':';
+ if (level == SAL_DETAIL_LOG_LEVEL_DEBUG) {
+ s << ' ';
+ } else {
+ const size_t nStrLen(std::strlen(SRCDIR "/"));
+ s << (where
+ + (std::strncmp(where, SRCDIR "/", nStrLen) == 0
+ ? nStrLen : 0));
+ }
+ s << message;
+ if (backtraceDepth != 0) {
+ s << " at:\n" << osl::detail::backtraceAsString(backtraceDepth);
+ }
+
+#if defined ANDROID
+ int android_log_level;
+ switch (level) {
+ case SAL_DETAIL_LOG_LEVEL_INFO:
+ android_log_level = ANDROID_LOG_INFO;
+ break;
+ case SAL_DETAIL_LOG_LEVEL_WARN:
+ android_log_level = ANDROID_LOG_WARN;
+ break;
+ case SAL_DETAIL_LOG_LEVEL_DEBUG:
+ android_log_level = ANDROID_LOG_DEBUG;
+ break;
+ default:
+ android_log_level = ANDROID_LOG_INFO;
+ break;
+ }
+ __android_log_print(
+ android_log_level, area == 0 ? "LibreOffice" : area, "%s",
+ s.str().c_str());
+#else
+ if (sal_use_syslog) {
+#if HAVE_SYSLOG_H
+ int prio;
+ switch (level) {
+ case SAL_DETAIL_LOG_LEVEL_INFO:
+ prio = LOG_INFO;
+ break;
+ case SAL_DETAIL_LOG_LEVEL_WARN:
+ prio = LOG_WARNING;
+ break;
+ case SAL_DETAIL_LOG_LEVEL_DEBUG:
+ prio = LOG_DEBUG;
+ break;
+ default:
+ assert(false); // this cannot happen
+ prio = LOG_WARNING;
+ }
+ syslog(prio, "%s", s.str().c_str());
+#endif
+ } else {
+ // avoid calling getLogFile() more than once
+ static std::ofstream * logFile = getLogFile();
+ if (logFile) {
+ *logFile << s.str() << std::endl;
+ }
+ else {
+ s << '\n';
+#ifdef _WIN32
+ // write to Windows debugger console, too
+ OutputDebugStringA(s.str().c_str());
+#endif
+ std::fputs(s.str().c_str(), stderr);
+ std::fflush(stderr);
+ }
+ }
+#endif
+}
+
+void sal_detail_set_log_selector(char const *logSelector)
+{
+ pLogSelector = logSelector;
+}
+
+void sal_detail_logFormat(
+ sal_detail_LogLevel level, char const * area, char const * where,
+ char const * format, ...)
+{
+ const sal_detail_LogAction eAction
+ = static_cast<sal_detail_LogAction>(sal_detail_log_report(level, area));
+ if (eAction == SAL_DETAIL_LOG_ACTION_IGNORE)
+ return;
+
+ std::va_list args;
+ va_start(args, format);
+ char buf[1024];
+ int const len = sizeof buf - RTL_CONSTASCII_LENGTH("...");
+ int n = vsnprintf(buf, len, format, args);
+ if (n < 0) {
+ std::strcpy(buf, "???");
+ } else if (n >= len) {
+ std::strcpy(buf + len - 1, "...");
+ }
+ sal_detail_log(level, area, where, buf, 0);
+ va_end(args);
+
+ if (eAction == SAL_DETAIL_LOG_ACTION_FATAL)
+ std::abort();
+}
+
+unsigned char sal_detail_log_report(sal_detail_LogLevel level, char const * area)
+{
+ if (level == SAL_DETAIL_LOG_LEVEL_DEBUG) {
+ return SAL_DETAIL_LOG_ACTION_LOG;
+ }
+ assert(area != nullptr);
+ static char const* const envEnv = [] {
+ char const* pResult = getLogLevelEnvVar();
+ if (!pResult)
+ pResult = "+WARN";
+ return pResult;
+ }();
+ char const* const env = (pLogSelector == nullptr ? envEnv : pLogSelector);
+ std::size_t areaLen = std::strlen(area);
+ enum Sense { POSITIVE = 0, NEGATIVE = 1 };
+ std::size_t senseLen[2] = { 0, 1 };
+ // initial senseLen[POSITIVE] < senseLen[NEGATIVE], so that if there are
+ // no matching switches at all, the result will be negative (and
+ // initializing with 1 is safe as the length of a valid switch, even
+ // without the "+"/"-" prefix, will always be > 1)
+ bool senseFatal[2] = { false, false };
+ bool seenWarn = false;
+ bool bFlagFatal = false;
+ for (char const * p = env;;) {
+ Sense sense;
+ switch (*p++) {
+ case '\0':
+ {
+ if (level == SAL_DETAIL_LOG_LEVEL_WARN && !seenWarn)
+ return sal_detail_log_report(SAL_DETAIL_LOG_LEVEL_INFO, area);
+
+ sal_detail_LogAction eAction = SAL_DETAIL_LOG_ACTION_IGNORE;
+ // if a specific item is positive and negative (==), default to positive
+ if (senseLen[POSITIVE] >= senseLen[NEGATIVE])
+ {
+ if (senseFatal[POSITIVE]) eAction = SAL_DETAIL_LOG_ACTION_FATAL;
+ else eAction = SAL_DETAIL_LOG_ACTION_LOG;
+ }
+ return eAction;
+ }
+ case '+':
+ sense = POSITIVE;
+ break;
+ case '-':
+ sense = NEGATIVE;
+ break;
+ default:
+ return SAL_DETAIL_LOG_ACTION_LOG; // upon an illegal SAL_LOG value, enable everything
+ }
+ char const * p1 = p;
+ while (*p1 != '.' && *p1 != '+' && *p1 != '-' && *p1 != '\0') {
+ ++p1;
+ }
+ bool match;
+ if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("INFO"))) {
+ match = level == SAL_DETAIL_LOG_LEVEL_INFO;
+ } else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("WARN")))
+ {
+ match = level == SAL_DETAIL_LOG_LEVEL_WARN;
+ seenWarn = true;
+ } else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("FATAL")))
+ {
+ bFlagFatal = (sense == POSITIVE);
+ match = false;
+ } else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("TIMESTAMP")) ||
+ equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("RELATIVETIMER")))
+ {
+ // handled later
+ match = false;
+ } else {
+ return SAL_DETAIL_LOG_ACTION_LOG;
+ // upon an illegal SAL_LOG value, everything is considered
+ // positive
+ }
+ char const * p2 = p1;
+ while (*p2 != '+' && *p2 != '-' && *p2 != '\0') {
+ ++p2;
+ }
+ if (match) {
+ if (*p1 == '.') {
+ ++p1;
+ std::size_t n = p2 - p1;
+ if ((n == areaLen && equalStrings(p1, n, area, areaLen))
+ || (n < areaLen && area[n] == '.'
+ && equalStrings(p1, n, area, n)))
+ {
+ senseLen[sense] = p2 - p;
+ senseFatal[sense] = bFlagFatal;
+ }
+ } else {
+ senseLen[sense] = p1 - p;
+ senseFatal[sense] = bFlagFatal;
+ }
+ }
+ p = p2;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/all/mutexshared.cxx b/sal/osl/all/mutexshared.cxx
new file mode 100644
index 000000000..3de7f9930
--- /dev/null
+++ b/sal/osl/all/mutexshared.cxx
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <osl/mutex.hxx>
+
+oslMutex* SAL_CALL osl_getGlobalMutex()
+{
+ static osl::Mutex g_Mutex;
+ return &g_Mutex.mutex;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sal/osl/all/signalshared.cxx b/sal/osl/all/signalshared.cxx
new file mode 100644
index 000000000..530413f26
--- /dev/null
+++ b/sal/osl/all/signalshared.cxx
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <stdlib.h>
+
+#include <signalshared.hxx>
+
+#include <osl/diagnose.h>
+#include <mutex>
+
+namespace
+{
+oslSignalHandlerImpl* SignalList;
+bool bInitSignal = false;
+
+std::mutex& getSignalMutex()
+{
+ static std::mutex aMutex;
+ return aMutex;
+}
+}
+
+oslSignalAction callSignalHandler(oslSignalInfo* pInfo)
+{
+ oslSignalHandlerImpl* pHandler = SignalList;
+ oslSignalAction Action = osl_Signal_ActCallNextHdl;
+
+ while (pHandler)
+ {
+ if ((Action = pHandler->Handler(pHandler->pData, pInfo)) != osl_Signal_ActCallNextHdl)
+ break;
+
+ pHandler = pHandler->pNext;
+ }
+
+ return Action;
+}
+
+oslSignalHandler SAL_CALL osl_addSignalHandler(oslSignalHandlerFunction handler, void* pData)
+{
+ if (!handler)
+ return nullptr;
+
+ oslSignalHandlerImpl* pHandler
+ = static_cast<oslSignalHandlerImpl*>(calloc(1, sizeof(oslSignalHandlerImpl)));
+
+ std::scoped_lock aGuard(getSignalMutex());
+
+ if (!bInitSignal)
+ bInitSignal = onInitSignal();
+
+ if (pHandler)
+ {
+ pHandler->Handler = handler;
+ pHandler->pData = pData;
+
+ pHandler->pNext = SignalList;
+ SignalList = pHandler;
+
+ return pHandler;
+ }
+
+ return nullptr;
+}
+
+sal_Bool SAL_CALL osl_removeSignalHandler(oslSignalHandler handler)
+{
+ std::scoped_lock aGuard(getSignalMutex());
+
+ if (!bInitSignal)
+ bInitSignal = onInitSignal();
+
+ oslSignalHandlerImpl* pHandler = SignalList;
+ oslSignalHandlerImpl* pPrevious = nullptr;
+
+ while (pHandler)
+ {
+ if (pHandler == handler)
+ {
+ if (pPrevious)
+ pPrevious->pNext = pHandler->pNext;
+ else
+ SignalList = pHandler->pNext;
+
+ if (SignalList == nullptr)
+ bInitSignal = onDeInitSignal();
+
+ free(pHandler);
+
+ return true;
+ }
+
+ pPrevious = pHandler;
+ pHandler = pHandler->pNext;
+ }
+
+ return false;
+}
+
+oslSignalAction SAL_CALL osl_raiseSignal(sal_Int32 userSignal, void* userData)
+{
+ std::scoped_lock aGuard(getSignalMutex());
+
+ if (!bInitSignal)
+ bInitSignal = onInitSignal();
+
+ oslSignalInfo info;
+ info.Signal = osl_Signal_User;
+ info.UserSignal = userSignal;
+ info.UserData = userData;
+
+ oslSignalAction action = callSignalHandler(&info);
+
+ return action;
+}
+
+sal_Bool SAL_CALL osl_setErrorReporting(sal_Bool /*bEnable*/)
+{
+ // this is part of the stable API
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/all/utility.cxx b/sal/osl/all/utility.cxx
new file mode 100644
index 000000000..e22bdd3de
--- /dev/null
+++ b/sal/osl/all/utility.cxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/time.h>
+
+/*
+ * mfe : maybe it would be wishful to include initialization
+ * of the global timer in dllmain or _init directly.
+ * But nonetheless this (should) work too.
+ */
+namespace osl
+{
+
+namespace {
+
+class OGlobalTimer
+{
+
+public:
+
+ OGlobalTimer() {
+ osl_getGlobalTimer();
+ }
+
+};
+
+}
+
+static OGlobalTimer aGlobalTimer;
+
+} // namespace osl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/asm/interlck_sparc.s b/sal/osl/unx/asm/interlck_sparc.s
new file mode 100644
index 000000000..7b971a50a
--- /dev/null
+++ b/sal/osl/unx/asm/interlck_sparc.s
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/*
+ * Implements osl_[increment|decrement]InterlockedCount:
+ * sparcv9/sparcv8plus architecture: use the "cas" instruction
+ *
+ * 32 bit mode with v8plus support or 64 bit mode:
+ * sparcv9 mode is implied. Assemble with -xarch=v8plus (32 bit) or
+ * -xarch=v9 (64 bit).
+ *
+ */
+
+#if !defined(__sparcv8plus) && !defined(__sparcv9) && !defined(__sparc_v9__)
+
+#error LibreOffice requires SPARCv8plus or SPARCv9 CPU with "cas" instruction
+
+#endif
+
+.section ".text"
+ .global osl_incrementInterlockedCount
+ .align 8
+
+! Implements osl_[increment|decrement]InterlockedCount with sparcv9(sparcv8plus) "cas"
+! instruction.
+
+osl_incrementInterlockedCount:
+
+1: ld [%o0], %o1
+ add %o1, 1, %o2
+! allow linux to build for v8
+ .word 0xD5E21009
+! cas [%o0], %o1, %o2
+ cmp %o1, %o2
+ bne 1b
+ nop ! delay slot
+ retl
+ add %o2, 1, %o0 ! delay slot
+
+ .type osl_incrementInterlockedCount,#function
+ .size osl_incrementInterlockedCount,.-osl_incrementInterlockedCount
+
+
+.section ".text"
+ .global osl_decrementInterlockedCount
+ .align 8
+
+osl_decrementInterlockedCount:
+
+1: ld [%o0], %o1
+ sub %o1, 1, %o2
+! allow linux to build for v8
+ .word 0xD5E21009
+! cas [%o0], %o1, %o2
+ cmp %o1, %o2
+ bne 1b
+ nop ! delay slot
+ retl
+ sub %o2, 1, %o0 ! delay slot
+
+ .type osl_decrementInterlockedCount,#function
+ .size osl_decrementInterlockedCount,.-osl_decrementInterlockedCount
+
diff --git a/sal/osl/unx/backtrace.c b/sal/osl/unx/backtrace.c
new file mode 100644
index 000000000..f7bd01c33
--- /dev/null
+++ b/sal/osl/unx/backtrace.c
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "backtrace.h"
+
+#if ! HAVE_FEATURE_BACKTRACE /* no GNU backtrace implementation available */
+
+#include <sal/types.h>
+
+#ifdef __sun /* Solaris */
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <sys/frame.h>
+
+#if defined(SPARC)
+
+#if defined IS_LP64
+
+#define FRAME_PTR_OFFSET 1
+#define FRAME_OFFSET 0
+#define STACK_BIAS 0x7ff
+
+#else
+
+#define FRAME_PTR_OFFSET 1
+#define FRAME_OFFSET 0
+#define STACK_BIAS 0
+
+#endif
+
+#elif defined( INTEL )
+
+#define FRAME_PTR_OFFSET 3
+#define FRAME_OFFSET 0
+#define STACK_BIAS 0
+
+#else
+
+#error Unknown Solaris target platform.
+
+#endif /* defined SPARC or INTEL */
+
+int backtrace( void **buffer, int max_frames )
+{
+ jmp_buf ctx;
+ long fpval;
+ struct frame *fp;
+ int i;
+
+ /* flush register windows */
+#ifdef SPARC
+ asm("ta 3");
+#endif
+
+ /* get stack- and framepointer */
+ setjmp(ctx);
+
+ fpval = ((long*)(ctx))[FRAME_PTR_OFFSET];
+ fp = (struct frame*)((char*)(fpval) + STACK_BIAS);
+
+ for (i = 0; (i < FRAME_OFFSET) && (fp != 0); i++)
+ fp = (struct frame*)((char*)(fp->fr_savfp) + STACK_BIAS);
+
+ /* iterate through backtrace */
+ for (i = 0; (fp != 0) && (fp->fr_savpc != 0) && (i < max_frames); i++)
+ {
+ /* saved (prev) frame */
+ struct frame * prev = (struct frame*)((char*)(fp->fr_savfp) + STACK_BIAS);
+
+ /* store frame */
+ *(buffer++) = (void*)(fp->fr_savpc);
+
+ /* prev frame (w/ stack growing top down) */
+ fp = (prev > fp) ? prev : 0;
+ }
+
+ /* return number of frames stored */
+ return i;
+}
+
+char ** backtrace_symbols(void * const * buffer, int size)
+{
+ (void)buffer; (void)size;
+ return NULL; /*TODO*/
+}
+
+void backtrace_symbols_fd( void **buffer, int size, int fd )
+{
+ FILE *fp = fdopen( fd, "w" );
+
+ if ( fp )
+ {
+ void **pFramePtr;
+
+ for ( pFramePtr = buffer; size > 0 && pFramePtr && *pFramePtr; pFramePtr++, size-- )
+ {
+ Dl_info dli;
+ ptrdiff_t offset;
+
+ if ( 0 != dladdr( *pFramePtr, &dli ) )
+ {
+ if ( dli.dli_fname && dli.dli_fbase )
+ {
+ offset = (ptrdiff_t)*pFramePtr - (ptrdiff_t)dli.dli_fbase;
+ fprintf( fp, "%s+0x%" SAL_PRI_PTRDIFFT "x", dli.dli_fname, offset );
+ }
+ if ( dli.dli_sname && dli.dli_saddr )
+ {
+ offset = (ptrdiff_t)*pFramePtr - (ptrdiff_t)dli.dli_saddr;
+ fprintf( fp, "(%s+0x%" SAL_PRI_PTRDIFFT "x)", dli.dli_sname, offset );
+ }
+ }
+ fprintf( fp, "[%p]\n", *pFramePtr );
+ }
+
+ fclose( fp );
+ }
+}
+
+#elif defined FREEBSD || defined NETBSD || defined OPENBSD || defined(DRAGONFLY)
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdio.h>
+
+/* no frame.h on FreeBSD */
+struct frame {
+ struct frame *fr_savfp;
+ long fr_savpc;
+};
+
+#if defined(POWERPC) || defined(POWERPC64)
+
+#define FRAME_PTR_OFFSET 1
+#define FRAME_OFFSET 0
+
+#else
+
+#define FRAME_PTR_OFFSET 3
+#define FRAME_OFFSET 0
+
+#endif
+
+int backtrace( void **buffer, int max_frames )
+{
+ struct frame *fp;
+ jmp_buf ctx;
+ int i;
+ /* get stack- and framepointer */
+ setjmp(ctx);
+ fp = (struct frame*)(((size_t*)(ctx))[FRAME_PTR_OFFSET]);
+ for ( i=0; (i<FRAME_OFFSET) && (fp!=0); i++)
+ fp = fp->fr_savfp;
+ /* iterate through backtrace */
+ for (i=0; fp && fp->fr_savpc && i<max_frames; i++)
+ {
+ /* store frame */
+ *(buffer++) = (void *)fp->fr_savpc;
+ /* next frame */
+ fp=fp->fr_savfp;
+ }
+ return i;
+}
+
+char ** backtrace_symbols(void * const * buffer, int size)
+{
+ (void)buffer; (void)size;
+ return NULL; /*TODO*/
+}
+
+void backtrace_symbols_fd( void **buffer, int size, int fd )
+{
+ FILE *fp = fdopen( fd, "w" );
+
+ if ( fp )
+ {
+ void **pFramePtr;
+ for ( pFramePtr = buffer; size > 0 && pFramePtr && *pFramePtr; pFramePtr++, size-- )
+ {
+ Dl_info dli;
+ ptrdiff_t offset;
+
+ if ( 0 != dladdr( *pFramePtr, &dli ) )
+ {
+ if ( dli.dli_fname && dli.dli_fbase )
+ {
+ offset = (ptrdiff_t)*pFramePtr - (ptrdiff_t)dli.dli_fbase;
+ fprintf( fp, "%s+0x%" SAL_PRI_PTRDIFFT "x", dli.dli_fname, offset );
+ }
+ if ( dli.dli_sname && dli.dli_saddr )
+ {
+ offset = (ptrdiff_t)*pFramePtr - (ptrdiff_t)dli.dli_saddr;
+ fprintf( fp, "(%s+0x%" SAL_PRI_PTRDIFFT "x)", dli.dli_sname, offset );
+ }
+ }
+ fprintf( fp, "[%p]\n", *pFramePtr );
+ }
+ fclose( fp );
+ }
+}
+
+#else /* not GNU/BSD/Solaris */
+
+int backtrace( void **buffer, int max_frames )
+{
+ (void)buffer; (void)max_frames;
+ return 0;
+}
+
+char ** backtrace_symbols(void * const * buffer, int size)
+{
+ (void)buffer; (void)size;
+ return NULL; /*TODO*/
+}
+
+void backtrace_symbols_fd( void **buffer, int size, int fd )
+{
+ (void)buffer; (void)size; (void)fd;
+}
+
+#endif /* not GNU/BSD/Solaris */
+
+#endif /* ! HAVE_FEATURE_BACKTRACE */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/backtrace.h b/sal/osl/unx/backtrace.h
new file mode 100644
index 000000000..11a9da52e
--- /dev/null
+++ b/sal/osl/unx/backtrace.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_BACKTRACE_H
+#define INCLUDED_SAL_OSL_UNX_BACKTRACE_H
+
+#include <config_features.h>
+
+#if HAVE_FEATURE_BACKTRACE /* GNU backtrace implementation available */
+
+#include <execinfo.h>
+
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* backtrace function with same behaviour as defined in GNU libc */
+
+int backtrace( void **buffer, int max_frames );
+
+char ** backtrace_symbols(void * const * buffer, int size);
+
+void backtrace_symbols_fd( void **buffer, int size, int fd );
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/backtraceapi.cxx b/sal/osl/unx/backtraceapi.cxx
new file mode 100644
index 000000000..a4cec46ca
--- /dev/null
+++ b/sal/osl/unx/backtraceapi.cxx
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstdlib>
+#include <limits>
+#include <memory>
+#include <mutex>
+
+#include <o3tl/runtimetooustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <sal/log.hxx>
+#include <sal/backtrace.hxx>
+
+#include "backtrace.h"
+#include <backtraceasstring.hxx>
+
+OUString osl::detail::backtraceAsString(sal_uInt32 maxDepth) {
+ std::unique_ptr<sal::BacktraceState> backtrace = sal::backtrace_get( maxDepth );
+ return sal::backtrace_to_string( backtrace.get());
+}
+
+std::unique_ptr<sal::BacktraceState> sal::backtrace_get(sal_uInt32 maxDepth)
+{
+ assert(maxDepth != 0);
+ auto const maxInt = static_cast<unsigned int>(
+ std::numeric_limits<int>::max());
+ if (maxDepth > maxInt) {
+ maxDepth = static_cast<sal_uInt32>(maxInt);
+ }
+ auto b1 = new void *[maxDepth];
+ int n = backtrace(b1, static_cast<int>(maxDepth));
+ return std::unique_ptr<BacktraceState>(new BacktraceState{ b1, n });
+}
+
+#if OSL_DEBUG_LEVEL > 0 && (defined LINUX || defined MACOSX || defined FREEBSD || defined NETBSD || defined OPENBSD || defined(DRAGONFLY))
+// The backtrace_symbols() function is unreliable, it requires -rdynamic and even then it cannot resolve names
+// of many functions, such as those with hidden ELF visibility. Libunwind doesn't resolve names for me either,
+// boost::stacktrace doesn't work properly, the best result I've found is addr2line. Using addr2line is relatively
+// slow, but I don't find that to be a big problem for printing of backtraces. Feel free to improve if needed
+// (e.g. the calls could be grouped by the binary).
+#include <dlfcn.h>
+#include <unistd.h>
+#include <vector>
+#include <osl/process.h>
+#include <rtl/strbuf.hxx>
+#include <osl/mutex.hxx>
+#include <o3tl/lru_map.hxx>
+#include "file_url.hxx"
+
+namespace
+{
+struct FrameData
+{
+ const char* file = nullptr;
+ void* addr;
+ ptrdiff_t offset;
+ OString info;
+ bool handled = false;
+};
+
+typedef o3tl::lru_map<void*, OString> FrameCache;
+std::mutex frameCacheMutex;
+FrameCache frameCache( 256 );
+
+void process_file_addr2line( const char* file, std::vector<FrameData>& frameData )
+{
+ if(access( file, R_OK ) != 0)
+ return; // cannot read info from the binary file anyway
+ OUString binary("addr2line");
+ OUString dummy;
+#if defined __clang__
+ // llvm-addr2line is faster than addr2line
+ if(osl::detail::find_in_PATH("llvm-addr2line", dummy))
+ binary = "llvm-addr2line";
+#endif
+ if(!osl::detail::find_in_PATH(binary, dummy))
+ return; // Will not work, avoid warnings from osl process code.
+ OUString arg1("-Cfe");
+ OUString arg2 = OUString::fromUtf8(file);
+ std::vector<OUString> addrs;
+ std::vector<rtl_uString*> args;
+ args.reserve(frameData.size() + 2);
+ args.push_back( arg1.pData );
+ args.push_back( arg2.pData );
+ for( FrameData& frame : frameData )
+ {
+ if( frame.file != nullptr && strcmp( file, frame.file ) == 0 )
+ {
+ addrs.push_back("0x" + OUString::number(frame.offset, 16));
+ args.push_back(addrs.back().pData);
+ frame.handled = true;
+ }
+ }
+
+ oslProcess aProcess;
+ oslFileHandle pOut = nullptr;
+ oslFileHandle pErr = nullptr;
+ oslSecurity pSecurity = osl_getCurrentSecurity();
+ oslProcessError eErr = osl_executeProcess_WithRedirectedIO(
+ binary.pData, args.data(), args.size(), osl_Process_SEARCHPATH | osl_Process_HIDDEN, pSecurity, nullptr,
+ nullptr, 0, &aProcess, nullptr, &pOut, &pErr);
+ osl_freeSecurityHandle(pSecurity);
+
+ if (eErr != osl_Process_E_None)
+ {
+ SAL_WARN("sal.osl", binary << " call to resolve " << file << " symbols failed");
+ return;
+ }
+
+ OStringBuffer outputBuffer;
+ if (pOut)
+ {
+ const sal_uInt64 BUF_SIZE = 1024;
+ char buffer[BUF_SIZE];
+ while (true)
+ {
+ sal_uInt64 bytesRead = 0;
+ while(osl_readFile(pErr, buffer, BUF_SIZE, &bytesRead) == osl_File_E_None
+ && bytesRead != 0)
+ ; // discard possible stderr output
+ oslFileError err = osl_readFile(pOut, buffer, BUF_SIZE, &bytesRead);
+ if(bytesRead == 0 && err == osl_File_E_None)
+ break;
+ outputBuffer.append(buffer, bytesRead);
+ if (err != osl_File_E_None && err != osl_File_E_AGAIN)
+ break;
+ }
+ osl_closeFile(pOut);
+ }
+ if(pErr)
+ osl_closeFile(pErr);
+ eErr = osl_joinProcess(aProcess);
+ osl_freeProcessHandle(aProcess);
+
+ OString output = outputBuffer.makeStringAndClear();
+ std::vector<OString> lines;
+ sal_Int32 outputPos = 0;
+ while(outputPos < output.getLength())
+ {
+ sal_Int32 end1 = output.indexOf('\n', outputPos);
+ if(end1 < 0)
+ break;
+ sal_Int32 end2 = output.indexOf('\n', end1 + 1);
+ if(end2 < 0)
+ end2 = output.getLength();
+ lines.push_back(output.copy( outputPos, end1 - outputPos ));
+ lines.push_back(output.copy( end1 + 1, end2 - end1 - 1 ));
+ outputPos = end2 + 1;
+ }
+ if(lines.size() != addrs.size() * 2)
+ {
+ SAL_WARN("sal.osl", "failed to parse " << binary << " call output to resolve " << file << " symbols ");
+ return; // addr2line problem?
+ }
+ size_t linesPos = 0;
+ for( FrameData& frame : frameData )
+ {
+ if( frame.file != nullptr && strcmp( file, frame.file ) == 0 )
+ {
+ // There should be two lines, first function name and second source file information.
+ // If each of them starts with ??, it is invalid/unknown.
+ OString function = lines[linesPos];
+ OString source = lines[linesPos+1];
+ linesPos += 2;
+ if(function.isEmpty() || function.startsWith("??"))
+ {
+ // Cache that the address cannot be resolved.
+ std::lock_guard guard(frameCacheMutex);
+ frameCache.insert( { frame.addr, "" } );
+ }
+ else
+ {
+ if( source.startsWith("??"))
+ frame.info = function + " in " + file;
+ else
+ frame.info = function + " at " + source;
+ std::lock_guard guard(frameCacheMutex);
+ frameCache.insert( { frame.addr, frame.info } );
+ }
+ }
+ }
+}
+
+} // namespace
+
+OUString sal::backtrace_to_string(BacktraceState* backtraceState)
+{
+ // Collect frames for each binary and process each binary in one addr2line
+ // call for better performance.
+ std::vector< FrameData > frameData;
+ frameData.resize(backtraceState->nDepth);
+ for (int i = 0; i != backtraceState->nDepth; ++i)
+ {
+ Dl_info dli;
+ void* addr = backtraceState->buffer[i];
+ std::unique_lock guard(frameCacheMutex);
+ auto it = frameCache.find(addr);
+ bool found = it != frameCache.end();
+ guard.unlock();
+ if( found )
+ {
+ frameData[ i ].info = it->second;
+ frameData[ i ].handled = true;
+ }
+ else if (dladdr(addr, &dli) != 0)
+ {
+ if (dli.dli_fname && dli.dli_fbase)
+ {
+ frameData[ i ].file = dli.dli_fname;
+ frameData[ i ].addr = addr;
+ frameData[ i ].offset = reinterpret_cast<ptrdiff_t>(addr) - reinterpret_cast<ptrdiff_t>(dli.dli_fbase);
+ }
+ }
+ }
+ for (int i = 0; i != backtraceState->nDepth; ++i)
+ {
+ if(frameData[ i ].file != nullptr && !frameData[ i ].handled)
+ process_file_addr2line( frameData[ i ].file, frameData );
+ }
+ OUStringBuffer b3;
+ std::unique_ptr<char*, decltype(free)*> b2{ nullptr, free };
+ bool fallbackInitDone = false;
+ for (int i = 0; i != backtraceState->nDepth; ++i)
+ {
+ if (i != 0)
+ b3.append("\n");
+ b3.append( "#" + OUString::number( i ) + " " );
+ if(!frameData[i].info.isEmpty())
+ b3.append(o3tl::runtimeToOUString(frameData[i].info.getStr()));
+ else
+ {
+ if(!fallbackInitDone)
+ {
+ b2 = std::unique_ptr<char*, decltype(free)*>
+ {backtrace_symbols(backtraceState->buffer, backtraceState->nDepth), free};
+ fallbackInitDone = true;
+ }
+ if(b2)
+ b3.append(o3tl::runtimeToOUString(b2.get()[i]));
+ else
+ b3.append("??");
+ }
+ }
+ return b3.makeStringAndClear();
+}
+
+#else
+
+OUString sal::backtrace_to_string(BacktraceState* backtraceState)
+{
+ std::unique_ptr<char*, decltype(free)*> b2{backtrace_symbols(backtraceState->buffer, backtraceState->nDepth), free};
+ if (!b2) {
+ return OUString();
+ }
+ OUStringBuffer b3;
+ for (int i = 0; i != backtraceState->nDepth; ++i) {
+ if (i != 0) {
+ b3.append("\n");
+ }
+ b3.append( "#" + OUString::number( i ) + " " );
+ b3.append(o3tl::runtimeToOUString(b2.get()[i]));
+ }
+ return b3.makeStringAndClear();
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/conditn.cxx b/sal/osl/unx/conditn.cxx
new file mode 100644
index 000000000..602240922
--- /dev/null
+++ b/sal/osl/unx/conditn.cxx
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <assert.h>
+
+#include "system.hxx"
+#include "unixerrnostring.hxx"
+#include <sal/log.hxx>
+#include <sal/types.h>
+
+#include <osl/conditn.h>
+#include <osl/time.h>
+
+namespace {
+
+struct oslConditionImpl
+{
+ pthread_cond_t m_Condition;
+ pthread_mutex_t m_Lock;
+ bool m_State;
+};
+
+}
+
+oslCondition SAL_CALL osl_createCondition()
+{
+ oslConditionImpl* pCond;
+ int nRet=0;
+
+ pCond = static_cast<oslConditionImpl*>(malloc(sizeof(oslConditionImpl)));
+
+ if ( pCond == nullptr )
+ {
+ return nullptr;
+ }
+
+ pCond->m_State = false;
+
+ /* init condition variable with default attr. (PTHREAD_PROCESS_PRIVAT) */
+ nRet = pthread_cond_init(&pCond->m_Condition, PTHREAD_CONDATTR_DEFAULT);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "pthread_cond_init failed: " << UnixErrnoString(nRet) );
+
+ free(pCond);
+ return nullptr;
+ }
+
+ nRet = pthread_mutex_init(&pCond->m_Lock, PTHREAD_MUTEXATTR_DEFAULT);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "pthread_mutex_init failed: " << UnixErrnoString(nRet) );
+
+ nRet = pthread_cond_destroy(&pCond->m_Condition);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "pthread_cond_destroy failed: " << UnixErrnoString(nRet) );
+
+ free(pCond);
+ pCond = nullptr;
+ }
+
+ SAL_INFO( "sal.osl.condition", "osl_createCondition(): " << pCond );
+
+ return static_cast<oslCondition>(pCond);
+}
+
+void SAL_CALL osl_destroyCondition(oslCondition Condition)
+{
+ oslConditionImpl* pCond;
+
+ pCond = static_cast<oslConditionImpl*>(Condition);
+
+ SAL_INFO( "sal.osl.condition", "osl_destroyCondition(" << pCond << ")" );
+
+ if ( pCond )
+ {
+ int nRet = pthread_cond_destroy(&pCond->m_Condition);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "pthread_cond_destroy failed: " << UnixErrnoString(nRet) );
+ nRet = pthread_mutex_destroy(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "pthread_mutex_destroy failed: " << UnixErrnoString(nRet) );
+
+ free(Condition);
+ }
+}
+
+sal_Bool SAL_CALL osl_setCondition(oslCondition Condition)
+{
+ oslConditionImpl* pCond;
+ int nRet=0;
+
+ assert(Condition);
+ pCond = static_cast<oslConditionImpl*>(Condition);
+
+ nRet = pthread_mutex_lock(&pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_setCondition(" << pCond << "): pthread_mutex_lock failed: " << UnixErrnoString(nRet) );
+ return false;
+ }
+
+ pCond->m_State = true;
+ nRet = pthread_cond_broadcast(&pCond->m_Condition);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_setCondition(" << pCond << "): pthread_cond_broadcast failed: " << UnixErrnoString(nRet) );
+ // try to unlock the mutex
+ pthread_mutex_unlock(&pCond->m_Lock);
+ return false;
+ }
+
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_setCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+ return false;
+ }
+
+ SAL_INFO( "sal.osl.condition", "osl_setCondition(" << pCond << ")" );
+
+ return true;
+
+}
+
+sal_Bool SAL_CALL osl_resetCondition(oslCondition Condition)
+{
+ oslConditionImpl* pCond;
+ int nRet=0;
+
+ assert(Condition);
+
+ pCond = static_cast<oslConditionImpl*>(Condition);
+
+ nRet = pthread_mutex_lock(&pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_resetCondition(" << pCond << "): pthread_mutex_lock failed: " << UnixErrnoString(nRet) );
+ return false;
+ }
+
+ pCond->m_State = false;
+
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_resetCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+ return false;
+ }
+
+ SAL_INFO( "sal.osl.condition", "osl_resetCondition(" << pCond << ")" );
+
+ return true;
+}
+
+oslConditionResult SAL_CALL osl_waitCondition(oslCondition Condition, const TimeValue* pTimeout)
+{
+ oslConditionImpl* pCond;
+ int nRet=0;
+
+ assert(Condition);
+ pCond = static_cast<oslConditionImpl*>(Condition);
+
+ SAL_INFO( "sal.osl.condition", "osl_waitCondition(" << pCond << ")" );
+
+ nRet = pthread_mutex_lock(&pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_mutex_lock failed: " << UnixErrnoString(nRet) );
+ return osl_cond_result_error;
+ }
+
+ if ( pTimeout )
+ {
+ if ( ! pCond->m_State )
+ {
+ struct timeval tp;
+ struct timespec to;
+
+ gettimeofday(&tp, nullptr);
+
+ SET_TIMESPEC( to, tp.tv_sec + pTimeout->Seconds,
+ tp.tv_usec * 1000 + pTimeout->Nanosec );
+
+ /* spurious wake up prevention */
+ do
+ {
+ const int ret = pthread_cond_timedwait(&pCond->m_Condition, &pCond->m_Lock, &to);
+ if ( ret != 0 )
+ {
+ if ( ret == ETIME || ret == ETIMEDOUT )
+ {
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+
+ return osl_cond_result_timeout;
+ }
+ if ( ret != EINTR )
+ {
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+ return osl_cond_result_error;
+ }
+ }
+ }
+ while ( !pCond->m_State );
+ }
+ }
+ else
+ {
+ while ( !pCond->m_State )
+ {
+ nRet = pthread_cond_wait(&pCond->m_Condition, &pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_cond_wait failed: " << UnixErrnoString(nRet) );
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+
+ return osl_cond_result_error;
+ }
+ }
+ }
+
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+
+ SAL_INFO( "sal.osl.condition", "osl_waitCondition(" << pCond << "): OK" );
+
+ return osl_cond_result_ok;
+}
+
+sal_Bool SAL_CALL osl_checkCondition(oslCondition Condition)
+{
+ bool State;
+ oslConditionImpl* pCond;
+ int nRet=0;
+
+ assert(Condition);
+ pCond = static_cast<oslConditionImpl*>(Condition);
+
+ nRet = pthread_mutex_lock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_checkCondition(" << pCond << "): pthread_mutex_lock failed: " << UnixErrnoString(nRet) );
+
+ State = pCond->m_State;
+
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_checkCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+
+ SAL_INFO( "sal.osl.condition", "osl_checkCondition(" << pCond << "): " << (State ? "YES" : "NO") );
+
+ return State;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/createfilehandlefromfd.hxx b/sal/osl/unx/createfilehandlefromfd.hxx
new file mode 100644
index 000000000..bb8a768ef
--- /dev/null
+++ b/sal/osl/unx/createfilehandlefromfd.hxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_CREATEFILEHANDLEFROMFD_HXX
+#define INCLUDED_SAL_OSL_UNX_CREATEFILEHANDLEFROMFD_HXX
+
+#include <sal/config.h>
+
+#include <osl/file.h>
+
+namespace osl::detail
+{
+oslFileHandle createFileHandleFromFD(int fd); // defined in file.cxx
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file.cxx b/sal/osl/unx/file.cxx
new file mode 100644
index 000000000..ac93ffacd
--- /dev/null
+++ b/sal/osl/unx/file.cxx
@@ -0,0 +1,1606 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <osl/detail/file.h>
+#include <rtl/alloc.h>
+#include <rtl/byteseq.hxx>
+#include <rtl/string.hxx>
+
+#include "system.hxx"
+#include "createfilehandlefromfd.hxx"
+#include "file_error_transl.hxx"
+#include "file_impl.hxx"
+#include "file_url.hxx"
+#include "uunxapi.hxx"
+#include "unixerrnostring.hxx"
+
+#include <algorithm>
+#include <atomic>
+#include <vector>
+#include <cassert>
+#include <limits>
+
+#include <string.h>
+#include <pthread.h>
+#include <sys/mman.h>
+
+#if defined(MACOSX)
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#define HAVE_O_EXLOCK
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#endif /* MACOSX */
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#include <android/log.h>
+#include <android/asset_manager.h>
+#endif
+
+namespace {
+
+enum class State
+{
+ Seekable = 1, /*< default */
+ Readable = 2, /*< default */
+ Writeable = 4, /*< open() sets, write() requires, else osl_File_E_BADF */
+ Modified = 8 /*< write() sets, flush() resets */
+};
+
+}
+
+template<> struct o3tl::typed_flags<State>: o3tl::is_typed_flags<State, 15> {};
+
+namespace {
+
+struct FileHandle_Impl
+{
+ pthread_mutex_t m_mutex;
+ OString m_strFilePath; /*< holds native file path */
+ int m_fd;
+
+ enum Kind
+ {
+ KIND_FD = 1,
+ KIND_MEM = 2
+ };
+ int m_kind;
+ /** State
+ */
+ State m_state;
+
+ sal_uInt64 m_size; /*< file size */
+ off_t m_offset; /*< physical offset from begin of file */
+ // m_fileptr is hit hard in some situations, where the overhead of a mutex starts to show up, so use an atomic
+ std::atomic<off_t> m_fileptr; /*< logical offset from begin of file */
+
+ off_t m_bufptr; /*< buffer offset from begin of file */
+ size_t m_buflen; /*< buffer filled [0, m_bufsiz - 1] */
+
+ size_t m_bufsiz;
+ sal_uInt8 * m_buffer;
+#ifdef ANDROID
+ rtl_String* m_memstreambuf; /*< used for in-memory streams */
+#endif
+
+ explicit FileHandle_Impl(int fd, Kind kind = KIND_FD, OString path = "<anon>");
+ ~FileHandle_Impl();
+
+ static size_t getpagesize();
+
+ sal_uInt64 getPos() const;
+ void setPos(sal_uInt64 uPos);
+
+ sal_uInt64 getSize() const;
+ oslFileError setSize(sal_uInt64 uSize);
+
+ oslFileError readAt(
+ off_t nOffset,
+ void* pBuffer,
+ size_t nBytesRequested,
+ sal_uInt64* pBytesRead);
+
+ oslFileError writeAt(
+ off_t nOffset,
+ void const* pBuffer,
+ size_t nBytesToWrite,
+ sal_uInt64* pBytesWritten);
+
+ oslFileError readFileAt(
+ off_t nOffset,
+ void* pBuffer,
+ size_t nBytesRequested,
+ sal_uInt64* pBytesRead);
+
+ oslFileError writeFileAt(
+ off_t nOffset,
+ void const* pBuffer,
+ size_t nBytesToWrite,
+ sal_uInt64* pBytesWritten);
+
+ oslFileError readLineAt(
+ off_t nOffset,
+ sal_Sequence** ppSequence,
+ sal_uInt64* pBytesRead);
+
+ static oslFileError writeSequence_Impl(
+ sal_Sequence** ppSequence,
+ size_t* pnOffset,
+ const void* pBuffer,
+ size_t nBytes);
+
+ oslFileError syncFile();
+
+ class Guard
+ {
+ pthread_mutex_t *m_mutex;
+
+ public:
+ explicit Guard(pthread_mutex_t *pMutex);
+ ~Guard();
+ };
+};
+
+}
+
+FileHandle_Impl::Guard::Guard(pthread_mutex_t * pMutex)
+ : m_mutex(pMutex)
+{
+ assert(m_mutex);
+ (void) pthread_mutex_lock(m_mutex); // ignoring EINVAL if a null mutex is passed ...
+}
+
+FileHandle_Impl::Guard::~Guard()
+{
+ assert(m_mutex);
+ (void) pthread_mutex_unlock(m_mutex);
+}
+
+FileHandle_Impl::FileHandle_Impl(int fd, enum Kind kind, OString path)
+ : m_strFilePath(std::move(path)),
+ m_fd (fd),
+ m_kind (kind),
+ m_state (State::Seekable | State::Readable),
+ m_size (0),
+ m_offset (0),
+ m_fileptr (0),
+ m_bufptr (-1),
+ m_buflen (0),
+ m_bufsiz (0),
+ m_buffer (nullptr)
+{
+ (void) pthread_mutex_init(&m_mutex, nullptr);
+ if (m_kind == KIND_FD)
+ {
+ size_t const pagesize = getpagesize();
+ if (pagesize != size_t(-1))
+ {
+ m_bufsiz = pagesize;
+ m_buffer = static_cast<sal_uInt8 *>(malloc(m_bufsiz));
+ if (m_buffer)
+ memset(m_buffer, 0, m_bufsiz);
+ }
+ }
+}
+
+FileHandle_Impl::~FileHandle_Impl()
+{
+ if (m_kind == KIND_FD)
+ {
+ free(m_buffer);
+ m_buffer = nullptr;
+ }
+
+ (void) pthread_mutex_destroy(&m_mutex); // ignoring EBUSY ...
+}
+
+size_t FileHandle_Impl::getpagesize()
+{
+ return sal::static_int_cast< size_t >(::sysconf(_SC_PAGESIZE));
+}
+
+sal_uInt64 FileHandle_Impl::getPos() const
+{
+ return sal::static_int_cast< sal_uInt64 >(m_fileptr.load());
+}
+
+void FileHandle_Impl::setPos(sal_uInt64 uPos)
+{
+ m_fileptr = sal::static_int_cast< off_t >(uPos);
+}
+
+sal_uInt64 FileHandle_Impl::getSize() const
+{
+ off_t const bufend = std::max(off_t(0), m_bufptr) + m_buflen;
+ return std::max(m_size, sal::static_int_cast< sal_uInt64 >(bufend));
+}
+
+oslFileError FileHandle_Impl::setSize(sal_uInt64 uSize)
+{
+ off_t const nSize = sal::static_int_cast< off_t >(uSize);
+ if (ftruncate_with_name(m_fd, nSize, m_strFilePath) == -1)
+ {
+ /* Failure. Save original result. Try fallback algorithm */
+ oslFileError result = oslTranslateFileError(errno);
+
+ /* Check against current size. Fail upon 'shrink' */
+ if (uSize <= getSize())
+ {
+ /* Failure upon 'shrink'. Return original result */
+ return result;
+ }
+
+ /* Save current position */
+ off_t const nCurPos = lseek(m_fd, off_t(0), SEEK_CUR);
+ if (nCurPos == off_t(-1))
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "lseek(" << m_fd << ",0,SEEK_CUR): " << UnixErrnoString(e));
+ return result;
+ }
+ else
+ SAL_INFO("sal.file", "lseek(" << m_fd << ",0,SEEK_CUR): OK");
+
+ /* Try 'expand' via 'lseek()' and 'write()' */
+ if (lseek(m_fd, static_cast<off_t>(nSize - 1), SEEK_SET) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "lseek(" << m_fd << "," << nSize - 1 << ",SEEK_SET): " << UnixErrnoString(e));
+ return result;
+ }
+ else
+ SAL_INFO("sal.file", "lseek(" << m_fd << "," << nSize - 1 << ",SEEK_SET): OK");
+
+ if (write(m_fd, "", size_t(1)) == -1)
+ {
+ /* Failure. Restore saved position */
+ int e = errno;
+ SAL_INFO("sal.file", "write(" << m_fd << ",\"\",1): " << UnixErrnoString(e));
+ (void) lseek(m_fd, nCurPos, SEEK_SET);
+ return result;
+ }
+ else
+ SAL_INFO("sal.file", "write(" << m_fd << ",\"\",1): OK");
+
+ /* Success. Restore saved position */
+ if (lseek(m_fd, nCurPos, SEEK_SET) == -1)
+ return result;
+ }
+
+ m_size = sal::static_int_cast< sal_uInt64 >(nSize);
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::readAt(
+ off_t nOffset,
+ void * pBuffer,
+ size_t nBytesRequested,
+ sal_uInt64 * pBytesRead)
+{
+ SAL_WARN_IF(!(m_state & State::Seekable), "sal.osl", "FileHandle_Impl::readAt(): not seekable");
+ if (!(m_state & State::Seekable))
+ return osl_File_E_SPIPE;
+
+ SAL_WARN_IF(!(m_state & State::Readable), "sal.osl", "FileHandle_Impl::readAt(): not readable");
+ if (!(m_state & State::Readable))
+ return osl_File_E_BADF;
+
+ if (m_kind == KIND_MEM)
+ {
+ ssize_t nBytes;
+
+ m_offset = nOffset;
+
+ if (o3tl::make_unsigned(m_offset) >= m_size)
+ {
+ nBytes = 0;
+ }
+ else
+ {
+ nBytes = std::min(nBytesRequested, static_cast<size_t>(m_size - m_offset));
+ memmove(pBuffer, m_buffer + m_offset, nBytes);
+ m_offset += nBytes;
+ }
+ *pBytesRead = nBytes;
+ return osl_File_E_None;
+ }
+
+ ssize_t nBytes = ::pread(m_fd, pBuffer, nBytesRequested, nOffset);
+ if ((nBytes == -1) && (errno == EOVERFLOW))
+ {
+ /* Some 'pread()'s fail with EOVERFLOW when reading at (or past)
+ * end-of-file, different from 'lseek() + read()' behaviour.
+ * Returning '0 bytes read' and 'osl_File_E_None' instead.
+ */
+ nBytes = 0;
+ }
+
+ if (nBytes == -1)
+ return oslTranslateFileError(errno);
+
+ *pBytesRead = nBytes;
+
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::writeAt(
+ off_t nOffset,
+ void const * pBuffer,
+ size_t nBytesToWrite,
+ sal_uInt64 * pBytesWritten)
+{
+ SAL_WARN_IF(!(m_state & State::Seekable), "sal.osl", "FileHandle_Impl::writeAt(): not seekable");
+ if (!(m_state & State::Seekable))
+ return osl_File_E_SPIPE;
+
+ SAL_WARN_IF(!(m_state & State::Writeable), "sal.osl", "FileHandle_Impl::writeAt(): not writeable");
+ if (!(m_state & State::Writeable))
+ return osl_File_E_BADF;
+
+ ssize_t nBytes = ::pwrite(m_fd, pBuffer, nBytesToWrite, nOffset);
+ if (nBytes == -1)
+ return oslTranslateFileError(errno);
+
+ m_size = std::max(m_size, sal::static_int_cast< sal_uInt64 >(nOffset + nBytes));
+
+ *pBytesWritten = nBytes;
+
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::readFileAt(
+ off_t nOffset,
+ void* pBuffer,
+ size_t nBytesRequested,
+ sal_uInt64* pBytesRead)
+{
+ if (!(m_state & State::Seekable))
+ {
+ // not seekable (pipe)
+ ssize_t nBytes = ::read(m_fd, pBuffer, nBytesRequested);
+ if (nBytes == -1)
+ return oslTranslateFileError(errno);
+
+ *pBytesRead = nBytes;
+
+ return osl_File_E_None;
+ }
+
+ if (m_kind == KIND_MEM || !m_buffer)
+ {
+ // not buffered
+ return readAt(nOffset, pBuffer, nBytesRequested, pBytesRead);
+ }
+
+ sal_uInt8 *buffer = static_cast<sal_uInt8*>(pBuffer);
+ for (*pBytesRead = 0; nBytesRequested > 0; )
+ {
+ off_t const bufptr = (nOffset / m_bufsiz) * m_bufsiz;
+ size_t const bufpos = nOffset % m_bufsiz;
+
+ if (bufptr != m_bufptr)
+ {
+ // flush current buffer
+ oslFileError result = syncFile();
+ if (result != osl_File_E_None)
+ return result;
+
+ m_bufptr = -1;
+ m_buflen = 0;
+
+ if (nBytesRequested >= m_bufsiz)
+ {
+ // buffer too small, read through from file
+ sal_uInt64 uDone = 0;
+ result = readAt(nOffset, &(buffer[*pBytesRead]), nBytesRequested, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ *pBytesRead += uDone;
+
+ return osl_File_E_None;
+ }
+
+ // update buffer (pointer)
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ m_bufptr = bufptr;
+ m_buflen = uDone;
+ }
+
+ if (bufpos >= m_buflen)
+ {
+ // end of file
+ return osl_File_E_None;
+ }
+
+ size_t const bytes = std::min(m_buflen - bufpos, nBytesRequested);
+ SAL_INFO("sal.fileio", "FileHandle_Impl::readFileAt(" << m_fd << ", " << nOffset << ", " << bytes << ")");
+
+ memcpy(&(buffer[*pBytesRead]), &(m_buffer[bufpos]), bytes);
+ nBytesRequested -= bytes;
+ *pBytesRead += bytes;
+ nOffset += bytes;
+ }
+
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::writeFileAt(
+ off_t nOffset,
+ void const * pBuffer,
+ size_t nBytesToWrite,
+ sal_uInt64 * pBytesWritten)
+{
+ if (!(m_state & State::Seekable))
+ {
+ // not seekable (pipe)
+ ssize_t nBytes = ::write(m_fd, pBuffer, nBytesToWrite);
+ if (nBytes == -1)
+ return oslTranslateFileError(errno);
+
+ *pBytesWritten = nBytes;
+
+ return osl_File_E_None;
+ }
+ if (!m_buffer)
+ {
+ // not buffered
+ return writeAt(nOffset, pBuffer, nBytesToWrite, pBytesWritten);
+ }
+
+ sal_uInt8 const * buffer = static_cast<sal_uInt8 const *>(pBuffer);
+ for (*pBytesWritten = 0; nBytesToWrite > 0;)
+ {
+ off_t const bufptr = (nOffset / m_bufsiz) * m_bufsiz;
+ size_t const bufpos = nOffset % m_bufsiz;
+ if (bufptr != m_bufptr)
+ {
+ // flush current buffer
+ oslFileError result = syncFile();
+ if (result != osl_File_E_None)
+ return result;
+ m_bufptr = -1;
+ m_buflen = 0;
+
+ if (nBytesToWrite >= m_bufsiz)
+ {
+ // buffer too small, write through to file
+ sal_uInt64 uDone = 0;
+ result = writeAt(nOffset, &(buffer[*pBytesWritten]), nBytesToWrite, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ if (uDone != nBytesToWrite)
+ return osl_File_E_IO;
+
+ *pBytesWritten += uDone;
+
+ return osl_File_E_None;
+ }
+
+ // update buffer (pointer)
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ m_bufptr = bufptr;
+ m_buflen = uDone;
+ }
+
+ size_t const bytes = std::min(m_bufsiz - bufpos, nBytesToWrite);
+ SAL_INFO("sal.fileio", "FileHandle_Impl::writeFileAt(" << m_fd << ", " << nOffset << ", " << bytes << ")");
+
+ memcpy(&(m_buffer[bufpos]), &(buffer[*pBytesWritten]), bytes);
+ nBytesToWrite -= bytes;
+ *pBytesWritten += bytes;
+ nOffset += bytes;
+
+ m_buflen = std::max(m_buflen, bufpos + bytes);
+ m_state |= State::Modified;
+ }
+
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::readLineAt(
+ off_t nOffset,
+ sal_Sequence ** ppSequence,
+ sal_uInt64 * pBytesRead)
+{
+ oslFileError result = osl_File_E_None;
+
+ off_t bufptr = nOffset / m_bufsiz * m_bufsiz;
+ if (bufptr != m_bufptr)
+ {
+ /* flush current buffer */
+ result = syncFile();
+ if (result != osl_File_E_None)
+ return result;
+
+ /* update buffer (pointer) */
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ m_bufptr = bufptr;
+ m_buflen = uDone;
+ }
+
+ static int const LINE_STATE_BEGIN = 0;
+ static int const LINE_STATE_CR = 1;
+ static int const LINE_STATE_LF = 2;
+
+ size_t bufpos = nOffset - m_bufptr, curpos = bufpos, dstpos = 0;
+ int state = (bufpos >= m_buflen) ? LINE_STATE_LF : LINE_STATE_BEGIN;
+
+ while (state != LINE_STATE_LF)
+ {
+ if (curpos >= m_buflen)
+ {
+ /* buffer examined */
+ if ((curpos - bufpos) > 0)
+ {
+ /* flush buffer to sequence */
+ result = writeSequence_Impl(
+ ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos);
+ if (result != osl_File_E_None)
+ return result;
+
+ *pBytesRead += curpos - bufpos;
+ nOffset += curpos - bufpos;
+ }
+
+ bufptr = nOffset / m_bufsiz * m_bufsiz;
+ if (bufptr != m_bufptr)
+ {
+ /* update buffer (pointer) */
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ m_bufptr = bufptr;
+ m_buflen = uDone;
+ }
+
+ bufpos = nOffset - m_bufptr;
+ curpos = bufpos;
+ if (bufpos >= m_buflen)
+ break;
+ }
+
+ switch (state)
+ {
+ case LINE_STATE_CR:
+ state = LINE_STATE_LF;
+ switch (m_buffer[curpos])
+ {
+ case 0x0A: /* CRLF */
+ /* eat current char */
+ curpos++;
+ break;
+ default: /* single CR */
+ /* keep current char */
+ break;
+ }
+ break;
+ default:
+ /* determine next state */
+ switch (m_buffer[curpos])
+ {
+ case 0x0A: /* single LF */
+ state = LINE_STATE_LF;
+ break;
+ case 0x0D: /* CR */
+ state = LINE_STATE_CR;
+ break;
+ default: /* advance to next char */
+ curpos++;
+ break;
+ }
+ if (state != LINE_STATE_BEGIN)
+ {
+ /* skip the newline char */
+ curpos++;
+
+ /* flush buffer to sequence */
+ result = writeSequence_Impl(
+ ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos - 1);
+ if (result != osl_File_E_None)
+ return result;
+
+ *pBytesRead += curpos - bufpos;
+ nOffset += curpos - bufpos;
+ }
+ break;
+ }
+ }
+
+ result = writeSequence_Impl(ppSequence, &dstpos, nullptr, 0);
+ if (result != osl_File_E_None)
+ return result;
+
+ if (dstpos > 0)
+ return osl_File_E_None;
+
+ if (bufpos >= m_buflen)
+ return osl_File_E_AGAIN;
+
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::writeSequence_Impl(
+ sal_Sequence ** ppSequence,
+ size_t * pnOffset,
+ const void * pBuffer,
+ size_t nBytes)
+{
+ sal_Int32 nElements = *pnOffset + nBytes;
+ if (!*ppSequence)
+ {
+ /* construct sequence */
+ rtl_byte_sequence_constructNoDefault(ppSequence, nElements);
+ }
+ else if (nElements != (*ppSequence)->nElements)
+ {
+ /* resize sequence */
+ rtl_byte_sequence_realloc(ppSequence, nElements);
+ }
+
+ if (*ppSequence && nBytes != 0)
+ {
+ /* fill sequence */
+ memcpy(&((*ppSequence)->elements[*pnOffset]), pBuffer, nBytes);
+ *pnOffset += nBytes;
+ }
+
+ return (*ppSequence) ? osl_File_E_None : osl_File_E_NOMEM;
+}
+
+oslFileError FileHandle_Impl::syncFile()
+{
+ oslFileError result = osl_File_E_None;
+ if (m_state & State::Modified)
+ {
+ sal_uInt64 uDone = 0;
+ result = writeAt(m_bufptr, m_buffer, m_buflen, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ if (uDone != m_buflen)
+ return osl_File_E_IO;
+
+ m_state &= ~State::Modified;
+ }
+
+ return result;
+}
+
+oslFileHandle osl::detail::createFileHandleFromFD(int fd)
+{
+ if (fd == -1)
+ return nullptr; // EINVAL
+
+ struct stat aFileStat;
+ if (fstat(fd, &aFileStat) == -1)
+ return nullptr; // EBADF
+
+ FileHandle_Impl *pImpl = new FileHandle_Impl(fd);
+
+ // assume writeable
+ pImpl->m_state |= State::Writeable;
+ if (!S_ISREG(aFileStat.st_mode))
+ {
+ /* not a regular file, mark not seekable */
+ pImpl->m_state &= ~State::Seekable;
+ }
+ else
+ {
+ /* regular file, init current size */
+ pImpl->m_size = sal::static_int_cast< sal_uInt64 >(aFileStat.st_size);
+ }
+
+ SAL_INFO("sal.file", "osl::detail::createFileHandleFromFD(" << pImpl->m_fd << ", writeable) => " << pImpl->m_strFilePath);
+
+ return static_cast<oslFileHandle>(pImpl);
+}
+
+static int osl_file_adjustLockFlags(const OString& path, int flags)
+{
+#ifdef MACOSX
+ /*
+ * The AFP implementation of MacOS X 10.4 treats O_EXLOCK in a way
+ * that makes it impossible for OOo to create a backup copy of the
+ * file it keeps opened. OTOH O_SHLOCK for AFP behaves as desired by
+ * the OOo file handling, so we need to check the path of the file
+ * for the filesystem name.
+ */
+ struct statfs s;
+ if(statfs(path.getStr(), &s) >= 0)
+ {
+ if(strncmp("afpfs", s.f_fstypename, 5) == 0)
+ {
+ flags &= ~O_EXLOCK;
+ flags |= O_SHLOCK;
+ }
+ else
+ {
+ /* Needed flags to allow opening a webdav file */
+ flags &= ~(O_EXLOCK | O_SHLOCK | O_NONBLOCK);
+ }
+ }
+#else
+ (void) path;
+#endif
+
+ return flags;
+}
+
+static bool osl_file_queryLocking(sal_uInt32 uFlags)
+{
+#if !defined HAVE_O_EXLOCK
+ if (!(uFlags & osl_File_OpenFlag_NoLock)
+ && ((uFlags & osl_File_OpenFlag_Write)
+ || (uFlags & osl_File_OpenFlag_Create)))
+ {
+ static bool enabled = getenv("SAL_ENABLE_FILE_LOCKING") != nullptr;
+ // getenv is not thread safe, so minimize use of result
+ return enabled;
+ }
+#else
+ (void) uFlags;
+#endif
+ return false;
+}
+
+#ifdef HAVE_O_EXLOCK
+#define OPEN_WRITE_FLAGS ( O_RDWR | O_EXLOCK | O_NONBLOCK )
+#define OPEN_CREATE_FLAGS ( O_CREAT | O_RDWR | O_EXLOCK | O_NONBLOCK )
+#else
+#define OPEN_WRITE_FLAGS ( O_RDWR )
+#define OPEN_CREATE_FLAGS ( O_CREAT | O_RDWR )
+#endif
+
+#if defined ANDROID
+
+namespace {
+
+static oslFileError openMemoryAsFile(const OString &rData,
+ oslFileHandle *pHandle,
+ const OString& path)
+{
+ const char *address = rData.getStr();
+ size_t size = rData.getLength();
+
+ FileHandle_Impl *pImpl = new FileHandle_Impl(-1, FileHandle_Impl::KIND_MEM, path);
+ pImpl->m_size = sal::static_int_cast< sal_uInt64 >(size);
+
+ *pHandle = (oslFileHandle)(pImpl);
+
+ pImpl->m_bufptr = 0;
+ pImpl->m_buflen = size;
+ pImpl->m_memstreambuf = rData.pData;
+ rtl_string_acquire(pImpl->m_memstreambuf);
+
+ pImpl->m_bufsiz = size;
+ pImpl->m_buffer = reinterpret_cast<sal_uInt8*>(const_cast<char *>(address));
+
+ return osl_File_E_None;
+}
+
+/*
+ * Reading files from /assets/ on Android via a transition into the VM
+ * shows on profiles and is rather slow; so we cache small files as
+ * used by UNO, UI-builder etc.
+ */
+class AndroidFileCache {
+public:
+ struct Entry {
+ OString maFilePath;
+ OString maData;
+ };
+ AndroidFileCache(size_t nElements)
+ : mnCur(0)
+ {
+ maEntries.resize(nElements);
+ assert (maEntries.size() == nElements);
+ }
+ Entry *find(const char *cpFilePath)
+ {
+ for (auto &it : maEntries)
+ {
+ if (!strcmp(it.maFilePath.getStr(), cpFilePath))
+ return &it;
+ }
+ return nullptr;
+ }
+ // no clever LRU - but - good enough for now.
+ void insert(const char *cpFilePath, OString &rData)
+ {
+ assert (maEntries.size() > 0);
+ if (++mnCur >= maEntries.size())
+ mnCur = 0;
+ maEntries[mnCur].maFilePath = OString(cpFilePath, strlen(cpFilePath));
+ maEntries[mnCur].maData = rData;
+ }
+ static AndroidFileCache &getHitCache()
+ {
+ static AndroidFileCache *pCache = new AndroidFileCache(16);
+ return *pCache;
+ }
+ static AndroidFileCache &getMissCache()
+ {
+ static AndroidFileCache *pCache = new AndroidFileCache(32);
+ return *pCache;
+ }
+private:
+ size_t mnCur;
+ std::vector<Entry> maEntries;
+};
+
+} // namespace
+
+#endif
+
+oslFileError openFilePath(const OString& filePath, oslFileHandle* pHandle,
+ sal_uInt32 uFlags, mode_t mode)
+{
+ oslFileError eRet;
+
+#ifdef ANDROID
+ /* Opening a file from /assets read-only means
+ * we should mmap it from the .apk file
+ */
+ if (o3tl::starts_with(filePath, "/assets/"))
+ {
+ OString aData;
+ bool bCache = true;
+
+ const char *cpAssetsPath = filePath.getStr() + sizeof("/assets/") - 1;
+ // some requests are /assets//foo...
+ if (cpAssetsPath[0] == '/')
+ {
+ __android_log_print(ANDROID_LOG_DEBUG,"libo:sal/osl/unx/file", "double-slash in path: %s", filePath.getStr());
+ cpAssetsPath++;
+ }
+
+ AndroidFileCache::Entry *pHit = AndroidFileCache::getHitCache().find(cpAssetsPath);
+ if (pHit)
+ aData = pHit->maData;
+
+ else
+ {
+ bCache = false;
+ AndroidFileCache::Entry *pMiss = AndroidFileCache::getMissCache().find(cpAssetsPath);
+ if (pMiss)
+ {
+ errno = ENOENT;
+ __android_log_print(ANDROID_LOG_ERROR,"libo:sal/osl/unx/file", "miss cache: failed to open %s", filePath.getStr());
+ return osl_File_E_NOENT;
+ }
+ AAssetManager* mgr = lo_get_native_assetmgr();
+ AAsset* asset = AAssetManager_open(mgr, cpAssetsPath, AASSET_MODE_BUFFER);
+ if (!asset)
+ {
+ AndroidFileCache::getMissCache().insert(cpAssetsPath, aData);
+ errno = ENOENT;
+ __android_log_print(ANDROID_LOG_ERROR,"libo:sal/osl/unx/file", "failed to open %s", filePath.getStr());
+ return osl_File_E_NOENT;
+ }
+ else
+ {
+ rtl_String *pData = nullptr;
+ size_t size = AAsset_getLength(asset);
+ rtl_string_new_WithLength(&pData, size);
+ pData->length = size;
+ AAsset_read(asset, pData->buffer, size);
+ AAsset_close(asset);
+
+ aData = OString(pData, SAL_NO_ACQUIRE);
+
+ if (pData->length < 50 * 1024)
+ AndroidFileCache::getHitCache().insert(cpAssetsPath, aData);
+ }
+ }
+
+ if (uFlags & osl_File_OpenFlag_Write)
+ {
+ // It seems to work better to silently "open" it read-only
+ // and let write attempts, if any, fail later. Otherwise
+ // loading a document from /assets fails with that idiotic
+ // "General Error" dialog...
+ }
+ SAL_INFO("sal.file", "osl_openFile(" << filePath << ") => '" << cpAssetsPath << "'"
+ << aData.getLength() << " bytes from file " << (bCache ? "cache" : "system"));
+ return openMemoryAsFile(aData, pHandle, filePath);
+ }
+#endif
+
+ /* set mode and flags */
+ int defmode = (uFlags & osl_File_OpenFlag_Private) ? S_IRUSR : S_IRUSR | S_IRGRP | S_IROTH;
+ int flags = O_RDONLY;
+
+ if (uFlags & osl_File_OpenFlag_Write)
+ {
+ defmode |= (uFlags & osl_File_OpenFlag_Private) ? S_IWUSR : S_IWUSR | S_IWGRP | S_IWOTH;
+ flags = OPEN_WRITE_FLAGS;
+ }
+
+ if (uFlags & osl_File_OpenFlag_Create)
+ {
+ defmode |= (uFlags & osl_File_OpenFlag_Private) ? S_IWUSR : S_IWUSR | S_IWGRP | S_IWOTH;
+ flags = OPEN_CREATE_FLAGS;
+ }
+
+ if (mode == mode_t(-1))
+ mode = defmode;
+
+ /* Check for flags passed in from SvFileStream::Open() */
+ if (uFlags & osl_File_OpenFlag_Trunc)
+ flags |= O_TRUNC;
+
+ if (!(uFlags & osl_File_OpenFlag_NoExcl))
+ flags |= O_EXCL;
+
+ if (uFlags & osl_File_OpenFlag_NoLock)
+ {
+#ifdef HAVE_O_EXLOCK
+ flags &= ~(O_EXLOCK | O_SHLOCK | O_NONBLOCK);
+#endif /* HAVE_O_EXLOCK */
+ }
+ else
+ {
+ flags = osl_file_adjustLockFlags (filePath, flags);
+ }
+
+ // O_EXCL can be set only when O_CREAT is set
+ if (flags & O_EXCL && !(flags & O_CREAT))
+ flags &= ~O_EXCL;
+
+ /* open the file */
+ int fd = open_c( filePath, flags, mode );
+ if (fd == -1)
+ {
+ return oslTranslateFileError(errno);
+ }
+
+#if !HAVE_FEATURE_MACOSX_SANDBOX
+ /* reset O_NONBLOCK flag */
+ if (flags & O_NONBLOCK)
+ {
+ int f = fcntl(fd, F_GETFL, 0);
+ if (f == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "fcntl(" << fd << ",F_GETFL,0): " << UnixErrnoString(e));
+ eRet = oslTranslateFileError(e);
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return eRet;
+ }
+ else
+ SAL_INFO("sal.file", "fcntl(" << fd << ",F_GETFL,0): OK");
+
+ if (fcntl(fd, F_SETFL, (f & ~O_NONBLOCK)) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "fcntl(" << fd << ",F_SETFL,(f & ~O_NONBLOCK)): " << UnixErrnoString(e));
+ eRet = oslTranslateFileError(e);
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return eRet;
+ }
+ else
+ SAL_INFO("sal.file", "fcntl(" << fd << ",F_SETFL,(f & ~O_NONBLOCK)): OK");
+ }
+#endif
+
+ /* get file status (mode, size) */
+ struct stat aFileStat;
+ if (fstat(fd, &aFileStat) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "fstat(" << fd << "): " << UnixErrnoString(e));
+ eRet = oslTranslateFileError(e);
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return eRet;
+ }
+ else
+ SAL_INFO("sal.file", "fstat(" << fd << "): OK");
+
+ if (!S_ISREG(aFileStat.st_mode))
+ {
+ /* we only open regular files here */
+ SAL_INFO("sal.file", "osl_openFile(" << filePath << "): not a regular file");
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return osl_File_E_INVAL;
+ }
+
+ if (osl_file_queryLocking(uFlags))
+ {
+#ifdef MACOSX
+ if (flock(fd, LOCK_EX | LOCK_NB) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "flock(" << fd << ",LOCK_EX|LOCK_NB): " << UnixErrnoString(e));
+ /* Mac OSX returns ENOTSUP for webdav drives. We should try read lock */
+
+ // Restore errno after possibly having been overwritten by the SAL_INFO above...
+ errno = e;
+ if ((errno != ENOTSUP) || ((flock(fd, LOCK_SH | LOCK_NB) == 1) && (errno != ENOTSUP)))
+ {
+ eRet = oslTranslateFileError(errno);
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return eRet;
+ }
+ }
+ else
+ SAL_INFO("sal.file", "flock(" << fd << ",LOCK_EX|LOCK_NB): OK");
+#else /* F_SETLK */
+ struct flock aflock;
+
+ aflock.l_type = F_WRLCK;
+ aflock.l_whence = SEEK_SET;
+ aflock.l_start = 0;
+ aflock.l_len = 0;
+
+ if (fcntl(fd, F_SETLK, &aflock) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "fcntl(" << fd << ",F_SETLK): " << UnixErrnoString(e));
+ eRet = oslTranslateFileError(e);
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return eRet;
+ }
+#endif /* F_SETLK */
+ }
+
+ /* allocate memory for impl structure */
+ FileHandle_Impl *pImpl = new FileHandle_Impl(fd, FileHandle_Impl::KIND_FD, filePath);
+ if (flags & O_RDWR)
+ pImpl->m_state |= State::Writeable;
+
+ pImpl->m_size = sal::static_int_cast< sal_uInt64 >(aFileStat.st_size);
+
+ *pHandle = static_cast<oslFileHandle>(pImpl);
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_openFile(rtl_uString* ustrFileURL, oslFileHandle* pHandle, sal_uInt32 uFlags)
+{
+ return openFile(ustrFileURL, pHandle, uFlags, mode_t(-1));
+}
+
+oslFileError openFile(rtl_uString* ustrFileURL, oslFileHandle* pHandle, sal_uInt32 uFlags, mode_t mode)
+{
+ oslFileError eRet;
+
+ if ((!ustrFileURL) || (ustrFileURL->length == 0) || (!pHandle))
+ return osl_File_E_INVAL;
+
+ /* convert file URL to system path */
+ char buffer[PATH_MAX];
+ eRet = FileURLToPath(buffer, sizeof(buffer), ustrFileURL);
+ if (eRet != osl_File_E_None)
+ return eRet;
+
+#ifdef MACOSX
+ if (macxp_resolveAlias(buffer, sizeof(buffer)) != 0)
+ return oslTranslateFileError(errno);
+#endif /* MACOSX */
+
+ return openFilePath(buffer, pHandle, uFlags, mode);
+}
+
+oslFileError SAL_CALL osl_closeFile(oslFileHandle Handle)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if (!pImpl)
+ return osl_File_E_INVAL;
+
+ if (pImpl->m_kind == FileHandle_Impl::KIND_MEM)
+ {
+#ifdef ANDROID
+ rtl_string_release(pImpl->m_memstreambuf);
+ pImpl->m_memstreambuf = nullptr;
+
+ pImpl->m_buffer = NULL;
+#endif
+ delete pImpl;
+ return osl_File_E_None;
+ }
+
+ if (pImpl->m_fd < 0)
+ return osl_File_E_INVAL;
+
+ (void) pthread_mutex_lock(&(pImpl->m_mutex));
+
+ /* close(2) implicitly (and unconditionally) unlocks */
+ oslFileError result = pImpl->syncFile();
+ if (result != osl_File_E_None)
+ {
+ /* close, ignoring double failure */
+ (void) close(pImpl->m_fd);
+ SAL_INFO("sal.file", "close(" << pImpl->m_fd << ")");
+ }
+ else if (close(pImpl->m_fd) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "close(" << pImpl->m_fd << "): " << UnixErrnoString(e));
+ /* translate error code */
+ result = oslTranslateFileError(e);
+ }
+ else
+ SAL_INFO("sal.file", "close(" << pImpl->m_fd << "): OK");
+
+ (void) pthread_mutex_unlock(&(pImpl->m_mutex));
+ delete pImpl;
+ return result;
+}
+
+oslFileError SAL_CALL osl_syncFile(oslFileHandle Handle)
+{
+ FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)))
+ return osl_File_E_INVAL;
+
+ if (pImpl->m_kind == FileHandle_Impl::KIND_MEM)
+ return osl_File_E_None;
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+
+ oslFileError result = pImpl->syncFile();
+
+ if (result != osl_File_E_None)
+ return result;
+
+ if (fsync(pImpl->m_fd) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "fsync(" << pImpl->m_fd << "): " << UnixErrnoString(e));
+ return oslTranslateFileError(e);
+ }
+ else
+ SAL_INFO("sal.file", "fsync(" << pImpl->m_fd << "): OK");
+
+ return osl_File_E_None;
+}
+
+const off_t MAX_OFF_T = std::numeric_limits< off_t >::max();
+
+namespace {
+
+// coverity[result_independent_of_operands] - crossplatform requirement
+template<typename T> bool exceedsMaxOffT(T n) { return n > MAX_OFF_T; }
+
+// coverity[result_independent_of_operands] - crossplatform requirement
+template<typename T> bool exceedsMinOffT(T n)
+{ return n < std::numeric_limits<off_t>::min(); }
+
+}
+
+oslFileError SAL_CALL osl_mapFile(
+ oslFileHandle Handle,
+ void** ppAddr,
+ sal_uInt64 uLength,
+ sal_uInt64 uOffset,
+ sal_uInt32 uFlags
+)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!ppAddr))
+ return osl_File_E_INVAL;
+
+ *ppAddr = nullptr;
+
+ if (uLength > SAL_MAX_SIZE)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nLength = sal::static_int_cast< size_t >(uLength);
+
+ if (exceedsMaxOffT(uOffset))
+ return osl_File_E_OVERFLOW;
+
+ if (pImpl->m_kind == FileHandle_Impl::KIND_MEM)
+ {
+ *ppAddr = pImpl->m_buffer + uOffset;
+ return osl_File_E_None;
+ }
+
+ off_t const nOffset = sal::static_int_cast< off_t >(uOffset);
+
+ void* p = mmap(nullptr, nLength, PROT_READ, MAP_SHARED, pImpl->m_fd, nOffset);
+
+ if (p == MAP_FAILED)
+ return oslTranslateFileError(errno);
+
+ *ppAddr = p;
+
+ if (uFlags & osl_File_MapFlag_RandomAccess)
+ {
+ // Determine memory pagesize.
+ size_t const nPageSize = FileHandle_Impl::getpagesize();
+ if (nPageSize != size_t(-1))
+ {
+ /*
+ * Pagein, touching first byte of every memory page.
+ * Note: volatile disables optimizing the loop away.
+ */
+ sal_uInt8 volatile *pData(static_cast<sal_uInt8*>(*ppAddr));
+ size_t nSize(nLength);
+
+ while (nSize > nPageSize)
+ {
+ pData[0];
+ pData += nPageSize;
+ nSize -= nPageSize;
+ }
+
+ if (nSize > 0)
+ pData[0];
+ }
+ }
+
+ if (uFlags & osl_File_MapFlag_WillNeed)
+ {
+ // On Linux, madvise(..., MADV_WILLNEED) appears to have the undesirable
+ // effect of not returning until the data has actually been paged in, so
+ // that its net effect would typically be to slow down the process
+ // (which could start processing at the beginning of the data while the
+ // OS simultaneously pages in the rest); on other platforms, it remains
+ // to be evaluated whether madvise or equivalent is available and
+ // actually useful:
+#if defined MACOSX || (defined(__sun) && (!defined(__XOPEN_OR_POSIX) || defined(_XPG6) || defined(__EXTENSIONS__)))
+ int e = posix_madvise(p, nLength, POSIX_MADV_WILLNEED);
+ if (e != 0)
+ SAL_INFO("sal.file", "posix_madvise(..., POSIX_MADV_WILLNEED) failed with " << e);
+
+#elif defined __sun
+ if (madvise(static_cast< caddr_t >(p), nLength, MADV_WILLNEED) != 0)
+ SAL_INFO("sal.file", "madvise(..., MADV_WILLNEED) failed with " << UnixErrnoString(errno));
+#endif
+ }
+
+ return osl_File_E_None;
+}
+
+static oslFileError unmapFile(void* pAddr, sal_uInt64 uLength)
+{
+ if (!pAddr)
+ return osl_File_E_INVAL;
+
+ if (uLength > SAL_MAX_SIZE)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nLength = sal::static_int_cast< size_t >(uLength);
+
+ if (munmap(pAddr, nLength) == -1)
+ return oslTranslateFileError(errno);
+
+ return osl_File_E_None;
+}
+
+#ifndef ANDROID
+
+// Note that osl_unmapFile() just won't work on Android in general
+// where for (uncompressed) files inside the .apk, in the /assets
+// folder osl_mapFile just returns a pointer to the file inside the
+// already mmapped .apk archive.
+
+oslFileError SAL_CALL osl_unmapFile(void* pAddr, sal_uInt64 uLength)
+{
+ return unmapFile(pAddr, uLength);
+}
+
+#endif
+
+oslFileError SAL_CALL osl_unmapMappedFile(oslFileHandle Handle, void* pAddr, sal_uInt64 uLength)
+{
+ FileHandle_Impl *pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if (!pImpl)
+ return osl_File_E_INVAL;
+
+ if (pImpl->m_kind == FileHandle_Impl::KIND_FD)
+ return unmapFile(pAddr, uLength);
+
+ // For parts of already mmapped "parent" files, whose mapping we
+ // can't change, not much we can or should do...
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_readLine(
+ oslFileHandle Handle,
+ sal_Sequence ** ppSequence)
+{
+ FileHandle_Impl *pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!ppSequence))
+ return osl_File_E_INVAL;
+
+ sal_uInt64 uBytesRead = 0;
+
+ // read at current fileptr; fileptr += uBytesRead;
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ oslFileError result = pImpl->readLineAt(pImpl->m_fileptr, ppSequence, &uBytesRead);
+
+ if (result == osl_File_E_None)
+ pImpl->m_fileptr += uBytesRead;
+
+ return result;
+}
+
+oslFileError SAL_CALL osl_readFile(
+ oslFileHandle Handle,
+ void * pBuffer,
+ sal_uInt64 uBytesRequested,
+ sal_uInt64 * pBytesRead)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!pBuffer) || (!pBytesRead))
+ return osl_File_E_INVAL;
+
+ static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max();
+ if (g_limit_ssize_t < uBytesRequested)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nBytesRequested = sal::static_int_cast< size_t >(uBytesRequested);
+
+ // read at current fileptr; fileptr += *pBytesRead;
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ oslFileError result = pImpl->readFileAt(pImpl->m_fileptr, pBuffer, nBytesRequested, pBytesRead);
+
+ if (result == osl_File_E_None)
+ pImpl->m_fileptr += *pBytesRead;
+
+ return result;
+}
+
+oslFileError SAL_CALL osl_writeFile(
+ oslFileHandle Handle,
+ const void * pBuffer,
+ sal_uInt64 uBytesToWrite,
+ sal_uInt64 * pBytesWritten)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || (pImpl->m_fd == -1) || (!pBuffer) || (!pBytesWritten))
+ return osl_File_E_INVAL;
+
+ if (!(pImpl->m_state & State::Writeable))
+ return osl_File_E_BADF;
+
+ static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max();
+ if (g_limit_ssize_t < uBytesToWrite)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nBytesToWrite = sal::static_int_cast< size_t >(uBytesToWrite);
+
+ // write at current fileptr; fileptr += *pBytesWritten;
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ oslFileError result = pImpl->writeFileAt(pImpl->m_fileptr, pBuffer, nBytesToWrite, pBytesWritten);
+ if (result == osl_File_E_None)
+ pImpl->m_fileptr += *pBytesWritten;
+
+ return result;
+}
+
+oslFileError SAL_CALL osl_readFileAt(
+ oslFileHandle Handle,
+ sal_uInt64 uOffset,
+ void* pBuffer,
+ sal_uInt64 uBytesRequested,
+ sal_uInt64* pBytesRead)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!pBuffer) || (!pBytesRead))
+ return osl_File_E_INVAL;
+
+ if (!(pImpl->m_state & State::Seekable))
+ return osl_File_E_SPIPE;
+
+ if (exceedsMaxOffT(uOffset))
+ return osl_File_E_OVERFLOW;
+
+ off_t const nOffset = sal::static_int_cast< off_t >(uOffset);
+
+ static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max();
+ if (g_limit_ssize_t < uBytesRequested)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nBytesRequested = sal::static_int_cast< size_t >(uBytesRequested);
+
+ // read at specified fileptr
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+
+ return pImpl->readFileAt(nOffset, pBuffer, nBytesRequested, pBytesRead);
+}
+
+oslFileError SAL_CALL osl_writeFileAt(
+ oslFileHandle Handle,
+ sal_uInt64 uOffset,
+ const void* pBuffer,
+ sal_uInt64 uBytesToWrite,
+ sal_uInt64* pBytesWritten)
+{
+ FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || (pImpl->m_fd == -1) || (!pBuffer) || (!pBytesWritten))
+ return osl_File_E_INVAL;
+
+ if (!(pImpl->m_state & State::Seekable))
+ return osl_File_E_SPIPE;
+
+ if (!(pImpl->m_state & State::Writeable))
+ return osl_File_E_BADF;
+
+ if (exceedsMaxOffT(uOffset))
+ return osl_File_E_OVERFLOW;
+
+ off_t const nOffset = sal::static_int_cast< off_t >(uOffset);
+
+ static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max();
+ if (g_limit_ssize_t < uBytesToWrite)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nBytesToWrite = sal::static_int_cast< size_t >(uBytesToWrite);
+
+ // write at specified fileptr
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+
+ return pImpl->writeFileAt(nOffset, pBuffer, nBytesToWrite, pBytesWritten);
+}
+
+oslFileError SAL_CALL osl_isEndOfFile(oslFileHandle Handle, sal_Bool *pIsEOF)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!pIsEOF))
+ return osl_File_E_INVAL;
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ *pIsEOF = (pImpl->getPos() == pImpl->getSize());
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_getFilePos(oslFileHandle Handle, sal_uInt64* pPos)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!pPos))
+ return osl_File_E_INVAL;
+
+ // no need to lock because pos is atomic
+ *pPos = pImpl->getPos();
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_setFilePos(oslFileHandle Handle, sal_uInt32 uHow, sal_Int64 uOffset)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)))
+ return osl_File_E_INVAL;
+
+ if (exceedsMaxOffT(uOffset) || exceedsMinOffT(uOffset))
+ return osl_File_E_OVERFLOW;
+
+ off_t nPos = 0, nOffset = sal::static_int_cast< off_t >(uOffset);
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ switch (uHow)
+ {
+ case osl_Pos_Absolut:
+ if (nOffset < 0)
+ return osl_File_E_INVAL;
+ break;
+
+ case osl_Pos_Current:
+ nPos = sal::static_int_cast< off_t >(pImpl->getPos());
+ if ((nOffset < 0) && (nPos < -1*nOffset))
+ return osl_File_E_INVAL;
+
+ assert(nPos >= 0);
+ if (nOffset > MAX_OFF_T - nPos)
+ return osl_File_E_OVERFLOW;
+ break;
+
+ case osl_Pos_End:
+ nPos = sal::static_int_cast< off_t >(pImpl->getSize());
+ if ((nOffset < 0) && (nPos < -1*nOffset))
+ return osl_File_E_INVAL;
+
+ assert(nPos >= 0);
+ if (nOffset > MAX_OFF_T - nPos)
+ return osl_File_E_OVERFLOW;
+ break;
+
+ default:
+ return osl_File_E_INVAL;
+ }
+
+ pImpl->setPos(nPos + nOffset);
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_getFileSize(oslFileHandle Handle, sal_uInt64* pSize)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!pSize))
+ return osl_File_E_INVAL;
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ *pSize = pImpl->getSize();
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_setFileSize(oslFileHandle Handle, sal_uInt64 uSize)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || (pImpl->m_fd == -1))
+ return osl_File_E_INVAL;
+
+ if (!(pImpl->m_state & State::Writeable))
+ return osl_File_E_BADF;
+
+ if (exceedsMaxOffT(uSize))
+ return osl_File_E_OVERFLOW;
+
+ oslFileError result = pImpl->syncFile();
+ if (result != osl_File_E_None)
+ return result;
+
+ pImpl->m_bufptr = -1;
+ pImpl->m_buflen = 0;
+
+ return pImpl->setSize(uSize);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_error_transl.cxx b/sal/osl/unx/file_error_transl.cxx
new file mode 100644
index 000000000..ad6e00558
--- /dev/null
+++ b/sal/osl/unx/file_error_transl.cxx
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cerrno>
+
+#include "file_error_transl.hxx"
+
+oslFileError oslTranslateFileError(int Errno)
+{
+ switch (Errno)
+ {
+ case EPERM:
+ return osl_File_E_PERM;
+
+ case ENOENT:
+ return osl_File_E_NOENT;
+
+ case ESRCH:
+ return osl_File_E_SRCH;
+
+ case EINTR:
+ return osl_File_E_INTR;
+
+ case EIO:
+ return osl_File_E_IO;
+
+ case ENXIO:
+ return osl_File_E_IO;
+
+ case E2BIG:
+ return osl_File_E_2BIG;
+
+ case ENOEXEC:
+ return osl_File_E_NOEXEC;
+
+ case EBADF:
+ return osl_File_E_BADF;
+
+ case ECHILD:
+ return osl_File_E_CHILD;
+
+ case EAGAIN:
+ return osl_File_E_AGAIN;
+
+ case ENOMEM:
+ return osl_File_E_NOMEM;
+
+ case EACCES:
+ return osl_File_E_ACCES;
+
+ case EFAULT:
+ return osl_File_E_FAULT;
+
+ case EBUSY:
+ return osl_File_E_BUSY;
+
+ case EEXIST:
+ return osl_File_E_EXIST;
+
+ case EXDEV:
+ return osl_File_E_XDEV;
+
+ case ENODEV:
+ return osl_File_E_NODEV;
+
+ case ENOTDIR:
+ return osl_File_E_NOTDIR;
+
+ case EISDIR:
+ return osl_File_E_ISDIR;
+
+ case EINVAL:
+ return osl_File_E_INVAL;
+
+ case ENFILE:
+ return osl_File_E_NFILE;
+
+ case EMFILE:
+ return osl_File_E_MFILE;
+
+ case ENOTTY:
+ return osl_File_E_NOTTY;
+
+ case EFBIG:
+ return osl_File_E_FBIG;
+
+ case ENOSPC:
+ return osl_File_E_NOSPC;
+
+ case ESPIPE:
+ return osl_File_E_SPIPE;
+
+ case EROFS:
+ return osl_File_E_ROFS;
+
+ case EMLINK:
+ return osl_File_E_MLINK;
+
+ case EPIPE:
+ return osl_File_E_PIPE;
+
+ case EDOM:
+ return osl_File_E_DOM;
+
+ case ERANGE:
+ return osl_File_E_RANGE;
+
+ case EDEADLK:
+ return osl_File_E_DEADLK;
+
+ case ENAMETOOLONG:
+ return osl_File_E_NAMETOOLONG;
+
+ case ENOLCK:
+ return osl_File_E_NOLCK;
+
+ case ENOSYS:
+ case ENOTSUP:
+#if EOPNOTSUPP != ENOTSUP
+ case EOPNOTSUPP:
+#endif
+ return osl_File_E_NOSYS;
+
+#if !defined(AIX) || !(defined(_ALL_SOURCE) && !defined(_LINUX_SOURCE_COMPAT))
+ case ENOTEMPTY:
+ return osl_File_E_NOTEMPTY;
+#endif
+
+ case ELOOP:
+ return osl_File_E_LOOP;
+
+#if !(defined(MACOSX) || defined(NETBSD) || defined(FREEBSD) || defined(OPENBSD) \
+ || defined(DRAGONFLY))
+ case EILSEQ:
+ return osl_File_E_ILSEQ;
+
+ case ENOLINK:
+ return osl_File_E_NOLINK;
+
+ case EMULTIHOP:
+ return osl_File_E_MULTIHOP;
+#endif /* MACOSX */
+
+#if !defined(HAIKU)
+ case EUSERS:
+ return osl_File_E_USERS;
+#endif
+
+ case EOVERFLOW:
+ return osl_File_E_OVERFLOW;
+
+ case ETIMEDOUT:
+ return osl_File_E_TIMEDOUT;
+
+ default:
+ assert(Errno != 0);
+ /* FIXME translateFileError: is this alright? Or add a new one: osl_File_E_Unknown? */
+ return osl_File_E_invalidError;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_error_transl.hxx b/sal/osl/unx/file_error_transl.hxx
new file mode 100644
index 000000000..81d972032
--- /dev/null
+++ b/sal/osl/unx/file_error_transl.hxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_FILE_ERROR_TRANSL_HXX
+#define INCLUDED_SAL_OSL_UNX_FILE_ERROR_TRANSL_HXX
+
+#include <osl/file.h>
+#include <sal/types.h>
+
+/** Translate errno's to osl file errors
+
+ @param [in] nErrno the errno; must not be 0
+
+ @returns the osl error code appropriate to the errno
+
+*/
+oslFileError oslTranslateFileError(int Errno);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_impl.hxx b/sal/osl/unx/file_impl.hxx
new file mode 100644
index 000000000..2b374cb68
--- /dev/null
+++ b/sal/osl/unx/file_impl.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_FILE_IMPL_HXX
+#define INCLUDED_SAL_OSL_UNX_FILE_IMPL_HXX
+
+#include <osl/file.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <rtl/string.hxx>
+
+struct DirectoryItem_Impl
+{
+ OString m_strFilePath; /* holds native file name */
+ sal_Int32 m_RefCount;
+ unsigned char m_DType;
+
+ explicit DirectoryItem_Impl(
+ OString strFilePath, unsigned char DType = 0);
+ ~DirectoryItem_Impl();
+
+ void acquire(); /* @see osl_acquireDirectoryItem() */
+ void release(); /* @see osl_releaseDirectoryItem() */
+
+ oslFileType getFileType() const;
+};
+
+oslFileError openFile(
+ rtl_uString * pustrFileURL, oslFileHandle * pHandle, sal_uInt32 uFlags,
+ mode_t mode);
+
+oslFileError openFilePath(
+ const OString& filePath,
+ oslFileHandle* pHandle,
+ sal_uInt32 uFlags, mode_t mode );
+
+#endif // INCLUDED_SAL_OSL_UNX_FILE_IMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_misc.cxx b/sal/osl/unx/file_misc.cxx
new file mode 100644
index 000000000..b2d6010fe
--- /dev/null
+++ b/sal/osl/unx/file_misc.cxx
@@ -0,0 +1,1049 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/file.hxx>
+#include <osl/detail/file.h>
+
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/signal.h>
+#include <rtl/alloc.h>
+#include <rtl/string.hxx>
+#include <sal/log.hxx>
+
+#include "system.hxx"
+#include "file_impl.hxx"
+#include "file_error_transl.hxx"
+#include "file_path_helper.hxx"
+#include "file_url.hxx"
+#include "uunxapi.hxx"
+#include "readwrite_helper.hxx"
+#include "unixerrnostring.hxx"
+
+#include <sys/types.h>
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <new>
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+/************************************************************************
+ * TODO
+ *
+ * - Fix: check for corresponding struct sizes in exported functions
+ * - check size/use of oslDirectory
+ * - check size/use of oslDirectoryItem
+ ***********************************************************************/
+
+namespace {
+
+struct DirectoryImpl
+{
+ OString strPath; /* holds native directory path */
+ DIR* pDirStruct;
+#ifdef ANDROID
+ enum Kind
+ {
+ KIND_DIRENT = 1,
+ KIND_ASSETS = 2
+ };
+ int eKind;
+ lo_apk_dir* pApkDirStruct;
+#endif
+};
+
+}
+
+DirectoryItem_Impl::DirectoryItem_Impl(
+ OString strFilePath, unsigned char DType)
+ : m_strFilePath (std::move(strFilePath)),
+ m_RefCount (1),
+ m_DType (DType)
+{
+}
+DirectoryItem_Impl::~DirectoryItem_Impl()
+{
+}
+
+void DirectoryItem_Impl::acquire()
+{
+ ++m_RefCount;
+}
+void DirectoryItem_Impl::release()
+{
+ if (--m_RefCount == 0)
+ delete this;
+}
+
+oslFileType DirectoryItem_Impl::getFileType() const
+{
+ switch (m_DType)
+ {
+#ifdef _DIRENT_HAVE_D_TYPE
+ case DT_LNK:
+ return osl_File_Type_Link;
+ case DT_DIR:
+ return osl_File_Type_Directory;
+ case DT_REG:
+ return osl_File_Type_Regular;
+ case DT_FIFO:
+ return osl_File_Type_Fifo;
+ case DT_SOCK:
+ return osl_File_Type_Socket;
+ case DT_CHR:
+ case DT_BLK:
+ return osl_File_Type_Special;
+#endif /* _DIRENT_HAVE_D_TYPE */
+ default:
+ break;
+ }
+ return osl_File_Type_Unknown;
+}
+
+static oslFileError osl_psz_createDirectory(
+ char const * pszPath, sal_uInt32 flags);
+static oslFileError osl_psz_removeDirectory(const char* pszPath);
+
+oslFileError SAL_CALL osl_openDirectory(rtl_uString* ustrDirectoryURL, oslDirectory* pDirectory)
+{
+ oslFileError eRet;
+
+ OString path;
+
+ if ((ustrDirectoryURL == nullptr) || (ustrDirectoryURL->length == 0) || (pDirectory == nullptr))
+ return osl_File_E_INVAL;
+
+ /* convert file URL to system path */
+ eRet = osl::detail::convertUrlToPathname(OUString::unacquired(&ustrDirectoryURL), &path);
+
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+ osl_systemPathRemoveSeparator(path.pData);
+
+#ifdef MACOSX
+ {
+ auto const n = std::max(int(path.getLength() + 1), int(PATH_MAX));
+ auto const tmp = std::make_unique<char[]>(n);
+ std::strcpy(tmp.get(), path.getStr());
+ if (macxp_resolveAlias(tmp.get(), n) != 0) {
+ return oslTranslateFileError(errno);
+ }
+ path = OString(tmp.get(), std::strlen(tmp.get()));
+ }
+#endif /* MACOSX */
+
+#ifdef ANDROID
+ if( strncmp( path.getStr(), "/assets/", sizeof( "/assets/" ) - 1) == 0 )
+ {
+ lo_apk_dir *pdir = lo_apk_opendir( path.getStr() );
+
+ if( pdir )
+ {
+ DirectoryImpl* pDirImpl = new(std::nothrow) DirectoryImpl;
+
+ if( pDirImpl )
+ {
+ pDirImpl->eKind = DirectoryImpl::KIND_ASSETS;
+ pDirImpl->pApkDirStruct = pdir;
+ pDirImpl->strPath = path;
+
+ *pDirectory = (oslDirectory) pDirImpl;
+ return osl_File_E_None;
+ }
+ else
+ {
+ errno = ENOMEM;
+ lo_apk_closedir( pdir );
+ }
+ }
+ }
+ else
+#endif
+ {
+ /* open directory */
+ DIR *pdir = opendir( path.getStr() );
+
+ if( pdir )
+ {
+ SAL_INFO("sal.file", "opendir(" << path << ") => " << pdir);
+
+ /* create and initialize impl structure */
+ DirectoryImpl* pDirImpl = new(std::nothrow) DirectoryImpl;
+
+ if( pDirImpl )
+ {
+ pDirImpl->pDirStruct = pdir;
+ pDirImpl->strPath = path;
+#ifdef ANDROID
+ pDirImpl->eKind = DirectoryImpl::KIND_DIRENT;
+#endif
+ *pDirectory = static_cast<oslDirectory>(pDirImpl);
+ return osl_File_E_None;
+ }
+ errno = ENOMEM;
+ closedir( pdir );
+ }
+ else
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "opendir(" << path << "): " << UnixErrnoString(e));
+ // Restore errno after possible modification by SAL_INFO above
+ errno = e;
+ }
+ }
+
+ return oslTranslateFileError(errno);
+}
+
+oslFileError SAL_CALL osl_closeDirectory(oslDirectory pDirectory)
+{
+ SAL_WARN_IF(!pDirectory, "sal.file", "pDirectory is nullptr");
+ DirectoryImpl* pDirImpl = static_cast<DirectoryImpl*>(pDirectory);
+ oslFileError err = osl_File_E_None;
+
+ if (!pDirImpl)
+ return osl_File_E_INVAL;
+
+#ifdef ANDROID
+ if (pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
+ {
+ if (lo_apk_closedir(pDirImpl->pApkDirStruct))
+ err = osl_File_E_IO;
+ }
+ else
+#endif
+ {
+ if (closedir( pDirImpl->pDirStruct) != 0)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "closedir(" << pDirImpl->pDirStruct << "): " << UnixErrnoString(e));
+ err = oslTranslateFileError(e);
+ }
+ else
+ SAL_INFO("sal.file", "closedir(" << pDirImpl->pDirStruct << "): OK");
+ }
+
+ delete pDirImpl;
+
+ return err;
+}
+
+/**********************************************
+ * osl_readdir_impl_
+ *
+ * readdir wrapper, filters out "." and ".."
+ * on request
+ *********************************************/
+
+static struct dirent* osl_readdir_impl_(DIR* pdir)
+{
+ struct dirent* pdirent;
+
+ while ((pdirent = readdir(pdir)) != nullptr)
+ {
+ if ((strcmp(pdirent->d_name, ".") == 0) || (strcmp(pdirent->d_name, "..") == 0))
+ continue;
+ break;
+ }
+
+ return pdirent;
+}
+
+oslFileError SAL_CALL osl_getNextDirectoryItem(oslDirectory pDirectory,
+ oslDirectoryItem* pItem, SAL_UNUSED_PARAMETER sal_uInt32 /*uHint*/)
+{
+ SAL_WARN_IF(!pDirectory, "sal.file", "pDirectory is nullptr");
+ SAL_WARN_IF(!pItem, "sal.file", "pItem is nullptr");
+
+ DirectoryImpl* pDirImpl = static_cast<DirectoryImpl*>(pDirectory);
+ OString strFileName;
+ struct dirent* pEntry;
+
+ if ((pDirectory == nullptr) || (pItem == nullptr))
+ return osl_File_E_INVAL;
+
+#ifdef ANDROID
+ if(pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
+ {
+ pEntry = lo_apk_readdir(pDirImpl->pApkDirStruct);
+ }
+ else
+#endif
+ {
+ pEntry = osl_readdir_impl_(pDirImpl->pDirStruct);
+ }
+
+ if (!pEntry)
+ return osl_File_E_NOENT;
+
+ char const * filename = pEntry->d_name;
+
+#if defined(MACOSX)
+ // convert decomposed filename to precomposed UTF-8
+ char composed_name[BUFSIZ];
+ CFMutableStringRef strRef = CFStringCreateMutable(nullptr, 0 );
+ CFStringAppendCString(strRef, filename, kCFStringEncodingUTF8); // UTF8 is default on Mac OSX
+ CFStringNormalize(strRef, kCFStringNormalizationFormC);
+ CFStringGetCString(strRef, composed_name, BUFSIZ, kCFStringEncodingUTF8);
+ CFRelease(strRef);
+ filename = composed_name;
+#endif
+
+ strFileName = OString(filename, strlen(filename));
+
+ auto const strFilePath = osl::systemPathMakeAbsolutePath(pDirImpl->strPath, strFileName);
+
+ DirectoryItem_Impl* pImpl = static_cast< DirectoryItem_Impl* >(*pItem);
+ if (pImpl)
+ {
+ pImpl->release();
+ pImpl = nullptr;
+ }
+#ifdef _DIRENT_HAVE_D_TYPE
+ pImpl = new DirectoryItem_Impl(std::move(strFilePath), pEntry->d_type);
+#else
+ pImpl = new DirectoryItem_Impl(std::move(strFilePath));
+#endif /* _DIRENT_HAVE_D_TYPE */
+ *pItem = pImpl;
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_getDirectoryItem(rtl_uString* ustrFileURL, oslDirectoryItem* pItem)
+{
+ OString strSystemPath;
+ oslFileError osl_error = osl_File_E_INVAL;
+
+ if ((!ustrFileURL) || (ustrFileURL->length == 0) || (!pItem))
+ return osl_File_E_INVAL;
+
+ osl_error = osl::detail::convertUrlToPathname(OUString::unacquired(&ustrFileURL), &strSystemPath);
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+
+ osl_systemPathRemoveSeparator(strSystemPath.pData);
+
+ if (osl::access(strSystemPath, F_OK) == -1)
+ {
+ osl_error = oslTranslateFileError(errno);
+ }
+ else
+ {
+ *pItem = new DirectoryItem_Impl(std::move(strSystemPath));
+ }
+
+ return osl_error;
+}
+
+oslFileError SAL_CALL osl_acquireDirectoryItem( oslDirectoryItem Item )
+{
+ DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
+ if (pImpl == nullptr)
+ return osl_File_E_INVAL;
+
+ pImpl->acquire();
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_releaseDirectoryItem( oslDirectoryItem Item )
+{
+ DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
+ if (pImpl == nullptr)
+ return osl_File_E_INVAL;
+
+ pImpl->release();
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_createDirectory( rtl_uString* ustrDirectoryURL )
+{
+ return osl_createDirectoryWithFlags(
+ ustrDirectoryURL, osl_File_OpenFlag_Read | osl_File_OpenFlag_Write);
+}
+
+oslFileError osl_createDirectoryWithFlags(
+ rtl_uString * ustrDirectoryURL, sal_uInt32 flags)
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ SAL_WARN_IF((!ustrDirectoryURL) || (ustrDirectoryURL->length == 0),
+ "sal.file", "Invalid directory URL");
+
+ /* convert directory url to system path */
+ eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_createDirectory( path, flags );
+}
+
+oslFileError SAL_CALL osl_removeDirectory( rtl_uString* ustrDirectoryURL )
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ SAL_WARN_IF((!ustrDirectoryURL) || (ustrDirectoryURL->length == 0),
+ "sal.file", "Invalid directory URL");
+
+ /* convert directory url to system path */
+ eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_removeDirectory( path );
+}
+
+oslFileError osl_psz_createDirectory(char const * pszPath, sal_uInt32 flags)
+{
+ int nRet=0;
+ int mode
+ = (((flags & osl_File_OpenFlag_Read) == 0
+ ? 0
+ : ((flags & osl_File_OpenFlag_Private) == 0
+ ? S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
+ : S_IRUSR | S_IXUSR))
+ | ((flags & osl_File_OpenFlag_Write) == 0
+ ? 0
+ : ((flags & osl_File_OpenFlag_Private) == 0
+ ? S_IWUSR | S_IWGRP | S_IWOTH
+ : S_IWUSR)));
+
+ nRet = mkdir(pszPath,mode);
+
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ SAL_INFO("sal.file", "mkdir(" << pszPath << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
+ return oslTranslateFileError(nRet);
+ }
+ else
+ SAL_INFO("sal.file", "mkdir(" << pszPath << ",0" << std::oct << mode << std::dec << "): OK");
+
+ return osl_File_E_None;
+}
+
+static oslFileError osl_psz_removeDirectory( const char* pszPath )
+{
+ int nRet = rmdir(pszPath);
+
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ SAL_INFO("sal.file", "rmdir(" << pszPath << "): " << UnixErrnoString(nRet));
+ return oslTranslateFileError(nRet);
+ }
+ else
+ SAL_INFO("sal.file", "rmdir(" << pszPath << "): OK");
+
+ return osl_File_E_None;
+}
+
+static int path_make_parent(char* path)
+{
+ int i = rtl_str_lastIndexOfChar(path, '/');
+
+ if (i > 0)
+ {
+ *(path + i) = 0;
+ return i;
+ }
+ return 0;
+}
+
+static int create_dir_with_callback(
+ char* directory_path,
+ oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
+ void* pData)
+{
+ if (osl::mkdir(directory_path, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
+ {
+ if (aDirectoryCreationCallbackFunc)
+ {
+ OUString url;
+ osl::detail::convertPathnameToUrl(directory_path, &url);
+ aDirectoryCreationCallbackFunc(pData, url.pData);
+ }
+ return 0;
+ }
+ return errno;
+}
+
+static oslFileError create_dir_recursively_(
+ char* dir_path,
+ oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
+ void* pData)
+{
+ OSL_PRECOND((rtl_str_getLength(dir_path) > 0) && ((dir_path + (rtl_str_getLength(dir_path) - 1)) != (dir_path + rtl_str_lastIndexOfChar(dir_path, '/'))),
+ "Path must not end with a slash");
+
+ int native_err = create_dir_with_callback(
+ dir_path, aDirectoryCreationCallbackFunc, pData);
+
+ if (native_err == 0)
+ return osl_File_E_None;
+
+ if (native_err != ENOENT)
+ return oslTranslateFileError(native_err);
+
+ // we step back until '/a_dir' at maximum because
+ // we should get an error unequal ENOENT when
+ // we try to create 'a_dir' at '/' and would so
+ // return before
+ int pos = path_make_parent(dir_path);
+
+ oslFileError osl_error = create_dir_recursively_(
+ dir_path, aDirectoryCreationCallbackFunc, pData);
+
+ if (osl_error != osl_File_E_None && osl_error != osl_File_E_EXIST)
+ return osl_error;
+
+ dir_path[pos] = '/';
+
+ return create_dir_recursively_(dir_path, aDirectoryCreationCallbackFunc, pData);
+}
+
+oslFileError SAL_CALL osl_createDirectoryPath(
+ rtl_uString* aDirectoryUrl,
+ oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
+ void* pData)
+{
+ if (aDirectoryUrl == nullptr)
+ return osl_File_E_INVAL;
+
+ OString sys_path;
+ oslFileError osl_error = osl::detail::convertUrlToPathname(
+ OUString::unacquired(&aDirectoryUrl), &sys_path);
+
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+
+ osl::systemPathRemoveSeparator(sys_path);
+
+ // const_cast because sys_path is a local copy which we want to modify inplace instead of
+ // copy it into another buffer on the heap again
+ return create_dir_recursively_(sys_path.pData->buffer, aDirectoryCreationCallbackFunc, pData);
+}
+
+static oslFileError osl_unlinkFile(const char* pszPath);
+static oslFileError osl_psz_copyFile(const char* pszPath, const char* pszDestPath, bool preserveMetadata);
+static oslFileError osl_psz_moveFile(const char* pszPath, const char* pszDestPath);
+
+static oslFileError oslDoCopy(const char* pszSourceFileName, const char* pszDestFileName, mode_t nMode, size_t nSourceSize, bool DestFileExists);
+static void attemptChangeMetadata(const char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID);
+static int oslDoCopyLink(const char* pszSourceFileName, const char* pszDestFileName);
+static int oslDoCopyFile(const char* pszSourceFileName, const char* pszDestFileName, size_t nSourceSize, mode_t mode);
+static oslFileError oslDoMoveFile(const char* pszPath, const char* pszDestPath);
+
+oslFileError SAL_CALL osl_moveFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
+{
+ char srcPath[PATH_MAX];
+ char destPath[PATH_MAX];
+ oslFileError eRet;
+
+ SAL_WARN_IF((!ustrFileURL) || (ustrFileURL->length == 0), "sal.file", "Invalid source file URL");
+ SAL_WARN_IF((!ustrDestURL) || (ustrDestURL->length == 0), "sal.file", "Invalid destination file URL");
+
+ /* convert source url to system path */
+ eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+ /* convert destination url to system path */
+ eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return oslDoMoveFile( srcPath, destPath );
+}
+
+oslFileError SAL_CALL osl_replaceFile(rtl_uString* ustrFileURL, rtl_uString* ustrDestURL)
+{
+ int nGid = -1;
+ char destPath[PATH_MAX];
+ oslFileError eRet = FileURLToPath(destPath, PATH_MAX, ustrDestURL);
+ if (eRet == osl_File_E_None)
+ {
+ struct stat aFileStat;
+ // coverity[fs_check_call] - unavoidable TOCTOU
+ int nRet = stat(destPath, &aFileStat);
+ if (nRet == -1)
+ {
+ nRet = errno;
+ SAL_INFO("sal.file", "stat(" << destPath << "): " << UnixErrnoString(nRet));
+ }
+ else
+ {
+ nGid = aFileStat.st_gid;
+ }
+ }
+
+ eRet = osl_moveFile(ustrFileURL, ustrDestURL);
+
+ if (eRet == osl_File_E_None && nGid != -1)
+ {
+ int nRet = chown(destPath, -1, nGid);
+ if (nRet == -1)
+ {
+ nRet = errno;
+ SAL_INFO("sal.file",
+ "chown(" << destPath << "-1, " << nGid << "): " << UnixErrnoString(nRet));
+ }
+ }
+
+ return eRet;
+}
+
+oslFileError SAL_CALL osl_copyFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
+{
+ char srcPath[PATH_MAX];
+ char destPath[PATH_MAX];
+ oslFileError eRet;
+
+ SAL_WARN_IF((!ustrFileURL) || (ustrFileURL->length == 0), "sal.file", "Invalid source file URL");
+ SAL_WARN_IF((!ustrDestURL) || (ustrDestURL->length == 0), "sal.file", "Invalid destination file URL");
+
+ /* convert source url to system path */
+ eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+ /* convert destination url to system path */
+ eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_copyFile( srcPath, destPath, false );
+}
+
+oslFileError SAL_CALL osl_removeFile(rtl_uString* ustrFileURL)
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ SAL_WARN_IF(!ustrFileURL || ustrFileURL->length == 0, "sal.file", "Invalid file URL");
+
+ /* convert file url to system path */
+ eRet = FileURLToPath(path, PATH_MAX, ustrFileURL);
+ if (eRet != osl_File_E_None)
+ return eRet;
+
+#ifdef MACOSX
+ if (macxp_resolveAlias(path, PATH_MAX) != 0)
+ return oslTranslateFileError(errno);
+#endif/* MACOSX */
+
+ return osl_unlinkFile(path);
+}
+
+static oslFileError oslDoMoveFile(const char* pszPath, const char* pszDestPath)
+{
+ oslFileError tErr = osl_psz_moveFile(pszPath,pszDestPath);
+ if (tErr == osl_File_E_None)
+ return tErr;
+
+ if (tErr != osl_File_E_XDEV)
+ return tErr;
+
+ tErr = osl_psz_copyFile(pszPath,pszDestPath, true);
+
+ if (tErr != osl_File_E_None)
+ {
+ osl_unlinkFile(pszDestPath);
+ return tErr;
+ }
+
+ tErr = osl_unlinkFile(pszPath);
+
+ return tErr;
+}
+
+static oslFileError osl_unlinkFile(const char* pszPath)
+{
+ int nRet=0;
+ struct stat aStat;
+
+ nRet = lstat_c(pszPath,&aStat);
+ if (nRet < 0)
+ {
+ nRet=errno;
+ return oslTranslateFileError(nRet);
+ }
+
+ if (S_ISDIR(aStat.st_mode))
+ return osl_File_E_ISDIR;
+
+ nRet = unlink(pszPath);
+ if (nRet < 0)
+ {
+ nRet=errno;
+ SAL_INFO("sal.file", "unlink(" << pszPath << "): " << UnixErrnoString(nRet));
+ return oslTranslateFileError(nRet);
+ }
+ else
+ SAL_INFO("sal.file", "unlink(" << pszPath << "): OK");
+
+ return osl_File_E_None;
+}
+
+static oslFileError osl_psz_moveFile(const char* pszPath, const char* pszDestPath)
+{
+ int nRet = rename(pszPath,pszDestPath);
+
+ if (nRet < 0)
+ {
+ nRet=errno;
+ SAL_INFO("sal.file", "rename(" << pszPath << "," << pszDestPath << "): " << UnixErrnoString(nRet));
+ return oslTranslateFileError(nRet);
+ }
+ else
+ SAL_INFO("sal.file", "rename(" << pszPath << "," << pszDestPath << "): OK");
+
+ return osl_File_E_None;
+}
+
+static oslFileError osl_psz_copyFile( const char* pszPath, const char* pszDestPath, bool preserveMetadata )
+{
+ time_t nAcTime=0;
+ time_t nModTime=0;
+ uid_t nUID=0;
+ gid_t nGID=0;
+ int nRet=0;
+ mode_t nMode=0;
+ struct stat aFileStat;
+ oslFileError tErr=osl_File_E_invalidError;
+ size_t nSourceSize=0;
+ bool DestFileExists=true;
+
+ /* mfe: does the source file really exists? */
+ nRet = lstat_c(pszPath,&aFileStat);
+
+ if (nRet < 0)
+ {
+ nRet=errno;
+ return oslTranslateFileError(nRet);
+ }
+
+ /* we do only copy files here */
+ if (S_ISDIR(aFileStat.st_mode))
+ return osl_File_E_ISDIR;
+
+ nSourceSize = static_cast< size_t >(aFileStat.st_size);
+ nMode = aFileStat.st_mode;
+ nAcTime = aFileStat.st_atime;
+ nModTime = aFileStat.st_mtime;
+ nUID = aFileStat.st_uid;
+ nGID = aFileStat.st_gid;
+
+ nRet = stat_c(pszDestPath,&aFileStat);
+ if (nRet < 0)
+ {
+ nRet=errno;
+
+#ifdef IOS
+ // Checking for nonexistent files at least in the iCloud cache directory (like
+ // "/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/helloodt0.odt" fails
+ // with EPERM, not ENOENT.
+ if (nRet == EPERM)
+ DestFileExists=false;
+#endif
+
+ if (nRet == ENOENT)
+ DestFileExists=false;
+ }
+
+ /* mfe: the destination file must not be a directory! */
+ if (nRet == 0 && S_ISDIR(aFileStat.st_mode))
+ return osl_File_E_ISDIR;
+
+ /* mfe: file does not exists or is no dir */
+
+ tErr = oslDoCopy(pszPath, pszDestPath, nMode, nSourceSize, DestFileExists);
+
+ if (tErr != osl_File_E_None)
+ return tErr;
+
+ if (preserveMetadata)
+ attemptChangeMetadata(pszDestPath, nMode, nAcTime, nModTime, nUID, nGID);
+
+ return tErr;
+}
+
+static oslFileError oslDoCopy(const char* pszSourceFileName, const char* pszDestFileName, mode_t nMode, size_t nSourceSize, bool DestFileExists)
+{
+ int nRet=0;
+
+ OString tmpDestFile;
+ if ( DestFileExists )
+ {
+ //TODO: better pick a temp file name instead of adding .osl-tmp:
+ // use the destination file to avoid EXDEV /* Cross-device link */
+ tmpDestFile = pszDestFileName + OString::Concat(".osl-tmp");
+ if (rename(pszDestFileName, tmpDestFile.getStr()) != 0)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "rename(" << pszDestFileName << ", " << tmpDestFile
+ << "): " << UnixErrnoString(e));
+ if (e == ENOENT)
+ {
+ DestFileExists = false;
+ }
+ else
+ {
+ return osl_File_E_EXIST; // for want of a better error code
+ }
+ }
+ else
+ {
+ SAL_INFO("sal.file", "rename(" << pszDestFileName << ", " << tmpDestFile
+ << "): OK");
+ }
+ }
+
+ /* mfe: should be S_ISREG */
+ if ( !S_ISLNK(nMode) )
+ {
+ /* copy SourceFile to DestFile */
+ nRet = oslDoCopyFile(pszSourceFileName,pszDestFileName,nSourceSize, nMode);
+ }
+ /* mfe: OK redundant at the moment */
+ else if ( S_ISLNK(nMode) )
+ {
+ nRet = oslDoCopyLink(pszSourceFileName,pszDestFileName);
+ }
+ else
+ {
+ /* mfe: what to do here? */
+ nRet=ENOSYS;
+ }
+
+ if ( nRet > 0 && DestFileExists )
+ {
+ if (unlink(pszDestFileName) != 0)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "unlink(" << pszDestFileName << "): " << UnixErrnoString(e));
+ }
+ else
+ SAL_INFO("sal.file", "unlink(" << pszDestFileName << "): OK");
+
+ if (rename(tmpDestFile.getStr(), pszDestFileName) != 0)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "rename(" << tmpDestFile << ", " << pszDestFileName
+ << "): " << UnixErrnoString(e));
+ }
+ else
+ SAL_INFO("sal.file", "rename(" << tmpDestFile << ", " << pszDestFileName << "): OK");
+ }
+
+ if ( nRet > 0 )
+ {
+ return oslTranslateFileError(nRet);
+ }
+
+ if ( DestFileExists )
+ {
+ unlink(tmpDestFile.getStr());
+ }
+
+ return osl_File_E_None;
+}
+
+void attemptChangeMetadata( const char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID)
+{
+ struct utimbuf aTimeBuffer;
+
+#if !defined AT_FDCWD
+ if (!S_ISLNK(nMode) && chmod(pszFileName, nMode) < 0)
+#else
+ if ( fchmodat(AT_FDCWD, pszFileName, nMode, AT_SYMLINK_NOFOLLOW) < 0 )
+#endif
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): " << UnixErrnoString(e));
+ }
+ else
+ SAL_INFO("sal.file", "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): OK");
+
+ // No way to change utime of a symlink itself:
+ if (!S_ISLNK(nMode))
+ {
+ aTimeBuffer.actime=nAcTime;
+ aTimeBuffer.modtime=nModTime;
+ if ( utime(pszFileName,&aTimeBuffer) < 0 )
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "utime(" << pszFileName << "): errno " << e);
+ }
+ }
+
+ if ( nUID != getuid() )
+ {
+ nUID=getuid();
+ }
+ if ( lchown(pszFileName,nUID,nGID) < 0 )
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "lchown(" << pszFileName << "): errno " << e);
+ }
+ else
+ SAL_INFO("sal.file", "lchown(" << pszFileName << "): OK");
+}
+
+static int oslDoCopyLink(const char* pszSourceFileName, const char* pszDestFileName)
+{
+ int nRet=0;
+
+ /* mfe: if dest file is symbolic link remove the link and place the file instead (hro says so) */
+ /* mfe: if source is a link copy the link and not the file it points to (hro says so) */
+ char pszLinkContent[PATH_MAX+1];
+
+ pszLinkContent[0] = '\0';
+
+ nRet = readlink(pszSourceFileName,pszLinkContent,PATH_MAX);
+
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ return nRet;
+ }
+
+ pszLinkContent[ nRet ] = 0;
+
+ nRet = symlink(pszLinkContent,pszDestFileName);
+
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ return nRet;
+ }
+
+ return 0;
+}
+
+static int oslDoCopyFile(const char* pszSourceFileName, const char* pszDestFileName, size_t nSourceSize, mode_t mode)
+{
+ oslFileHandle SourceFileFH=nullptr;
+ int DestFileFD=0;
+ int nRet=0;
+
+ if (openFilePath(pszSourceFileName,
+ &SourceFileFH,
+ osl_File_OpenFlag_Read|osl_File_OpenFlag_NoLock|osl_File_OpenFlag_NoExcl, mode_t(-1)) != osl_File_E_None)
+ {
+ // Let's hope errno is still set relevantly after openFilePath...
+ nRet=errno;
+ return nRet;
+ }
+
+ DestFileFD=open(pszDestFileName, O_WRONLY | O_CREAT, mode);
+
+ if ( DestFileFD < 0 )
+ {
+ nRet=errno;
+ SAL_INFO("sal.file", "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
+ osl_closeFile(SourceFileFH);
+ return nRet;
+ }
+ else
+ SAL_INFO("sal.file", "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): OK");
+
+ size_t nRemains = nSourceSize;
+
+ if ( nRemains )
+ {
+ /* mmap has problems, try the direct streaming */
+ char pBuffer[0x7FFF];
+
+ do
+ {
+ size_t nToRead = std::min( sizeof(pBuffer), nRemains );
+ sal_uInt64 nRead;
+ bool succeeded;
+ if ( osl_readFile( SourceFileFH, pBuffer, nToRead, &nRead ) != osl_File_E_None || nRead > nToRead || nRead == 0 )
+ break;
+
+ succeeded = safeWrite( DestFileFD, pBuffer, nRead );
+ if ( !succeeded )
+ break;
+
+ // We know nRead <= nToRead, so it must fit in a size_t
+ nRemains -= static_cast<size_t>(nRead);
+ }
+ while( nRemains );
+ }
+
+ if ( nRemains )
+ {
+ if ( errno )
+ nRet = errno;
+ else
+ nRet = ENOSPC;
+ }
+
+ osl_closeFile( SourceFileFH );
+ if ( close( DestFileFD ) == -1 )
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "close(" << DestFileFD << "): " << UnixErrnoString(e));
+ if ( nRet == 0 )
+ nRet = e;
+ }
+ else
+ SAL_INFO("sal.file", "close(" << DestFileFD << "): OK");
+
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_path_helper.cxx b/sal/osl/unx/file_path_helper.cxx
new file mode 100644
index 000000000..a8ee5238f
--- /dev/null
+++ b/sal/osl/unx/file_path_helper.cxx
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <utility>
+
+#include "file_path_helper.hxx"
+#include "uunxapi.hxx"
+
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+const sal_Unicode FPH_CHAR_PATH_SEPARATOR = '/';
+const sal_Unicode FPH_CHAR_DOT = '.';
+const sal_Unicode FPH_CHAR_COLON = ':';
+
+void osl_systemPathRemoveSeparator(rtl_String* pstrPath)
+{
+ OSL_PRECOND(nullptr != pstrPath, "osl_systemPathRemoveSeparator: Invalid parameter");
+ if (pstrPath == nullptr)
+ return;
+
+ // maybe there are more than one separator at end
+ // so we run in a loop
+ while ((pstrPath->length > 1) && (pstrPath->buffer[pstrPath->length - 1] == FPH_CHAR_PATH_SEPARATOR))
+ {
+ pstrPath->length--;
+ pstrPath->buffer[pstrPath->length] = '\0';
+ }
+
+ SAL_WARN_IF( !((0 == pstrPath->length) || (1 == pstrPath->length) ||
+ (pstrPath->length > 1 && pstrPath->buffer[pstrPath->length - 1] != FPH_CHAR_PATH_SEPARATOR)),
+ "sal.osl",
+ "osl_systemPathRemoveSeparator: Post condition failed");
+}
+
+namespace {
+
+template<typename T> void systemPathEnsureSeparator(T* ppstrPath)
+{
+ assert(nullptr != ppstrPath);
+ sal_Int32 lp = ppstrPath->getLength();
+ sal_Int32 i = ppstrPath->lastIndexOf(FPH_CHAR_PATH_SEPARATOR);
+
+ if ((lp > 1 && i != (lp - 1)) || ((lp < 2) && i < 0))
+ {
+ *ppstrPath += "/";
+ }
+
+ SAL_WARN_IF( !ppstrPath->endsWith("/"),
+ "sal.osl",
+ "systemPathEnsureSeparator: Post condition failed");
+}
+
+}
+
+bool osl_systemPathIsRelativePath(const rtl_uString* pustrPath)
+{
+ OSL_PRECOND(nullptr != pustrPath, "osl_systemPathIsRelativePath: Invalid parameter");
+ return ((pustrPath == nullptr) || (pustrPath->length == 0) || (pustrPath->buffer[0] != FPH_CHAR_PATH_SEPARATOR));
+}
+
+namespace {
+
+template<typename T> T systemPathMakeAbsolutePath_(
+ const T& BasePath,
+ const T& RelPath)
+{
+ T base(BasePath);
+
+ if (!base.isEmpty())
+ systemPathEnsureSeparator(&base);
+
+ return base + RelPath;
+}
+
+}
+
+OString osl::systemPathMakeAbsolutePath(
+ const OString& BasePath,
+ const OString& RelPath)
+{
+ return systemPathMakeAbsolutePath_(BasePath, RelPath);
+}
+
+OUString osl::systemPathMakeAbsolutePath(
+ const OUString& BasePath,
+ const OUString& RelPath)
+{
+ return systemPathMakeAbsolutePath_(BasePath, RelPath);
+}
+
+void osl_systemPathGetFileNameOrLastDirectoryPart(
+ const rtl_String* pstrPath,
+ rtl_String** ppstrFileNameOrLastDirPart)
+{
+ OSL_PRECOND(pstrPath && ppstrFileNameOrLastDirPart,
+ "osl_systemPathGetFileNameOrLastDirectoryPart: Invalid parameter");
+
+ OString path(const_cast<rtl_String*>(pstrPath));
+
+ osl_systemPathRemoveSeparator(path.pData);
+
+ OString last_part;
+
+ if (path.getLength() > 1 || (path.getLength() == 1 && path[0] != FPH_CHAR_PATH_SEPARATOR))
+ {
+ sal_Int32 idx_ps = path.lastIndexOf(FPH_CHAR_PATH_SEPARATOR);
+ idx_ps++; // always right to increment by one even if idx_ps == -1!
+ last_part = path.copy(idx_ps);
+ }
+ rtl_string_assign(ppstrFileNameOrLastDirPart, last_part.pData);
+}
+
+bool osl_systemPathIsHiddenFileOrDirectoryEntry(
+ const rtl_String* pstrPath)
+{
+ OSL_PRECOND(nullptr != pstrPath, "osl_systemPathIsHiddenFileOrDirectoryEntry: Invalid parameter");
+ if ((pstrPath == nullptr) || (pstrPath->length == 0))
+ return false;
+
+ OString fdp;
+ osl_systemPathGetFileNameOrLastDirectoryPart(pstrPath, &fdp.pData);
+
+ return ((fdp.pData->length > 0) &&
+ (fdp.pData->buffer[0] == FPH_CHAR_DOT) &&
+ !osl_systemPathIsLocalOrParentDirectoryEntry(fdp.pData));
+}
+
+bool osl_systemPathIsLocalOrParentDirectoryEntry(
+ const rtl_String* pstrPath)
+{
+ OSL_PRECOND(pstrPath, "osl_systemPathIsLocalOrParentDirectoryEntry: Invalid parameter");
+
+ OString dirent;
+
+ osl_systemPathGetFileNameOrLastDirectoryPart(pstrPath, &dirent.pData);
+
+ return (dirent == "." ||
+ dirent == "..");
+}
+
+namespace {
+
+/** Simple iterator for a path list separated by the specified character
+*/
+class path_list_iterator
+{
+public:
+
+ /* after construction get_current_item
+ returns the first path in list, no need
+ to call reset first
+ */
+ path_list_iterator(OUString path_list, sal_Unicode list_separator = FPH_CHAR_COLON) :
+ m_path_list(std::move(path_list)),
+ m_end(m_path_list.getStr() + m_path_list.getLength() + 1),
+ m_separator(list_separator)
+ {
+ reset();
+ }
+
+ path_list_iterator(const path_list_iterator&) = delete;
+ path_list_iterator& operator=(const path_list_iterator&) = delete;
+
+ void reset()
+ {
+ m_path_segment_begin = m_path_segment_end = m_path_list.getStr();
+ advance();
+ }
+
+ void next()
+ {
+ OSL_PRECOND(!done(), "path_list_iterator: Already done!");
+
+ m_path_segment_begin = ++m_path_segment_end;
+ advance();
+ }
+
+ bool done() const
+ {
+ return (m_path_segment_end >= m_end);
+ }
+
+ OUString get_current_item() const
+ {
+ return OUString(
+ m_path_segment_begin,
+ (m_path_segment_end - m_path_segment_begin));
+ }
+
+private:
+ /* move m_path_end to the next separator or
+ to the end of the string
+ */
+ void advance()
+ {
+ while (!done() && *m_path_segment_end && (*m_path_segment_end != m_separator))
+ ++m_path_segment_end;
+
+ OSL_ASSERT(m_path_segment_end <= m_end);
+ }
+
+private:
+ OUString m_path_list;
+ const sal_Unicode* m_end;
+ const sal_Unicode m_separator;
+ const sal_Unicode* m_path_segment_begin;
+ const sal_Unicode* m_path_segment_end;
+};
+
+}
+
+bool osl_searchPath(
+ const rtl_uString* pustrFilePath,
+ const rtl_uString* pustrSearchPathList,
+ rtl_uString** ppustrPathFound)
+{
+ OSL_PRECOND(pustrFilePath && pustrSearchPathList && ppustrPathFound, "osl_searchPath: Invalid parameter");
+
+ bool bfound = false;
+ OUString fp(const_cast<rtl_uString*>(pustrFilePath));
+ OUString pl(const_cast<rtl_uString*>(pustrSearchPathList));
+ path_list_iterator pli(pl);
+
+ while (!pli.done())
+ {
+ OUString p = pli.get_current_item();
+ systemPathEnsureSeparator(&p);
+ p += fp;
+
+ if (osl::access(osl::OUStringToOString(p), F_OK) > -1)
+ {
+ bfound = true;
+ rtl_uString_assign(ppustrPathFound, p.pData);
+ break;
+ }
+ pli.next();
+ }
+ return bfound;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_path_helper.hxx b/sal/osl/unx/file_path_helper.hxx
new file mode 100644
index 000000000..e210b27a3
--- /dev/null
+++ b/sal/osl/unx/file_path_helper.hxx
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_FILE_PATH_HELPER_HXX
+#define INCLUDED_SAL_OSL_UNX_FILE_PATH_HELPER_HXX
+
+#include <sal/types.h>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+
+/**
+ Removes the last separator from the given system path if any and if the path
+ is not the root path '/'
+
+ @param ppstrPath[inout] a system path if the path is not the root path
+ and the last character is a path separator it
+ will be cut off ppstrPath must not be NULL and
+ must point to a valid rtl_String
+
+ @returns nothing
+
+*/
+void osl_systemPathRemoveSeparator(rtl_String* pstrPath);
+
+/**
+ Returns true if the given path is a relative path and so starts not with '/'
+
+ @param pustrPath [in] a system path - must not be NULL
+
+ @retval sal_True the given path doesn't start with a separator
+ @retval sal_False the given path starts with a separator
+
+*/
+bool osl_systemPathIsRelativePath(
+ const rtl_uString* pustrPath);
+
+/**
+ Returns the file or the directory part of the given path
+
+ @param pstrPath [in] a system path, must not be NULL
+
+ @param ppstrFileOrDirPart [out] on return receives the last part of the
+ given directory or the file name if pstrPath is the
+ root path '/' an empty string will be returned if
+ pstrPath has a trailing '/' the last part before the
+ '/' will be returned else the part after the last '/'
+ will be returned
+
+ @returns nothing
+
+*/
+void osl_systemPathGetFileNameOrLastDirectoryPart(
+ const rtl_String* pstrPath,
+ rtl_String** ppstrFileNameOrLastDirPart);
+
+/**
+ @param pustrPath [in] a system path, must not be NULL
+
+ @retval sal_True the last part of the given system path starts with '.'
+ @retval sal_False the last part of the given system path is '.' or '..'
+ alone or doesn't start with a dot
+
+*/
+bool osl_systemPathIsHiddenFileOrDirectoryEntry(
+ const rtl_String* pustrPath);
+
+/************************************************
+ osl_systemPathIsLocalOrParentDirectoryEntry
+ Returns sal_True if the last part of the given
+ system path is the local directory entry '.'
+ or the parent directory entry '..'
+
+ @param pstrPath [in] a system path,
+ must not be NULL
+
+ @returns sal_True if the last part of the
+ given system path is '.' or '..'
+ else sal_False
+
+************************************************/
+
+bool osl_systemPathIsLocalOrParentDirectoryEntry(
+ const rtl_String* pstrPath);
+
+/************************************************
+ osl_searchPath
+ Searches for a file name or path name in all
+ directories specified by a given path list.
+ Symbolic links in the resulting path will not be
+ resolved, it's up to the caller to do this.
+
+ @param pustrFilePath [in] a file name or
+ directory name to search for, the name must
+ be provided as system path not as a file URL
+
+ @param pustrSearchPathList [in] a ':'
+ separated list of paths in which to search for
+ the file or directory name
+
+ @param ppustrPathFound [out] on success receives the
+ complete path of the file or directory found
+ as a system path
+
+ @returns sal_True if the specified file or
+ directory was found else sal_False
+ ***********************************************/
+
+bool osl_searchPath(
+ const rtl_uString* pustrFilePath,
+ const rtl_uString* pustrSearchPathList,
+ rtl_uString** ppustrPathFound);
+
+namespace osl
+{
+
+ /*******************************************
+ systemPathRemoveSeparator
+ Removes the last separator from the
+ given system path if any and if the path
+ is not the root path '/'
+
+ @param ppustrPath [inout] a system path
+ if the path is not the root path
+ and the last character is a
+ path separator it will be cut off
+ ppustrPath must not be NULL and
+ must point to a valid rtl_uString
+
+ @returns nothing
+
+ ******************************************/
+
+ inline void systemPathRemoveSeparator(/*inout*/ OString& Path)
+ {
+ osl_systemPathRemoveSeparator(Path.pData);
+ }
+
+ /*******************************************
+ systemPathIsRelativePath
+ Returns true if the given path is a
+ relative path and so starts not with '/'
+
+ @param pustrPath [in] a system path
+ pustrPath must not be NULL
+
+ @returns sal_True if the given path
+ doesn't start with a separator
+ else sal_False will be returned
+
+ ******************************************/
+
+ inline bool systemPathIsRelativePath(const OUString& Path)
+ {
+ return osl_systemPathIsRelativePath(Path.pData);
+ }
+
+ /******************************************
+ systemPathMakeAbsolutePath
+ Append a relative path to a base path
+
+ @param BasePath [in] a system
+ path that will be considered as
+ base path
+
+ @param RelPath [in] a system path
+ that will be considered as
+ relative path
+
+ @return the
+ resulting path which is a
+ concatenation of the base and
+ the relative path
+ if base path is empty the
+ resulting absolute path is the
+ relative path
+ if relative path is empty the
+ resulting absolute path is the
+ base path
+ if base and relative path are
+ empty the resulting absolute
+ path is also empty
+
+ *****************************************/
+
+ OString systemPathMakeAbsolutePath(
+ const OString& BasePath,
+ const OString& RelPath);
+
+ OUString systemPathMakeAbsolutePath(
+ const OUString& BasePath,
+ const OUString& RelPath);
+
+ /********************************************
+ systemPathIsHiddenFileOrDirectoryEntry
+ Returns sal_True if the last part of
+ given system path is not '.' or '..'
+ alone and starts with a '.'
+
+ @param pustrPath [in] a system path,
+ must not be NULL
+
+ @returns sal_True if the last part of
+ the given system path starts
+ with '.' or sal_False the last
+ part is '.' or '..' alone or
+ doesn't start with a dot
+
+ *********************************************/
+
+ inline bool systemPathIsHiddenFileOrDirectoryEntry(
+ const OString& Path)
+ {
+ return osl_systemPathIsHiddenFileOrDirectoryEntry(Path.pData);
+ }
+
+ /************************************************
+ searchPath
+ ***********************************************/
+
+ inline bool searchPath(
+ const OUString& ustrFilePath,
+ const OUString& ustrSearchPathList,
+ OUString& ustrPathFound)
+ {
+ return osl_searchPath(
+ ustrFilePath.pData,
+ ustrSearchPathList.pData,
+ &ustrPathFound.pData);
+ }
+
+ } // namespace osl
+
+ #endif /* #ifndef _OSL_PATH_HELPER_HXX_ */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_stat.cxx b/sal/osl/unx/file_stat.cxx
new file mode 100644
index 000000000..3b702905c
--- /dev/null
+++ b/sal/osl/unx/file_stat.cxx
@@ -0,0 +1,453 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/file.h>
+
+#include "system.hxx"
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+
+#include "file_impl.hxx"
+#include "file_error_transl.hxx"
+#include "file_path_helper.hxx"
+#include "file_url.hxx"
+#include "uunxapi.hxx"
+
+namespace
+{
+ void set_file_type(const struct stat& file_stat, oslFileStatus* pStat)
+ {
+ /* links to directories state also to be a directory */
+ if (S_ISLNK(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Link;
+ else if (S_ISDIR(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Directory;
+ else if (S_ISREG(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Regular;
+ else if (S_ISFIFO(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Fifo;
+ else if (S_ISSOCK(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Socket;
+ else if (S_ISCHR(file_stat.st_mode) || S_ISBLK(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Special;
+ else
+ pStat->eType = osl_File_Type_Unknown;
+
+ pStat->uValidFields |= osl_FileStatus_Mask_Type;
+ }
+
+ void set_file_access_mask(const struct stat& file_stat, oslFileStatus* pStat)
+ {
+ // user permissions
+ if (S_IRUSR & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OwnRead;
+
+ if (S_IWUSR & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OwnWrite;
+
+ if (S_IXUSR & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OwnExe;
+
+ // group permissions
+ if (S_IRGRP & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_GrpRead;
+
+ if (S_IWGRP & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_GrpWrite;
+
+ if (S_IXGRP & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_GrpExe;
+
+ // others permissions
+ if (S_IROTH & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OthRead;
+
+ if (S_IWOTH & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OthWrite;
+
+ if (S_IXOTH & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OthExe;
+
+ pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
+ }
+
+ /* This code used not to use access(...) because access follows links which
+ may cause performance problems see #97133. (That apparently references a
+ no-longer accessible Hamburg-internal bug-tracking system.)
+ However, contrary to what is stated above the use of access calls is
+ required on network file systems not using unix semantics (AFS, see
+ fdo#43095).
+ */
+ void set_file_access_rights(const OString& file_path, oslFileStatus* pStat)
+ {
+ pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
+
+ if (osl::access(file_path, W_OK) < 0)
+ pStat->uAttributes |= osl_File_Attribute_ReadOnly;
+
+ if (osl::access(file_path, X_OK) == 0)
+ pStat->uAttributes |= osl_File_Attribute_Executable;
+ }
+
+ void set_file_hidden_status(const OString& file_path, oslFileStatus* pStat)
+ {
+ pStat->uAttributes = osl::systemPathIsHiddenFileOrDirectoryEntry(file_path) ? osl_File_Attribute_Hidden : 0;
+ pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
+ }
+
+ /* the set_file_access_rights must be called after set_file_hidden_status(...) and
+ set_file_access_mask(...) because of the hack in set_file_access_rights(...) */
+ void set_file_attributes(
+ const OString& file_path, const struct stat& file_stat, const sal_uInt32 uFieldMask, oslFileStatus* pStat)
+ {
+ set_file_hidden_status(file_path, pStat);
+ set_file_access_mask(file_stat, pStat);
+
+ // we set the file access rights only on demand
+ // because it's potentially expensive
+ if (uFieldMask & osl_FileStatus_Mask_Attributes)
+ set_file_access_rights(file_path, pStat);
+ }
+
+ void set_file_access_time(const struct stat& file_stat, oslFileStatus* pStat)
+ {
+ pStat->aAccessTime.Seconds = file_stat.st_atime;
+ pStat->aAccessTime.Nanosec = 0;
+ pStat->uValidFields |= osl_FileStatus_Mask_AccessTime;
+ }
+
+ void set_file_modify_time(const struct stat& file_stat, oslFileStatus* pStat)
+ {
+ pStat->aModifyTime.Seconds = file_stat.st_mtime;
+ pStat->aModifyTime.Nanosec = 0;
+ pStat->uValidFields |= osl_FileStatus_Mask_ModifyTime;
+ }
+
+ void set_file_size(const struct stat& file_stat, oslFileStatus* pStat)
+ {
+ if (S_ISREG(file_stat.st_mode))
+ {
+ pStat->uFileSize = file_stat.st_size;
+ pStat->uValidFields |= osl_FileStatus_Mask_FileSize;
+ }
+ }
+
+ /* we only need to call stat or lstat if one of the
+ following flags is set */
+ bool is_stat_call_necessary(sal_uInt32 field_mask, oslFileType file_type)
+ {
+ return (
+ ((field_mask & osl_FileStatus_Mask_Type) && (file_type == osl_File_Type_Unknown)) ||
+ (field_mask & osl_FileStatus_Mask_Attributes) ||
+ (field_mask & osl_FileStatus_Mask_CreationTime) ||
+ (field_mask & osl_FileStatus_Mask_AccessTime) ||
+ (field_mask & osl_FileStatus_Mask_ModifyTime) ||
+ (field_mask & osl_FileStatus_Mask_FileSize) ||
+ (field_mask & osl_FileStatus_Mask_LinkTargetURL) ||
+ (field_mask & osl_FileStatus_Mask_Validate));
+ }
+
+ oslFileError set_link_target_url(const OString& file_path, oslFileStatus* pStat)
+ {
+ OString link_target;
+ if (!osl::realpath(file_path, link_target))
+ return oslTranslateFileError(errno);
+
+ OUString url;
+ oslFileError osl_error = osl::detail::convertPathnameToUrl(link_target, &url);
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+ rtl_uString_assign(&pStat->ustrLinkTargetURL, url.pData);
+
+ pStat->uValidFields |= osl_FileStatus_Mask_LinkTargetURL;
+ return osl_File_E_None;
+ }
+
+ oslFileError setup_osl_getFileStatus(
+ DirectoryItem_Impl * pImpl, oslFileStatus* pStat, OString& file_path)
+ {
+ if ((pImpl == nullptr) || (pStat == nullptr))
+ return osl_File_E_INVAL;
+
+ file_path = pImpl->m_strFilePath;
+ OSL_ASSERT(!file_path.isEmpty());
+ if (file_path.isEmpty())
+ return osl_File_E_INVAL;
+
+ pStat->uValidFields = 0;
+ return osl_File_E_None;
+ }
+
+}
+
+oslFileError SAL_CALL osl_getFileStatus(oslDirectoryItem Item, oslFileStatus* pStat, sal_uInt32 uFieldMask)
+{
+ DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
+
+ OString file_path;
+ oslFileError osl_error = setup_osl_getFileStatus(pImpl, pStat, file_path);
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+
+ struct stat file_stat;
+
+ bool bStatNeeded = is_stat_call_necessary(uFieldMask, pImpl->getFileType());
+ if (bStatNeeded && (osl::lstat(file_path, file_stat) != 0))
+ return oslTranslateFileError(errno);
+
+ if (bStatNeeded)
+ {
+ // we set all these attributes because it's cheap
+ set_file_type(file_stat, pStat);
+ set_file_access_time(file_stat, pStat);
+ set_file_modify_time(file_stat, pStat);
+ set_file_size(file_stat, pStat);
+ set_file_attributes(file_path, file_stat, uFieldMask, pStat);
+
+ // file exists semantic of osl_FileStatus_Mask_Validate
+ if ((uFieldMask & osl_FileStatus_Mask_LinkTargetURL) && S_ISLNK(file_stat.st_mode))
+ {
+ osl_error = set_link_target_url(file_path, pStat);
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+ }
+ }
+#ifdef _DIRENT_HAVE_D_TYPE
+ else if (uFieldMask & osl_FileStatus_Mask_Type)
+ {
+ pStat->eType = pImpl->getFileType();
+ pStat->uValidFields |= osl_FileStatus_Mask_Type;
+ }
+#endif /* _DIRENT_HAVE_D_TYPE */
+
+ if (uFieldMask & osl_FileStatus_Mask_FileURL)
+ {
+ OUString url;
+ if ((osl_error = osl::detail::convertPathnameToUrl(file_path, &url)) != osl_File_E_None)
+ return osl_error;
+ rtl_uString_assign(&pStat->ustrFileURL, url.pData);
+
+ pStat->uValidFields |= osl_FileStatus_Mask_FileURL;
+ }
+
+ if (uFieldMask & osl_FileStatus_Mask_FileName)
+ {
+ OString name;
+ osl_systemPathGetFileNameOrLastDirectoryPart(file_path.pData, &name.pData);
+ bool ok = rtl_convertStringToUString(
+ &pStat->ustrFileName, name.getStr(), name.getLength(), osl_getThreadTextEncoding(),
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT));
+ assert(ok); (void)ok;
+ pStat->uValidFields |= osl_FileStatus_Mask_FileName;
+ }
+ return osl_File_E_None;
+}
+
+static oslFileError osl_psz_setFileAttributes( const char* pszFilePath, sal_uInt64 uAttributes )
+{
+ oslFileError osl_error = osl_File_E_None;
+ mode_t nNewMode = 0;
+
+ OSL_ENSURE(!(osl_File_Attribute_Hidden & uAttributes), "osl_File_Attribute_Hidden doesn't work under Unix");
+
+ if (uAttributes & osl_File_Attribute_OwnRead)
+ nNewMode |= S_IRUSR;
+
+ if (uAttributes & osl_File_Attribute_OwnWrite)
+ nNewMode|=S_IWUSR;
+
+ if (uAttributes & osl_File_Attribute_OwnExe)
+ nNewMode|=S_IXUSR;
+
+ if (uAttributes & osl_File_Attribute_GrpRead)
+ nNewMode|=S_IRGRP;
+
+ if (uAttributes & osl_File_Attribute_GrpWrite)
+ nNewMode|=S_IWGRP;
+
+ if (uAttributes & osl_File_Attribute_GrpExe)
+ nNewMode|=S_IXGRP;
+
+ if (uAttributes & osl_File_Attribute_OthRead)
+ nNewMode|=S_IROTH;
+
+ if (uAttributes & osl_File_Attribute_OthWrite)
+ nNewMode|=S_IWOTH;
+
+ if (uAttributes & osl_File_Attribute_OthExe)
+ nNewMode|=S_IXOTH;
+
+ if (chmod(pszFilePath, nNewMode) < 0)
+ osl_error = oslTranslateFileError(errno);
+
+ return osl_error;
+}
+
+oslFileError SAL_CALL osl_setFileAttributes( rtl_uString* ustrFileURL, sal_uInt64 uAttributes )
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ OSL_ASSERT( ustrFileURL );
+
+ /* convert file url to system path */
+ eRet = FileURLToPath( path, PATH_MAX, ustrFileURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_setFileAttributes( path, uAttributes );
+}
+
+static oslFileError osl_psz_setFileTime (
+ const char* pszFilePath,
+ const TimeValue* pLastAccessTime,
+ const TimeValue* pLastWriteTime )
+{
+ int nRet=0;
+ struct utimbuf aTimeBuffer;
+ struct stat aFileStat;
+#ifdef DEBUG_OSL_FILE
+ struct tm* pTM=0;
+#endif
+
+ nRet = lstat_c(pszFilePath,&aFileStat);
+
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ return oslTranslateFileError(nRet);
+ }
+
+#ifdef DEBUG_OSL_FILE
+ fprintf(stderr,"File Times are (in localtime):\n");
+ pTM=localtime(&aFileStat.st_ctime);
+ fprintf(stderr,"CreationTime is '%s'\n",asctime(pTM));
+ pTM=localtime(&aFileStat.st_atime);
+ fprintf(stderr,"AccessTime is '%s'\n",asctime(pTM));
+ pTM=localtime(&aFileStat.st_mtime);
+ fprintf(stderr,"Modification is '%s'\n",asctime(pTM));
+
+ fprintf(stderr,"File Times are (in UTC):\n");
+ fprintf(stderr,"CreationTime is '%s'\n",ctime(&aFileStat.st_ctime));
+ fprintf(stderr,"AccessTime is '%s'\n",ctime(&aTimeBuffer.actime));
+ fprintf(stderr,"Modification is '%s'\n",ctime(&aTimeBuffer.modtime));
+#endif
+
+ if ( pLastAccessTime != nullptr )
+ {
+ aTimeBuffer.actime=pLastAccessTime->Seconds;
+ }
+ else
+ {
+ aTimeBuffer.actime=aFileStat.st_atime;
+ }
+
+ if ( pLastWriteTime != nullptr )
+ {
+ aTimeBuffer.modtime=pLastWriteTime->Seconds;
+ }
+ else
+ {
+ aTimeBuffer.modtime=aFileStat.st_mtime;
+ }
+
+ /* mfe: Creation time not used here! */
+
+#ifdef DEBUG_OSL_FILE
+ fprintf(stderr,"File Times are (in localtime):\n");
+ pTM=localtime(&aFileStat.st_ctime);
+ fprintf(stderr,"CreationTime now '%s'\n",asctime(pTM));
+ pTM=localtime(&aTimeBuffer.actime);
+ fprintf(stderr,"AccessTime now '%s'\n",asctime(pTM));
+ pTM=localtime(&aTimeBuffer.modtime);
+ fprintf(stderr,"Modification now '%s'\n",asctime(pTM));
+
+ fprintf(stderr,"File Times are (in UTC):\n");
+ fprintf(stderr,"CreationTime now '%s'\n",ctime(&aFileStat.st_ctime));
+ fprintf(stderr,"AccessTime now '%s'\n",ctime(&aTimeBuffer.actime));
+ fprintf(stderr,"Modification now '%s'\n",ctime(&aTimeBuffer.modtime));
+#endif
+
+ nRet = utime_c(pszFilePath,&aTimeBuffer);
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ return oslTranslateFileError(nRet);
+ }
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_setFileTime (
+ rtl_uString* ustrFileURL,
+ SAL_UNUSED_PARAMETER const TimeValue* /* pCreationTime */,
+ const TimeValue* pLastAccessTime,
+ const TimeValue* pLastWriteTime )
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ OSL_ASSERT( ustrFileURL );
+
+ /* convert file url to system path */
+ eRet = FileURLToPath( path, PATH_MAX, ustrFileURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_setFileTime( path, pLastAccessTime, pLastWriteTime );
+}
+
+sal_Bool
+SAL_CALL osl_identicalDirectoryItem( oslDirectoryItem a, oslDirectoryItem b)
+{
+ DirectoryItem_Impl *pA = static_cast<DirectoryItem_Impl *>(a);
+ DirectoryItem_Impl *pB = static_cast<DirectoryItem_Impl *>(b);
+ if (a == b)
+ return true;
+ /* same name => same item, unless renaming / moving madness has occurred */
+ if (pA->m_strFilePath == pB->m_strFilePath)
+ return true;
+
+ struct stat a_stat, b_stat;
+
+ if (osl::lstat(pA->m_strFilePath, a_stat) != 0 ||
+ osl::lstat(pB->m_strFilePath, b_stat) != 0)
+ return false;
+
+ return (a_stat.st_ino == b_stat.st_ino);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_url.cxx b/sal/osl/unx/file_url.cxx
new file mode 100644
index 000000000..4b5a16885
--- /dev/null
+++ b/sal/osl/unx/file_url.cxx
@@ -0,0 +1,972 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "file_url.hxx"
+
+#include "system.hxx"
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <stdexcept>
+#include <string_view>
+#include <type_traits>
+#include <limits.h>
+#include <errno.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <o3tl/safeint.hxx>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <osl/socket.h>
+#include <oslsocket.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/process.h>
+
+#include <rtl/character.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/uri.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/textcvt.h>
+#include <sal/log.hxx>
+
+#include <uri_internal.hxx>
+
+#include "file_error_transl.hxx"
+#include "file_path_helper.hxx"
+
+#include "uunxapi.hxx"
+
+/** @file
+
+ General note
+
+ This file contains the part that handles File URLs.
+
+ File URLs as scheme specific notion of URIs
+ (RFC2396) may be handled platform independent, but
+ will not in osl which is considered wrong.
+ Future version of osl should handle File URLs this
+ way. In rtl/uri there is already a URI parser etc.
+ so this code should be consolidated.
+
+*/
+
+using namespace osl;
+
+namespace {
+
+// A slightly modified version of Pchar in rtl/source/uri.c, but without
+// encoding slashes:
+constexpr auto uriCharClass = rtl::createUriCharClass(
+ u8"!$&'()*+,-./0123456789:=@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~");
+
+}
+
+oslFileError SAL_CALL osl_getCanonicalName( rtl_uString* ustrFileURL, rtl_uString** pustrValidURL )
+{
+ OSL_FAIL("osl_getCanonicalName not implemented");
+
+ rtl_uString_newFromString(pustrValidURL, ustrFileURL);
+ return osl_File_E_None;
+}
+
+namespace {
+
+ class UnicodeToTextConverter_Impl
+ {
+ rtl_UnicodeToTextConverter m_converter;
+
+ UnicodeToTextConverter_Impl()
+ : m_converter (rtl_createUnicodeToTextConverter (osl_getThreadTextEncoding()))
+ {}
+
+ ~UnicodeToTextConverter_Impl()
+ {
+ rtl_destroyUnicodeToTextConverter (m_converter);
+ }
+ public:
+ static UnicodeToTextConverter_Impl & getInstance()
+ {
+ static UnicodeToTextConverter_Impl g_theConverter;
+ return g_theConverter;
+ }
+
+ sal_Size convert(
+ sal_Unicode const * pSrcBuf, sal_Size nSrcChars, char * pDstBuf, sal_Size nDstBytes,
+ sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtChars)
+ {
+ OSL_ASSERT(m_converter != nullptr);
+ return rtl_convertUnicodeToText (
+ m_converter, nullptr, pSrcBuf, nSrcChars, pDstBuf, nDstBytes, nFlags, pInfo, pSrcCvtChars);
+ }
+ };
+
+bool convert(OUStringBuffer const & in, OStringBuffer * append) {
+ assert(append != nullptr);
+ for (sal_Size nConvert = in.getLength();;) {
+ auto const oldLen = append->getLength();
+ auto n = std::min(
+ std::max(nConvert, sal_Size(PATH_MAX)),
+ sal_Size(std::numeric_limits<sal_Int32>::max() - oldLen));
+ // approximation of required converted size
+ auto s = append->appendUninitialized(n);
+ sal_uInt32 info;
+ sal_Size converted;
+ //TODO: context, for reliable treatment of DESTBUFFERTOSMALL:
+ n = UnicodeToTextConverter_Impl::getInstance().convert(
+ in.getStr() + in.getLength() - nConvert, nConvert, s, n,
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_FLUSH),
+ &info, &converted);
+ if ((info & RTL_UNICODETOTEXT_INFO_ERROR) != 0) {
+ return false;
+ }
+ append->setLength(oldLen + n);
+ assert(converted <= nConvert);
+ nConvert -= converted;
+ assert((nConvert == 0) == ((info & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0));
+ if ((info & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0) {
+ break;
+ }
+ }
+ return true;
+}
+
+bool decodeFromUtf8(std::u16string_view text, OString * result) {
+ assert(result != nullptr);
+ auto p = text.data();
+ auto const end = p + text.size();
+ OUStringBuffer ubuf(static_cast<int>(text.size()));
+ OStringBuffer bbuf(PATH_MAX);
+ while (p < end) {
+ rtl::uri::detail::EscapeType t;
+ sal_uInt32 c = rtl::uri::detail::readUcs4(&p, end, true, RTL_TEXTENCODING_UTF8, &t);
+ switch (t) {
+ case rtl::uri::detail::EscapeNo:
+ if (c == '%') {
+ return false;
+ }
+ [[fallthrough]];
+ case rtl::uri::detail::EscapeChar:
+ if (rtl::isSurrogate(c)) {
+ return false;
+ }
+ ubuf.appendUtf32(c);
+ break;
+ case rtl::uri::detail::EscapeOctet:
+ if (!convert(ubuf, &bbuf)) {
+ return false;
+ }
+ ubuf.setLength(0);
+ assert(c <= 0xFF);
+ bbuf.append(char(c));
+ break;
+ }
+ }
+ if (!convert(ubuf, &bbuf)) {
+ return false;
+ }
+ *result = bbuf.makeStringAndClear();
+ return true;
+}
+
+template<typename T> oslFileError getSystemPathFromFileUrl(
+ OUString const & url, T * path, bool resolveHome)
+{
+ assert(path != nullptr);
+ // For compatibility with assumptions in other parts of the code base,
+ // assume that anything starting with a slash is a system path instead of a
+ // (relative) file URL (except if it starts with two slashes, in which case
+ // it is a relative URL with an authority component):
+ if (url.isEmpty()
+ || (url[0] == '/' && (url.getLength() == 1 || url[1] != '/')))
+ {
+ return osl_File_E_INVAL;
+ }
+ // Check for non file scheme:
+ sal_Int32 i = 0;
+ if (rtl::isAsciiAlpha(url[0])) {
+ for (sal_Int32 j = 1; j != url.getLength(); ++j) {
+ auto c = url[j];
+ if (c == ':') {
+ if (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
+ url.pData->buffer, j,
+ RTL_CONSTASCII_STRINGPARAM("file"))
+ != 0)
+ {
+ return osl_File_E_INVAL;
+ }
+ i = j + 1;
+ break;
+ }
+ if (!rtl::isAsciiAlphanumeric(c) && c != '+' && c != '-'
+ && c != '.')
+ {
+ break;
+ }
+ }
+ }
+ // Handle query or fragment:
+ if (url.indexOf('?', i) != -1 || url.indexOf('#', i) != -1)
+ return osl_File_E_INVAL;
+ // Handle authority, supporting a host of "localhost", "127.0.0.1", or the exact value (e.g.,
+ // not supporting an additional final dot, for simplicity) reported by osl_getLocalHostnameFQDN
+ // (and, in each case, ignoring case of ASCII letters):
+ if (url.getLength() - i >= 2 && url[i] == '/' && url[i + 1] == '/')
+ {
+ i += 2;
+ sal_Int32 j = url.indexOf('/', i);
+ if (j == -1)
+ j = url.getLength();
+ if (j != i
+ && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
+ url.pData->buffer + i, j - i,
+ RTL_CONSTASCII_STRINGPARAM("localhost"))
+ != 0)
+ && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
+ url.pData->buffer + i, j - i,
+ RTL_CONSTASCII_STRINGPARAM("127.0.0.1"))
+ != 0))
+ {
+ OUString hostname;
+ // The 'file' URI Scheme does imply that we want a FQDN in this case
+ // See https://tools.ietf.org/html/rfc8089#section-3
+ if (osl_getLocalHostnameFQDN(&hostname.pData) != osl_Socket_Ok
+ || (rtl_ustr_compareIgnoreAsciiCase_WithLength(
+ url.pData->buffer + i, j - i, hostname.getStr(), hostname.getLength())
+ != 0))
+ {
+ return osl_File_E_INVAL;
+ }
+ }
+ i = j;
+ }
+ // Handle empty path:
+ if (i == url.getLength())
+ {
+ *path = "/";
+ return osl_File_E_None;
+ }
+ // Path must not contain %2F:
+ if (url.indexOf("%2F", i) != -1 || url.indexOf("%2f", i) != -1)
+ return osl_File_E_INVAL;
+
+ if constexpr (std::is_same_v<T, rtl::OString>) {
+ if (!decodeFromUtf8(url.subView(i), path)) {
+ return osl_File_E_INVAL;
+ }
+ } else if constexpr (std::is_same_v<T, rtl::OUString>) {
+ *path = rtl::Uri::decode(
+ url.copy(i), rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
+ } else {
+ static_assert(std::is_same_v<T, rtl::OString> || std::is_same_v<T, rtl::OUString>);
+ }
+ // Path must not contain %2F:
+ if (path->indexOf('\0') != -1)
+ return osl_File_E_INVAL;
+
+ // Handle ~ notation:
+ if (resolveHome && path->getLength() >= 2 && (*path)[1] == '~')
+ {
+ sal_Int32 j = path->indexOf('/', 2);
+ if (j == -1)
+ j = path->getLength();
+
+ if (j == 2)
+ {
+ OUString home;
+ if (!osl::Security().getHomeDir(home))
+ {
+ SAL_WARN("sal.file", "osl::Security::getHomeDir failed");
+ return osl_File_E_INVAL;
+ }
+
+ i = url.indexOf('/', i + 1);
+
+ if (i == -1)
+ i = url.getLength();
+ else
+ ++i;
+
+ //TODO: cheesy way of ensuring home's path ends in slash:
+ if (!home.isEmpty() && home[home.getLength() - 1] != '/')
+ home += "/";
+ try
+ {
+ home = rtl::Uri::convertRelToAbs(home, url.copy(i));
+ }
+ catch (rtl::MalformedUriException & e)
+ {
+ SAL_WARN("sal.file", "rtl::MalformedUriException " << e.getMessage());
+ return osl_File_E_INVAL;
+ }
+ return getSystemPathFromFileUrl(home, path, false);
+ }
+ // FIXME: replace ~user with user's home directory
+ return osl_File_E_INVAL;
+ }
+ return osl_File_E_None;
+}
+
+}
+
+oslFileError SAL_CALL osl_getSystemPathFromFileURL( rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath )
+{
+ OUString path;
+ oslFileError e;
+ try
+ {
+ e = getSystemPathFromFileUrl(
+ OUString::unacquired(&ustrFileURL), &path, true);
+ }
+ catch (std::length_error &)
+ {
+ e = osl_File_E_RANGE;
+ }
+
+ if (e == osl_File_E_None)
+ rtl_uString_assign(pustrSystemPath, path.pData);
+
+ return e;
+}
+
+oslFileError SAL_CALL osl_getFileURLFromSystemPath( rtl_uString *ustrSystemPath, rtl_uString **pustrFileURL )
+{
+ rtl_uString *pTmp = nullptr;
+ sal_Int32 nIndex;
+
+ auto const & systemPath = OUString::unacquired(&ustrSystemPath);
+
+ if( systemPath.isEmpty() )
+ return osl_File_E_INVAL;
+
+ if( systemPath.startsWith( "file:" ) )
+ return osl_File_E_INVAL;
+
+ /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */
+ if( systemPath.startsWith("~") )
+ {
+ /* check if another user is specified */
+ if( ( systemPath.getLength() == 1 ) ||
+ ( systemPath[1] == '/' ) )
+ {
+ /* osl_getHomeDir returns file URL */
+ oslSecurity pSecurity = osl_getCurrentSecurity();
+ osl_getHomeDir( pSecurity , &pTmp );
+ osl_freeSecurityHandle( pSecurity );
+
+ if (!pTmp)
+ return osl_File_E_INVAL;
+
+ /* remove "file://" prefix */
+ rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + 7, pTmp->length - 7 );
+
+ /* replace '~' in original string */
+ rtl_uString_newReplaceStrAt( &pTmp, systemPath.pData, 0, 1, pTmp );
+ }
+ else
+ {
+ /* FIXME: replace ~user with users home directory */
+ return osl_File_E_INVAL;
+ }
+ }
+
+ /* check if initial string contains repeated '/' characters */
+ nIndex = systemPath.indexOf( "//" );
+ if( nIndex != -1 )
+ {
+ sal_Int32 nSrcIndex;
+ sal_Int32 nDeleted = 0;
+
+ /* if pTmp is not already allocated, copy systemPath for modification */
+ if( pTmp == nullptr )
+ rtl_uString_newFromString( &pTmp, systemPath.pData );
+
+ /* adapt index to pTmp */
+ nIndex += pTmp->length - systemPath.getLength();
+
+ /* replace repeated '/' characters with a single '/' */
+ for( nSrcIndex = nIndex + 1; nSrcIndex < pTmp->length; nSrcIndex++ )
+ {
+ if( (pTmp->buffer[nSrcIndex] == '/') && (pTmp->buffer[nIndex] == '/') )
+ nDeleted++;
+ else
+ pTmp->buffer[++nIndex] = pTmp->buffer[nSrcIndex];
+ }
+
+ /* adjust length member */
+ pTmp->length -= nDeleted;
+ }
+
+ if( pTmp == nullptr )
+ rtl_uString_assign( &pTmp, systemPath.pData );
+
+ /* file URLs must be URI encoded */
+ rtl_uriEncode( pTmp, uriCharClass.data(), rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8, pustrFileURL );
+
+ rtl_uString_release( pTmp );
+
+ /* absolute urls should start with 'file://' */
+ if( (*pustrFileURL)->buffer[0] == '/' )
+ {
+ rtl_uString *pProtocol = nullptr;
+
+ rtl_uString_newFromAscii( &pProtocol, "file://" );
+ rtl_uString_newConcat( pustrFileURL, pProtocol, *pustrFileURL );
+ rtl_uString_release( pProtocol );
+ }
+
+ return osl_File_E_None;
+}
+
+/*
+ * relative URLs are not accepted
+ */
+oslFileError getSystemPathFromFileURL_Ex(
+ rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath)
+{
+ rtl_uString* temp = nullptr;
+ oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &temp);
+
+ if (osl_error == osl_File_E_None)
+ {
+ if (temp->buffer[0] == '/')
+ {
+ *pustrSystemPath = temp;
+ }
+ else
+ {
+ rtl_uString_release(temp);
+ osl_error = osl_File_E_INVAL;
+ }
+ }
+
+ return osl_error;
+}
+
+namespace
+{
+
+ /** Helper function, return a pointer to the final '\0'
+ of a string
+ */
+
+ sal_Unicode* ustrtoend(sal_Unicode* pStr)
+ {
+ return (pStr + rtl_ustr_getLength(pStr));
+ }
+
+ sal_Unicode* ustrchrcat(const sal_Unicode chr, sal_Unicode* d)
+ {
+ sal_Unicode* p = ustrtoend(d);
+ *p++ = chr;
+ *p = 0;
+ return d;
+ }
+
+ bool _islastchr(sal_Unicode* pStr, sal_Unicode Chr)
+ {
+ sal_Unicode* p = ustrtoend(pStr);
+ if (p > pStr)
+ p--;
+ return (*p == Chr);
+ }
+
+ /**
+ Remove the last part of a path, a path that has
+ only a '/' or no '/' at all will be returned
+ unmodified
+ */
+
+ sal_Unicode* _rmlastpathtoken(sal_Unicode* aPath)
+ {
+ /* we may always skip -2 because we
+ may at least stand on a '/' but
+ either there is no other character
+ before this '/' or it's another
+ character than the '/'
+ */
+ sal_Unicode* p = ustrtoend(aPath) - 2;
+
+ /* move back to the next path separator
+ or to the start of the string */
+ while ((p > aPath) && (*p != '/'))
+ p--;
+
+ if (p >= aPath)
+ {
+ if (*p == '/')
+ {
+ p++;
+ *p = '\0';
+ }
+ else
+ {
+ *p = '\0';
+ }
+ }
+
+ return aPath;
+ }
+
+ oslFileError _osl_resolvepath(
+ /*inout*/ sal_Unicode* path,
+ /*inout*/ bool* failed)
+ {
+ oslFileError ferr = osl_File_E_None;
+
+ if (!*failed)
+ {
+ char unresolved_path[PATH_MAX];
+ if (!UnicodeToText(unresolved_path, sizeof(unresolved_path), path, rtl_ustr_getLength(path)))
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ char resolved_path[PATH_MAX];
+ if (realpath(unresolved_path, resolved_path))
+ {
+ if (!TextToUnicode(resolved_path, strlen(resolved_path), path, PATH_MAX))
+ return oslTranslateFileError(ENAMETOOLONG);
+ }
+ else
+ {
+ if (EACCES == errno || ENOTDIR == errno || ENOENT == errno)
+ *failed = true;
+ else
+ ferr = oslTranslateFileError(errno);
+ }
+ }
+
+ return ferr;
+ }
+
+ /**
+ Works even with non existing paths. The resulting path must not exceed
+ PATH_MAX else osl_File_E_NAMETOOLONG is the result
+ */
+
+ oslFileError osl_getAbsoluteFileURL_impl_(const OUString& unresolved_path, OUString& resolved_path)
+ {
+ /* the given unresolved path must not exceed PATH_MAX */
+ if (unresolved_path.getLength() >= (PATH_MAX - 2))
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ sal_Unicode path_resolved_so_far[PATH_MAX];
+ const sal_Unicode* punresolved = unresolved_path.getStr();
+ sal_Unicode* presolvedsf = path_resolved_so_far;
+
+ /* reserve space for leading '/' and trailing '\0'
+ do not exceed this limit */
+ sal_Unicode* sentinel = path_resolved_so_far + PATH_MAX - 2;
+
+ /* if realpath fails with error ENOTDIR, EACCES or ENOENT
+ we will not call it again, because _osl_realpath should also
+ work with non existing directories etc. */
+ bool realpath_failed = false;
+ oslFileError ferr;
+
+ path_resolved_so_far[0] = '\0';
+
+ while (*punresolved != '\0')
+ {
+ /* ignore '/.' , skip one part back when '/..' */
+ if ((*punresolved == '.') && (*presolvedsf == '/'))
+ {
+ if (*(punresolved + 1) == '\0')
+ {
+ punresolved++;
+ continue;
+ }
+ if (*(punresolved + 1) == '/')
+ {
+ punresolved += 2;
+ continue;
+ }
+ if ((*(punresolved + 1) == '.') && (*(punresolved + 2) == '\0' || (*(punresolved + 2) == '/')))
+ {
+ _rmlastpathtoken(path_resolved_so_far);
+
+ presolvedsf = ustrtoend(path_resolved_so_far) - 1;
+
+ if (*(punresolved + 2) == '/')
+ punresolved += 3;
+ else
+ punresolved += 2;
+
+ continue;
+ }
+
+ /* a file or directory name may start with '.' */
+ if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ ustrchrcat(*punresolved++, path_resolved_so_far);
+
+ if (*punresolved == '\0' && !realpath_failed)
+ {
+ ferr = _osl_resolvepath(
+ path_resolved_so_far,
+ &realpath_failed);
+
+ if (ferr != osl_File_E_None)
+ return ferr;
+ }
+ }
+ else if (*punresolved == '/')
+ {
+ if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ ustrchrcat(*punresolved++, path_resolved_so_far);
+
+ if (!realpath_failed)
+ {
+ ferr = _osl_resolvepath(
+ path_resolved_so_far,
+ &realpath_failed);
+
+ if (ferr != osl_File_E_None)
+ return ferr;
+
+ if (!_islastchr(path_resolved_so_far, '/'))
+ {
+ if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ ustrchrcat('/', path_resolved_so_far);
+ }
+ }
+ }
+ else // any other character
+ {
+ if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ ustrchrcat(*punresolved++, path_resolved_so_far);
+
+ if (*punresolved == '\0' && !realpath_failed)
+ {
+ ferr = _osl_resolvepath(
+ path_resolved_so_far,
+ &realpath_failed);
+
+ if (ferr != osl_File_E_None)
+ return ferr;
+ }
+ }
+ }
+
+ sal_Int32 len = rtl_ustr_getLength(path_resolved_so_far);
+
+ OSL_ASSERT(len < PATH_MAX);
+
+ resolved_path = OUString(path_resolved_so_far, len);
+
+ return osl_File_E_None;
+ }
+
+}
+
+oslFileError osl_getAbsoluteFileURL(
+ rtl_uString* ustrBaseDirURL,
+ rtl_uString* ustrRelativeURL,
+ rtl_uString** pustrAbsoluteURL)
+{
+ /* Work around the below call to getSystemPathFromFileURL rejecting input
+ that starts with "/" (for whatever reason it behaves that way; but
+ changing that would start to break lots of tests at least) */
+ OUString relUrl(ustrRelativeURL);
+ if (relUrl.startsWith("//"))
+ relUrl = "file:" + relUrl;
+ else if (relUrl.startsWith("/"))
+ relUrl = "file://" + relUrl;
+
+ OUString unresolved_path;
+
+ FileBase::RC frc = FileBase::getSystemPathFromFileURL(relUrl, unresolved_path);
+ if (frc != FileBase::E_None)
+ return oslFileError(frc);
+
+ if (systemPathIsRelativePath(unresolved_path))
+ {
+ OUString base_path;
+ oslFileError rc = getSystemPathFromFileURL_Ex(ustrBaseDirURL, &base_path.pData);
+ if (rc != osl_File_E_None)
+ return rc;
+
+ unresolved_path = systemPathMakeAbsolutePath(base_path, unresolved_path);
+ }
+
+ OUString resolved_path;
+ oslFileError rc = osl_getAbsoluteFileURL_impl_(unresolved_path, resolved_path);
+ if (rc == osl_File_E_None)
+ {
+ rc = osl_getFileURLFromSystemPath(resolved_path.pData, pustrAbsoluteURL);
+ OSL_ASSERT(osl_File_E_None == rc);
+ }
+
+ return rc;
+}
+
+namespace osl::detail {
+
+ /**
+ No separate error code if unicode to text conversion or getenv fails because for the
+ caller there is no difference why a file could not be found in $PATH
+ */
+ bool find_in_PATH(const OUString& file_path, OUString& result)
+ {
+ bool bfound = false;
+ OUString path("PATH");
+ OUString env_path;
+
+ if (osl_getEnvironment(path.pData, &env_path.pData) == osl_Process_E_None)
+ bfound = osl::searchPath(file_path, env_path, result);
+
+ return bfound;
+ }
+}
+
+namespace
+{
+ /**
+ No separate error code if unicode to text conversion or getcwd fails because for the
+ caller there is no difference why a file could not be found in CDW
+ */
+ bool find_in_CWD(const OUString& file_path, OUString& result)
+ {
+ bool bfound = false;
+ OUString cwd_url;
+
+ if (osl_getProcessWorkingDir(&cwd_url.pData) == osl_Process_E_None)
+ {
+ OUString cwd;
+ FileBase::getSystemPathFromFileURL(cwd_url, cwd);
+ bfound = osl::searchPath(file_path, cwd, result);
+ }
+ return bfound;
+ }
+
+ bool find_in_searchPath(const OUString& file_path, rtl_uString* search_path, OUString& result)
+ {
+ return (search_path && osl::searchPath(file_path, OUString(search_path), result));
+ }
+
+}
+
+oslFileError osl_searchFileURL(rtl_uString* ustrFilePath, rtl_uString* ustrSearchPath, rtl_uString** pustrURL)
+{
+ OSL_PRECOND(ustrFilePath && pustrURL, "osl_searchFileURL: invalid parameter");
+
+ FileBase::RC rc;
+ OUString file_path;
+
+ // try to interpret search path as file url else assume it's a system path list
+ rc = FileBase::getSystemPathFromFileURL(ustrFilePath, file_path);
+ if (rc == FileBase::E_INVAL)
+ file_path = ustrFilePath;
+ else if (rc != FileBase::E_None)
+ return oslFileError(rc);
+
+ bool bfound = false;
+ OUString result;
+
+ if (find_in_searchPath(file_path, ustrSearchPath, result) ||
+ osl::detail::find_in_PATH(file_path, result) ||
+ find_in_CWD(file_path, result))
+ {
+ OUString resolved;
+
+ if (osl::realpath(result, resolved))
+ {
+ oslFileError osl_error = osl_getFileURLFromSystemPath(resolved.pData, pustrURL);
+ SAL_WARN_IF(osl_File_E_None != osl_error, "sal.file", "osl_getFileURLFromSystemPath failed");
+ bfound = true;
+ }
+ }
+ return bfound ? osl_File_E_None : osl_File_E_NOENT;
+}
+
+oslFileError FileURLToPath(char * buffer, size_t bufLen, rtl_uString* ustrFileURL)
+{
+ OString strSystemPath;
+ oslFileError osl_error = osl::detail::convertUrlToPathname(
+ OUString::unacquired(&ustrFileURL), &strSystemPath);
+
+ if(osl_error != osl_File_E_None)
+ return osl_error;
+
+ osl_systemPathRemoveSeparator(strSystemPath.pData);
+
+ if (o3tl::make_unsigned(strSystemPath.getLength()) >= bufLen) {
+ return osl_File_E_OVERFLOW;
+ }
+ std::strcpy(buffer, strSystemPath.getStr());
+
+ return osl_error;
+}
+
+int UnicodeToText( char * buffer, size_t bufLen, const sal_Unicode * uniText, sal_Int32 uniTextLen )
+{
+ sal_uInt32 nInfo = 0;
+ sal_Size nSrcChars = 0;
+
+ sal_Size nDestBytes = UnicodeToTextConverter_Impl::getInstance().convert (
+ uniText, uniTextLen, buffer, bufLen,
+ OUSTRING_TO_OSTRING_CVTFLAGS | RTL_UNICODETOTEXT_FLAGS_FLUSH, &nInfo, &nSrcChars);
+
+ if( nInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL )
+ {
+ errno = EOVERFLOW;
+ return 0;
+ }
+
+ /* ensure trailing '\0' */
+ buffer[nDestBytes] = '\0';
+ return nDestBytes;
+}
+
+namespace
+{
+ class TextToUnicodeConverter_Impl
+ {
+ rtl_TextToUnicodeConverter m_converter;
+
+ TextToUnicodeConverter_Impl()
+ : m_converter (rtl_createTextToUnicodeConverter (osl_getThreadTextEncoding()))
+ {}
+
+ ~TextToUnicodeConverter_Impl()
+ {
+ rtl_destroyTextToUnicodeConverter (m_converter);
+ }
+
+ public:
+ static TextToUnicodeConverter_Impl & getInstance()
+ {
+ static TextToUnicodeConverter_Impl g_theConverter;
+ return g_theConverter;
+ }
+
+ sal_Size convert(
+ char const * pSrcBuf, sal_Size nSrcBytes, sal_Unicode * pDstBuf, sal_Size nDstChars,
+ sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtBytes)
+ {
+ OSL_ASSERT(m_converter != nullptr);
+ return rtl_convertTextToUnicode (
+ m_converter, nullptr, pSrcBuf, nSrcBytes, pDstBuf, nDstChars, nFlags, pInfo, pSrcCvtBytes);
+ }
+ };
+}
+
+int TextToUnicode(
+ const char* text,
+ size_t text_buffer_size,
+ sal_Unicode* unic_text,
+ sal_Int32 unic_text_buffer_size)
+{
+ sal_uInt32 nInfo = 0;
+ sal_Size nSrcChars = 0;
+
+ sal_Size nDestBytes = TextToUnicodeConverter_Impl::getInstance().convert(
+ text, text_buffer_size, unic_text, unic_text_buffer_size,
+ OSTRING_TO_OUSTRING_CVTFLAGS | RTL_TEXTTOUNICODE_FLAGS_FLUSH, &nInfo, &nSrcChars);
+
+ if (nInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL)
+ {
+ errno = EOVERFLOW;
+ return 0;
+ }
+
+ /* ensure trailing '\0' */
+ unic_text[nDestBytes] = '\0';
+ return nDestBytes;
+}
+
+oslFileError osl::detail::convertUrlToPathname(OUString const & url, OString * pathname) {
+ assert(pathname != nullptr);
+ oslFileError e;
+ try {
+ e = getSystemPathFromFileUrl(url, pathname, true);
+ } catch (std::length_error &) {
+ e = osl_File_E_RANGE;
+ }
+ if (e == osl_File_E_None && !pathname->startsWith("/")) {
+ e = osl_File_E_INVAL;
+ }
+ return e;
+}
+
+oslFileError osl::detail::convertPathnameToUrl(OString const & pathname, OUString * url) {
+ assert(url != nullptr);
+ OUStringBuffer buf(10+pathname.getLength());
+ buf.append("file:");
+ if (pathname.startsWith("/")) {
+ buf.append("//");
+ // so if pathname should ever start with "//" that isn't mistaken for an authority
+ // component
+ }
+ for (sal_Size convert = pathname.getLength();;) {
+ auto n = std::max(convert, sal_Size(PATH_MAX)); // approximation of required converted size
+ OUStringBuffer ubuf(static_cast<int>(n));
+ auto s = ubuf.appendUninitialized(n);
+ sal_uInt32 info;
+ sal_Size converted;
+ //TODO: context, for reliable treatment of DESTBUFFERTOSMALL:
+ n = TextToUnicodeConverter_Impl::getInstance().convert(
+ pathname.getStr() + pathname.getLength() - convert, convert, s, n,
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR | RTL_TEXTTOUNICODE_FLAGS_FLUSH),
+ &info, &converted);
+ ubuf.setLength(n);
+ buf.append(
+ rtl::Uri::encode(
+ ubuf.makeStringAndClear(), uriCharClass.data(), rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8));
+ assert(converted <= convert);
+ convert -= converted;
+ if ((info & RTL_TEXTTOUNICODE_INFO_ERROR) != 0) {
+ assert(convert > 0);
+ //TODO: see writeEscapeOctet in sal/rtl/uri.cxx
+ buf.append("%");
+ unsigned char c = pathname[pathname.getLength() - convert];
+ assert(c >= 0x80);
+ static sal_Unicode const aHex[16]
+ = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; /* '0'--'9', 'A'--'F' */
+ buf.append(aHex[c >> 4]);
+ buf.append(aHex[c & 15]);
+ --convert;
+ continue;
+ }
+ assert((convert == 0) == ((info & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL) == 0));
+ if ((info & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL) == 0) {
+ break;
+ }
+ }
+ *url = buf.makeStringAndClear();
+ return osl_File_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_url.hxx b/sal/osl/unx/file_url.hxx
new file mode 100644
index 000000000..3ffd9e06b
--- /dev/null
+++ b/sal/osl/unx/file_url.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_FILE_URL_HXX
+#define INCLUDED_SAL_OSL_UNX_FILE_URL_HXX
+
+#include <osl/file.h>
+
+namespace rtl {
+ class OString;
+ class OUString;
+}
+
+oslFileError getSystemPathFromFileURL_Ex(rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath);
+
+oslFileError FileURLToPath(char * buffer, size_t bufLen, rtl_uString* ustrFileURL);
+
+int UnicodeToText(char * buffer, size_t bufLen, const sal_Unicode * uniText, sal_Int32 uniTextLen);
+
+int TextToUnicode(const char* text, size_t text_buffer_size, sal_Unicode* unic_text, sal_Int32 unic_text_buffer_size);
+
+namespace osl::detail {
+
+oslFileError convertUrlToPathname(rtl::OUString const & url, rtl::OString * pathname);
+
+oslFileError convertPathnameToUrl(rtl::OString const & pathname, rtl::OUString * url);
+
+bool find_in_PATH(const rtl::OUString& file_path, rtl::OUString& result);
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_volume.cxx b/sal/osl/unx/file_volume.cxx
new file mode 100644
index 000000000..5e661bd65
--- /dev/null
+++ b/sal/osl/unx/file_volume.cxx
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <osl/file.h>
+
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <rtl/alloc.h>
+
+#include "file_error_transl.hxx"
+#include "file_url.hxx"
+#include "system.hxx"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sal/macros.h>
+
+#ifdef HAVE_STATFS_H
+#undef HAVE_STATFS_H
+#endif
+
+#if defined(LINUX) && defined(__FreeBSD_kernel__)
+#undef LINUX
+#define FREEBSD 1
+#endif
+
+#if defined(__sun)
+
+#include <sys/mnttab.h>
+#include <sys/statvfs.h>
+#define HAVE_STATFS_H
+
+#elif defined(LINUX)
+
+#include <mntent.h>
+#include <sys/vfs.h>
+#define HAVE_STATFS_H
+
+#elif defined(NETBSD) || defined(FREEBSD) || defined(OPENBSD) || defined(DRAGONFLY)
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#define HAVE_STATFS_H
+
+#elif defined(MACOSX)
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#define HAVE_STATFS_H
+
+#endif /* HAVE_STATFS_H */
+
+/************************************************************************
+ * ToDo
+ *
+ * - Fix: check for corresponding struct sizes in exported functions
+ * - check size/use of oslVolumeInfo
+ ***********************************************************************/
+
+/******************************************************************************
+ *
+ * C-String Function Declarations
+ *
+ *****************************************************************************/
+
+static oslFileError osl_psz_getVolumeInformation(const char* , oslVolumeInfo* pInfo, sal_uInt32 uFieldMask);
+
+/****************************************************************************/
+/* osl_getVolumeInformation */
+/****************************************************************************/
+
+oslFileError osl_getVolumeInformation( rtl_uString* ustrDirectoryURL, oslVolumeInfo* pInfo, sal_uInt32 uFieldMask )
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ OSL_ASSERT( ustrDirectoryURL );
+ OSL_ASSERT( pInfo );
+
+ /* convert directory url to system path */
+ eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_getVolumeInformation( path, pInfo, uFieldMask);
+}
+
+/******************************************************************************
+ *
+ * C-String Versions of Exported Module Functions
+ *
+ *****************************************************************************/
+
+#ifdef HAVE_STATFS_H
+
+#if defined(FREEBSD) || defined(MACOSX) || defined(OPENBSD) || defined(DRAGONFLY)
+# define OSL_detail_STATFS_STRUCT struct statfs
+# define OSL_detail_STATFS(dir, sfs) statfs((dir), (sfs))
+# define OSL_detail_STATFS_BLKSIZ(a) (static_cast<sal_uInt64>((a).f_bsize))
+# define OSL_detail_STATFS_TYPENAME(a) ((a).f_fstypename)
+#if defined(OPENBSD)
+# define OSL_detail_STATFS_ISREMOTE(a) (rtl_str_compare((a).f_fstypename, "nfs") == 0)
+#else
+# define OSL_detail_STATFS_ISREMOTE(a) (((a).f_type & MNT_LOCAL) == 0)
+#endif
+
+/* always return true if queried for the properties of
+ the file system. If you think this is wrong under any
+ of the target platforms fix it!!!! */
+# define OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(a) (true)
+# define OSL_detail_STATFS_IS_CASE_PRESERVING_FS(a) (true)
+#endif /* FREEBSD || MACOSX || OPENBSD */
+
+#if defined(NETBSD)
+
+# define OSL_detail_STATFS_STRUCT struct statvfs
+# define OSL_detail_STATFS(dir, sfs) statvfs((dir), (sfs))
+# define OSL_detail_STATFS_ISREMOTE(a) (((a).f_flag & ST_LOCAL) == 0)
+
+# define OSL_detail_STATFS_BLKSIZ(a) ((sal_uInt64)((a).f_bsize))
+# define OSL_detail_STATFS_TYPENAME(a) ((a).f_fstypename)
+
+# define OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(a) (strcmp((a).f_fstypename, "msdos") != 0 && strcmp((a).f_fstypename, "ntfs") != 0 && strcmp((a).f_fstypename, "smbfs") != 0)
+# define OSL_detail_STATFS_IS_CASE_PRESERVING_FS(a) (strcmp((a).f_fstypename, "msdos") != 0)
+#endif /* NETBSD */
+
+#if defined(LINUX)
+# define OSL_detail_NFS_SUPER_MAGIC 0x6969
+# define OSL_detail_SMB_SUPER_MAGIC 0x517B
+# define OSL_detail_MSDOS_SUPER_MAGIC 0x4d44
+# define OSL_detail_NTFS_SUPER_MAGIC 0x5346544e
+# define OSL_detail_STATFS_STRUCT struct statfs
+# define OSL_detail_STATFS(dir, sfs) statfs((dir), (sfs))
+# define OSL_detail_STATFS_BLKSIZ(a) (static_cast<sal_uInt64>((a).f_bsize))
+# define OSL_detail_STATFS_IS_NFS(a) (OSL_detail_NFS_SUPER_MAGIC == (a).f_type)
+# define OSL_detail_STATFS_IS_SMB(a) (OSL_detail_SMB_SUPER_MAGIC == (a).f_type)
+# define OSL_detail_STATFS_ISREMOTE(a) (OSL_detail_STATFS_IS_NFS((a)) || OSL_detail_STATFS_IS_SMB((a)))
+# define OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(a) ((OSL_detail_MSDOS_SUPER_MAGIC != (a).f_type) && (OSL_detail_NTFS_SUPER_MAGIC != (a).f_type))
+# define OSL_detail_STATFS_IS_CASE_PRESERVING_FS(a) ((OSL_detail_MSDOS_SUPER_MAGIC != (a).f_type))
+#endif /* LINUX */
+
+#if defined(__sun)
+# define OSL_detail_STATFS_STRUCT struct statvfs
+# define OSL_detail_STATFS(dir, sfs) statvfs((dir), (sfs))
+# define OSL_detail_STATFS_BLKSIZ(a) ((sal_uInt64)((a).f_frsize))
+# define OSL_detail_STATFS_TYPENAME(a) ((a).f_basetype)
+# define OSL_detail_STATFS_ISREMOTE(a) (rtl_str_compare((a).f_basetype, "nfs") == 0)
+
+/* always return true if queried for the properties of
+ the file system. If you think this is wrong under any
+ of the target platforms fix it!!!! */
+# define OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(a) (true)
+# define OSL_detail_STATFS_IS_CASE_PRESERVING_FS(a) (true)
+#endif /* __sun */
+
+# define OSL_detail_STATFS_INIT(a) (memset(&(a), 0, sizeof(OSL_detail_STATFS_STRUCT)))
+
+#else /* no statfs available */
+
+# define OSL_detail_STATFS_STRUCT struct dummy {int i;}
+# define OSL_detail_STATFS_INIT(a) ((void)a)
+# define OSL_detail_STATFS(dir, sfs) (1)
+# define OSL_detail_STATFS_ISREMOTE(sfs) (false)
+# define OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(a) (true)
+# define OSL_detail_STATFS_IS_CASE_PRESERVING_FS(a) (true)
+#endif /* HAVE_STATFS_H */
+
+static oslFileError osl_psz_getVolumeInformation (
+ const char* pszDirectory, oslVolumeInfo* pInfo, sal_uInt32 uFieldMask)
+{
+ if (!pInfo)
+ return osl_File_E_INVAL;
+
+ pInfo->uValidFields = 0;
+ pInfo->uAttributes = 0;
+ pInfo->uTotalSpace = 0;
+ pInfo->uFreeSpace = 0;
+ pInfo->uUsedSpace = 0;
+
+ if ((uFieldMask
+ & (osl_VolumeInfo_Mask_Attributes | osl_VolumeInfo_Mask_TotalSpace
+ | osl_VolumeInfo_Mask_UsedSpace | osl_VolumeInfo_Mask_FreeSpace
+ | osl_VolumeInfo_Mask_FileSystemName
+ | osl_VolumeInfo_Mask_FileSystemCaseHandling))
+ != 0)
+ {
+ OSL_detail_STATFS_STRUCT sfs;
+ OSL_detail_STATFS_INIT(sfs);
+ // coverity[fs_check_call : FALSE]
+ if ((OSL_detail_STATFS(pszDirectory, &sfs)) < (0))
+ {
+ oslFileError result = oslTranslateFileError(errno);
+ return result;
+ }
+
+ /* FIXME: how to detect the kind of storage (fixed, cdrom, ...) */
+ if (uFieldMask & osl_VolumeInfo_Mask_Attributes)
+ {
+ bool const remote = OSL_detail_STATFS_ISREMOTE(sfs);
+ // extracted from the 'if' to avoid Clang -Wunreachable-code
+ if (remote)
+ pInfo->uAttributes |= osl_Volume_Attribute_Remote;
+
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes;
+ }
+
+ if (uFieldMask & osl_VolumeInfo_Mask_FileSystemCaseHandling)
+ {
+ if (OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(sfs))
+ pInfo->uAttributes |= osl_Volume_Attribute_Case_Sensitive;
+
+ if (OSL_detail_STATFS_IS_CASE_PRESERVING_FS(sfs))
+ pInfo->uAttributes |= osl_Volume_Attribute_Case_Is_Preserved;
+
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes;
+ }
+
+#if defined(OSL_detail_STATFS_BLKSIZ)
+
+ if ((uFieldMask & osl_VolumeInfo_Mask_TotalSpace) ||
+ (uFieldMask & osl_VolumeInfo_Mask_UsedSpace))
+ {
+ pInfo->uTotalSpace = OSL_detail_STATFS_BLKSIZ(sfs);
+ pInfo->uTotalSpace *= static_cast<sal_uInt64>(sfs.f_blocks);
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_TotalSpace;
+ }
+
+ if ((uFieldMask & osl_VolumeInfo_Mask_FreeSpace) ||
+ (uFieldMask & osl_VolumeInfo_Mask_UsedSpace))
+ {
+ pInfo->uFreeSpace = OSL_detail_STATFS_BLKSIZ(sfs);
+
+ if (getuid() == 0)
+ pInfo->uFreeSpace *= static_cast<sal_uInt64>(sfs.f_bfree);
+ else
+ pInfo->uFreeSpace *= static_cast<sal_uInt64>(sfs.f_bavail);
+
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_FreeSpace;
+ }
+
+ if ((pInfo->uValidFields & osl_VolumeInfo_Mask_TotalSpace) &&
+ (pInfo->uValidFields & osl_VolumeInfo_Mask_FreeSpace ))
+ {
+ pInfo->uUsedSpace = pInfo->uTotalSpace - pInfo->uFreeSpace;
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_UsedSpace;
+ }
+
+#endif /* OSL_detail_STATFS_BLKSIZ */
+
+#if defined(OSL_detail_STATFS_TYPENAME)
+
+ if (uFieldMask & osl_VolumeInfo_Mask_FileSystemName)
+ {
+ rtl_string2UString(
+ &(pInfo->ustrFileSystemName),
+ OSL_detail_STATFS_TYPENAME(sfs),
+ rtl_str_getLength(OSL_detail_STATFS_TYPENAME(sfs)),
+ osl_getThreadTextEncoding(),
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+ OSL_ASSERT(pInfo->ustrFileSystemName != nullptr);
+
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_FileSystemName;
+ }
+
+#endif /* OSL_detail_STATFS_TYPENAME */
+ }
+
+ pInfo->uMaxNameLength = 0;
+ if (uFieldMask & osl_VolumeInfo_Mask_MaxNameLength)
+ {
+ long nLen = pathconf(pszDirectory, _PC_NAME_MAX);
+ if (nLen > 0)
+ {
+ pInfo->uMaxNameLength = static_cast<sal_uInt32>(nLen);
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_MaxNameLength;
+ }
+ }
+
+ pInfo->uMaxPathLength = 0;
+ if (uFieldMask & osl_VolumeInfo_Mask_MaxPathLength)
+ {
+ long nLen = pathconf (pszDirectory, _PC_PATH_MAX);
+ if (nLen > 0)
+ {
+ pInfo->uMaxPathLength = static_cast<sal_uInt32>(nLen);
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_MaxPathLength;
+ }
+ }
+
+ return osl_File_E_None;
+}
+
+oslFileError osl_getVolumeDeviceMountPath( oslVolumeDeviceHandle, rtl_uString ** )
+{
+ return osl_File_E_INVAL;
+}
+
+oslFileError osl_acquireVolumeDeviceHandle( oslVolumeDeviceHandle )
+{
+ return osl_File_E_INVAL;
+}
+
+oslFileError osl_releaseVolumeDeviceHandle( oslVolumeDeviceHandle )
+{
+ return osl_File_E_INVAL;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/interlck.cxx b/sal/osl/unx/interlck.cxx
new file mode 100644
index 000000000..81404cb57
--- /dev/null
+++ b/sal/osl/unx/interlck.cxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.hxx"
+
+#include <config_global.h>
+#include <osl/interlck.h>
+
+#if ( defined (__sun) || defined ( NETBSD ) ) && defined ( SPARC )
+#error please use asm/interlck_sparc.s
+#elif defined (__sun) && defined ( X86 )
+#error please use asm/interlck_x86.s
+#elif HAVE_GCC_BUILTIN_ATOMIC
+oslInterlockedCount SAL_CALL osl_incrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ return __sync_add_and_fetch(pCount, 1);
+}
+oslInterlockedCount SAL_CALL osl_decrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ return __sync_sub_and_fetch(pCount, 1);
+}
+#elif defined ( __GNUC__ ) && ( defined ( X86 ) || defined ( X86_64 ) )
+/* That's possible on x86-64 too since oslInterlockedCount is a sal_Int32 */
+
+oslInterlockedCount SAL_CALL osl_incrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ register oslInterlockedCount nCount asm("%eax");
+ nCount = 1;
+ __asm__ __volatile__ (
+ "lock\n\t"
+ "xaddl %0, %1\n\t"
+ : "+r" (nCount), "+m" (*pCount)
+ : /* nothing */
+ : "memory");
+ return ++nCount;
+}
+
+oslInterlockedCount SAL_CALL osl_decrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ register oslInterlockedCount nCount asm("%eax");
+ nCount = -1;
+ __asm__ __volatile__ (
+ "lock\n\t"
+ "xaddl %0, %1\n\t"
+ : "+r" (nCount), "+m" (*pCount)
+ : /* nothing */
+ : "memory");
+ return --nCount;
+}
+#else
+/* use only if nothing else works, expensive due to single mutex for all reference counts */
+
+static pthread_mutex_t InterLock = PTHREAD_MUTEX_INITIALIZER;
+
+oslInterlockedCount SAL_CALL osl_incrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ oslInterlockedCount Count;
+
+ pthread_mutex_lock(&InterLock);
+ Count = ++(*pCount);
+ pthread_mutex_unlock(&InterLock);
+
+ return Count;
+}
+
+oslInterlockedCount SAL_CALL osl_decrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ oslInterlockedCount Count;
+
+ pthread_mutex_lock(&InterLock);
+ Count = --(*pCount);
+ pthread_mutex_unlock(&InterLock);
+
+ return Count;
+}
+
+#endif /* default */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/memory.cxx b/sal/osl/unx/memory.cxx
new file mode 100644
index 000000000..be3e41fb5
--- /dev/null
+++ b/sal/osl/unx/memory.cxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <oslmemory.h>
+
+#include <stdlib.h>
+#ifdef __ANDROID__
+#include <malloc.h>
+#endif
+
+void* osl_aligned_alloc(sal_Size align, sal_Size size)
+{
+ if (size == 0)
+ {
+ return nullptr;
+ }
+
+#if defined __ANDROID__
+ return memalign(align, size);
+#else
+ void* ptr;
+ int err = posix_memalign(&ptr, align, size);
+ return err ? nullptr : ptr;
+#endif
+}
+
+void osl_aligned_free(void* p) { free(p); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/module.cxx b/sal/osl/unx/module.cxx
new file mode 100644
index 000000000..606c8fa47
--- /dev/null
+++ b/sal/osl/unx/module.cxx
@@ -0,0 +1,344 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <osl/module.h>
+#include <osl/thread.h>
+#include <osl/process.h>
+#include <osl/file.h>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <assert.h>
+#include "system.hxx"
+#include "file_url.hxx"
+
+#ifdef AIX
+#include <sys/ldr.h>
+#endif
+
+static bool getModulePathFromAddress(void * address, rtl_String ** path)
+{
+ bool result = false;
+#if defined(AIX)
+ int size = 4 * 1024;
+ char *buf, *filename=NULL;
+ struct ld_info *lp;
+
+ if ((buf = (char*)malloc(size)) == NULL)
+ return false;
+
+ //figure out how big a buffer we need
+ while (loadquery(L_GETINFO, buf, size) == -1 && errno == ENOMEM)
+ {
+ size += 4 * 1024;
+ free(buf);
+ if ((buf = (char*)malloc(size)) == NULL)
+ return false;
+ }
+
+ lp = (struct ld_info*) buf;
+ while (lp)
+ {
+ unsigned long start = (unsigned long)lp->ldinfo_dataorg;
+ unsigned long end = start + lp->ldinfo_datasize;
+ if (start <= (unsigned long)address && end > (unsigned long)address)
+ {
+ filename = lp->ldinfo_filename;
+ break;
+ }
+ if (!lp->ldinfo_next)
+ break;
+ lp = (struct ld_info*) ((char *) lp + lp->ldinfo_next);
+ }
+
+ if (filename)
+ {
+ rtl_string_newFromStr(path, filename);
+ result = sal_True;
+ }
+ else
+ {
+ result = sal_False;
+ }
+
+ free(buf);
+#else
+#if HAVE_UNIX_DLAPI
+ Dl_info dl_info;
+
+ result = dladdr(address, &dl_info) != 0;
+
+ if (result)
+ {
+ rtl_string_newFromStr(path, dl_info.dli_fname);
+ }
+#else
+ (void) address;
+ (void) path;
+#endif
+#endif
+ return result;
+}
+
+#ifndef DISABLE_DYNLOADING
+
+/*****************************************************************************/
+/* osl_loadModule */
+/*****************************************************************************/
+
+oslModule SAL_CALL osl_loadModule(rtl_uString *ustrModuleName, sal_Int32 nRtldMode)
+{
+ oslModule pModule=nullptr;
+ rtl_uString* ustrTmp = nullptr;
+
+ SAL_WARN_IF(ustrModuleName == nullptr, "sal.osl", "string is not valid");
+
+ /* ensure ustrTmp hold valid string */
+ if (osl_getSystemPathFromFileURL(ustrModuleName, &ustrTmp) != osl_File_E_None)
+ rtl_uString_assign(&ustrTmp, ustrModuleName);
+
+ if (ustrTmp)
+ {
+ char buffer[PATH_MAX];
+
+ if (UnicodeToText(buffer, PATH_MAX, ustrTmp->buffer, ustrTmp->length))
+ pModule = osl_loadModuleAscii(buffer, nRtldMode);
+ rtl_uString_release(ustrTmp);
+ }
+
+ return pModule;
+}
+
+/*****************************************************************************/
+/* osl_loadModuleAscii */
+/*****************************************************************************/
+
+oslModule SAL_CALL osl_loadModuleAscii(const char *pModuleName, sal_Int32 nRtldMode)
+{
+#if HAVE_UNIX_DLAPI
+ SAL_WARN_IF(
+ ((nRtldMode & SAL_LOADMODULE_LAZY) != 0
+ && (nRtldMode & SAL_LOADMODULE_NOW) != 0),
+ "sal.osl", "only either LAZY or NOW");
+ if (pModuleName)
+ {
+ int rtld_mode =
+ ((nRtldMode & SAL_LOADMODULE_NOW) ? RTLD_NOW : RTLD_LAZY) |
+ ((nRtldMode & SAL_LOADMODULE_GLOBAL) ? RTLD_GLOBAL : RTLD_LOCAL);
+ void* pLib = dlopen(pModuleName, rtld_mode);
+
+ SAL_WARN_IF(
+ pLib == nullptr, "sal.osl",
+ "dlopen(" << pModuleName << ", " << rtld_mode << "): "
+ << dlerror());
+ return pLib;
+ }
+#else
+ (void) pModuleName;
+ (void) nRtldMode;
+#endif
+ return nullptr;
+}
+
+oslModule osl_loadModuleRelativeAscii(
+ oslGenericFunction baseModule, char const * relativePath, sal_Int32 mode)
+{
+ assert(relativePath && "illegal argument");
+ if (relativePath[0] == '/') {
+ return osl_loadModuleAscii(relativePath, mode);
+ }
+ rtl_String * path = nullptr;
+ rtl_String * suffix = nullptr;
+ oslModule module;
+ if (!getModulePathFromAddress(
+ reinterpret_cast< void * >(baseModule), &path))
+ {
+ return nullptr;
+ }
+ rtl_string_newFromStr_WithLength(
+ &path, path->buffer,
+ (rtl_str_lastIndexOfChar_WithLength(path->buffer, path->length, '/')
+ + 1));
+ /* cut off everything after the last slash; should the original path
+ contain no slash, the resulting path is the empty string */
+ rtl_string_newFromStr(&suffix, relativePath);
+ rtl_string_newConcat(&path, path, suffix);
+ rtl_string_release(suffix);
+ module = osl_loadModuleAscii(path->buffer, mode);
+ rtl_string_release(path);
+ return module;
+}
+
+#endif // !DISABLE_DYNLOADING
+
+/*****************************************************************************/
+/* osl_getModuleHandle */
+/*****************************************************************************/
+
+sal_Bool SAL_CALL
+osl_getModuleHandle(rtl_uString *, oslModule *pResult)
+{
+#if HAVE_UNIX_DLAPI
+ *pResult = static_cast<oslModule>(RTLD_DEFAULT);
+ return true;
+#else
+ *pResult = nullptr;
+ return false;
+#endif
+}
+
+/*****************************************************************************/
+/* osl_unloadModule */
+/*****************************************************************************/
+void SAL_CALL osl_unloadModule(oslModule hModule)
+{
+#if !defined(DISABLE_DYNLOADING) && HAVE_UNIX_DLAPI
+ if (hModule)
+ {
+ int nRet = dlclose(hModule);
+ SAL_INFO_IF(
+ nRet != 0, "sal.osl", "dlclose(" << hModule << "): " << dlerror());
+ }
+#else
+ (void) hModule;
+#endif
+}
+
+namespace {
+
+void * getSymbol(oslModule module, char const * symbol)
+{
+ assert(symbol != nullptr);
+#if HAVE_UNIX_DLAPI
+ // We do want to use dlsym() also in the DISABLE_DYNLOADING case
+ // just to look up symbols in the static executable, I think:
+ void * p = dlsym(module, symbol);
+ SAL_INFO_IF(
+ p == nullptr, "sal.osl",
+ "dlsym(" << module << ", " << symbol << "): " << dlerror());
+#else
+ (void) module;
+ (void) symbol;
+ void *p = nullptr;
+#endif
+ return p;
+}
+
+}
+
+/*****************************************************************************/
+/* osl_getSymbol */
+/*****************************************************************************/
+void* SAL_CALL
+osl_getSymbol(oslModule Module, rtl_uString* pSymbolName)
+{
+ // Arbitrarily using UTF-8:
+ OString s;
+ if (!OUString::unacquired(&pSymbolName).convertToString(
+ &s, RTL_TEXTENCODING_UTF8,
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
+ RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ SAL_INFO(
+ "sal.osl", "cannot convert \"" << OUString::unacquired(&pSymbolName)
+ << "\" to UTF-8");
+ return nullptr;
+ }
+ if (s.indexOf('\0') != -1) {
+ SAL_INFO("sal.osl", "\"" << s << "\" contains embedded NUL");
+ return nullptr;
+ }
+ return getSymbol(Module, s.getStr());
+}
+
+/*****************************************************************************/
+/* osl_getAsciiFunctionSymbol */
+/*****************************************************************************/
+oslGenericFunction SAL_CALL
+osl_getAsciiFunctionSymbol(oslModule Module, const char *pSymbol)
+{
+ return reinterpret_cast<oslGenericFunction>(getSymbol(Module, pSymbol));
+ // requires conditionally-supported conversion from void * to function
+ // pointer
+}
+
+/*****************************************************************************/
+/* osl_getFunctionSymbol */
+/*****************************************************************************/
+oslGenericFunction SAL_CALL
+osl_getFunctionSymbol(oslModule module, rtl_uString *puFunctionSymbolName)
+{
+ return reinterpret_cast<oslGenericFunction>(
+ osl_getSymbol(module, puFunctionSymbolName));
+ // requires conditionally-supported conversion from void * to function
+ // pointer
+}
+
+/*****************************************************************************/
+/* osl_getModuleURLFromAddress */
+/*****************************************************************************/
+sal_Bool SAL_CALL osl_getModuleURLFromAddress(void * addr, rtl_uString ** ppLibraryUrl)
+{
+ bool result = false;
+ rtl_String * path = nullptr;
+ if (getModulePathFromAddress(addr, &path))
+ {
+ rtl_string2UString(ppLibraryUrl,
+ path->buffer,
+ path->length,
+ osl_getThreadTextEncoding(),
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+
+ SAL_WARN_IF(
+ *ppLibraryUrl == nullptr, "sal.osl", "rtl_string2UString failed");
+ auto const e = osl_getFileURLFromSystemPath(*ppLibraryUrl, ppLibraryUrl);
+ if (e == osl_File_E_None)
+ {
+ SAL_INFO("sal.osl", "osl_getModuleURLFromAddress(" << addr << ") => " << OUString(*ppLibraryUrl));
+
+ result = true;
+ }
+ else
+ {
+ SAL_WARN(
+ "sal.osl",
+ "osl_getModuleURLFromAddress(" << addr << "), osl_getFileURLFromSystemPath("
+ << OUString::unacquired(ppLibraryUrl) << ") failed with " << e);
+ result = false;
+ }
+ rtl_string_release(path);
+ }
+ return result;
+}
+
+/*****************************************************************************/
+/* osl_getModuleURLFromFunctionAddress */
+/*****************************************************************************/
+sal_Bool SAL_CALL osl_getModuleURLFromFunctionAddress(oslGenericFunction addr, rtl_uString ** ppLibraryUrl)
+{
+ return osl_getModuleURLFromAddress(
+ reinterpret_cast<void*>(addr), ppLibraryUrl);
+ // requires conditionally-supported conversion from function pointer to
+ // void *
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/mutex.cxx b/sal/osl/unx/mutex.cxx
new file mode 100644
index 000000000..1ca4958fd
--- /dev/null
+++ b/sal/osl/unx/mutex.cxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#if defined LINUX
+// to define __USE_UNIX98, via _XOPEN_SOURCE, enabling pthread_mutexattr_settype
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+#endif
+#include "system.hxx"
+#include "unixerrnostring.hxx"
+
+#include <sal/log.hxx>
+#include <osl/mutex.h>
+
+#include <pthread.h>
+#include <stdlib.h>
+
+
+typedef struct _oslMutexImpl
+{
+ pthread_mutex_t mutex;
+} oslMutexImpl;
+
+oslMutex SAL_CALL osl_createMutex()
+{
+ oslMutexImpl* pMutex = static_cast<oslMutexImpl*>(malloc(sizeof(oslMutexImpl)));
+ pthread_mutexattr_t aMutexAttr;
+ int nRet=0;
+
+ SAL_WARN_IF(!pMutex, "sal.osl.mutex", "null pMutex");
+
+ if ( pMutex == nullptr )
+ {
+ return nullptr;
+ }
+
+ pthread_mutexattr_init(&aMutexAttr);
+
+ nRet = pthread_mutexattr_settype(&aMutexAttr, PTHREAD_MUTEX_RECURSIVE);
+ if( nRet == 0 )
+ nRet = pthread_mutex_init(&(pMutex->mutex), &aMutexAttr);
+ if ( nRet != 0 )
+ {
+ SAL_WARN("sal.osl.mutex", "pthread_muxex_init failed: " << UnixErrnoString(nRet));
+
+ free(pMutex);
+ pMutex = nullptr;
+ }
+
+ pthread_mutexattr_destroy(&aMutexAttr);
+
+ return pMutex;
+}
+
+void SAL_CALL osl_destroyMutex(oslMutex pMutex)
+{
+ SAL_WARN_IF(!pMutex, "sal.osl.mutex", "null pMutex");
+
+ if ( pMutex != nullptr )
+ {
+ int nRet = pthread_mutex_destroy(&(pMutex->mutex));
+ if ( nRet != 0 )
+ {
+ SAL_WARN("sal.osl.mutex", "pthread_mutex_destroy failed: " << UnixErrnoString(nRet));
+ }
+
+ free(pMutex);
+ }
+}
+
+#ifdef __COVERITY__
+ extern void __coverity_recursive_lock_acquire__(void*);
+ extern void __coverity_recursive_lock_release__(void*);
+ extern void __coverity_assert_locked__(void*);
+#endif
+
+sal_Bool SAL_CALL osl_acquireMutex(oslMutex pMutex)
+{
+ SAL_WARN_IF(!pMutex, "sal.osl.mutex", "null pMutex");
+
+ if ( pMutex != nullptr )
+ {
+ int nRet = pthread_mutex_lock(&(pMutex->mutex));
+ if ( nRet != 0 )
+ {
+ SAL_WARN("sal.osl.mutex", "pthread_mutex_lock failed: " << UnixErrnoString(nRet));
+ return false;
+ }
+#ifdef __COVERITY__
+ __coverity_recursive_lock_acquire__(pMutex);
+#endif
+ return true;
+ }
+
+ /* not initialized */
+ return false;
+}
+
+sal_Bool SAL_CALL osl_tryToAcquireMutex(oslMutex pMutex)
+{
+ bool result = false;
+
+ SAL_WARN_IF(!pMutex, "sal.osl.mutex", "null pMutex");
+
+ if ( pMutex )
+ {
+ int nRet = pthread_mutex_trylock(&(pMutex->mutex));
+ if ( nRet == 0 )
+ {
+#ifdef __COVERITY__
+ __coverity_recursive_lock_acquire__(pMutex);
+#endif
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+sal_Bool SAL_CALL osl_releaseMutex(oslMutex pMutex)
+{
+#ifdef __COVERITY__
+ __coverity_assert_locked__(pMutex);
+#endif
+ SAL_WARN_IF(!pMutex, "sal.osl.mutex", "null pMutex");
+
+ if ( pMutex )
+ {
+ int nRet = pthread_mutex_unlock(&(pMutex->mutex));
+ if ( nRet != 0 )
+ {
+ SAL_WARN("sal.osl.mutex", "pthread_mutex_unlock failed: " << UnixErrnoString(nRet));
+ return false;
+ }
+
+#ifdef __COVERITY__
+ __coverity_recursive_lock_release__(pMutex);
+#endif
+ return true;
+ }
+
+ /* not initialized */
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/nlsupport.cxx b/sal/osl/unx/nlsupport.cxx
new file mode 100644
index 000000000..2780a7fdd
--- /dev/null
+++ b/sal/osl/unx/nlsupport.cxx
@@ -0,0 +1,872 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <cstring>
+
+#include <osl/nlsupport.h>
+#include <osl/diagnose.h>
+#include <osl/process.h>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include "nlsupport.hxx"
+
+// these share a lot, so use one define
+#if defined(LINUX) || defined(EMSCRIPTEN) || defined(__sun) || \
+ defined(FREEBSD) || defined(OPENBSD) || defined(DRAGONFLY) || defined(NETBSD)
+#define LO_COMMON_NLS_ARCHS 1
+#else
+#define LO_COMMON_NLS_ARCHS 0
+#endif
+
+#if LO_COMMON_NLS_ARCHS
+#include <locale.h>
+#include <langinfo.h>
+#elif defined(MACOSX) || defined(IOS)
+#include <osl/module.h>
+#include <osl/thread.h>
+#include "system.hxx"
+#endif
+
+#include <string.h>
+
+namespace {
+
+struct Pair {
+ const char *key;
+ const rtl_TextEncoding value;
+};
+
+}
+
+/*****************************************************************************
+ compare function for binary search
+ *****************************************************************************/
+
+static int
+pair_compare (const char *key, const Pair *pair)
+{
+ int result = rtl_str_compareIgnoreAsciiCase( key, pair->key );
+ return result;
+}
+
+/*****************************************************************************
+ binary search on encoding tables
+ *****************************************************************************/
+
+static const Pair*
+pair_search (const char *key, const Pair *base, unsigned int member )
+{
+ unsigned int lower = 0;
+ unsigned int upper = member;
+
+ /* check for validity of input */
+ if ( (key == nullptr) || (base == nullptr) || (member == 0) )
+ return nullptr;
+
+ /* binary search */
+ while ( lower < upper )
+ {
+ const unsigned int current = (lower + upper) / 2;
+ const int comparison = pair_compare( key, base + current );
+ if (comparison < 0)
+ upper = current;
+ else if (comparison > 0)
+ lower = current + 1;
+ else
+ return base + current;
+ }
+
+ return nullptr;
+}
+
+/*****************************************************************************
+ convert rtl_Locale to locale string
+ *****************************************************************************/
+
+static char * compose_locale( rtl_Locale * pLocale, char * buffer, size_t n )
+{
+ /* check if a valid locale is specified */
+ if( pLocale && pLocale->Language &&
+ (pLocale->Language->length == 2 || pLocale->Language->length == 3) )
+ {
+ size_t offset = 0;
+
+ /* convert language code to ascii */
+ {
+ rtl_String *pLanguage = nullptr;
+
+ rtl_uString2String( &pLanguage,
+ pLocale->Language->buffer, pLocale->Language->length,
+ RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
+
+ if( sal::static_int_cast<sal_uInt32>(pLanguage->length) < n )
+ {
+ strcpy( buffer, pLanguage->buffer );
+ offset = pLanguage->length;
+ }
+
+ rtl_string_release( pLanguage );
+ }
+
+ /* convert country code to ascii */
+ if( pLocale->Country && (pLocale->Country->length == 2) )
+ {
+ rtl_String *pCountry = nullptr;
+
+ rtl_uString2String( &pCountry,
+ pLocale->Country->buffer, pLocale->Country->length,
+ RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
+
+ if( offset + pCountry->length + 1 < n )
+ {
+ strcpy( buffer + offset++, "_" );
+ strcpy( buffer + offset, pCountry->buffer );
+ offset += pCountry->length;
+ }
+
+ rtl_string_release( pCountry );
+ }
+
+ /* convert variant to ascii - check if there is enough space for the variant string */
+ if( pLocale->Variant && pLocale->Variant->length &&
+ ( sal::static_int_cast<sal_uInt32>(pLocale->Variant->length) < n - 6 ) )
+ {
+ rtl_String *pVariant = nullptr;
+
+ rtl_uString2String( &pVariant,
+ pLocale->Variant->buffer, pLocale->Variant->length,
+ RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
+
+ if( offset + pVariant->length + 1 < n )
+ {
+ strcpy( buffer + offset, pVariant->buffer );
+ }
+
+ rtl_string_release( pVariant );
+ }
+
+ return buffer;
+ }
+
+ return nullptr;
+}
+
+/*****************************************************************************
+ convert locale string to rtl_Locale
+ *****************************************************************************/
+
+static rtl_Locale * parse_locale( const char * locale )
+{
+ assert(locale != nullptr);
+
+ if (*locale == '\0' || std::strcmp(locale, "C") == 0
+ || std::strcmp(locale, "POSIX") == 0)
+ {
+ return rtl_locale_register(u"C", u"", u"");
+ }
+
+ size_t len = strlen( locale );
+
+ rtl_uString * pLanguage = nullptr;
+ rtl_uString * pCountry = nullptr;
+ rtl_uString * pVariant = nullptr;
+
+ size_t offset = std::min<size_t>(len, 2);
+
+ rtl_Locale * ret;
+
+ /* language is a two or three letter code */
+ if( (len > 3 && locale[3] == '_') || (len == 3 && locale[2] != '_') )
+ offset = 3;
+
+ /* convert language code to unicode */
+ rtl_string2UString( &pLanguage, locale, offset, RTL_TEXTENCODING_ASCII_US, OSTRING_TO_OUSTRING_CVTFLAGS );
+ OSL_ASSERT(pLanguage != nullptr);
+
+ /* convert country code to unicode */
+ if( len >= offset+3 && locale[offset] == '_' )
+ {
+ rtl_string2UString( &pCountry, locale + offset + 1, 2, RTL_TEXTENCODING_ASCII_US, OSTRING_TO_OUSTRING_CVTFLAGS );
+ OSL_ASSERT(pCountry != nullptr);
+ offset += 3;
+ }
+
+ /* convert variant code to unicode - do not rely on "." as delimiter */
+ if( len > offset ) {
+ rtl_string2UString( &pVariant, locale + offset, len - offset, RTL_TEXTENCODING_ASCII_US, OSTRING_TO_OUSTRING_CVTFLAGS );
+ OSL_ASSERT(pVariant != nullptr);
+ }
+
+ ret = rtl_locale_register( pLanguage->buffer, pCountry ? pCountry->buffer : u"", pVariant ? pVariant->buffer : u"" );
+
+ if (pVariant) rtl_uString_release(pVariant);
+ if (pCountry) rtl_uString_release(pCountry);
+ if (pLanguage) rtl_uString_release(pLanguage);
+
+ return ret;
+}
+
+#if LO_COMMON_NLS_ARCHS
+
+/*
+ * This implementation of osl_getTextEncodingFromLocale maps
+ * from nl_langinfo_l(CODESET) to rtl_textencoding defines.
+ * nl_langinfo() is supported only on Linux, Solaris,
+ * >= NetBSD 1.6 and >= FreeBSD 4.4
+ *
+ * _nl_language_list[] is an array list of supported encodings. Because
+ * we are using a binary search, the list has to be in ascending order.
+ * We are comparing the encodings case insensitive, so the list has
+ * to be completely upper or lowercase.
+ */
+
+#if defined(__sun)
+
+/* The values in the below list can be obtained with a script like
+ * #!/bin/sh
+ * for i in `locale -a`; do
+ * LC_ALL=$i locale -k code_set_name
+ * done
+ */
+static const Pair nl_language_list[] = {
+ { "5601", RTL_TEXTENCODING_EUC_KR }, /* ko_KR.EUC */
+ { "646", RTL_TEXTENCODING_ISO_8859_1 }, /* fake: ASCII_US */
+ { "ANSI-1251", RTL_TEXTENCODING_MS_1251 }, /* ru_RU.ANSI1251 */
+ { "BIG5", RTL_TEXTENCODING_BIG5 }, /* zh_CN.BIG5 */
+ { "BIG5-HKSCS", RTL_TEXTENCODING_BIG5_HKSCS }, /* zh_CN.BIG5HK */
+ { "CNS11643", RTL_TEXTENCODING_EUC_TW }, /* zh_TW.EUC */
+ { "EUCJP", RTL_TEXTENCODING_EUC_JP }, /* ja_JP.eucjp */
+ { "GB18030", RTL_TEXTENCODING_GB_18030 }, /* zh_CN.GB18030 */
+ { "GB2312", RTL_TEXTENCODING_GB_2312 }, /* zh_CN */
+ { "GBK", RTL_TEXTENCODING_GBK }, /* zh_CN.GBK */
+ { "ISO8859-1", RTL_TEXTENCODING_ISO_8859_1 },
+ { "ISO8859-10", RTL_TEXTENCODING_ISO_8859_10 },
+ { "ISO8859-13", RTL_TEXTENCODING_ISO_8859_13 }, /* lt_LT lv_LV */
+ { "ISO8859-14", RTL_TEXTENCODING_ISO_8859_14 },
+ { "ISO8859-15", RTL_TEXTENCODING_ISO_8859_15 },
+ { "ISO8859-2", RTL_TEXTENCODING_ISO_8859_2 },
+ { "ISO8859-3", RTL_TEXTENCODING_ISO_8859_3 },
+ { "ISO8859-4", RTL_TEXTENCODING_ISO_8859_4 },
+ { "ISO8859-5", RTL_TEXTENCODING_ISO_8859_5 },
+ { "ISO8859-6", RTL_TEXTENCODING_ISO_8859_6 },
+ { "ISO8859-7", RTL_TEXTENCODING_ISO_8859_7 },
+ { "ISO8859-8", RTL_TEXTENCODING_ISO_8859_8 },
+ { "ISO8859-9", RTL_TEXTENCODING_ISO_8859_9 },
+ { "KOI8-R", RTL_TEXTENCODING_KOI8_R },
+ { "KOI8-U", RTL_TEXTENCODING_KOI8_U },
+ { "PCK", RTL_TEXTENCODING_MS_932 },
+ { "SUN_EU_GREEK", RTL_TEXTENCODING_ISO_8859_7 }, /* 8859-7 + Euro */
+ { "TIS620.2533", RTL_TEXTENCODING_MS_874 }, /* th_TH.TIS620 */
+ { "UTF-8", RTL_TEXTENCODING_UTF8 }
+};
+
+/* XXX MS-874 is an extension to tis620, so this is not
+ * really equivalent */
+
+#elif defined(LINUX) || defined(EMSCRIPTEN)
+
+#if !defined(CODESET)
+#define CODESET _NL_CTYPE_CODESET_NAME
+#endif
+
+const Pair nl_language_list[] = {
+ { "ANSI_X3.110-1983", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-99 NAPLPS */
+ { "ANSI_X3.4-1968", RTL_TEXTENCODING_ISO_8859_1 }, /* fake: ASCII_US */
+ { "ASMO_449", RTL_TEXTENCODING_DONTKNOW }, /* ISO_9036 ARABIC7 */
+ { "BALTIC", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-179 */
+ { "BIG5", RTL_TEXTENCODING_BIG5 }, /* locale: zh_TW */
+ { "BIG5-HKSCS", RTL_TEXTENCODING_BIG5_HKSCS }, /* locale: zh_CN.BIG5HK */
+ { "BIG5HKSCS", RTL_TEXTENCODING_BIG5_HKSCS }, /* deprecated */
+ { "BS_4730", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-4 ISO646-GB */
+ { "BS_VIEWDATA", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-47 */
+ { "CP1250", RTL_TEXTENCODING_MS_1250 }, /* MS-EE */
+ { "CP1251", RTL_TEXTENCODING_MS_1251 }, /* MS-CYRL */
+ { "CP1252", RTL_TEXTENCODING_MS_1252 }, /* MS-ANSI */
+ { "CP1253", RTL_TEXTENCODING_MS_1253 }, /* MS-GREEK */
+ { "CP1254", RTL_TEXTENCODING_MS_1254 }, /* MS-TURK */
+ { "CP1255", RTL_TEXTENCODING_MS_1255 }, /* MS-HEBR */
+ { "CP1256", RTL_TEXTENCODING_MS_1256 }, /* MS-ARAB */
+ { "CP1257", RTL_TEXTENCODING_MS_1257 }, /* WINBALTRIM */
+ { "CSA_Z243.4-1985-1", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-121 */
+ { "CSA_Z243.4-1985-2", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-122 CSA7-2 */
+ { "CSA_Z243.4-1985-GR", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-123 */
+ { "CSN_369103", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-139 */
+ { "CWI", RTL_TEXTENCODING_DONTKNOW }, /* CWI-2 CP-HU */
+ { "DEC-MCS", RTL_TEXTENCODING_DONTKNOW }, /* DEC */
+ { "DIN_66003", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-21 */
+ { "DS_2089", RTL_TEXTENCODING_DONTKNOW }, /* DS2089 ISO646-DK */
+ { "EBCDIC-AT-DE", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-AT-DE-A", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-CA-FR", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-DK-NO", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-DK-NO-A", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-ES", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-ES-A", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-ES-S", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-FI-SE", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-FI-SE-A", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-FR", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-IS-FRISS", RTL_TEXTENCODING_DONTKNOW }, /* FRISS */
+ { "EBCDIC-IT", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-PT", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-UK", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-US", RTL_TEXTENCODING_DONTKNOW },
+ { "ECMA-CYRILLIC", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-111 */
+ { "ES", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-17 */
+ { "ES2", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-85 */
+ { "EUC-JP", RTL_TEXTENCODING_EUC_JP }, /* locale: ja_JP.eucjp */
+ { "EUC-KR", RTL_TEXTENCODING_EUC_KR }, /* locale: ko_KR.euckr */
+ { "EUC-TW", RTL_TEXTENCODING_EUC_TW }, /* locale: zh_TW.euctw */
+ { "GB18030", RTL_TEXTENCODING_GB_18030 }, /* locale: zh_CN.gb18030 */
+ { "GB2312", RTL_TEXTENCODING_GB_2312 }, /* locale: zh_CN */
+ { "GB_1988-80", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-57 */
+ { "GBK", RTL_TEXTENCODING_GBK }, /* locale: zh_CN.GBK */
+ { "GOST_19768-74", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-153 */
+ { "GREEK-CCITT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-150 */
+ { "GREEK7", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-88 */
+ { "GREEK7-OLD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-18 */
+ { "HP-ROMAN8", RTL_TEXTENCODING_DONTKNOW }, /* ROMAN8 R8 */
+ { "IBM037", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-[US|CA|WT] */
+ { "IBM038", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-INT CP038 */
+ { "IBM1004", RTL_TEXTENCODING_DONTKNOW }, /* CP1004 OS2LATIN1 */
+ { "IBM1026", RTL_TEXTENCODING_DONTKNOW }, /* CP1026 1026 */
+ { "IBM1047", RTL_TEXTENCODING_DONTKNOW }, /* CP1047 1047 */
+ { "IBM256", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-INT1 */
+ { "IBM273", RTL_TEXTENCODING_DONTKNOW }, /* CP273 */
+ { "IBM274", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-BE CP274 */
+ { "IBM275", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-BR CP275 */
+ { "IBM277", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CP-[DK|NO] */
+ { "IBM278", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CP-[FISE]*/
+ { "IBM280", RTL_TEXTENCODING_DONTKNOW }, /* CP280 EBCDIC-CP-IT*/
+ { "IBM281", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-JP-E CP281 */
+ { "IBM284", RTL_TEXTENCODING_DONTKNOW }, /* CP284 EBCDIC-CP-ES */
+ { "IBM285", RTL_TEXTENCODING_DONTKNOW }, /* CP285 EBCDIC-CP-GB */
+ { "IBM290", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-JP-KANA */
+ { "IBM297", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CP-FR */
+ { "IBM420", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CP-AR1 */
+ { "IBM423", RTL_TEXTENCODING_DONTKNOW }, /* CP423 EBCDIC-CP-GR */
+ { "IBM424", RTL_TEXTENCODING_DONTKNOW }, /* CP424 EBCDIC-CP-HE */
+ { "IBM437", RTL_TEXTENCODING_IBM_437 }, /* CP437 437 */
+ { "IBM500", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CP-[BE|CH] */
+ { "IBM850", RTL_TEXTENCODING_IBM_850 }, /* CP850 850 */
+ { "IBM851", RTL_TEXTENCODING_DONTKNOW }, /* CP851 851 */
+ { "IBM852", RTL_TEXTENCODING_IBM_852 }, /* CP852 852 */
+ { "IBM855", RTL_TEXTENCODING_IBM_855 }, /* CP855 855 */
+ { "IBM857", RTL_TEXTENCODING_IBM_857 }, /* CP857 857 */
+ { "IBM860", RTL_TEXTENCODING_IBM_860 }, /* CP860 860 */
+ { "IBM861", RTL_TEXTENCODING_IBM_861 }, /* CP861 861 CP-IS */
+ { "IBM862", RTL_TEXTENCODING_IBM_862 }, /* CP862 862 */
+ { "IBM863", RTL_TEXTENCODING_IBM_863 }, /* CP863 863 */
+ { "IBM864", RTL_TEXTENCODING_IBM_864 }, /* CP864 */
+ { "IBM865", RTL_TEXTENCODING_IBM_865 }, /* CP865 865 */
+ { "IBM866", RTL_TEXTENCODING_IBM_866 }, /* CP866 866 */
+ { "IBM868", RTL_TEXTENCODING_DONTKNOW }, /* CP868 CP-AR */
+ { "IBM869", RTL_TEXTENCODING_IBM_869 }, /* CP869 869 CP-GR */
+ { "IBM870", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-[ROECE|YU] */
+ { "IBM871", RTL_TEXTENCODING_DONTKNOW }, /* CP871 EBCDIC-CP-IS */
+ { "IBM875", RTL_TEXTENCODING_DONTKNOW }, /* CP875 EBCDIC-GREEK */
+ { "IBM880", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CYRILLIC */
+ { "IBM891", RTL_TEXTENCODING_DONTKNOW }, /* CP891 */
+ { "IBM903", RTL_TEXTENCODING_DONTKNOW }, /* CP903 */
+ { "IBM904", RTL_TEXTENCODING_DONTKNOW }, /* CP904 904 */
+ { "IBM905", RTL_TEXTENCODING_DONTKNOW }, /* CP905 EBCDIC-CP-TR */
+ { "IBM918", RTL_TEXTENCODING_DONTKNOW }, /* CP918 EBCDIC-AR2 */
+ { "IEC_P27-1", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-143 */
+ { "INIS", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-49 */
+ { "INIS-8", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-50 */
+ { "INIS-CYRILLIC", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-51 */
+ { "INVARIANT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-170 */
+ { "ISO-8859-1", RTL_TEXTENCODING_ISO_8859_1 }, /* ISO-IR-100 CP819 */
+ { "ISO-8859-10", RTL_TEXTENCODING_ISO_8859_10 }, /* ISO-IR-157 LATIN6 */
+ { "ISO-8859-13", RTL_TEXTENCODING_ISO_8859_13 }, /* ISO-IR-179 LATIN7 */
+ { "ISO-8859-14", RTL_TEXTENCODING_ISO_8859_14 }, /* LATIN8 L8 */
+ { "ISO-8859-15", RTL_TEXTENCODING_ISO_8859_15 },
+ { "ISO-8859-2", RTL_TEXTENCODING_ISO_8859_2 }, /* LATIN2 L2 */
+ { "ISO-8859-3", RTL_TEXTENCODING_ISO_8859_3 }, /* LATIN3 L3 */
+ { "ISO-8859-4", RTL_TEXTENCODING_ISO_8859_4 }, /* LATIN4 L4 */
+ { "ISO-8859-5", RTL_TEXTENCODING_ISO_8859_5 }, /* CYRILLIC */
+ { "ISO-8859-6", RTL_TEXTENCODING_ISO_8859_6 }, /* ECMA-114 ARABIC */
+ { "ISO-8859-7", RTL_TEXTENCODING_ISO_8859_7 }, /* ECMA-118 GREEK8 */
+ { "ISO-8859-8", RTL_TEXTENCODING_ISO_8859_8 }, /* ISO_8859-8 HEBREW */
+ { "ISO-8859-9", RTL_TEXTENCODING_ISO_8859_9 }, /* ISO_8859-9 LATIN5 */
+ { "ISO-IR-90", RTL_TEXTENCODING_DONTKNOW }, /* ISO_6937-2:1983 */
+ { "ISO_10367-BOX", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-155 */
+ { "ISO_2033-1983", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-98 E13B */
+ { "ISO_5427", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-37 KOI-7 */
+ { "ISO_5427-EXT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-54 */
+ { "ISO_5428", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-55 */
+ { "ISO_646.BASIC", RTL_TEXTENCODING_ASCII_US }, /* REF */
+ { "ISO_646.IRV", RTL_TEXTENCODING_ASCII_US }, /* ISO-IR-2 IRV */
+ { "ISO_646.IRV:1983", RTL_TEXTENCODING_ISO_8859_1 }, /* fake: ASCII_US, used for "C" locale*/
+ { "ISO_6937", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-156 ISO6937*/
+ { "ISO_6937-2-25", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-152 */
+ { "ISO_6937-2-ADD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-142 */
+ { "ISO_8859-SUPP", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-154 */
+ { "IT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-15 */
+ { "JIS_C6220-1969-JP", RTL_TEXTENCODING_DONTKNOW }, /* KATAKANA X0201-7 */
+ { "JIS_C6220-1969-RO", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-14 */
+ { "JIS_C6229-1984-A", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-91 */
+ { "JIS_C6229-1984-B", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-92 */
+ { "JIS_C6229-1984-B-ADD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-93 */
+ { "JIS_C6229-1984-HAND", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-94 */
+ { "JIS_C6229-1984-HAND-ADD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-95 */
+ { "JIS_C6229-1984-KANA", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-96 */
+ { "JIS_X0201", RTL_TEXTENCODING_DONTKNOW }, /* X0201 */
+ { "JUS_I.B1.002", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-141 */
+ { "JUS_I.B1.003-MAC", RTL_TEXTENCODING_DONTKNOW }, /* MACEDONIAN */
+ { "JUS_I.B1.003-SERB", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-146 SERBIAN */
+ { "KOI-8", RTL_TEXTENCODING_DONTKNOW },
+ { "KOI8-R", RTL_TEXTENCODING_KOI8_R },
+ { "KOI8-U", RTL_TEXTENCODING_KOI8_U },
+ { "KSC5636", RTL_TEXTENCODING_DONTKNOW }, /* ISO646-KR */
+ { "LATIN-GREEK", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-19 */
+ { "LATIN-GREEK-1", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-27 */
+ { "MAC-IS", RTL_TEXTENCODING_APPLE_ROMAN },
+ { "MAC-UK", RTL_TEXTENCODING_APPLE_ROMAN },
+ { "MACINTOSH", RTL_TEXTENCODING_APPLE_ROMAN }, /* MAC */
+ { "MSZ_7795.3", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-86 */
+ { "NATS-DANO", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-9-1 */
+ { "NATS-DANO-ADD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-9-2 */
+ { "NATS-SEFI", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-8-1 */
+ { "NATS-SEFI-ADD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-8-2 */
+ { "NC_NC00-10", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-151 */
+ { "NEXTSTEP", RTL_TEXTENCODING_DONTKNOW }, /* NEXT */
+ { "NF_Z_62-010", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-69 */
+ { "NF_Z_62-010_(1973)", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-25 */
+ { "NS_4551-1", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-60 */
+ { "NS_4551-2", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-61 */
+ { "PT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-16 */
+ { "PT2", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-84 */
+ { "SAMI", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-158 */
+ { "SEN_850200_B", RTL_TEXTENCODING_DONTKNOW }, /* ISO646-[FI|SE] */
+ { "SEN_850200_C", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-11 */
+ { "T.101-G2", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-128 */
+ { "T.61-7BIT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-102 */
+ { "T.61-8BIT", RTL_TEXTENCODING_DONTKNOW }, /* T.61 ISO-IR-103 */
+ { "TIS-620", RTL_TEXTENCODING_MS_874 }, /* locale: th_TH */
+ { "UTF-8", RTL_TEXTENCODING_UTF8 }, /* ISO-10646/UTF-8 */
+ { "VIDEOTEX-SUPPL", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-70 */
+ { "WIN-SAMI-2", RTL_TEXTENCODING_DONTKNOW } /* WS2 */
+};
+
+#elif defined(FREEBSD) || defined(DRAGONFLY)
+
+static const Pair nl_language_list[] = {
+ { "ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "BIG5", RTL_TEXTENCODING_BIG5 }, /* China - Traditional Chinese */
+ { "CP1251", RTL_TEXTENCODING_MS_1251 }, /* MS-CYRL */
+ { "CP866", RTL_TEXTENCODING_IBM_866 }, /* CP866 866 */
+ { "EUCCN", RTL_TEXTENCODING_EUC_CN }, /* China - Simplified Chinese */
+ { "EUCJP", RTL_TEXTENCODING_EUC_JP }, /* Japan */
+ { "EUCKR", RTL_TEXTENCODING_EUC_KR }, /* Korea */
+ { "ISO8859-1", RTL_TEXTENCODING_ISO_8859_1 }, /* Western */
+ { "ISO8859-15", RTL_TEXTENCODING_ISO_8859_15 }, /* Western Updated (w/Euro sign) */
+ { "ISO8859-2", RTL_TEXTENCODING_ISO_8859_2 }, /* Central European */
+ { "ISO8859-4", RTL_TEXTENCODING_ISO_8859_4 }, /* LATIN4 L4 */
+ { "ISO8859-5", RTL_TEXTENCODING_ISO_8859_5 }, /* Cyrillic */
+ { "ISO8859-7", RTL_TEXTENCODING_ISO_8859_7 }, /* Greek */
+ { "ISO8859-9", RTL_TEXTENCODING_ISO_8859_9 }, /* Turkish */
+ { "KOI8-R", RTL_TEXTENCODING_KOI8_R }, /* KOI8-R */
+ { "KOI8-U", RTL_TEXTENCODING_KOI8_U }, /* KOI8-U */
+ { "SJIS", RTL_TEXTENCODING_SHIFT_JIS }, /* Japan */
+ { "US-ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "UTF-8", RTL_TEXTENCODING_UTF8 } /* ISO-10646/UTF-8 */
+};
+
+#elif defined(NETBSD)
+
+static const Pair nl_language_list[] = {
+ { "ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "BIG5", RTL_TEXTENCODING_BIG5 }, /* China - Traditional Chinese */
+ { "Big5", RTL_TEXTENCODING_BIG5 }, /* China - Traditional Chinese */
+ { "Big5-HKSCS", RTL_TEXTENCODING_BIG5_HKSCS }, /* locale: zh_CN.BIG5HK */
+ { "Big5HKSCS", RTL_TEXTENCODING_BIG5_HKSCS }, /* deprecated */
+ { "CP1251", RTL_TEXTENCODING_MS_1251 }, /* MS-CYRL */
+ { "CP866", RTL_TEXTENCODING_IBM_866 }, /* CP866 866 */
+ { "CTEXT", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "eucCN", RTL_TEXTENCODING_EUC_CN }, /* China - Simplified Chinese */
+ { "eucJP", RTL_TEXTENCODING_EUC_JP }, /* Japan */
+ { "eucKR", RTL_TEXTENCODING_EUC_KR }, /* Korea */
+ { "eucTW", RTL_TEXTENCODING_EUC_TW }, /* China - Traditional Chinese */
+ { "GB18030", RTL_TEXTENCODING_GB_18030 }, /* locale: zh_CN.gb18030 */
+ { "GB2312", RTL_TEXTENCODING_GB_2312 }, /* locale: zh_CN */
+ { "ISO-2022-JP", RTL_TEXTENCODING_DONTKNOW }, /* */
+ { "ISO-2022-JP-2", RTL_TEXTENCODING_DONTKNOW }, /* */
+ { "ISO8859-1", RTL_TEXTENCODING_ISO_8859_1 }, /* Western */
+ { "ISO8859-13", RTL_TEXTENCODING_ISO_8859_13 }, /* ISO-IR-179 LATIN7 */
+ { "ISO8859-15", RTL_TEXTENCODING_ISO_8859_15 }, /* Western Updated (w/Euro sign) */
+ { "ISO8859-2", RTL_TEXTENCODING_ISO_8859_2 }, /* Central European */
+ { "ISO8859-4", RTL_TEXTENCODING_ISO_8859_4 }, /* LATIN4 L4 */
+ { "ISO8859-5", RTL_TEXTENCODING_ISO_8859_5 }, /* Cyrillic */
+ { "ISO8859-7", RTL_TEXTENCODING_ISO_8859_7 }, /* Greek */
+ { "ISO8859-9", RTL_TEXTENCODING_ISO_8859_9 }, /* Turkish */
+ { "KOI8-R", RTL_TEXTENCODING_KOI8_R }, /* KOI8-R */
+ { "KOI8-U", RTL_TEXTENCODING_KOI8_U }, /* KOI8-U */
+ { "PT154", RTL_TEXTENCODING_PT154 }, /* */
+ { "SJIS", RTL_TEXTENCODING_SHIFT_JIS }, /* Japan */
+ { "US-ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "UTF-8", RTL_TEXTENCODING_UTF8 } /* ISO-10646/UTF-8 */
+};
+
+#elif defined(OPENBSD)
+
+static const Pair nl_language_list[] = {
+ { "ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "BIG5", RTL_TEXTENCODING_BIG5 }, /* China - Traditional Chinese */
+ { "CP1251", RTL_TEXTENCODING_MS_1251 }, /* MS-CYRL */
+ { "CP866", RTL_TEXTENCODING_IBM_866 }, /* CP866 866 */
+ { "EUCCN", RTL_TEXTENCODING_EUC_CN }, /* China - Simplified Chinese */
+ { "EUCJP", RTL_TEXTENCODING_EUC_JP }, /* Japan */
+ { "EUCKR", RTL_TEXTENCODING_EUC_KR }, /* Korea */
+ { "ISO8859-1", RTL_TEXTENCODING_ISO_8859_1 }, /* Western */
+ { "ISO8859-15", RTL_TEXTENCODING_ISO_8859_15 }, /* Western Updated (w/Euro sign) */
+ { "ISO8859-2", RTL_TEXTENCODING_ISO_8859_2 }, /* Central European */
+ { "ISO8859-4", RTL_TEXTENCODING_ISO_8859_4 }, /* LATIN4 L4 */
+ { "ISO8859-5", RTL_TEXTENCODING_ISO_8859_5 }, /* Cyrillic */
+ { "ISO8859-7", RTL_TEXTENCODING_ISO_8859_7 }, /* Greek */
+ { "ISO8859-9", RTL_TEXTENCODING_ISO_8859_9 }, /* Turkish */
+ { "KOI8-R", RTL_TEXTENCODING_KOI8_R }, /* KOI8-R */
+ { "KOI8-U", RTL_TEXTENCODING_KOI8_U }, /* KOI8-U */
+ { "SJIS", RTL_TEXTENCODING_SHIFT_JIS }, /* Japan */
+ { "US-ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "UTF-8", RTL_TEXTENCODING_UTF8 } /* ISO-10646/UTF-8 */
+};
+
+#else
+#error Unhandled individual LO_COMMON_NLS_ARCHS
+#endif // individual common NLS archs
+
+/*****************************************************************************
+ return the text encoding corresponding to the given locale
+ *****************************************************************************/
+
+rtl_TextEncoding osl_getTextEncodingFromLocale( rtl_Locale * pLocale )
+{
+ const Pair *language=nullptr;
+
+ char locale_buf[64] = "";
+ char codeset_buf[64];
+
+ char *codeset = nullptr;
+
+ /* default to process locale if pLocale == NULL */
+ if( pLocale == nullptr )
+ osl_getProcessLocale( &pLocale );
+
+ /* convert rtl_Locale to locale string */
+ compose_locale( pLocale, locale_buf, 64 );
+
+ locale_t ctype_locale = newlocale(
+ LC_CTYPE_MASK, locale_buf, static_cast<locale_t>(0));
+ if (ctype_locale == static_cast<locale_t>(0))
+ {
+ return RTL_TEXTENCODING_DONTKNOW;
+ }
+
+ /* get the charset as indicated by the LC_CTYPE locale */
+#if defined(NETBSD) && !defined(CODESET)
+ codeset = NULL;
+#else
+ codeset = nl_langinfo_l(CODESET, ctype_locale);
+ // per SUSv4, the return value of nl_langinfo_l can be invalidated by a
+ // subsequent call to nl_langinfo (not nl_langinfo_l) in any thread, but
+ // we cannot guard against that (at least, no code in LO itself should
+ // call nl_langinfo)
+#endif
+
+ if ( codeset != nullptr )
+ {
+ /* get codeset into mt save memory */
+ strncpy( codeset_buf, codeset, sizeof(codeset_buf) );
+ codeset_buf[sizeof(codeset_buf) - 1] = 0;
+ codeset = codeset_buf;
+ }
+
+ freelocale(ctype_locale);
+
+ /* search the codeset in our language list */
+ if ( codeset != nullptr )
+ {
+ language = pair_search (codeset, nl_language_list, SAL_N_ELEMENTS( nl_language_list ) );
+ }
+
+ OSL_ASSERT( language && ( RTL_TEXTENCODING_DONTKNOW != language->value ) );
+
+ /* a matching item in our list provides a mapping from codeset to
+ * rtl-codeset */
+ if ( language != nullptr )
+ return language->value;
+
+ return RTL_TEXTENCODING_DONTKNOW;
+}
+
+/*****************************************************************************
+ return the current process locale
+ *****************************************************************************/
+
+void imp_getProcessLocale( rtl_Locale ** ppLocale )
+{
+ char const * locale = getenv("LC_ALL");
+ if (locale == nullptr || *locale == '\0') {
+ locale = getenv("LC_CTYPE");
+ if (locale == nullptr || *locale == '\0') {
+ locale = getenv("LANG");
+ if (locale == nullptr || *locale == '\0') {
+ locale = "C";
+ }
+ }
+ }
+ // coverity[overrun-buffer-val : FALSE] - coverity gets this very wrong
+ *ppLocale = parse_locale(locale);
+}
+
+#else // !LO_COMMON_NLS_ARCHS
+
+/*
+ * This implementation of osl_getTextEncodingFromLocale maps
+ * from the ISO language codes.
+ */
+
+const Pair full_locale_list[] = {
+ { "ja_JP.eucJP", RTL_TEXTENCODING_EUC_JP },
+ { "ja_JP.EUC", RTL_TEXTENCODING_EUC_JP },
+ { "ko_KR.EUC", RTL_TEXTENCODING_EUC_KR },
+ { "zh_CN.EUC", RTL_TEXTENCODING_EUC_CN },
+ { "zh_TW.EUC", RTL_TEXTENCODING_EUC_TW }
+};
+
+const Pair locale_extension_list[] = {
+ { "big5", RTL_TEXTENCODING_BIG5 },
+ { "big5hk", RTL_TEXTENCODING_BIG5_HKSCS },
+ { "gb18030", RTL_TEXTENCODING_GB_18030 },
+ { "euc", RTL_TEXTENCODING_EUC_JP },
+ { "iso8859-1", RTL_TEXTENCODING_ISO_8859_1 },
+ { "iso8859-10", RTL_TEXTENCODING_ISO_8859_10 },
+ { "iso8859-13", RTL_TEXTENCODING_ISO_8859_13 },
+ { "iso8859-14", RTL_TEXTENCODING_ISO_8859_14 },
+ { "iso8859-15", RTL_TEXTENCODING_ISO_8859_15 },
+ { "iso8859-2", RTL_TEXTENCODING_ISO_8859_2 },
+ { "iso8859-3", RTL_TEXTENCODING_ISO_8859_3 },
+ { "iso8859-4", RTL_TEXTENCODING_ISO_8859_4 },
+ { "iso8859-5", RTL_TEXTENCODING_ISO_8859_5 },
+ { "iso8859-6", RTL_TEXTENCODING_ISO_8859_6 },
+ { "iso8859-7", RTL_TEXTENCODING_ISO_8859_7 },
+ { "iso8859-8", RTL_TEXTENCODING_ISO_8859_8 },
+ { "iso8859-9", RTL_TEXTENCODING_ISO_8859_9 },
+ { "koi8-r", RTL_TEXTENCODING_KOI8_R },
+ { "koi8-u", RTL_TEXTENCODING_KOI8_U },
+ { "pck", RTL_TEXTENCODING_MS_932 },
+#if (0)
+ { "sun_eu_greek", RTL_TEXTENCODING_DONTKNOW },
+#endif
+ { "utf-16", RTL_TEXTENCODING_UNICODE },
+ { "utf-7", RTL_TEXTENCODING_UTF7 },
+ { "utf-8", RTL_TEXTENCODING_UTF8 }
+};
+
+const Pair iso_language_list[] = {
+ { "af", RTL_TEXTENCODING_ISO_8859_1 },
+ { "ar", RTL_TEXTENCODING_ISO_8859_6 },
+ { "az", RTL_TEXTENCODING_ISO_8859_9 },
+ { "be", RTL_TEXTENCODING_ISO_8859_5 },
+ { "bg", RTL_TEXTENCODING_ISO_8859_5 },
+ { "ca", RTL_TEXTENCODING_ISO_8859_1 },
+ { "cs", RTL_TEXTENCODING_ISO_8859_2 },
+ { "da", RTL_TEXTENCODING_ISO_8859_1 },
+ { "de", RTL_TEXTENCODING_ISO_8859_1 },
+ { "el", RTL_TEXTENCODING_ISO_8859_7 },
+ { "en", RTL_TEXTENCODING_ISO_8859_1 },
+ { "es", RTL_TEXTENCODING_ISO_8859_1 },
+ { "et", RTL_TEXTENCODING_ISO_8859_4 },
+ { "eu", RTL_TEXTENCODING_ISO_8859_1 },
+ { "fa", RTL_TEXTENCODING_ISO_8859_6 },
+ { "fi", RTL_TEXTENCODING_ISO_8859_1 },
+ { "fo", RTL_TEXTENCODING_ISO_8859_1 },
+ { "fr", RTL_TEXTENCODING_ISO_8859_1 },
+ { "gr", RTL_TEXTENCODING_ISO_8859_7 },
+ { "he", RTL_TEXTENCODING_ISO_8859_8 },
+ { "hi", RTL_TEXTENCODING_DONTKNOW },
+ { "hr", RTL_TEXTENCODING_ISO_8859_2 },
+ { "hu", RTL_TEXTENCODING_ISO_8859_2 },
+ { "hy", RTL_TEXTENCODING_DONTKNOW },
+ { "id", RTL_TEXTENCODING_ISO_8859_1 },
+ { "is", RTL_TEXTENCODING_ISO_8859_1 },
+ { "it", RTL_TEXTENCODING_ISO_8859_1 },
+ { "iw", RTL_TEXTENCODING_ISO_8859_8 },
+ { "ja", RTL_TEXTENCODING_EUC_JP },
+ { "ka", RTL_TEXTENCODING_DONTKNOW },
+ { "kk", RTL_TEXTENCODING_ISO_8859_5 },
+ { "ko", RTL_TEXTENCODING_EUC_KR },
+ { "lt", RTL_TEXTENCODING_ISO_8859_4 },
+ { "lv", RTL_TEXTENCODING_ISO_8859_4 },
+ { "mk", RTL_TEXTENCODING_ISO_8859_5 },
+ { "mr", RTL_TEXTENCODING_DONTKNOW },
+ { "ms", RTL_TEXTENCODING_ISO_8859_1 },
+ { "nl", RTL_TEXTENCODING_ISO_8859_1 },
+ { "no", RTL_TEXTENCODING_ISO_8859_1 },
+ { "pl", RTL_TEXTENCODING_ISO_8859_2 },
+ { "pt", RTL_TEXTENCODING_ISO_8859_1 },
+ { "ro", RTL_TEXTENCODING_ISO_8859_2 },
+ { "ru", RTL_TEXTENCODING_ISO_8859_5 },
+ { "sa", RTL_TEXTENCODING_DONTKNOW },
+ { "sk", RTL_TEXTENCODING_ISO_8859_2 },
+ { "sl", RTL_TEXTENCODING_ISO_8859_2 },
+ { "sq", RTL_TEXTENCODING_ISO_8859_2 },
+ { "sv", RTL_TEXTENCODING_ISO_8859_1 },
+ { "sw", RTL_TEXTENCODING_ISO_8859_1 },
+ { "ta", RTL_TEXTENCODING_DONTKNOW },
+ { "th", RTL_TEXTENCODING_DONTKNOW },
+ { "tr", RTL_TEXTENCODING_ISO_8859_9 },
+ { "tt", RTL_TEXTENCODING_ISO_8859_5 },
+ { "uk", RTL_TEXTENCODING_ISO_8859_5 },
+ { "ur", RTL_TEXTENCODING_ISO_8859_6 },
+ { "uz", RTL_TEXTENCODING_ISO_8859_9 },
+ { "vi", RTL_TEXTENCODING_DONTKNOW },
+ { "zh", RTL_TEXTENCODING_BIG5 }
+};
+
+/*****************************************************************************
+ return the text encoding corresponding to the given locale
+ *****************************************************************************/
+
+rtl_TextEncoding osl_getTextEncodingFromLocale( rtl_Locale * pLocale )
+{
+ const Pair *language = nullptr;
+ char locale_buf[64] = "";
+
+ /* default to process locale if pLocale == NULL */
+ if( nullptr == pLocale )
+ osl_getProcessLocale( &pLocale );
+
+ /* convert rtl_Locale to locale string */
+ if( compose_locale( pLocale, locale_buf, 64 ) )
+ {
+ /* check special handling list (EUC) first */
+ language = pair_search( locale_buf, full_locale_list, SAL_N_ELEMENTS( full_locale_list ) );
+
+ if( nullptr == language )
+ {
+ /*
+ * check if there is a charset qualifier at the end of the given locale string
+ * e.g. de.ISO8859-15 or de.ISO8859-15@euro which strongly indicates what
+ * charset to use
+ */
+ char* cp = strrchr( locale_buf, '.' );
+
+ if( nullptr != cp )
+ {
+ language = pair_search( cp + 1, locale_extension_list, SAL_N_ELEMENTS( locale_extension_list ) );
+ }
+ }
+
+ /* use iso language code to determine the charset */
+ if( nullptr == language )
+ {
+ /* iso lang codes have 2 characters */
+ locale_buf[2] = '\0';
+
+ language = pair_search( locale_buf, iso_language_list, SAL_N_ELEMENTS( iso_language_list ) );
+ }
+ }
+
+ /* a matching item in our list provides a mapping from codeset to
+ * rtl-codeset */
+ if ( language != nullptr )
+ return language->value;
+
+ return RTL_TEXTENCODING_DONTKNOW;
+}
+
+#if defined(MACOSX) || defined(IOS)
+
+/*****************************************************************************
+ return the current process locale
+ *****************************************************************************/
+
+void imp_getProcessLocale( rtl_Locale ** ppLocale )
+{
+ OUString loc16(macosx_getLocale());
+ OString locale;
+ if (!loc16.convertToString(
+ &locale, RTL_TEXTENCODING_UTF8,
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ SAL_INFO("sal.osl", "Cannot convert \"" << loc16 << "\" to UTF-8");
+ }
+
+ /* handle the case where OS specific method of finding locale fails */
+ if ( locale.isEmpty() )
+ {
+ /* simulate behavior of setlocale */
+ locale = getenv( "LC_ALL" );
+
+ if( locale.isEmpty() )
+ locale = getenv( "LC_CTYPE" );
+
+ if( locale.isEmpty() )
+ locale = getenv( "LANG" );
+
+ if( locale.isEmpty() )
+ locale = "C";
+ }
+
+ /* return the locale */
+ *ppLocale = parse_locale( locale.getStr() );
+}
+
+#else // !MACOSX && !IOS
+
+/*****************************************************************************
+ return the current process locale
+ *****************************************************************************/
+
+void imp_getProcessLocale( rtl_Locale ** ppLocale )
+{
+#ifdef ANDROID
+ /* No locale environment variables on Android, so why even bother
+ * with getenv().
+ */
+ const char* locale = "en-US.UTF-8";
+#else
+ /* simulate behavior off setlocale */
+ const char* locale = getenv("LC_ALL");
+
+ if( NULL == locale )
+ locale = getenv( "LC_CTYPE" );
+
+ if( NULL == locale )
+ locale = getenv( "LANG" );
+
+ if( NULL == locale )
+ locale = "C";
+
+#endif
+ *ppLocale = parse_locale( locale );
+}
+
+#endif // !MACOSX && !IOS
+#endif // !LO_COMMON_NLS_ARCHS
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/nlsupport.hxx b/sal/osl/unx/nlsupport.hxx
new file mode 100644
index 000000000..ba5acf23c
--- /dev/null
+++ b/sal/osl/unx/nlsupport.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_NLSUPPORT_HXX
+#define INCLUDED_SAL_OSL_UNX_NLSUPPORT_HXX
+
+#include <sal/config.h>
+
+#include <rtl/locale.h>
+
+namespace rtl
+{
+class OUString;
+}
+
+void imp_getProcessLocale(rtl_Locale**);
+
+#if defined IOS || defined MACOSX
+OUString macosx_getLocale();
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/osxlocale.cxx b/sal/osl/unx/osxlocale.cxx
new file mode 100644
index 000000000..f82ea1436
--- /dev/null
+++ b/sal/osl/unx/osxlocale.cxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/types.h>
+#include <assert.h>
+
+#include <premac.h>
+#ifndef IOS
+#include <CoreServices/CoreServices.h>
+#endif
+#include <CoreFoundation/CoreFoundation.h>
+#include <postmac.h>
+
+#include <rtl/ustrbuf.hxx>
+
+#include "nlsupport.hxx"
+
+namespace
+{
+ template <typename T>
+ class CFGuard
+ {
+ public:
+ explicit CFGuard(T& rT) : rT_(rT) {}
+ ~CFGuard() { if (rT_) CFRelease(rT_); }
+ private:
+ T& rT_;
+ };
+
+ typedef CFGuard<CFArrayRef> CFArrayGuard;
+ typedef CFGuard<CFStringRef> CFStringGuard;
+ typedef CFGuard<CFPropertyListRef> CFPropertyListGuard;
+
+ /** Get the current process locale from system
+ */
+ CFStringRef getProcessLocale()
+ {
+ CFPropertyListRef pref = CFPreferencesCopyAppValue(CFSTR("AppleLanguages"), kCFPreferencesCurrentApplication);
+ CFPropertyListGuard proplGuard(pref);
+
+ if (pref == nullptr) // return fallback value 'en_US'
+ return CFStringCreateWithCString(kCFAllocatorDefault, "en_US", kCFStringEncodingASCII);
+
+ CFStringRef sref = (CFGetTypeID(pref) == CFArrayGetTypeID()) ? static_cast<CFStringRef>(CFArrayGetValueAtIndex(static_cast<CFArrayRef>(pref), 0)) : static_cast<CFStringRef>(pref);
+
+ return CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault, sref);
+ }
+
+ void append(OUStringBuffer & buffer, CFStringRef string) {
+ CFIndex n = CFStringGetLength(string);
+ CFStringGetCharacters(
+ string, CFRangeMake(0, n),
+ reinterpret_cast<UniChar *>(buffer.appendUninitialized(n)));
+ }
+}
+
+/** Grab current locale from system.
+*/
+OUString macosx_getLocale()
+{
+ CFStringRef sref = getProcessLocale();
+ CFStringGuard sGuard(sref);
+
+ assert(sref != nullptr && "osxlocale.cxx: getProcessLocale must return a non-NULL value");
+
+ // split the string into substrings; the first two (if there are two) substrings
+ // are language and country
+ CFArrayRef subs = CFStringCreateArrayBySeparatingStrings(nullptr, sref, CFSTR("-"));
+ CFArrayGuard arrGuard(subs);
+
+ OUStringBuffer buf;
+ append(buf, static_cast<CFStringRef>(CFArrayGetValueAtIndex(subs, 0)));
+
+ // country also available? Assumption: if the array contains more than one
+ // value the second value is always the country!
+ if (CFArrayGetCount(subs) > 1)
+ {
+ buf.append("_");
+ append(buf, static_cast<CFStringRef>(CFArrayGetValueAtIndex(subs, 1)));
+ }
+ // Append 'UTF-8' to the locale because the macOS file
+ // system interface is UTF-8 based and sal tries to determine
+ // the file system locale from the locale information
+ buf.append(".UTF-8");
+ return buf.makeStringAndClear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/pipe.cxx b/sal/osl/unx/pipe.cxx
new file mode 100644
index 000000000..45d31e008
--- /dev/null
+++ b/sal/osl/unx/pipe.cxx
@@ -0,0 +1,523 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.hxx"
+
+#include <o3tl/safeint.hxx>
+#include <osl/pipe.h>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/interlck.h>
+#include <rtl/string.h>
+#include <rtl/ustring.h>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+
+#include "sockimpl.hxx"
+#include "secimpl.hxx"
+#include "unixerrnostring.hxx"
+
+#include <cassert>
+#include <cstring>
+
+constexpr OStringLiteral PIPEDEFAULTPATH = "/tmp";
+constexpr OStringLiteral PIPEALTERNATEPATH = "/var/tmp";
+
+static oslPipe osl_psz_createPipe(const char *pszPipeName, oslPipeOptions Options, oslSecurity Security);
+
+struct
+{
+ int errcode;
+ oslPipeError error;
+} const PipeError[]= {
+ { 0, osl_Pipe_E_None }, /* no error */
+ { EPROTOTYPE, osl_Pipe_E_NoProtocol }, /* Protocol wrong type for socket */
+ { ENOPROTOOPT, osl_Pipe_E_NoProtocol }, /* Protocol not available */
+ { EPROTONOSUPPORT, osl_Pipe_E_NoProtocol }, /* Protocol not supported */
+#ifdef ESOCKTNOSUPPORT
+ { ESOCKTNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Socket type not supported */
+#endif
+ { EPFNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Protocol family not supported */
+ { EAFNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Address family not supported by */
+ /* protocol family */
+ { ENETRESET, osl_Pipe_E_NetworkReset }, /* Network dropped connection because */
+ /* of reset */
+ { ECONNABORTED, osl_Pipe_E_ConnectionAbort }, /* Software caused connection abort */
+ { ECONNRESET, osl_Pipe_E_ConnectionReset }, /* Connection reset by peer */
+ { ENOBUFS, osl_Pipe_E_NoBufferSpace }, /* No buffer space available */
+ { ETIMEDOUT, osl_Pipe_E_TimedOut }, /* Connection timed out */
+ { ECONNREFUSED, osl_Pipe_E_ConnectionRefused }, /* Connection refused */
+ { -1, osl_Pipe_E_invalidError }
+};
+
+static oslPipeError osl_PipeErrorFromNative(int nativeType)
+{
+ int i = 0;
+
+ while ((PipeError[i].error != osl_Pipe_E_invalidError) &&
+ (PipeError[i].errcode != nativeType))
+ {
+ i++;
+ }
+
+ return PipeError[i].error;
+}
+
+static oslPipe createPipeImpl()
+{
+ oslPipe pPipeImpl;
+
+ pPipeImpl = static_cast< oslPipe >(calloc(1, sizeof(struct oslPipeImpl)));
+ if (!pPipeImpl)
+ return nullptr;
+
+ pPipeImpl->m_nRefCount = 1;
+ pPipeImpl->m_bClosed = false;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pPipeImpl->m_bIsInShutdown = false;
+ pPipeImpl->m_bIsAccepting = false;
+#endif
+
+ return pPipeImpl;
+}
+
+static void destroyPipeImpl(oslPipe pImpl)
+{
+ if (pImpl)
+ free(pImpl);
+}
+
+oslPipe SAL_CALL osl_createPipe(rtl_uString *ustrPipeName, oslPipeOptions Options, oslSecurity Security)
+{
+ oslPipe pPipe = nullptr;
+ rtl_String* strPipeName = nullptr;
+
+ if (ustrPipeName)
+ {
+ rtl_uString2String(&strPipeName,
+ rtl_uString_getStr(ustrPipeName),
+ rtl_uString_getLength(ustrPipeName),
+ osl_getThreadTextEncoding(),
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+ char* pszPipeName = rtl_string_getStr(strPipeName);
+ pPipe = osl_psz_createPipe(pszPipeName, Options, Security);
+
+ if (strPipeName)
+ rtl_string_release(strPipeName);
+ }
+
+ return pPipe;
+
+}
+
+static OString
+getBootstrapSocketPath()
+{
+ OUString pValue;
+
+ if (rtl::Bootstrap::get("OSL_SOCKET_PATH", pValue))
+ {
+ return OUStringToOString(pValue, RTL_TEXTENCODING_UTF8);
+ }
+ return "";
+}
+
+static oslPipe osl_psz_createPipe(const char *pszPipeName, oslPipeOptions Options,
+ oslSecurity Security)
+{
+ int Flags;
+ size_t len;
+ struct sockaddr_un addr;
+
+ OString name;
+ oslPipe pPipe;
+
+ if (access(PIPEDEFAULTPATH.getStr(), W_OK) == 0)
+ name = PIPEDEFAULTPATH;
+ else if (access(PIPEALTERNATEPATH.getStr(), W_OK) == 0)
+ name = PIPEALTERNATEPATH;
+ else {
+ name = getBootstrapSocketPath ();
+ }
+
+ name += "/";
+
+ if (Security)
+ {
+ char Ident[256];
+
+ Ident[0] = '\0';
+
+ OSL_VERIFY(osl_psz_getUserIdent(Security, Ident, sizeof(Ident)));
+
+ name += OString::Concat("OSL_PIPE_") + Ident + "_" + pszPipeName;
+ }
+ else
+ {
+ name += OString::Concat("OSL_PIPE_") + pszPipeName;
+ }
+
+ if (o3tl::make_unsigned(name.getLength()) >= sizeof addr.sun_path)
+ {
+ SAL_WARN("sal.osl.pipe", "osl_createPipe: pipe name too long");
+ return nullptr;
+ }
+
+ /* alloc memory */
+ pPipe = createPipeImpl();
+
+ if (!pPipe)
+ return nullptr;
+
+ /* create socket */
+ pPipe->m_Socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (pPipe->m_Socket < 0)
+ {
+ SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno));
+ destroyPipeImpl(pPipe);
+ return nullptr;
+ }
+
+ /* set close-on-exec flag */
+ if ((Flags = fcntl(pPipe->m_Socket, F_GETFD, 0)) != -1)
+ {
+ Flags |= FD_CLOEXEC;
+ if (fcntl(pPipe->m_Socket, F_SETFD, Flags) == -1)
+ {
+ SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno));
+ }
+ }
+
+ memset(&addr, 0, sizeof(addr));
+
+ SAL_INFO("sal.osl.pipe", "new pipe on fd " << pPipe->m_Socket << " '" << name << "'");
+
+ addr.sun_family = AF_UNIX;
+ // coverity[fixed_size_dest : FALSE] - safe, see check above
+ strcpy(addr.sun_path, name.getStr());
+#if defined(FREEBSD)
+ len = SUN_LEN(&addr);
+#else
+ len = sizeof(addr);
+#endif
+
+ if (Options & osl_Pipe_CREATE)
+ {
+ struct stat status;
+
+ /* check if there exists an orphan filesystem entry */
+ if ((stat(name.getStr(), &status) == 0) &&
+ (S_ISSOCK(status.st_mode) || S_ISFIFO(status.st_mode)))
+ {
+ if (connect(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) >= 0)
+ {
+ close (pPipe->m_Socket);
+ destroyPipeImpl(pPipe);
+ return nullptr;
+ }
+
+ unlink(name.getStr());
+ }
+
+ /* ok, fs clean */
+ if (bind(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) < 0)
+ {
+ SAL_WARN("sal.osl.pipe", "bind() failed: " << UnixErrnoString(errno));
+ close(pPipe->m_Socket);
+ destroyPipeImpl(pPipe);
+ return nullptr;
+ }
+
+ /* Only give access to all if no security handle was specified, otherwise security
+ depends on umask */
+
+ if (!Security)
+ (void)chmod(name.getStr(),S_IRWXU | S_IRWXG |S_IRWXO);
+
+ strcpy(pPipe->m_Name, name.getStr()); // safe, see check above
+
+ if (listen(pPipe->m_Socket, 5) < 0)
+ {
+ SAL_WARN("sal.osl.pipe", "listen() failed: " << UnixErrnoString(errno));
+ // cid#1255391 warns about unlink(name) after stat(name, &status)
+ // above, but the intervening call to bind makes those two clearly
+ // unrelated, as it would fail if name existed at that point in
+ // time:
+ // coverity[toctou] - this is bogus
+ unlink(name.getStr()); /* remove filesystem entry */
+ close(pPipe->m_Socket);
+ destroyPipeImpl(pPipe);
+ return nullptr;
+ }
+
+ return pPipe;
+ }
+
+ /* osl_pipe_OPEN */
+ if (access(name.getStr(), F_OK) != -1)
+ {
+ if (connect(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) >= 0)
+ return pPipe;
+
+ SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
+ }
+
+ close (pPipe->m_Socket);
+ destroyPipeImpl(pPipe);
+ return nullptr;
+}
+
+void SAL_CALL osl_acquirePipe(oslPipe pPipe)
+{
+ osl_atomic_increment(&(pPipe->m_nRefCount));
+}
+
+void SAL_CALL osl_releasePipe(oslPipe pPipe)
+{
+ if (!pPipe)
+ return;
+
+ if (osl_atomic_decrement(&(pPipe->m_nRefCount)) == 0)
+ {
+ if (!pPipe->m_bClosed)
+ osl_closePipe(pPipe);
+
+ destroyPipeImpl(pPipe);
+ }
+}
+
+void SAL_CALL osl_closePipe(oslPipe pPipe)
+{
+ int nRet;
+ int ConnFD;
+
+ if (!pPipe)
+ return;
+
+ if (pPipe->m_bClosed)
+ return;
+
+ ConnFD = pPipe->m_Socket;
+
+ /* Thread does not return from accept on linux, so
+ connect to the accepting pipe
+ */
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ struct sockaddr_un addr;
+
+ if (pPipe->m_bIsAccepting)
+ {
+ pPipe->m_bIsInShutdown = true;
+ pPipe->m_Socket = -1;
+
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ {
+ SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno));
+ return;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+
+ SAL_INFO("sal.osl.pipe", "osl_destroyPipe : Pipe Name '" << pPipe->m_Name << "'");
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, pPipe->m_Name); // safe, as both are same size
+
+ nRet = connect(fd, reinterpret_cast< sockaddr* >(&addr), sizeof(addr));
+ if (nRet < 0)
+ SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
+
+ close(fd);
+ }
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+
+ nRet = shutdown(ConnFD, 2);
+ if (nRet < 0)
+ SAL_WARN("sal.osl.pipe", "shutdown() failed: " << UnixErrnoString(errno));
+
+ nRet = close(ConnFD);
+ if (nRet < 0)
+ SAL_WARN("sal.osl.pipe", "close() failed: " << UnixErrnoString(errno));
+
+ /* remove filesystem entry */
+ if (pPipe->m_Name[0] != '\0')
+ unlink(pPipe->m_Name);
+
+ pPipe->m_bClosed = true;
+}
+
+oslPipe SAL_CALL osl_acceptPipe(oslPipe pPipe)
+{
+ int s;
+ oslPipe pAcceptedPipe;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "invalid pipe");
+ if (!pPipe)
+ return nullptr;
+
+ assert(pPipe->m_Name[0] != '\0'); // you cannot have an empty pipe name
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pPipe->m_bIsAccepting = true;
+#endif
+
+ s = accept(pPipe->m_Socket, nullptr, nullptr);
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pPipe->m_bIsAccepting = false;
+#endif
+
+ if (s < 0)
+ {
+ SAL_WARN("sal.osl.pipe", "accept() failed: " << UnixErrnoString(errno));
+ return nullptr;
+ }
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ if (pPipe->m_bIsInShutdown)
+ {
+ close(s);
+ return nullptr;
+ }
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+
+ /* alloc memory */
+ pAcceptedPipe = createPipeImpl();
+
+ assert(pAcceptedPipe); // should never be the case that an oslPipe cannot be initialized
+ if (!pAcceptedPipe)
+ {
+ close(s);
+ return nullptr;
+ }
+
+ /* set close-on-exec flag */
+ int flags;
+ if ((flags = fcntl(s, F_GETFD, 0)) >= 0)
+ {
+ flags |= FD_CLOEXEC;
+ if (fcntl(s, F_SETFD, flags) < 0)
+ SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno));
+ }
+
+ pAcceptedPipe->m_Socket = s;
+
+ return pAcceptedPipe;
+}
+
+sal_Int32 SAL_CALL osl_receivePipe(oslPipe pPipe,
+ void* pBuffer,
+ sal_Int32 BytesToRead)
+{
+ int nRet = 0;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_receivePipe: invalid pipe");
+ if (!pPipe)
+ {
+ SAL_WARN("sal.osl.pipe", "osl_receivePipe: Invalid socket");
+ errno=EINVAL;
+ return -1;
+ }
+
+ nRet = recv(pPipe->m_Socket, pBuffer, BytesToRead, 0);
+
+ if (nRet < 0)
+ SAL_WARN("sal.osl.pipe", "recv() failed: " << UnixErrnoString(errno));
+
+ return nRet;
+}
+
+sal_Int32 SAL_CALL osl_sendPipe(oslPipe pPipe,
+ const void* pBuffer,
+ sal_Int32 BytesToSend)
+{
+ int nRet=0;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_sendPipe: invalid pipe");
+ if (!pPipe)
+ {
+ SAL_WARN("sal.osl.pipe", "osl_sendPipe: Invalid socket");
+ errno=EINVAL;
+ return -1;
+ }
+
+ nRet = send(pPipe->m_Socket, pBuffer, BytesToSend, 0);
+
+ if (nRet <= 0)
+ SAL_WARN("sal.osl.pipe", "send() failed: " << UnixErrnoString(errno));
+
+ return nRet;
+}
+
+oslPipeError SAL_CALL osl_getLastPipeError(SAL_UNUSED_PARAMETER oslPipe)
+{
+ return osl_PipeErrorFromNative(errno);
+}
+
+sal_Int32 SAL_CALL osl_writePipe(oslPipe pPipe, const void *pBuffer, sal_Int32 n)
+{
+ /* loop until all desired bytes were send or an error occurred */
+ sal_Int32 BytesSend = 0;
+ sal_Int32 BytesToSend = n;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_writePipe: invalid pipe"); // osl_sendPipe detects invalid pipe
+ while (BytesToSend > 0)
+ {
+ sal_Int32 RetVal;
+
+ RetVal= osl_sendPipe(pPipe, pBuffer, BytesToSend);
+
+ /* error occurred? */
+ if (RetVal <= 0)
+ break;
+
+ BytesToSend -= RetVal;
+ BytesSend += RetVal;
+ pBuffer= static_cast< char const* >(pBuffer) + RetVal;
+ }
+
+ return BytesSend;
+}
+
+sal_Int32 SAL_CALL osl_readPipe( oslPipe pPipe, void *pBuffer , sal_Int32 n )
+{
+ /* loop until all desired bytes were read or an error occurred */
+ sal_Int32 BytesRead = 0;
+ sal_Int32 BytesToRead = n;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_readPipe: invalid pipe"); // osl_receivePipe detects invalid pipe
+ while (BytesToRead > 0)
+ {
+ sal_Int32 RetVal;
+ RetVal= osl_receivePipe(pPipe, pBuffer, BytesToRead);
+
+ /* error occurred? */
+ if (RetVal <= 0)
+ break;
+
+ BytesToRead -= RetVal;
+ BytesRead += RetVal;
+ pBuffer= static_cast< char* >(pBuffer) + RetVal;
+ }
+
+ return BytesRead;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/process.cxx b/sal/osl/unx/process.cxx
new file mode 100644
index 000000000..f75534fdc
--- /dev/null
+++ b/sal/osl/unx/process.cxx
@@ -0,0 +1,1194 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+
+#include <cassert>
+
+/*
+ * ToDo:
+ * - cleanup of process status things
+ * - cleanup of process spawning
+ * - cleanup of resource transfer
+ */
+
+#if defined(__sun)
+ // The procfs may only be used without LFS in 32bits.
+# ifdef _FILE_OFFSET_BITS
+# undef _FILE_OFFSET_BITS
+# endif
+#endif
+
+#if defined(FREEBSD) || defined(NETBSD) || defined(DRAGONFLY)
+#include <machine/param.h>
+#endif
+
+#include "system.hxx"
+#include "unixerrnostring.hxx"
+#if defined(__sun)
+# include <sys/procfs.h>
+#endif
+#include <osl/diagnose.h>
+#include <osl/mutex.h>
+#include <osl/process.h>
+#include <osl/conditn.h>
+#include <osl/thread.h>
+#include <osl/file.h>
+#include <osl/file.hxx>
+#include <osl/signal.h>
+#include <rtl/alloc.h>
+#include <sal/log.hxx>
+
+#include <grp.h>
+
+#include "createfilehandlefromfd.hxx"
+#include "file_url.hxx"
+#include "readwrite_helper.hxx"
+#include "sockimpl.hxx"
+#include "secimpl.hxx"
+
+#define MAX_ARGS 255
+#define MAX_ENVS 255
+
+namespace
+{
+
+struct oslProcessImpl {
+ pid_t m_pid;
+ oslCondition m_terminated;
+ int m_status;
+ oslProcessImpl* m_pnext;
+};
+
+struct ProcessData
+{
+ const char* m_pszArgs[MAX_ARGS + 1];
+ const char* m_pszDir;
+ char* m_pszEnv[MAX_ENVS + 1];
+ uid_t m_uid;
+ gid_t m_gid;
+ char* m_name;
+ oslCondition m_started;
+ oslProcessImpl* m_pProcImpl;
+ oslFileHandle *m_pInputWrite;
+ oslFileHandle *m_pOutputRead;
+ oslFileHandle *m_pErrorRead;
+};
+
+oslProcessImpl* ChildList;
+oslMutex ChildListMutex;
+
+} //Anonymous namespace
+
+static oslProcessError osl_psz_executeProcess(char *pszImageName,
+ char *pszArguments[],
+ oslProcessOption Options,
+ oslSecurity Security,
+ char *pszDirectory,
+ char *pszEnvironments[],
+ oslProcess *pProcess,
+ oslFileHandle *pInputWrite,
+ oslFileHandle *pOutputRead,
+ oslFileHandle *pErrorRead );
+
+extern "C" {
+
+static void ChildStatusProc(void *pData)
+{
+ osl_setThreadName("osl_executeProcess");
+
+ pid_t pid = -1;
+ int status = 0;
+ int channel[2] = { -1, -1 };
+ ProcessData data;
+ ProcessData *pdata;
+ int stdOutput[2] = { -1, -1 }, stdInput[2] = { -1, -1 }, stdError[2] = { -1, -1 };
+
+ pdata = static_cast<ProcessData *>(pData);
+
+ /* make a copy of our data, because forking will only copy
+ our local stack of the thread, so the process data will not be accessible
+ in our child process */
+ memcpy(&data, pData, sizeof(data));
+
+#ifdef NO_CHILD_PROCESSES
+#define fork() (errno = EINVAL, -1)
+#endif
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == -1)
+ {
+ status = errno;
+ SAL_WARN("sal.osl", "executeProcess socketpair() errno " << status);
+ }
+
+ (void) fcntl(channel[0], F_SETFD, FD_CLOEXEC);
+ (void) fcntl(channel[1], F_SETFD, FD_CLOEXEC);
+
+ /* Create redirected IO pipes */
+ if ( status == 0 && data.m_pInputWrite && pipe( stdInput ) == -1 )
+ {
+ status = errno;
+ assert(status != 0);
+ SAL_WARN("sal.osl", "executeProcess pipe(stdInput) errno " << status);
+ }
+
+ if ( status == 0 && data.m_pOutputRead && pipe( stdOutput ) == -1 )
+ {
+ status = errno;
+ assert(status != 0);
+ SAL_WARN("sal.osl", "executeProcess pipe(stdOutput) errno " << status);
+ }
+
+ if ( status == 0 && data.m_pErrorRead && pipe( stdError ) == -1 )
+ {
+ status = errno;
+ assert(status != 0);
+ SAL_WARN("sal.osl", "executeProcess pipe(stdError) errno " << status);
+ }
+
+ if ( (status == 0) && ((pid = fork()) == 0) )
+ {
+ /* Child */
+ int chstatus = 0;
+ int errno_copy;
+
+ if (channel[0] != -1) close(channel[0]);
+
+ if ((data.m_uid != uid_t(-1)) && ((data.m_uid != getuid()) || (data.m_gid != getgid())))
+ {
+ OSL_ASSERT(geteuid() == 0); /* must be root */
+
+ if (! INIT_GROUPS(data.m_name, data.m_gid) || (setuid(data.m_uid) != 0))
+ {
+ // ignore; can't do much about it here after fork
+ }
+
+ unsetenv("HOME");
+ }
+
+ if (data.m_pszDir)
+ chstatus = chdir(data.m_pszDir);
+
+ if (chstatus == 0 && ((data.m_uid == uid_t(-1)) || ((data.m_uid == getuid()) && (data.m_gid == getgid()))))
+ {
+ int i;
+ for (i = 0; data.m_pszEnv[i] != nullptr; i++)
+ {
+ if (strchr(data.m_pszEnv[i], '=') == nullptr)
+ {
+ unsetenv(data.m_pszEnv[i]); /*TODO: check error return*/
+ }
+ else
+ {
+ putenv(data.m_pszEnv[i]); /*TODO: check error return*/
+ }
+ }
+
+ /* Connect std IO to pipe ends */
+
+ /* Write end of stdInput not used in child process */
+ if (stdInput[1] != -1) close( stdInput[1] );
+
+ /* Read end of stdOutput not used in child process */
+ if (stdOutput[0] != -1) close( stdOutput[0] );
+
+ /* Read end of stdError not used in child process */
+ if (stdError[0] != -1) close( stdError[0] );
+
+ /* Redirect pipe ends to std IO */
+
+ if ( stdInput[0] != STDIN_FILENO )
+ {
+ dup2( stdInput[0], STDIN_FILENO );
+ if (stdInput[0] != -1) close( stdInput[0] );
+ }
+
+ if ( stdOutput[1] != STDOUT_FILENO )
+ {
+ dup2( stdOutput[1], STDOUT_FILENO );
+ if (stdOutput[1] != -1) close( stdOutput[1] );
+ }
+
+ if ( stdError[1] != STDERR_FILENO )
+ {
+ dup2( stdError[1], STDERR_FILENO );
+ if (stdError[1] != -1) close( stdError[1] );
+ }
+
+ // No need to check the return value of execv. If we return from
+ // it, an error has occurred.
+ execv(data.m_pszArgs[0], const_cast<char **>(data.m_pszArgs));
+ }
+
+ /* if we reach here, something went wrong */
+ errno_copy = errno;
+ if ( !safeWrite(channel[1], &errno_copy, sizeof(errno_copy)) )
+ {
+ // ignore; can't do much about it here after fork
+ }
+
+ if ( channel[1] != -1 )
+ close(channel[1]);
+
+ _exit(255);
+ }
+ else
+ { /* Parent */
+ int i = -1;
+ if (channel[1] != -1) close(channel[1]);
+
+ /* Close unused pipe ends */
+ if (stdInput[0] != -1) close( stdInput[0] );
+ if (stdOutput[1] != -1) close( stdOutput[1] );
+ if (stdError[1] != -1) close( stdError[1] );
+
+ if (pid > 0)
+ {
+ while ((i = read(channel[0], &status, sizeof(status))) < 0)
+ {
+ if (errno != EINTR)
+ break;
+ }
+ }
+
+ if (channel[0] != -1) close(channel[0]);
+
+ if ((pid > 0) && (i == 0))
+ {
+ pid_t child_pid;
+ osl_acquireMutex(ChildListMutex);
+
+ pdata->m_pProcImpl->m_pid = pid;
+ pdata->m_pProcImpl->m_pnext = ChildList;
+ ChildList = pdata->m_pProcImpl;
+
+ /* Store used pipe ends in data structure */
+
+ if ( pdata->m_pInputWrite )
+ *(pdata->m_pInputWrite) = osl::detail::createFileHandleFromFD( stdInput[1] );
+
+ if ( pdata->m_pOutputRead )
+ *(pdata->m_pOutputRead) = osl::detail::createFileHandleFromFD( stdOutput[0] );
+
+ if ( pdata->m_pErrorRead )
+ *(pdata->m_pErrorRead) = osl::detail::createFileHandleFromFD( stdError[0] );
+
+ osl_releaseMutex(ChildListMutex);
+
+ osl_setCondition(pdata->m_started);
+
+ do
+ {
+ child_pid = waitpid(pid, &status, 0);
+ } while ( 0 > child_pid && EINTR == errno );
+
+ if ( child_pid < 0)
+ {
+ SAL_WARN("sal.osl", "Failed to wait for child process: " << UnixErrnoString(errno));
+
+ /*
+ We got another error than EINTR. Anyway we have to wake up the
+ waiting thread under any circumstances */
+
+ child_pid = pid;
+ }
+
+ if ( child_pid > 0 )
+ {
+ oslProcessImpl* pChild;
+
+ osl_acquireMutex(ChildListMutex);
+
+ pChild = ChildList;
+
+ /* check if it is one of our child processes */
+ while (pChild != nullptr)
+ {
+ if (pChild->m_pid == child_pid)
+ {
+ if (WIFEXITED(status))
+ pChild->m_status = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status))
+ pChild->m_status = 128 + WTERMSIG(status);
+ else
+ pChild->m_status = -1;
+
+ osl_setCondition(pChild->m_terminated);
+ }
+
+ pChild = pChild->m_pnext;
+ }
+
+ osl_releaseMutex(ChildListMutex);
+ }
+ }
+ else
+ {
+ SAL_WARN("sal.osl", "ChildStatusProc : starting '" << data.m_pszArgs[0] << "' failed");
+ SAL_WARN("sal.osl", "Failed to launch child process, child reports " << UnixErrnoString(status));
+
+ /* Close pipe ends */
+ if ( pdata->m_pInputWrite )
+ *pdata->m_pInputWrite = nullptr;
+
+ if ( pdata->m_pOutputRead )
+ *pdata->m_pOutputRead = nullptr;
+
+ if ( pdata->m_pErrorRead )
+ *pdata->m_pErrorRead = nullptr;
+
+ if (stdInput[1] != -1) close( stdInput[1] );
+ if (stdOutput[0] != -1) close( stdOutput[0] );
+ if (stdError[0] != -1) close( stdError[0] );
+
+ /* if pid > 0 then a process was created, even if it later failed
+ e.g. bash searching for a command to execute, and we still
+ need to clean it up to avoid "defunct" processes */
+ if (pid > 0)
+ {
+ pid_t child_pid;
+ do
+ {
+ child_pid = waitpid(pid, &status, 0);
+ } while ( 0 > child_pid && EINTR == errno );
+ }
+
+ /* notify (and unblock) parent thread */
+ osl_setCondition(pdata->m_started);
+ }
+ }
+}
+
+}
+
+oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO(
+ rtl_uString *ustrImageName,
+ rtl_uString *ustrArguments[],
+ sal_uInt32 nArguments,
+ oslProcessOption Options,
+ oslSecurity Security,
+ rtl_uString *ustrWorkDir,
+ rtl_uString *ustrEnvironment[],
+ sal_uInt32 nEnvironmentVars,
+ oslProcess *pProcess,
+ oslFileHandle *pInputWrite,
+ oslFileHandle *pOutputRead,
+ oslFileHandle *pErrorRead
+ )
+{
+ OUString image;
+ if (ustrImageName == nullptr)
+ {
+ if (nArguments == 0)
+ {
+ return osl_Process_E_InvalidError;
+ }
+ image = OUString::unacquired(ustrArguments);
+ }
+ else
+ {
+ osl::FileBase::RC e = osl::FileBase::getSystemPathFromFileURL(
+ OUString::unacquired(&ustrImageName), image);
+ if (e != osl::FileBase::E_None)
+ {
+ SAL_INFO(
+ "sal.osl",
+ "getSystemPathFromFileURL("
+ << OUString::unacquired(&ustrImageName)
+ << ") failed with " << e);
+ return osl_Process_E_Unknown;
+ }
+ }
+
+ if ((Options & osl_Process_SEARCHPATH) != 0)
+ {
+ OUString path;
+ if (osl::detail::find_in_PATH(image, path))
+ {
+ image = path;
+ }
+ }
+
+ oslProcessError Error;
+ char* pszWorkDir=nullptr;
+ char** pArguments=nullptr;
+ char** pEnvironment=nullptr;
+ unsigned int idx;
+
+ char szImagePath[PATH_MAX] = "";
+ if (!image.isEmpty()
+ && (UnicodeToText(
+ szImagePath, SAL_N_ELEMENTS(szImagePath), image.getStr(),
+ image.getLength())
+ == 0))
+ {
+ int e = errno;
+ SAL_INFO("sal.osl", "UnicodeToText(" << image << ") failed with " << e);
+ return osl_Process_E_Unknown;
+ }
+
+ char szWorkDir[PATH_MAX] = "";
+ if ( ustrWorkDir != nullptr && ustrWorkDir->length )
+ {
+ oslFileError e = FileURLToPath( szWorkDir, PATH_MAX, ustrWorkDir );
+ if (e != osl_File_E_None)
+ {
+ SAL_INFO(
+ "sal.osl",
+ "FileURLToPath(" << OUString::unacquired(&ustrWorkDir)
+ << ") failed with " << e);
+ return osl_Process_E_Unknown;
+ }
+ pszWorkDir = szWorkDir;
+ }
+
+ if ( pArguments == nullptr && nArguments > 0 )
+ {
+ pArguments = static_cast<char**>(malloc( ( nArguments + 2 ) * sizeof(char*) ));
+ }
+
+ for ( idx = 0 ; idx < nArguments ; ++idx )
+ {
+ rtl_String* strArg =nullptr;
+
+ rtl_uString2String( &strArg,
+ rtl_uString_getStr(ustrArguments[idx]),
+ rtl_uString_getLength(ustrArguments[idx]),
+ osl_getThreadTextEncoding(),
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+
+ pArguments[idx]=strdup(rtl_string_getStr(strArg));
+ rtl_string_release(strArg);
+ pArguments[idx+1]=nullptr;
+ }
+
+ for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
+ {
+ rtl_String* strEnv=nullptr;
+
+ if ( pEnvironment == nullptr )
+ {
+ pEnvironment = static_cast<char**>(malloc( ( nEnvironmentVars + 2 ) * sizeof(char*) ));
+ }
+
+ rtl_uString2String( &strEnv,
+ rtl_uString_getStr(ustrEnvironment[idx]),
+ rtl_uString_getLength(ustrEnvironment[idx]),
+ osl_getThreadTextEncoding(),
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+
+ pEnvironment[idx]=strdup(rtl_string_getStr(strEnv));
+ rtl_string_release(strEnv);
+ pEnvironment[idx+1]=nullptr;
+ }
+
+ Error = osl_psz_executeProcess(szImagePath,
+ pArguments,
+ Options,
+ Security,
+ pszWorkDir,
+ pEnvironment,
+ pProcess,
+ pInputWrite,
+ pOutputRead,
+ pErrorRead
+ );
+
+ if ( pArguments != nullptr )
+ {
+ for ( idx = 0 ; idx < nArguments ; ++idx )
+ {
+ if ( pArguments[idx] != nullptr )
+ {
+ free(pArguments[idx]);
+ }
+ }
+ free(pArguments);
+ }
+
+ if ( pEnvironment != nullptr )
+ {
+ for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
+ {
+ if ( pEnvironment[idx] != nullptr )
+ {
+ free(pEnvironment[idx]);
+ }
+ }
+ free(pEnvironment);
+ }
+
+ return Error;
+}
+
+oslProcessError SAL_CALL osl_executeProcess(
+ rtl_uString *ustrImageName,
+ rtl_uString *ustrArguments[],
+ sal_uInt32 nArguments,
+ oslProcessOption Options,
+ oslSecurity Security,
+ rtl_uString *ustrWorkDir,
+ rtl_uString *ustrEnvironment[],
+ sal_uInt32 nEnvironmentVars,
+ oslProcess *pProcess
+ )
+{
+ return osl_executeProcess_WithRedirectedIO(
+ ustrImageName,
+ ustrArguments,
+ nArguments,
+ Options,
+ Security,
+ ustrWorkDir,
+ ustrEnvironment,
+ nEnvironmentVars,
+ pProcess,
+ nullptr,
+ nullptr,
+ nullptr
+ );
+}
+
+oslProcessError osl_psz_executeProcess(char *pszImageName,
+ char *pszArguments[],
+ oslProcessOption Options,
+ oslSecurity Security,
+ char *pszDirectory,
+ char *pszEnvironments[],
+ oslProcess *pProcess,
+ oslFileHandle *pInputWrite,
+ oslFileHandle *pOutputRead,
+ oslFileHandle *pErrorRead
+ )
+{
+ int i;
+ ProcessData Data;
+ oslThread hThread;
+
+ memset(&Data,0,sizeof(ProcessData));
+ Data.m_pInputWrite = pInputWrite;
+ Data.m_pOutputRead = pOutputRead;
+ Data.m_pErrorRead = pErrorRead;
+
+ OSL_ASSERT(pszImageName != nullptr);
+
+ if ( pszImageName == nullptr )
+ {
+ return osl_Process_E_NotFound;
+ }
+
+ Data.m_pszArgs[0] = strdup(pszImageName);
+ Data.m_pszArgs[1] = nullptr;
+
+ if ( pszArguments != nullptr )
+ {
+ for (i = 0; ((i + 2) < MAX_ARGS) && (pszArguments[i] != nullptr); i++)
+ Data.m_pszArgs[i+1] = strdup(pszArguments[i]);
+ Data.m_pszArgs[i+2] = nullptr;
+ }
+
+ Data.m_pszDir = (pszDirectory != nullptr) ? strdup(pszDirectory) : nullptr;
+
+ if (pszEnvironments != nullptr)
+ {
+ for (i = 0; ((i + 1) < MAX_ENVS) && (pszEnvironments[i] != nullptr); i++)
+ Data.m_pszEnv[i] = strdup(pszEnvironments[i]);
+ Data.m_pszEnv[i+1] = nullptr;
+ }
+ else
+ Data.m_pszEnv[0] = nullptr;
+
+ if (Security != nullptr)
+ {
+ Data.m_uid = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_uid;
+ Data.m_gid = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_gid;
+ Data.m_name = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_name;
+ }
+ else
+ Data.m_uid = uid_t(-1);
+
+ Data.m_pProcImpl = static_cast<oslProcessImpl*>(malloc(sizeof(oslProcessImpl)));
+ Data.m_pProcImpl->m_pid = 0;
+ Data.m_pProcImpl->m_terminated = osl_createCondition();
+ Data.m_pProcImpl->m_pnext = nullptr;
+
+ if (ChildListMutex == nullptr)
+ ChildListMutex = osl_createMutex();
+
+ Data.m_started = osl_createCondition();
+
+ hThread = osl_createThread(ChildStatusProc, &Data);
+
+ if (hThread != nullptr)
+ {
+ osl_waitCondition(Data.m_started, nullptr);
+ }
+ osl_destroyCondition(Data.m_started);
+
+ for (i = 0; Data.m_pszArgs[i] != nullptr; i++)
+ free(const_cast<char *>(Data.m_pszArgs[i]));
+
+ for (i = 0; Data.m_pszEnv[i] != nullptr; i++)
+ free(Data.m_pszEnv[i]);
+
+ if ( Data.m_pszDir != nullptr )
+ {
+ free(const_cast<char *>(Data.m_pszDir));
+ }
+
+ osl_destroyThread(hThread);
+
+ if (Data.m_pProcImpl->m_pid != 0)
+ {
+ assert(hThread != nullptr);
+
+ *pProcess = Data.m_pProcImpl;
+
+ if (Options & osl_Process_WAIT)
+ osl_joinProcess(*pProcess);
+
+ return osl_Process_E_None;
+ }
+
+ osl_destroyCondition(Data.m_pProcImpl->m_terminated);
+ free(Data.m_pProcImpl);
+
+ return osl_Process_E_Unknown;
+}
+
+oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process)
+{
+ if (Process == nullptr)
+ return osl_Process_E_Unknown;
+
+ if (kill(static_cast<oslProcessImpl*>(Process)->m_pid, SIGKILL) != 0)
+ {
+ switch (errno)
+ {
+ case EPERM:
+ return osl_Process_E_NoPermission;
+
+ case ESRCH:
+ return osl_Process_E_NotFound;
+
+ default:
+ return osl_Process_E_Unknown;
+ }
+ }
+
+ return osl_Process_E_None;
+}
+
+oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident)
+{
+ oslProcessImpl *pProcImpl;
+
+ if (kill(Ident, 0) != -1)
+ {
+ oslProcessImpl* pChild;
+
+ if (ChildListMutex == nullptr)
+ ChildListMutex = osl_createMutex();
+
+ osl_acquireMutex(ChildListMutex);
+
+ pChild = ChildList;
+
+ /* check if it is one of our child processes */
+ while (pChild != nullptr)
+ {
+ if (Ident == static_cast<sal_uInt32>(pChild->m_pid))
+ break;
+
+ pChild = pChild->m_pnext;
+ }
+
+ pProcImpl = static_cast<oslProcessImpl*>(malloc(sizeof(oslProcessImpl)));
+ pProcImpl->m_pid = Ident;
+ pProcImpl->m_terminated = osl_createCondition();
+
+ if (pChild != nullptr)
+ {
+ /* process is a child so insert into list */
+ pProcImpl->m_pnext = pChild->m_pnext;
+ pChild->m_pnext = pProcImpl;
+
+ pProcImpl->m_status = pChild->m_status;
+
+ if (osl_checkCondition(pChild->m_terminated))
+ osl_setCondition(pProcImpl->m_terminated);
+ }
+ else
+ pProcImpl->m_pnext = nullptr;
+
+ osl_releaseMutex(ChildListMutex);
+ }
+ else
+ pProcImpl = nullptr;
+
+ return pProcImpl;
+}
+
+void SAL_CALL osl_freeProcessHandle(oslProcess Process)
+{
+ if (Process == nullptr)
+ return;
+
+ oslProcessImpl *pChild, *pPrev = nullptr;
+
+ OSL_ASSERT(ChildListMutex != nullptr);
+
+ if ( ChildListMutex == nullptr )
+ {
+ return;
+ }
+
+ osl_acquireMutex(ChildListMutex);
+
+ pChild = ChildList;
+
+ /* remove process from child list */
+ while (pChild != nullptr)
+ {
+ if (pChild == static_cast<oslProcessImpl*>(Process))
+ {
+ if (pPrev != nullptr)
+ pPrev->m_pnext = pChild->m_pnext;
+ else
+ ChildList = pChild->m_pnext;
+
+ break;
+ }
+
+ pPrev = pChild;
+ pChild = pChild->m_pnext;
+ }
+
+ osl_releaseMutex(ChildListMutex);
+
+ osl_destroyCondition(static_cast<oslProcessImpl*>(Process)->m_terminated);
+
+ free(Process);
+}
+
+#if defined(LINUX)
+namespace {
+
+struct osl_procStat
+{
+ /* from 'stat' */
+ pid_t pid; /* pid */
+ char command[16]; /* 'argv[0]' */ /* mfe: it all right char comm[16] in kernel! */
+ char state; /* state (running, stopped, ...) */
+ pid_t ppid; /* parent pid */
+ pid_t pgrp; /* parent group */
+ int session; /* session ID */
+ int tty; /* no of tty */
+ pid_t tpgid; /* group of process owning the tty */
+ unsigned long flags; /* flags dunno */
+ unsigned long minflt; /* minor page faults */
+ unsigned long cminflt; /* minor page faults with children */
+ unsigned long majflt; /* major page faults */
+ unsigned long cmajflt; /* major page faults with children */
+ unsigned long utime; /* no of jiffies in user mode */
+ unsigned long stime; /* no of jiffies in kernel mode */
+ unsigned long cutime; /* no of jiffies in user mode with children */
+ unsigned long cstime; /* no of jiffies in kernel mode with children */
+ unsigned long priority; /* nice value + 15 (kernel scheduling prio)*/
+ long nice; /* nice value */
+ long timeout; /* no of jiffies of next process timeout */
+ long itrealvalue; /* no jiffies before next SIGALRM */
+ unsigned long starttime; /* process started this no of jiffies after boot */
+ unsigned long vsize; /* virtual memory size (in bytes) */
+ long rss; /* resident set size (in pages) */
+ unsigned long rss_rlim; /* rss limit (in bytes) */
+ unsigned long startcode; /* address above program text can run */
+ unsigned long endcode; /* address below program text can run */
+ unsigned long startstack; /* address of start of stack */
+ unsigned long kstkesp; /* current value of 'esp' (stack pointer) */
+ unsigned long kstkeip; /* current value of 'eip' (instruction pointer) */
+ /* mfe: Linux > 2.1.7x have more signals (88) */
+ char signal[24]; /* pending signals */
+ char blocked[24]; /* blocked signals */
+ char sigignore[24]; /* ignored signals */
+ char sigcatch[24]; /* caught signals */
+ unsigned long wchan; /* 'channel' the process is waiting in */
+ unsigned long nswap; /* ? */
+ unsigned long cnswap; /* ? */
+
+ /* from 'status' */
+ int ruid; /* real uid */
+ int euid; /* effective uid */
+ int suid; /* saved uid */
+ int fuid; /* file access uid */
+ int rgid; /* real gid */
+ int egid; /* effective gid */
+ int sgid; /* saved gid */
+ int fgid; /* file access gid */
+ unsigned long vm_size; /* like vsize but on kb */
+ unsigned long vm_lock; /* locked pages in kb */
+ unsigned long vm_rss; /* like rss but in kb */
+ unsigned long vm_data; /* data size */
+ unsigned long vm_stack; /* stack size */
+ unsigned long vm_exe; /* executable size */
+ unsigned long vm_lib; /* library size */
+};
+
+}
+
+static bool osl_getProcStat(pid_t pid, struct osl_procStat* procstat)
+{
+ int fd = 0;
+ bool bRet = false;
+ char name[PATH_MAX + 1];
+ snprintf(name, sizeof(name), "/proc/%u/stat", pid);
+
+ if ((fd = open(name,O_RDONLY)) >=0 )
+ {
+ char* tmp=nullptr;
+ char prstatbuf[512];
+ memset(prstatbuf,0,512);
+ bRet = safeRead(fd, prstatbuf, 511);
+
+ close(fd);
+
+ if (!bRet)
+ return false;
+
+ tmp = strrchr(prstatbuf, ')');
+ if(tmp)
+ {
+ *tmp = '\0';
+
+ memset(procstat->command, 0, sizeof(procstat->command));
+
+ sscanf(prstatbuf, "%d (%15c", &procstat->pid, procstat->command);
+ sscanf(tmp + 2,
+ "%c"
+ "%i %i %i %i %i"
+ "%lu %lu %lu %lu %lu"
+ "%lu %lu %lu %lu"
+ "%lu %li %li %li"
+ "%lu %lu %li %lu"
+ "%lu %lu %lu %lu %lu"
+ "%23s %23s %23s %23s"
+ "%lu %lu %lu",
+ &procstat->state,
+ &procstat->ppid, &procstat->pgrp, &procstat->session, &procstat->tty, &procstat->tpgid,
+ &procstat->flags, &procstat->minflt, &procstat->cminflt, &procstat->majflt, &procstat->cmajflt,
+ &procstat->utime, &procstat->stime, &procstat->cutime, &procstat->cstime,
+ &procstat->priority, &procstat->nice, &procstat->timeout, &procstat->itrealvalue,
+ &procstat->starttime, &procstat->vsize, &procstat->rss, &procstat->rss_rlim,
+ &procstat->startcode, &procstat->endcode, &procstat->startstack, &procstat->kstkesp, &procstat->kstkeip,
+ procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch,
+ &procstat->wchan, &procstat->nswap, &procstat->cnswap
+ );
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ return bRet;
+}
+
+static bool osl_getProcStatus(pid_t pid, struct osl_procStat* procstat)
+{
+ int fd = 0;
+ char name[PATH_MAX + 1];
+ bool bRet = false;
+
+ snprintf(name, sizeof(name), "/proc/%u/status", pid);
+
+ if ((fd = open(name,O_RDONLY)) >=0 )
+ {
+ char* tmp=nullptr;
+ char prstatusbuf[512];
+ memset(prstatusbuf,0,512);
+ bRet = safeRead(fd, prstatusbuf, 511);
+
+ close(fd);
+
+ if (!bRet)
+ return false;
+
+ tmp = strstr(prstatusbuf,"Uid:");
+ if(tmp)
+ {
+ sscanf(tmp,"Uid:\t%d\t%d\t%d\t%d",
+ &procstat->ruid, &procstat->euid, &procstat->suid, &procstat->fuid
+ );
+ }
+
+ tmp = strstr(prstatusbuf,"Gid:");
+ if(tmp)
+ {
+ sscanf(tmp,"Gid:\t%d\t%d\t%d\t%d",
+ &procstat->rgid, &procstat->egid, &procstat->sgid, &procstat->fgid
+ );
+ }
+
+ tmp = strstr(prstatusbuf,"VmSize:");
+ if(tmp)
+ {
+ sscanf(tmp,
+ "VmSize: %lu kB\n"
+ "VmLck: %lu kB\n"
+ "VmRSS: %lu kB\n"
+ "VmData: %lu kB\n"
+ "VmStk: %lu kB\n"
+ "VmExe: %lu kB\n"
+ "VmLib: %lu kB\n",
+ &procstat->vm_size, &procstat->vm_lock, &procstat->vm_rss, &procstat->vm_data,
+ &procstat->vm_stack, &procstat->vm_exe, &procstat->vm_lib
+ );
+ }
+
+ tmp = strstr(prstatusbuf,"SigPnd:");
+ if(tmp)
+ {
+ sscanf(tmp, "SigPnd: %23s SigBlk: %23s SigIgn: %23s %*s %23s",
+ procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch
+ );
+ }
+ }
+ return bRet;
+}
+
+#endif
+
+oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields, oslProcessInfo* pInfo)
+{
+ pid_t pid;
+
+ if (Process == nullptr)
+ pid = getpid();
+ else
+ pid = static_cast<oslProcessImpl*>(Process)->m_pid;
+
+ if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo)))
+ return osl_Process_E_Unknown;
+
+ pInfo->Fields = 0;
+
+ if (Fields & osl_Process_IDENTIFIER)
+ {
+ pInfo->Ident = pid;
+ pInfo->Fields |= osl_Process_IDENTIFIER;
+ }
+
+ if (Fields & osl_Process_EXITCODE)
+ {
+ if ((Process != nullptr) &&
+ osl_checkCondition(static_cast<oslProcessImpl*>(Process)->m_terminated))
+ {
+ pInfo->Code = static_cast<oslProcessImpl*>(Process)->m_status;
+ pInfo->Fields |= osl_Process_EXITCODE;
+ }
+ }
+
+ if (Fields & (osl_Process_HEAPUSAGE | osl_Process_CPUTIMES))
+ {
+
+#if defined(__sun)
+
+ int fd;
+ char name[PATH_MAX + 1];
+
+ snprintf(name, sizeof(name), "/proc/%ld", (long)pid);
+
+ if ((fd = open(name, O_RDONLY)) >= 0)
+ {
+ prstatus_t prstatus;
+
+ if (ioctl(fd, PIOCSTATUS, &prstatus) >= 0)
+ {
+ if (Fields & osl_Process_CPUTIMES)
+ {
+ pInfo->UserTime.Seconds = prstatus.pr_utime.tv_sec;
+ pInfo->UserTime.Nanosec = prstatus.pr_utime.tv_nsec;
+ pInfo->SystemTime.Seconds = prstatus.pr_stime.tv_sec;
+ pInfo->SystemTime.Nanosec = prstatus.pr_stime.tv_nsec;
+
+ pInfo->Fields |= osl_Process_CPUTIMES;
+ }
+
+ if (Fields & osl_Process_HEAPUSAGE)
+ {
+ pInfo->HeapUsage = prstatus.pr_brksize;
+
+ pInfo->Fields |= osl_Process_HEAPUSAGE;
+ }
+
+ close(fd);
+
+ return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
+ }
+ else
+ close(fd);
+ }
+
+#elif defined(LINUX)
+
+ if ( (Fields & osl_Process_CPUTIMES) || (Fields & osl_Process_HEAPUSAGE) )
+ {
+ struct osl_procStat procstat;
+ memset(&procstat,0,sizeof(procstat));
+
+ if ( (Fields & osl_Process_CPUTIMES) && osl_getProcStat(pid, &procstat) )
+ {
+ /*
+ * mfe:
+ * We calculate only time of the process proper.
+ * Threads are processes, we do not consider their time here!
+ * (For this, cutime and cstime should be used, it seems not
+ * to work in 2.0.36)
+ */
+
+ long clktck;
+ unsigned long hz;
+ unsigned long userseconds;
+ unsigned long systemseconds;
+
+ clktck = sysconf(_SC_CLK_TCK);
+ if (clktck <= 0) {
+ return osl_Process_E_Unknown;
+ }
+ hz = static_cast<unsigned long>(clktck);
+
+ userseconds = procstat.utime/hz;
+ systemseconds = procstat.stime/hz;
+
+ pInfo->UserTime.Seconds = userseconds;
+ pInfo->UserTime.Nanosec = procstat.utime - (userseconds * hz);
+ pInfo->SystemTime.Seconds = systemseconds;
+ pInfo->SystemTime.Nanosec = procstat.stime - (systemseconds * hz);
+
+ pInfo->Fields |= osl_Process_CPUTIMES;
+ }
+
+ if ( (Fields & osl_Process_HEAPUSAGE) && osl_getProcStatus(pid, &procstat) )
+ {
+ /*
+ * mfe:
+ * vm_data (found in status) shows the size of the data segment
+ * it a rough approximation of the core heap size
+ */
+ pInfo->HeapUsage = procstat.vm_data*1024;
+
+ pInfo->Fields |= osl_Process_HEAPUSAGE;
+ }
+ }
+
+ return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
+#endif
+
+ }
+
+ return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
+}
+
+/** Helper function for osl_joinProcessWithTimeout
+ */
+
+static bool is_timeout(const struct timeval* tend)
+{
+ struct timeval tcurrent;
+ gettimeofday(&tcurrent, nullptr);
+ return (tcurrent.tv_sec >= tend->tv_sec);
+}
+
+/* kill(pid, 0) is useful for checking if a
+ process is still alive, but remember that
+ kill even returns 0 if the process is already
+ a zombie. */
+
+static bool is_process_dead(pid_t pid)
+{
+ return ((kill(pid, 0) == -1) && (ESRCH == errno));
+}
+
+oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout)
+{
+ oslProcessImpl* pChild = ChildList;
+ oslProcessError osl_error = osl_Process_E_None;
+
+ OSL_PRECOND(Process, "osl_joinProcess: Invalid parameter");
+ OSL_ASSERT(ChildListMutex);
+
+ if (Process == nullptr || ChildListMutex == nullptr)
+ return osl_Process_E_Unknown;
+
+ osl_acquireMutex(ChildListMutex);
+
+ /* check if process is a child of ours */
+ while (pChild != nullptr)
+ {
+ if (pChild == static_cast<oslProcessImpl*>(Process))
+ break;
+
+ pChild = pChild->m_pnext;
+ }
+
+ osl_releaseMutex(ChildListMutex);
+
+ if (pChild != nullptr)
+ {
+ oslConditionResult cond_res = osl_waitCondition(pChild->m_terminated, pTimeout);
+
+ if (cond_res == osl_cond_result_timeout)
+ osl_error = osl_Process_E_TimedOut;
+ else if (cond_res != osl_cond_result_ok)
+ osl_error = osl_Process_E_Unknown;
+ }
+ else /* alien process; StatusThread will not be able
+ to set the condition terminated */
+ {
+ pid_t pid = static_cast<oslProcessImpl*>(Process)->m_pid;
+
+ if (pTimeout)
+ {
+ bool timeout = false;
+ struct timeval tend;
+
+ gettimeofday(&tend, nullptr);
+
+ tend.tv_sec += pTimeout->Seconds;
+
+ while (!is_process_dead(pid) && !(timeout = is_timeout(&tend)))
+ sleep(1);
+
+ if (timeout)
+ osl_error = osl_Process_E_TimedOut;
+ }
+ else /* infinite */
+ {
+ while (!is_process_dead(pid))
+ sleep(1);
+ }
+ }
+ return osl_error;
+}
+
+oslProcessError SAL_CALL osl_joinProcess(oslProcess Process)
+{
+ return osl_joinProcessWithTimeout(Process, nullptr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/process_impl.cxx b/sal/osl/unx/process_impl.cxx
new file mode 100644
index 000000000..5d7f7644f
--- /dev/null
+++ b/sal/osl/unx/process_impl.cxx
@@ -0,0 +1,473 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <osl/process.h>
+
+#include <limits.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <osl/module.h>
+#include <osl/thread.h>
+#include <rtl/alloc.h>
+#include <rtl/ustring.hxx>
+#include <rtl/strbuf.h>
+#include <sal/log.hxx>
+
+#include "file_path_helper.hxx"
+
+#include "uunxapi.hxx"
+#include "nlsupport.hxx"
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+#if defined(MACOSX) || defined(IOS)
+#include <mach-o/dyld.h>
+
+namespace {
+
+oslProcessError bootstrap_getExecutableFile(rtl_uString ** ppFileURL)
+{
+ oslProcessError result = osl_Process_E_NotFound;
+
+ char buffer[PATH_MAX];
+ uint32_t buflen = sizeof(buffer);
+
+ if (_NSGetExecutablePath (buffer, &buflen) == 0)
+ {
+ /* Determine absolute path. */
+ char abspath[PATH_MAX];
+ if (realpath (buffer, abspath) != nullptr)
+ {
+ /* Convert from utf8 to unicode. */
+ rtl_uString * pAbsPath = nullptr;
+ rtl_string2UString (
+ &pAbsPath,
+ abspath, rtl_str_getLength (abspath),
+ RTL_TEXTENCODING_UTF8,
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+
+ if (pAbsPath)
+ {
+ /* Convert from path to url. */
+ if (osl_getFileURLFromSystemPath (pAbsPath, ppFileURL) == osl_File_E_None)
+ {
+ /* Success. */
+ result = osl_Process_E_None;
+ }
+ rtl_uString_release (pAbsPath);
+ }
+ }
+ }
+
+ return result;
+}
+
+}
+
+#else
+#include <dlfcn.h>
+
+namespace {
+
+oslProcessError bootstrap_getExecutableFile(rtl_uString ** ppFileURL)
+{
+ oslProcessError result = osl_Process_E_NotFound;
+
+#ifdef ANDROID
+ /* Now with just a single DSO, this one from lo-bootstrap.c is as good as
+ * any */
+ void * addr = dlsym (RTLD_DEFAULT, "JNI_OnLoad");
+#else
+#if defined __linux
+ // The below code looking for "main" with dlsym() will typically
+ // fail, as there is little reason for "main" to be exported, in
+ // the dlsym() sense, from an executable. But Linux has
+ // /proc/self/exe, try using that.
+ char buf[PATH_MAX];
+ int rc = readlink("/proc/self/exe", buf, sizeof(buf));
+ if (rc > 0 && rc < PATH_MAX)
+ {
+ buf[rc] = '\0';
+ OUString path = OUString::fromUtf8(buf);
+ OUString fileURL;
+ if (osl::File::getFileURLFromSystemPath(path, fileURL) == osl::File::E_None)
+ {
+ rtl_uString_acquire(fileURL.pData);
+ *ppFileURL = fileURL.pData;
+ return osl_Process_E_None;
+ }
+ }
+#endif
+ /* Determine address of "main()" function. */
+ void * addr = dlsym (RTLD_DEFAULT, "main");
+#endif
+ if (addr != nullptr)
+ {
+ /* Determine module URL. */
+ if (osl_getModuleURLFromAddress (addr, ppFileURL))
+ {
+ /* Success. */
+ result = osl_Process_E_None;
+ }
+ }
+
+ return result;
+}
+
+}
+
+#endif
+
+namespace {
+
+struct CommandArgs_Impl
+{
+ pthread_mutex_t m_mutex;
+ sal_uInt32 m_nCount;
+ rtl_uString ** m_ppArgs;
+};
+
+}
+
+static struct CommandArgs_Impl g_command_args =
+{
+ PTHREAD_MUTEX_INITIALIZER,
+ 0,
+ nullptr
+};
+
+oslProcessError SAL_CALL osl_getExecutableFile (rtl_uString ** ppustrFile)
+{
+ pthread_mutex_lock (&(g_command_args.m_mutex));
+ if (g_command_args.m_nCount == 0)
+ {
+ pthread_mutex_unlock (&(g_command_args.m_mutex));
+ return bootstrap_getExecutableFile(ppustrFile);
+ }
+
+ /* CommandArgs set. Obtain argv[0]. */
+ rtl_uString_assign (ppustrFile, g_command_args.m_ppArgs[0]);
+ pthread_mutex_unlock (&(g_command_args.m_mutex));
+ return osl_Process_E_None;
+}
+
+sal_uInt32 SAL_CALL osl_getCommandArgCount()
+{
+ sal_uInt32 result = 0;
+
+ pthread_mutex_lock (&(g_command_args.m_mutex));
+ SAL_INFO_IF(
+ g_command_args.m_nCount == 0, "sal.osl",
+ "osl_getCommandArgCount w/o prior call to osl_setCommandArgs");
+ if (g_command_args.m_nCount > 0)
+ result = g_command_args.m_nCount - 1;
+ pthread_mutex_unlock (&(g_command_args.m_mutex));
+
+ return result;
+}
+
+oslProcessError SAL_CALL osl_getCommandArg (sal_uInt32 nArg, rtl_uString ** strCommandArg)
+{
+ oslProcessError result = osl_Process_E_NotFound;
+
+ pthread_mutex_lock (&(g_command_args.m_mutex));
+ assert(g_command_args.m_nCount > 0);
+ if (g_command_args.m_nCount > (nArg + 1))
+ {
+ rtl_uString_assign (strCommandArg, g_command_args.m_ppArgs[nArg + 1]);
+ result = osl_Process_E_None;
+ }
+ pthread_mutex_unlock (&(g_command_args.m_mutex));
+
+ return result;
+}
+
+void SAL_CALL osl_setCommandArgs (int argc, char ** argv)
+{
+ assert(argc > 0);
+ pthread_mutex_lock (&(g_command_args.m_mutex));
+ SAL_WARN_IF(g_command_args.m_nCount != 0, "sal.osl", "args already set");
+ if (g_command_args.m_nCount == 0)
+ {
+ rtl_uString** ppArgs = static_cast<rtl_uString**>(rtl_allocateZeroMemory (argc * sizeof(rtl_uString*)));
+ if (ppArgs != nullptr)
+ {
+ rtl_TextEncoding encoding = osl_getThreadTextEncoding();
+ for (int i = 0; i < argc; i++)
+ {
+ rtl_string2UString (
+ &(ppArgs[i]),
+ argv[i], rtl_str_getLength (argv[i]), encoding,
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+ }
+ if (ppArgs[0] != nullptr)
+ {
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ // If we are called with a relative path in argv[0] in a sandboxed process
+ // osl::realpath() fails. So just use bootstrap_getExecutableFile() instead.
+ // Somewhat silly to use argv[0] and tediously figure out the absolute path from it
+ // anyway.
+ bootstrap_getExecutableFile(&ppArgs[0]);
+ OUString pArg0(ppArgs[0]);
+ osl_getFileURLFromSystemPath (pArg0.pData, &(ppArgs[0]));
+#else
+#if !defined(ANDROID) && !defined(IOS) // No use searching PATH on Android or iOS
+ /* see @ osl_getExecutableFile(). */
+ if (rtl_ustr_indexOfChar (rtl_uString_getStr(ppArgs[0]), '/') == -1)
+ {
+ const OUString PATH ("PATH");
+
+ rtl_uString * pSearchPath = nullptr;
+ osl_getEnvironment (PATH.pData, &pSearchPath);
+ if (pSearchPath)
+ {
+ rtl_uString * pSearchResult = nullptr;
+ osl_searchPath (ppArgs[0], pSearchPath, &pSearchResult);
+ if (pSearchResult)
+ {
+ rtl_uString_assign (&(ppArgs[0]), pSearchResult);
+ rtl_uString_release (pSearchResult);
+ }
+ rtl_uString_release (pSearchPath);
+ }
+ }
+#endif
+ OUString pArg0;
+ if (osl::realpath (OUString::unacquired(&ppArgs[0]), pArg0))
+ {
+ osl_getFileURLFromSystemPath (pArg0.pData, &(ppArgs[0]));
+ }
+#endif // !HAVE_FEATURE_MACOSX_SANDBOX
+ }
+ g_command_args.m_nCount = argc;
+ g_command_args.m_ppArgs = ppArgs;
+ }
+ }
+ pthread_mutex_unlock (&(g_command_args.m_mutex));
+}
+
+oslProcessError SAL_CALL osl_getEnvironment(rtl_uString* pustrEnvVar, rtl_uString** ppustrValue)
+{
+ oslProcessError result = osl_Process_E_NotFound;
+ rtl_TextEncoding encoding = osl_getThreadTextEncoding();
+ rtl_String* pstr_env_var = nullptr;
+
+ OSL_PRECOND(pustrEnvVar, "osl_getEnvironment(): Invalid parameter");
+ OSL_PRECOND(ppustrValue, "osl_getEnvironment(): Invalid parameter");
+
+ rtl_uString2String(
+ &pstr_env_var,
+ rtl_uString_getStr(pustrEnvVar), rtl_uString_getLength(pustrEnvVar), encoding,
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+ if (pstr_env_var != nullptr)
+ {
+ const char* p_env_var = getenv (rtl_string_getStr (pstr_env_var));
+ if (p_env_var != nullptr)
+ {
+ rtl_string2UString(
+ ppustrValue,
+ p_env_var, strlen(p_env_var), encoding,
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+ OSL_ASSERT(*ppustrValue != nullptr);
+
+ result = osl_Process_E_None;
+ }
+ rtl_string_release(pstr_env_var);
+ }
+
+ return result;
+}
+
+oslProcessError SAL_CALL osl_setEnvironment(rtl_uString* pustrEnvVar, rtl_uString* pustrValue)
+{
+ oslProcessError result = osl_Process_E_Unknown;
+ rtl_TextEncoding encoding = osl_getThreadTextEncoding();
+ rtl_String* pstr_env_var = nullptr;
+ rtl_String* pstr_val = nullptr;
+
+ OSL_PRECOND(pustrEnvVar, "osl_setEnvironment(): Invalid parameter");
+ OSL_PRECOND(pustrValue, "osl_setEnvironment(): Invalid parameter");
+
+ rtl_uString2String(
+ &pstr_env_var,
+ rtl_uString_getStr(pustrEnvVar), rtl_uString_getLength(pustrEnvVar), encoding,
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+
+ rtl_uString2String(
+ &pstr_val,
+ rtl_uString_getStr(pustrValue), rtl_uString_getLength(pustrValue), encoding,
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+
+ if (pstr_env_var != nullptr && pstr_val != nullptr)
+ {
+#if defined (__sun)
+ rtl_String * pBuffer = NULL;
+
+ sal_Int32 nCapacity = rtl_stringbuffer_newFromStringBuffer( &pBuffer,
+ rtl_string_getLength(pstr_env_var) + rtl_string_getLength(pstr_val) + 1,
+ pstr_env_var );
+ rtl_stringbuffer_insert( &pBuffer, &nCapacity, pBuffer->length, "=", 1);
+ rtl_stringbuffer_insert( &pBuffer, &nCapacity, pBuffer->length,
+ rtl_string_getStr(pstr_val), rtl_string_getLength(pstr_val) );
+
+ rtl_string_acquire(pBuffer); // argument to putenv must leak on success
+
+ if (putenv(rtl_string_getStr(pBuffer)) == 0)
+ result = osl_Process_E_None;
+ else
+ rtl_string_release(pBuffer);
+#else
+ if (setenv(rtl_string_getStr(pstr_env_var), rtl_string_getStr(pstr_val), 1) == 0)
+ result = osl_Process_E_None;
+#endif
+ }
+
+ if (pstr_val)
+ rtl_string_release(pstr_val);
+
+ if (pstr_env_var != nullptr)
+ rtl_string_release(pstr_env_var);
+
+ return result;
+}
+
+oslProcessError SAL_CALL osl_clearEnvironment(rtl_uString* pustrEnvVar)
+{
+ oslProcessError result = osl_Process_E_Unknown;
+ rtl_TextEncoding encoding = osl_getThreadTextEncoding();
+ rtl_String* pstr_env_var = nullptr;
+
+ OSL_PRECOND(pustrEnvVar, "osl_setEnvironment(): Invalid parameter");
+
+ rtl_uString2String(
+ &pstr_env_var,
+ rtl_uString_getStr(pustrEnvVar), rtl_uString_getLength(pustrEnvVar), encoding,
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+
+ if (pstr_env_var)
+ {
+#if defined (__sun)
+ rtl_String * pBuffer = NULL;
+
+ sal_Int32 nCapacity = rtl_stringbuffer_newFromStringBuffer( &pBuffer,
+ rtl_string_getLength(pstr_env_var) + 1, pstr_env_var );
+ rtl_stringbuffer_insert( &pBuffer, &nCapacity, pBuffer->length, "=", 1);
+
+ rtl_string_acquire(pBuffer); // argument to putenv must leak on success
+
+ if (putenv(rtl_string_getStr(pBuffer)) == 0)
+ result = osl_Process_E_None;
+ else
+ rtl_string_release(pBuffer);
+#elif (defined(MACOSX) || defined(NETBSD) || defined(FREEBSD))
+ // MacOSX baseline is 10.4, which has an old-school void return
+ // for unsetenv.
+ // See: http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/10.4/man3/unsetenv.3.html?useVersion=10.4
+ unsetenv(rtl_string_getStr(pstr_env_var));
+ result = osl_Process_E_None;
+#else
+ if (unsetenv(rtl_string_getStr(pstr_env_var)) == 0)
+ result = osl_Process_E_None;
+#endif
+ rtl_string_release(pstr_env_var);
+ }
+
+ return result;
+}
+
+oslProcessError SAL_CALL osl_getProcessWorkingDir(rtl_uString **ppustrWorkingDir)
+{
+ oslProcessError result = osl_Process_E_Unknown;
+ char buffer[PATH_MAX];
+
+ OSL_PRECOND(ppustrWorkingDir, "osl_getProcessWorkingDir(): Invalid parameter");
+
+ if (getcwd (buffer, sizeof(buffer)) != nullptr)
+ {
+ rtl_uString* ustrTmp = nullptr;
+
+ rtl_string2UString(
+ &ustrTmp,
+ buffer, strlen(buffer), osl_getThreadTextEncoding(),
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+ if (ustrTmp != nullptr)
+ {
+ if (osl_getFileURLFromSystemPath (ustrTmp, ppustrWorkingDir) == osl_File_E_None)
+ result = osl_Process_E_None;
+ rtl_uString_release (ustrTmp);
+ }
+ }
+
+ return result;
+}
+
+namespace {
+
+struct ProcessLocale_Impl
+{
+ pthread_mutex_t m_mutex;
+ rtl_Locale * m_pLocale;
+};
+
+}
+
+static struct ProcessLocale_Impl g_process_locale =
+{
+ PTHREAD_MUTEX_INITIALIZER,
+ nullptr
+};
+
+oslProcessError SAL_CALL osl_getProcessLocale( rtl_Locale ** ppLocale )
+{
+ oslProcessError result = osl_Process_E_Unknown;
+ OSL_PRECOND(ppLocale, "osl_getProcessLocale(): Invalid parameter.");
+ if (ppLocale)
+ {
+ pthread_mutex_lock(&(g_process_locale.m_mutex));
+
+ if (g_process_locale.m_pLocale == nullptr)
+ imp_getProcessLocale (&(g_process_locale.m_pLocale));
+ *ppLocale = g_process_locale.m_pLocale;
+ result = osl_Process_E_None;
+
+ pthread_mutex_unlock (&(g_process_locale.m_mutex));
+ }
+ return result;
+}
+
+oslProcessError SAL_CALL osl_setProcessLocale( rtl_Locale * pLocale )
+{
+ OSL_PRECOND(pLocale, "osl_setProcessLocale(): Invalid parameter.");
+
+ pthread_mutex_lock(&(g_process_locale.m_mutex));
+ g_process_locale.m_pLocale = pLocale;
+ pthread_mutex_unlock (&(g_process_locale.m_mutex));
+
+ return osl_Process_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/profile.cxx b/sal/osl/unx/profile.cxx
new file mode 100644
index 000000000..354fe626f
--- /dev/null
+++ b/sal/osl/unx/profile.cxx
@@ -0,0 +1,1866 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.hxx"
+#include "readwrite_helper.hxx"
+#include "file_url.hxx"
+#include "unixerrnostring.hxx"
+
+#include <osl/diagnose.h>
+#include <osl/profile.h>
+#include <osl/process.h>
+#include <osl/thread.h>
+#include <rtl/alloc.h>
+#include <sal/log.hxx>
+
+#define LINES_INI 32
+#define LINES_ADD 10
+#define SECTIONS_INI 5
+#define SECTIONS_ADD 3
+#define ENTRIES_INI 5
+#define ENTRIES_ADD 3
+
+#define STR_INI_BOOLYES "yes"
+#define STR_INI_BOOLON "on"
+#define STR_INI_BOOLONE "1"
+#define STR_INI_BOOLNO "no"
+#define STR_INI_BOOLOFF "off"
+#define STR_INI_BOOLZERO "0"
+
+#define FLG_USER 0x00FF
+#define FLG_AUTOOPEN 0x0100
+#define FLG_MODIFIED 0x0200
+
+#define DEFAULT_PMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
+
+typedef time_t osl_TStamp;
+
+namespace {
+
+enum osl_TLockMode
+{
+ un_lock, read_lock, write_lock
+};
+
+struct osl_TFile
+{
+ int m_Handle;
+ char* m_pReadPtr;
+ char m_ReadBuf[512];
+ char* m_pWriteBuf;
+ sal_uInt32 m_nWriteBufLen;
+ sal_uInt32 m_nWriteBufFree;
+};
+
+struct osl_TProfileEntry
+{
+ sal_uInt32 m_Line;
+ sal_uInt32 m_Offset;
+ sal_uInt32 m_Len;
+};
+
+struct osl_TProfileSection
+{
+ sal_uInt32 m_Line;
+ sal_uInt32 m_Offset;
+ sal_uInt32 m_Len;
+ sal_uInt32 m_NoEntries;
+ sal_uInt32 m_MaxEntries;
+ osl_TProfileEntry* m_Entries;
+};
+
+/* Profile-data structure hidden behind oslProfile: */
+struct osl_TProfileImpl
+{
+ sal_uInt32 m_Flags;
+ osl_TFile* m_pFile;
+ osl_TStamp m_Stamp;
+ char m_FileName[PATH_MAX + 1];
+ sal_uInt32 m_NoLines;
+ sal_uInt32 m_MaxLines;
+ sal_uInt32 m_NoSections;
+ sal_uInt32 m_MaxSections;
+ char** m_Lines;
+ osl_TProfileSection* m_Sections;
+ pthread_mutex_t m_AccessLock;
+ bool m_bIsValid;
+};
+
+}
+
+static osl_TFile* openFileImpl(const char* pszFilename, oslProfileOption ProfileFlags);
+static osl_TStamp closeFileImpl(osl_TFile* pFile, oslProfileOption Flags);
+static bool OslProfile_lockFile(const osl_TFile* pFile, osl_TLockMode eMode);
+static bool OslProfile_rewindFile(osl_TFile* pFile, bool bTruncate);
+static osl_TStamp OslProfile_getFileStamp(osl_TFile* pFile);
+
+static char* OslProfile_getLine(osl_TFile* pFile);
+static bool OslProfile_putLine(osl_TFile* pFile, const char *pszLine);
+static char* stripBlanks(char* String, sal_uInt32* pLen);
+static char* addLine(osl_TProfileImpl* pProfile, const char* Line);
+static char* insertLine(osl_TProfileImpl* pProfile, const char* Line, sal_uInt32 LineNo);
+static void removeLine(osl_TProfileImpl* pProfile, sal_uInt32 LineNo);
+static void setEntry(osl_TProfileImpl* pProfile, osl_TProfileSection* pSection,
+ sal_uInt32 NoEntry, sal_uInt32 Line,
+ char* Entry, sal_uInt32 Len);
+static bool addEntry(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection,
+ int Line, char* Entry, sal_uInt32 Len);
+static void removeEntry(osl_TProfileSection *pSection, sal_uInt32 NoEntry);
+static bool addSection(osl_TProfileImpl* pProfile, int Line, const char* Section, sal_uInt32 Len);
+static void removeSection(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection);
+static osl_TProfileSection* findEntry(osl_TProfileImpl* pProfile, const char* Section,
+ const char* Entry, sal_uInt32 *pNoEntry);
+static bool loadProfile(osl_TFile* pFile, osl_TProfileImpl* pProfile);
+static bool storeProfile(osl_TProfileImpl* pProfile, bool bCleanup);
+static osl_TProfileImpl* acquireProfile(oslProfile Profile, bool bWriteable);
+static bool releaseProfile(osl_TProfileImpl* pProfile);
+
+static bool writeProfileImpl (osl_TFile* pFile);
+static osl_TFile* osl_openTmpProfileImpl(osl_TProfileImpl*);
+static bool osl_ProfileSwapProfileNames(osl_TProfileImpl*);
+static void osl_ProfileGenerateExtension(const char* pszFileName, const char* pszExtension, char* pszTmpName, int BufferMaxLen);
+static oslProfile osl_psz_openProfile(const char *pszProfileName, oslProfileOption Flags);
+
+oslProfile SAL_CALL osl_openProfile(rtl_uString *ustrProfileName, oslProfileOption Options)
+{
+ char profilePath[PATH_MAX] = "";
+ return
+ (ustrProfileName == nullptr
+ || ustrProfileName->buffer[0] == 0
+ || (FileURLToPath(profilePath, PATH_MAX, ustrProfileName)
+ == osl_File_E_None))
+ ? osl_psz_openProfile(profilePath, Options)
+ : nullptr;
+}
+
+static oslProfile osl_psz_openProfile(const char *pszProfileName, oslProfileOption Flags)
+{
+ osl_TFile* pFile;
+ osl_TProfileImpl* pProfile;
+ bool bRet = false;
+
+ if ( ( pFile = openFileImpl(pszProfileName, Flags ) ) == nullptr )
+ {
+ return nullptr;
+ }
+
+ pProfile = static_cast<osl_TProfileImpl*>(calloc(1, sizeof(osl_TProfileImpl)));
+
+ if ( pProfile == nullptr )
+ {
+ closeFileImpl(pFile, Flags);
+ return nullptr;
+ }
+
+ pProfile->m_Flags = Flags & FLG_USER;
+
+ if ( Flags & ( osl_Profile_READLOCK | osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE ) )
+ {
+ pProfile->m_pFile = pFile;
+ }
+
+ pthread_mutex_init(&(pProfile->m_AccessLock),PTHREAD_MUTEXATTR_DEFAULT);
+ pProfile->m_bIsValid = true;
+
+ pProfile->m_Stamp = OslProfile_getFileStamp(pFile);
+ bRet=loadProfile(pFile, pProfile);
+ bRet &= realpath(pszProfileName, pProfile->m_FileName) != nullptr;
+ SAL_WARN_IF(!bRet, "sal.osl", "realpath(pszProfileName, pProfile->m_FileName) != NULL ==> false");
+
+ if (pProfile->m_pFile == nullptr)
+ closeFileImpl(pFile,pProfile->m_Flags);
+
+ // coverity[leaked_storage] - pFile is not leaked
+ return pProfile;
+}
+
+sal_Bool SAL_CALL osl_closeProfile(oslProfile Profile)
+{
+ osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
+ osl_TProfileImpl* pTmpProfile;
+
+ if ( Profile == nullptr )
+ {
+ return false;
+ }
+
+ pthread_mutex_lock(&(pProfile->m_AccessLock));
+
+ if ( !pProfile->m_bIsValid )
+ {
+ SAL_WARN("sal.osl", "!pProfile->m_bIsValid");
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+
+ return false;
+ }
+
+ pProfile->m_bIsValid = false;
+
+ if ( ! ( pProfile->m_Flags & osl_Profile_READLOCK ) && ( pProfile->m_Flags & FLG_MODIFIED ) )
+ {
+ pTmpProfile = acquireProfile(Profile, true);
+
+ if ( pTmpProfile != nullptr )
+ {
+ bool bRet = storeProfile(pTmpProfile, true);
+ SAL_WARN_IF(!bRet, "sal.osl", "storeProfile(pTmpProfile, true) ==> false");
+ }
+ }
+ else
+ {
+ pTmpProfile = acquireProfile(Profile, false);
+ }
+
+ if ( pTmpProfile == nullptr )
+ {
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+
+ SAL_INFO("sal.osl", "Out osl_closeProfile [pProfile==0]");
+ return false;
+ }
+
+ pProfile = pTmpProfile;
+
+ if (pProfile->m_pFile != nullptr)
+ closeFileImpl(pProfile->m_pFile,pProfile->m_Flags);
+
+ pProfile->m_pFile = nullptr;
+ pProfile->m_FileName[0] = '\0';
+
+ /* release whole profile data types memory */
+ if ( pProfile->m_NoLines > 0)
+ {
+ unsigned int idx=0;
+ if ( pProfile->m_Lines != nullptr )
+ {
+ for ( idx = 0 ; idx < pProfile->m_NoLines ; ++idx)
+ {
+ if ( pProfile->m_Lines[idx] != nullptr )
+ {
+ free(pProfile->m_Lines[idx]);
+ pProfile->m_Lines[idx]=nullptr;
+ }
+ }
+ free(pProfile->m_Lines);
+ pProfile->m_Lines=nullptr;
+ }
+ if ( pProfile->m_Sections != nullptr )
+ {
+ /*osl_TProfileSection* pSections=pProfile->m_Sections;*/
+ for ( idx = 0 ; idx < pProfile->m_NoSections ; ++idx )
+ {
+ if ( pProfile->m_Sections[idx].m_Entries != nullptr )
+ {
+ free(pProfile->m_Sections[idx].m_Entries);
+ pProfile->m_Sections[idx].m_Entries=nullptr;
+ }
+ }
+ free(pProfile->m_Sections);
+ pProfile->m_Sections=nullptr;
+ }
+ }
+
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+
+ pthread_mutex_destroy(&(pProfile->m_AccessLock));
+
+ free(pProfile);
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_flushProfile(oslProfile Profile)
+{
+ osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
+ osl_TFile* pFile;
+ bool bRet = false;
+
+ if ( pProfile == nullptr )
+ {
+ return false;
+ }
+
+ pthread_mutex_lock(&(pProfile->m_AccessLock));
+
+ if ( !pProfile->m_bIsValid )
+ {
+ SAL_WARN_IF(!pProfile->m_bIsValid, "sal.osl", "!pProfile->m_bIsValid");
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+ return false;
+ }
+
+ pFile = pProfile->m_pFile;
+ if ( pFile == nullptr || pFile->m_Handle < 0 )
+ {
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+
+ return false;
+ }
+
+ if ( pProfile->m_Flags & FLG_MODIFIED )
+ {
+ bRet = storeProfile(pProfile, false);
+ SAL_WARN_IF(!bRet, "sal.osl", "storeProfile(pProfile, false) ==> false");
+ }
+
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+ return bRet;
+}
+
+static bool writeProfileImpl(osl_TFile* pFile)
+{
+ if ( pFile == nullptr || pFile->m_Handle < 0 || pFile->m_pWriteBuf == nullptr )
+ {
+ return false;
+ }
+
+ SAL_WARN_IF(
+ (strlen(pFile->m_pWriteBuf)
+ != pFile->m_nWriteBufLen - pFile->m_nWriteBufFree),
+ "sal.osl",
+ strlen(pFile->m_pWriteBuf) << " != "
+ << (pFile->m_nWriteBufLen - pFile->m_nWriteBufFree));
+
+ if ( !safeWrite(pFile->m_Handle, pFile->m_pWriteBuf, pFile->m_nWriteBufLen - pFile->m_nWriteBufFree) )
+ {
+ SAL_INFO("sal.osl", "write failed: " << UnixErrnoString(errno));
+ return false;
+ }
+
+ free(pFile->m_pWriteBuf);
+ pFile->m_pWriteBuf=nullptr;
+ pFile->m_nWriteBufLen=0;
+ pFile->m_nWriteBufFree=0;
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_readProfileString(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ char* pszString,
+ sal_uInt32 MaxLen,
+ const char* pszDefault)
+{
+ sal_uInt32 NoEntry;
+ char* pStr=nullptr;
+ osl_TProfileImpl* pProfile=nullptr;
+ osl_TProfileImpl* pTmpProfile=nullptr;
+ bool bRet = false;
+
+ pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);
+
+ if ( pTmpProfile == nullptr )
+ {
+ return false;
+ }
+
+ pthread_mutex_lock(&(pTmpProfile->m_AccessLock));
+
+ if ( !pTmpProfile->m_bIsValid )
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ pProfile = acquireProfile(Profile, false);
+
+ if ( pProfile == nullptr )
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ osl_TProfileSection* pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry);
+ if ((pSec != nullptr) &&
+ (NoEntry < pSec->m_NoEntries) &&
+ ((pStr = strchr(pProfile->m_Lines[pSec->m_Entries[NoEntry].m_Line],
+ '=')) != nullptr))
+ {
+ pStr++;
+ }
+ else
+ {
+ pStr=const_cast<char*>(pszDefault);
+ }
+
+ if ( pStr != nullptr )
+ {
+ pStr = stripBlanks(pStr, nullptr);
+ MaxLen = (MaxLen - 1 < strlen(pStr)) ? (MaxLen - 1) : strlen(pStr);
+ pStr = stripBlanks(pStr, &MaxLen);
+ strncpy(pszString, pStr, MaxLen);
+ pszString[MaxLen] = '\0';
+ }
+ }
+ else
+ { /* not implemented */ }
+
+ bRet=releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ if ( pStr == nullptr )
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_readProfileBool(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ sal_Bool Default)
+{
+ char Line[32];
+ Line[0] = '\0';
+
+ if (osl_readProfileString(Profile, pszSection, pszEntry, Line, sizeof(Line), ""))
+ {
+ if ((strcasecmp(Line, STR_INI_BOOLYES) == 0) ||
+ (strcasecmp(Line, STR_INI_BOOLON) == 0) ||
+ (strcasecmp(Line, STR_INI_BOOLONE) == 0))
+ Default = true;
+ else
+ if ((strcasecmp(Line, STR_INI_BOOLNO) == 0) ||
+ (strcasecmp(Line, STR_INI_BOOLOFF) == 0) ||
+ (strcasecmp(Line, STR_INI_BOOLZERO) == 0))
+ Default = false;
+ }
+
+ return Default;
+}
+
+sal_uInt32 SAL_CALL osl_readProfileIdent(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ sal_uInt32 FirstId,
+ const char* Strings[],
+ sal_uInt32 Default)
+{
+ sal_uInt32 i;
+ char Line[256];
+ Line[0] = '\0';
+
+ if (osl_readProfileString(Profile, pszSection, pszEntry, Line, sizeof(Line), ""))
+ {
+ i = 0;
+ while (Strings[i] != nullptr)
+ {
+ if (strcasecmp(Line, Strings[i]) == 0)
+ {
+ Default = i + FirstId;
+ break;
+ }
+ i++;
+ }
+ }
+
+ return Default;
+}
+
+sal_Bool SAL_CALL osl_writeProfileString(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ const char* pszString)
+{
+ bool bRet = false;
+ sal_uInt32 NoEntry;
+ char* pStr;
+ char* Line = nullptr;
+ osl_TProfileImpl* pProfile = nullptr;
+ osl_TProfileImpl* pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);
+
+ if ( pTmpProfile == nullptr )
+ {
+ return false;
+ }
+
+ pthread_mutex_lock(&(pTmpProfile->m_AccessLock));
+
+ if ( !pTmpProfile->m_bIsValid )
+ {
+ SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid");
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ pProfile=acquireProfile(Profile, true);
+
+ if (pProfile == nullptr)
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ Line = static_cast<char*>(malloc(strlen(pszEntry)+strlen(pszString)+48));
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ osl_TProfileSection* pSec;
+ if ((pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry)) == nullptr)
+ {
+ Line[0] = '\0';
+ addLine(pProfile, Line);
+
+ Line[0] = '[';
+ strcpy(&Line[1], pszSection);
+ Line[1 + strlen(pszSection)] = ']';
+ Line[2 + strlen(pszSection)] = '\0';
+
+ pStr = addLine(pProfile, Line);
+ if ((pStr == nullptr) ||
+ (! addSection(pProfile, pProfile->m_NoLines - 1, &pStr[1], strlen(pszSection))))
+ {
+ bRet=releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ free(Line);
+ return false;
+ }
+
+ pSec = &pProfile->m_Sections[pProfile->m_NoSections - 1];
+ NoEntry = pSec->m_NoEntries;
+ }
+
+ Line[0] = '\0';
+ strcpy(&Line[0], pszEntry);
+ Line[0 + strlen(pszEntry)] = '=';
+ strcpy(&Line[1 + strlen(pszEntry)], pszString);
+
+ if (NoEntry >= pSec->m_NoEntries)
+ {
+ sal_uInt32 i;
+ if (pSec->m_NoEntries > 0)
+ i = pSec->m_Entries[pSec->m_NoEntries - 1].m_Line + 1;
+ else
+ i = pSec->m_Line + 1;
+
+ pStr = insertLine(pProfile, Line, i);
+ if ((pStr == nullptr) ||
+ (! addEntry(pProfile, pSec, i, pStr, strlen(pszEntry))))
+ {
+ bRet=releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+ free(Line);
+
+ return false;
+ }
+
+ pProfile->m_Flags |= FLG_MODIFIED;
+ }
+ else
+ {
+ sal_uInt32 i = pSec->m_Entries[NoEntry].m_Line;
+ free(pProfile->m_Lines[i]);
+ pProfile->m_Lines[i] = strdup(Line);
+ setEntry(pProfile, pSec, NoEntry, i, pProfile->m_Lines[i], strlen(pszEntry));
+
+ pProfile->m_Flags |= FLG_MODIFIED;
+ }
+ }
+ else {
+ /* not implemented */
+ }
+
+ bRet = releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+ if ( Line!= nullptr )
+ {
+ free(Line);
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL osl_writeProfileBool(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ sal_Bool Value)
+{
+ bool bRet = false;
+
+ if (Value)
+ bRet=osl_writeProfileString(Profile, pszSection, pszEntry, STR_INI_BOOLONE);
+ else
+ bRet=osl_writeProfileString(Profile, pszSection, pszEntry, STR_INI_BOOLZERO);
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL osl_writeProfileIdent(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ sal_uInt32 FirstId,
+ const char* Strings[],
+ sal_uInt32 Value)
+{
+ int i, n = 0;
+ bool bRet = false;
+
+ while (Strings[n] != nullptr)
+ ++n;
+
+ if ((i = Value - FirstId) >= n)
+ bRet = false;
+ else
+ bRet = osl_writeProfileString(Profile, pszSection, pszEntry, Strings[i]);
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL osl_removeProfileEntry(oslProfile Profile,
+ const char *pszSection,
+ const char *pszEntry)
+{
+ sal_uInt32 NoEntry;
+ osl_TProfileImpl* pProfile = nullptr;
+ osl_TProfileImpl* pTmpProfile = nullptr;
+ bool bRet = false;
+
+ pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);
+
+ if ( pTmpProfile == nullptr )
+ {
+ return false;
+ }
+
+ pthread_mutex_lock(&(pTmpProfile->m_AccessLock));
+
+ if ( !pTmpProfile->m_bIsValid )
+ {
+ SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid");
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+ return false;
+ }
+
+ pProfile = acquireProfile(Profile, true);
+
+ if (pProfile == nullptr)
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ osl_TProfileSection* pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry);
+ if ((pSec != nullptr) &&
+ (NoEntry < pSec->m_NoEntries))
+ {
+ removeLine(pProfile, pSec->m_Entries[NoEntry].m_Line);
+ removeEntry(pSec, NoEntry);
+ if (pSec->m_NoEntries == 0)
+ {
+ removeLine(pProfile, pSec->m_Line);
+
+ /* remove any empty separation line */
+ if ((pSec->m_Line > 0) && (pProfile->m_Lines[pSec->m_Line - 1][0] == '\0'))
+ removeLine(pProfile, pSec->m_Line - 1);
+
+ removeSection(pProfile, pSec);
+ }
+
+ pProfile->m_Flags |= FLG_MODIFIED;
+ }
+ }
+ else
+ { /* not implemented */ }
+
+ bRet = releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return bRet;
+}
+
+sal_uInt32 SAL_CALL osl_getProfileSectionEntries(oslProfile Profile,
+ const char *pszSection,
+ char* pszBuffer,
+ sal_uInt32 MaxLen)
+{
+ sal_uInt32 i, n = 0;
+ sal_uInt32 NoEntry;
+ osl_TProfileImpl* pProfile = nullptr;
+ osl_TProfileImpl* pTmpProfile = nullptr;
+ bool bRet = false;
+
+ pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);
+
+ if ( pTmpProfile == nullptr )
+ {
+ return 0;
+
+ }
+
+ pthread_mutex_lock(&(pTmpProfile->m_AccessLock));
+
+ if ( !pTmpProfile->m_bIsValid )
+ {
+ SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return 0;
+ }
+
+ pProfile = acquireProfile(Profile, false);
+
+ if (pProfile == nullptr)
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return 0;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ osl_TProfileSection* pSec;
+ if ((pSec = findEntry(pProfile, pszSection, "", &NoEntry)) != nullptr)
+ {
+ if (MaxLen != 0)
+ {
+ for (i = 0; i < pSec->m_NoEntries; i++)
+ {
+ if ((n + pSec->m_Entries[i].m_Len + 1) < MaxLen)
+ {
+ strncpy(&pszBuffer[n], &pProfile->m_Lines[pSec->m_Entries[i].m_Line]
+ [pSec->m_Entries[i].m_Offset], pSec->m_Entries[i].m_Len);
+ n += pSec->m_Entries[i].m_Len;
+ pszBuffer[n++] = '\0';
+ }
+ else
+ break;
+
+ }
+
+ pszBuffer[n++] = '\0';
+ }
+ else
+ {
+ for (i = 0; i < pSec->m_NoEntries; i++)
+ n += pSec->m_Entries[i].m_Len + 1;
+
+ n += 1;
+ }
+ }
+ else
+ n = 0;
+ }
+ else {
+ /* not implemented */
+ }
+
+ bRet=releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return n;
+}
+
+sal_uInt32 SAL_CALL osl_getProfileSections(oslProfile Profile,
+ char* pszBuffer,
+ sal_uInt32 MaxLen)
+{
+ sal_uInt32 i, n = 0;
+ osl_TProfileImpl* pProfile = nullptr;
+ osl_TProfileImpl* pTmpProfile = nullptr;
+ bool bRet = false;
+
+ pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);
+
+ if ( pTmpProfile == nullptr )
+ {
+ return 0;
+ }
+
+ pthread_mutex_lock(&(pTmpProfile->m_AccessLock));
+
+ if ( !pTmpProfile->m_bIsValid )
+ {
+ SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid");
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return 0;
+ }
+
+ pProfile = acquireProfile(Profile, false);
+
+ if (pProfile == nullptr)
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return 0;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ if (MaxLen != 0)
+ {
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ osl_TProfileSection* pSec = &pProfile->m_Sections[i];
+
+ if ((n + pSec->m_Len + 1) < MaxLen)
+ {
+ strncpy(&pszBuffer[n], &pProfile->m_Lines[pSec->m_Line][pSec->m_Offset],
+ pSec->m_Len);
+ n += pSec->m_Len;
+ pszBuffer[n++] = '\0';
+ }
+ else
+ break;
+ }
+
+ pszBuffer[n++] = '\0';
+ }
+ else
+ {
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ n += pProfile->m_Sections[i].m_Len + 1;
+
+ n += 1;
+ }
+ }
+ else
+ { /* not implemented */ }
+
+ bRet=releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return n;
+}
+
+static osl_TStamp OslProfile_getFileStamp(osl_TFile* pFile)
+{
+ struct stat status;
+
+ if ( (pFile->m_Handle < 0) || (fstat(pFile->m_Handle, &status) < 0) )
+ {
+ return 0;
+ }
+
+ return status.st_mtime;
+}
+
+static bool OslProfile_lockFile(const osl_TFile* pFile, osl_TLockMode eMode)
+{
+ struct flock lock;
+ static bool const bLockingDisabled = getenv( "STAR_PROFILE_LOCKING_DISABLED" ) != nullptr;
+
+ if (pFile->m_Handle < 0)
+ {
+ return false;
+ }
+
+ if ( bLockingDisabled )
+ {
+ return true;
+ }
+
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+
+ switch (eMode)
+ {
+ case un_lock:
+ lock.l_type = F_UNLCK;
+ break;
+
+ case read_lock:
+ lock.l_type = F_RDLCK;
+ break;
+
+ case write_lock:
+ lock.l_type = F_WRLCK;
+ break;
+ }
+
+#ifndef MACOSX
+ if ( fcntl(pFile->m_Handle, F_SETLKW, &lock) == -1 )
+#else
+ /* Mac OSX will return ENOTSUP for webdav drives so we should ignore it */
+ if ( fcntl(pFile->m_Handle, F_SETLKW, &lock) == -1 && errno != ENOTSUP )
+#endif
+ {
+ SAL_INFO("sal.osl", "fcntl failed: " << UnixErrnoString(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static osl_TFile* openFileImpl(const char* pszFilename, oslProfileOption ProfileFlags )
+{
+ int Flags;
+ osl_TFile* pFile = static_cast<osl_TFile*>(calloc(1, sizeof(osl_TFile)));
+ bool bWriteable = false;
+
+ if ( ProfileFlags & ( osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE ) )
+ {
+ bWriteable = true;
+ }
+
+ if (! bWriteable)
+ {
+ pFile->m_Handle = open(pszFilename, O_RDONLY);
+
+ if (pFile->m_Handle == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "open(" << pszFilename << ",O_RDONLY): " << UnixErrnoString(e));
+ }
+ else
+ SAL_INFO("sal.file", "open(" << pszFilename << ",O_RDONLY) => " << pFile->m_Handle);
+
+ /* mfe: argghh!!! do not check if the file could be opened */
+ /* default mode expects it that way!!! */
+ }
+ else
+ {
+ if (((pFile->m_Handle = open(pszFilename, O_RDWR | O_CREAT | O_EXCL, DEFAULT_PMODE)) < 0) &&
+ ((pFile->m_Handle = open(pszFilename, O_RDWR)) < 0))
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "open(" << pszFilename << ",...): " << UnixErrnoString(e));
+ free(pFile);
+ return nullptr;
+ }
+ else
+ SAL_INFO("sal.file", "open(" << pszFilename << ",...) => " << pFile->m_Handle);
+ }
+
+ /* set close-on-exec flag */
+ if ((Flags = fcntl(pFile->m_Handle, F_GETFD, 0)) != -1)
+ {
+ Flags |= FD_CLOEXEC;
+ int e = fcntl(pFile->m_Handle, F_SETFD, Flags);
+ SAL_INFO_IF(
+ e != 0, "sal.osl",
+ "fcntl to set FD_CLOEXEC failed for " << pszFilename);
+ }
+
+ pFile->m_pWriteBuf=nullptr;
+ pFile->m_nWriteBufFree=0;
+ pFile->m_nWriteBufLen=0;
+
+ if ( ProfileFlags & (osl_Profile_WRITELOCK | osl_Profile_READLOCK ) )
+ {
+ OslProfile_lockFile(pFile, bWriteable ? write_lock : read_lock);
+ }
+
+ return pFile;
+}
+
+static osl_TStamp closeFileImpl(osl_TFile* pFile, oslProfileOption Flags)
+{
+ osl_TStamp stamp = 0;
+
+ if ( pFile == nullptr )
+ {
+ return stamp;
+ }
+
+ if ( pFile->m_Handle >= 0 )
+ {
+ stamp = OslProfile_getFileStamp(pFile);
+
+ if ( Flags & (osl_Profile_WRITELOCK | osl_Profile_READLOCK ) )
+ {
+ OslProfile_lockFile(pFile, un_lock);
+ }
+
+ close(pFile->m_Handle);
+ SAL_INFO("sal.file", "close(" << pFile->m_Handle << ")");
+ pFile->m_Handle = -1;
+ }
+
+ if ( pFile->m_pWriteBuf )
+ {
+ free(pFile->m_pWriteBuf);
+ }
+
+ free(pFile);
+
+ return stamp;
+}
+
+static bool OslProfile_rewindFile(osl_TFile* pFile, bool bTruncate)
+{
+ bool bRet = true;
+
+ if (pFile->m_Handle >= 0)
+ {
+ pFile->m_pReadPtr = pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf);
+
+ bRet = (lseek(pFile->m_Handle, SEEK_SET, 0) == 0);
+
+ if (bTruncate)
+ {
+ bRet &= (ftruncate(pFile->m_Handle, 0) == 0);
+ }
+
+ }
+
+ return bRet;
+}
+
+static char* OslProfile_getLine(osl_TFile* pFile)
+{
+ int Max, Free, nLineBytes = 0;
+ char* pChr;
+ char* pLine = nullptr;
+ char* pNewLine;
+
+ if ( pFile == nullptr )
+ {
+ return nullptr;
+ }
+
+ if (pFile->m_Handle < 0)
+ return nullptr;
+
+ do
+ {
+ int Bytes = sizeof(pFile->m_ReadBuf) - (pFile->m_pReadPtr - pFile->m_ReadBuf);
+
+ if (Bytes <= 1)
+ {
+ /* refill buffer */
+ memcpy(pFile->m_ReadBuf, pFile->m_pReadPtr, Bytes);
+ pFile->m_pReadPtr = pFile->m_ReadBuf;
+
+ Free = sizeof(pFile->m_ReadBuf) - Bytes;
+
+ if ((Max = read(pFile->m_Handle, &pFile->m_ReadBuf[Bytes], Free)) < 0)
+ {
+ SAL_INFO("sal.osl", "read failed: " << UnixErrnoString(errno));
+
+ if( pLine )
+ free( pLine );
+ pLine = nullptr;
+ break;
+ }
+
+ if (Max < Free)
+ {
+ if ((Max == 0) && ! pLine)
+ break;
+
+ pFile->m_ReadBuf[Bytes + Max] = '\0';
+ }
+ }
+
+ for (pChr = pFile->m_pReadPtr;
+ (*pChr != '\n') && (*pChr != '\r') && (*pChr != '\0') &&
+ (pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf) - 1));
+ pChr++);
+
+ Max = pChr - pFile->m_pReadPtr;
+ pNewLine = static_cast<char*>(malloc( nLineBytes + Max + 1 ));
+ if( pLine )
+ {
+ memcpy( pNewLine, pLine, nLineBytes );
+ free( pLine );
+ }
+ memcpy(pNewLine+nLineBytes, pFile->m_pReadPtr, Max);
+ nLineBytes += Max;
+ pNewLine[ nLineBytes ] = 0;
+ pLine = pNewLine;
+
+ if (pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf) - 1))
+ {
+ if (*pChr != '\0')
+ {
+ if ((pChr[0] == '\r') && (pChr[1] == '\n'))
+ pChr += 2;
+ else
+ pChr += 1;
+ }
+
+ if ((pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf))) &&
+ (*pChr == '\0'))
+ pChr = pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf);
+
+ /* setting Max to -1 indicates terminating read loop */
+ Max = -1;
+ }
+
+ pFile->m_pReadPtr = pChr;
+ }
+ while (Max > 0) ;
+
+ return pLine;
+}
+
+static bool OslProfile_putLine(osl_TFile* pFile, const char *pszLine)
+{
+ unsigned int Len = strlen(pszLine);
+
+ if ( pFile == nullptr || pFile->m_Handle < 0 )
+ {
+ return false;
+ }
+
+ if ( pFile->m_pWriteBuf == nullptr )
+ {
+ pFile->m_pWriteBuf = static_cast<char*>(malloc(Len+3));
+ pFile->m_nWriteBufLen = Len+3;
+ pFile->m_nWriteBufFree = Len+3;
+ }
+ else
+ {
+ if ( pFile->m_nWriteBufFree <= Len + 3 )
+ {
+ char* pTmp;
+
+ pTmp=static_cast<char*>(realloc(pFile->m_pWriteBuf,( ( pFile->m_nWriteBufLen + Len ) * 2) ));
+ if ( pTmp == nullptr )
+ {
+ return false;
+ }
+ pFile->m_pWriteBuf = pTmp;
+ pFile->m_nWriteBufFree = pFile->m_nWriteBufFree + pFile->m_nWriteBufLen + ( 2 * Len );
+ pFile->m_nWriteBufLen = ( pFile->m_nWriteBufLen + Len ) * 2;
+ memset( (pFile->m_pWriteBuf) + ( pFile->m_nWriteBufLen - pFile->m_nWriteBufFree ), 0, pFile->m_nWriteBufFree);
+ }
+ }
+
+ memcpy(pFile->m_pWriteBuf + ( pFile->m_nWriteBufLen - pFile->m_nWriteBufFree ),pszLine,Len+1);
+ pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len]='\n';
+ pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len + 1]='\0';
+
+ pFile->m_nWriteBufFree-=Len+1;
+
+ return true;
+}
+
+static char* stripBlanks(char* String, sal_uInt32* pLen)
+{
+ if ( ( pLen != nullptr ) && ( *pLen != 0 ) )
+ {
+ while ((String[*pLen - 1] == ' ') || (String[*pLen - 1] == '\t'))
+ (*pLen)--;
+
+ while ( (*String == ' ') || (*String == '\t') )
+ {
+ String++;
+ (*pLen)--;
+ }
+ }
+ else
+ while ( (*String == ' ') || (*String == '\t') )
+ String++;
+
+ return String;
+}
+
+static char* addLine(osl_TProfileImpl* pProfile, const char* Line)
+{
+ if (pProfile->m_NoLines >= pProfile->m_MaxLines)
+ {
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_MaxLines = LINES_INI;
+ pProfile->m_Lines = static_cast<char **>(calloc(pProfile->m_MaxLines, sizeof(char *)));
+ }
+ else
+ {
+ unsigned int idx=0;
+ unsigned int oldmax=pProfile->m_MaxLines;
+
+ pProfile->m_MaxLines += LINES_ADD;
+ pProfile->m_Lines = static_cast<char **>(realloc(pProfile->m_Lines,
+ pProfile->m_MaxLines * sizeof(char *)));
+ for ( idx = oldmax ; idx < pProfile->m_MaxLines ; ++idx )
+ {
+ pProfile->m_Lines[idx]=nullptr;
+ }
+ }
+ }
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_NoLines = 0;
+ pProfile->m_MaxLines = 0;
+ return nullptr;
+ }
+
+ if ( pProfile->m_Lines[pProfile->m_NoLines] != nullptr )
+ {
+ free(pProfile->m_Lines[pProfile->m_NoLines]);
+ }
+ pProfile->m_Lines[pProfile->m_NoLines++] = strdup(Line);
+
+ return pProfile->m_Lines[pProfile->m_NoLines - 1];
+}
+
+static char* insertLine(osl_TProfileImpl* pProfile, const char* Line, sal_uInt32 LineNo)
+{
+ if (pProfile->m_NoLines >= pProfile->m_MaxLines)
+ {
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_MaxLines = LINES_INI;
+ pProfile->m_Lines = static_cast<char **>(calloc(pProfile->m_MaxLines, sizeof(char *)));
+ }
+ else
+ {
+ pProfile->m_MaxLines += LINES_ADD;
+ pProfile->m_Lines = static_cast<char **>(realloc(pProfile->m_Lines,
+ pProfile->m_MaxLines * sizeof(char *)));
+
+ memset(&pProfile->m_Lines[pProfile->m_NoLines],
+ 0,
+ (pProfile->m_MaxLines - pProfile->m_NoLines - 1) * sizeof(char*));
+ }
+
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_NoLines = 0;
+ pProfile->m_MaxLines = 0;
+ return nullptr;
+ }
+ }
+
+ LineNo = std::min(LineNo, pProfile->m_NoLines);
+
+ if (LineNo < pProfile->m_NoLines)
+ {
+ sal_uInt32 i, n;
+
+ memmove(&pProfile->m_Lines[LineNo + 1], &pProfile->m_Lines[LineNo],
+ (pProfile->m_NoLines - LineNo) * sizeof(char *));
+
+ /* adjust line references */
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ osl_TProfileSection* pSec = &pProfile->m_Sections[i];
+
+ if (pSec->m_Line >= LineNo)
+ pSec->m_Line++;
+
+ for (n = 0; n < pSec->m_NoEntries; n++)
+ if (pSec->m_Entries[n].m_Line >= LineNo)
+ pSec->m_Entries[n].m_Line++;
+ }
+ }
+
+ pProfile->m_NoLines++;
+
+ pProfile->m_Lines[LineNo] = strdup(Line);
+
+ return pProfile->m_Lines[LineNo];
+}
+
+static void removeLine(osl_TProfileImpl* pProfile, sal_uInt32 LineNo)
+{
+ if (LineNo >= pProfile->m_NoLines)
+ return;
+
+ free(pProfile->m_Lines[LineNo]);
+ pProfile->m_Lines[LineNo]=nullptr;
+ if (pProfile->m_NoLines - LineNo > 1)
+ {
+ sal_uInt32 i, n;
+
+ memmove(&pProfile->m_Lines[LineNo], &pProfile->m_Lines[LineNo + 1],
+ (pProfile->m_NoLines - LineNo - 1) * sizeof(char *));
+
+ memset(&pProfile->m_Lines[pProfile->m_NoLines - 1],
+ 0,
+ (pProfile->m_MaxLines - pProfile->m_NoLines) * sizeof(char*));
+
+ /* adjust line references */
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ osl_TProfileSection* pSec = &pProfile->m_Sections[i];
+
+ if (pSec->m_Line > LineNo)
+ pSec->m_Line--;
+
+ for (n = 0; n < pSec->m_NoEntries; n++)
+ if (pSec->m_Entries[n].m_Line > LineNo)
+ pSec->m_Entries[n].m_Line--;
+ }
+ }
+ else
+ {
+ pProfile->m_Lines[LineNo] = nullptr;
+ }
+
+ pProfile->m_NoLines--;
+}
+
+static void setEntry(osl_TProfileImpl* pProfile, osl_TProfileSection* pSection,
+ sal_uInt32 NoEntry, sal_uInt32 Line,
+ char* Entry, sal_uInt32 Len)
+{
+ Entry = stripBlanks(Entry, &Len);
+ pSection->m_Entries[NoEntry].m_Line = Line;
+ pSection->m_Entries[NoEntry].m_Offset = Entry - pProfile->m_Lines[Line];
+ pSection->m_Entries[NoEntry].m_Len = Len;
+}
+
+static bool addEntry(osl_TProfileImpl* pProfile,
+ osl_TProfileSection *pSection,
+ int Line, char* Entry,
+ sal_uInt32 Len)
+{
+ if (pSection != nullptr)
+ {
+ if (pSection->m_NoEntries >= pSection->m_MaxEntries)
+ {
+ if (pSection->m_Entries == nullptr)
+ {
+ pSection->m_MaxEntries = ENTRIES_INI;
+ pSection->m_Entries = static_cast<osl_TProfileEntry *>(malloc(
+ pSection->m_MaxEntries * sizeof(osl_TProfileEntry)));
+ }
+ else
+ {
+ pSection->m_MaxEntries += ENTRIES_ADD;
+ pSection->m_Entries = static_cast<osl_TProfileEntry *>(realloc(pSection->m_Entries,
+ pSection->m_MaxEntries * sizeof(osl_TProfileEntry)));
+ }
+
+ if (pSection->m_Entries == nullptr)
+ {
+ pSection->m_NoEntries = 0;
+ pSection->m_MaxEntries = 0;
+ return false;
+ }
+ }
+
+ pSection->m_NoEntries++;
+
+ Entry = stripBlanks(Entry, &Len);
+ setEntry(pProfile, pSection, pSection->m_NoEntries - 1, Line,
+ Entry, Len);
+
+ return true;
+ }
+
+ return false;
+}
+
+static void removeEntry(osl_TProfileSection *pSection, sal_uInt32 NoEntry)
+{
+ if (NoEntry >= pSection->m_NoEntries)
+ return;
+
+ if (pSection->m_NoEntries - NoEntry > 1)
+ {
+ memmove(&pSection->m_Entries[NoEntry],
+ &pSection->m_Entries[NoEntry + 1],
+ (pSection->m_NoEntries - NoEntry - 1) * sizeof(osl_TProfileEntry));
+ pSection->m_Entries[pSection->m_NoEntries - 1].m_Line=0;
+ pSection->m_Entries[pSection->m_NoEntries - 1].m_Offset=0;
+ pSection->m_Entries[pSection->m_NoEntries - 1].m_Len=0;
+ }
+
+ pSection->m_NoEntries--;
+
+}
+
+static bool addSection(osl_TProfileImpl* pProfile, int Line, const char* Section, sal_uInt32 Len)
+{
+ if (pProfile->m_NoSections >= pProfile->m_MaxSections)
+ {
+ if (pProfile->m_Sections == nullptr)
+ {
+ pProfile->m_MaxSections = SECTIONS_INI;
+ pProfile->m_Sections = static_cast<osl_TProfileSection *>(calloc(pProfile->m_MaxSections, sizeof(osl_TProfileSection)));
+ }
+ else
+ {
+ unsigned int idx=0;
+ unsigned int oldmax=pProfile->m_MaxSections;
+
+ pProfile->m_MaxSections += SECTIONS_ADD;
+ pProfile->m_Sections = static_cast<osl_TProfileSection *>(realloc(pProfile->m_Sections,
+ pProfile->m_MaxSections * sizeof(osl_TProfileSection)));
+ for ( idx = oldmax ; idx < pProfile->m_MaxSections ; ++idx )
+ {
+ pProfile->m_Sections[idx].m_Entries=nullptr;
+ }
+ }
+
+ if (pProfile->m_Sections == nullptr)
+ {
+ pProfile->m_NoSections = 0;
+ pProfile->m_MaxSections = 0;
+ return false;
+ }
+ }
+
+ pProfile->m_NoSections++;
+
+ if ( pProfile->m_Sections[(pProfile->m_NoSections) - 1].m_Entries != nullptr )
+ {
+ free(pProfile->m_Sections[(pProfile->m_NoSections) - 1].m_Entries);
+ }
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Entries = nullptr;
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_NoEntries = 0;
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_MaxEntries = 0;
+
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Line = Line;
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Offset = Section - pProfile->m_Lines[Line];
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Len = Len;
+
+ return true;
+}
+
+static void removeSection(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection)
+{
+ sal_uInt32 Section;
+
+ if ((Section = pSection - pProfile->m_Sections) >= pProfile->m_NoSections)
+ return;
+
+ free (pSection->m_Entries);
+ pSection->m_Entries=nullptr;
+ if (pProfile->m_NoSections - Section > 1)
+ {
+ memmove(&pProfile->m_Sections[Section], &pProfile->m_Sections[Section + 1],
+ (pProfile->m_NoSections - Section - 1) * sizeof(osl_TProfileSection));
+
+ memset(&pProfile->m_Sections[pProfile->m_NoSections - 1],
+ 0,
+ (pProfile->m_MaxSections - pProfile->m_NoSections) * sizeof(osl_TProfileSection));
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Entries = nullptr;
+ }
+ else
+ {
+ pSection->m_Entries = nullptr;
+ }
+
+ pProfile->m_NoSections--;
+}
+
+static osl_TProfileSection* findEntry(osl_TProfileImpl* pProfile,
+ const char* Section,
+ const char* Entry,
+ sal_uInt32 *pNoEntry)
+{
+ static sal_uInt32 Sect = 0;
+ sal_uInt32 i, n;
+ sal_uInt32 Len;
+ osl_TProfileSection* pSec=nullptr;
+
+ Len = strlen(Section);
+
+ n = Sect;
+
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ n %= pProfile->m_NoSections;
+ pSec = &pProfile->m_Sections[n];
+ if ((Len == pSec->m_Len) &&
+ (strncasecmp(Section, &pProfile->m_Lines[pSec->m_Line][pSec->m_Offset], pSec->m_Len)
+ == 0))
+ break;
+ n++;
+ }
+
+ Sect = n;
+
+ if (i < pProfile->m_NoSections)
+ {
+ Len = strlen(Entry);
+
+ *pNoEntry = pSec->m_NoEntries;
+
+ for (i = 0; i < pSec->m_NoEntries; i++)
+ {
+ const char* pStr = &pProfile->m_Lines[pSec->m_Entries[i].m_Line]
+ [pSec->m_Entries[i].m_Offset];
+ if ((Len == pSec->m_Entries[i].m_Len) &&
+ (strncasecmp(Entry, pStr, pSec->m_Entries[i].m_Len)
+ == 0))
+ {
+ *pNoEntry = i;
+ break;
+ }
+ }
+ }
+ else
+ pSec = nullptr;
+
+ return pSec;
+}
+
+static bool loadProfile(osl_TFile* pFile, osl_TProfileImpl* pProfile)
+{
+ sal_uInt32 i;
+ char* pStr;
+ char* pChar;
+
+ char* pLine;
+
+ if ( !pFile )
+ {
+ return false;
+ }
+
+ if ( !pProfile )
+ {
+ return false;
+ }
+
+ pProfile->m_NoLines = 0;
+ pProfile->m_NoSections = 0;
+
+ OSL_VERIFY(OslProfile_rewindFile(pFile, false));
+
+ while ( ( pLine=OslProfile_getLine(pFile) ) != nullptr )
+ {
+ char* bWasAdded = addLine( pProfile, pLine );
+ free( pLine );
+ SAL_WARN_IF(!bWasAdded, "sal.osl", "addLine( pProfile, pLine ) ==> false");
+ if ( ! bWasAdded )
+ return false;
+ }
+
+ for (i = 0; i < pProfile->m_NoLines; i++)
+ {
+ pStr = stripBlanks(pProfile->m_Lines[i], nullptr);
+
+ if ((*pStr == '\0') || (*pStr == ';'))
+ continue;
+
+ if ((*pStr != '[') || ((pChar = strrchr(pStr, ']')) == nullptr) ||
+ ((pChar - pStr) <= 2))
+ {
+ /* insert entry */
+
+ if (pProfile->m_NoSections < 1)
+ continue;
+
+ if ((pChar = strchr(pStr, '=')) == nullptr)
+ pChar = pStr + strlen(pStr);
+
+ if (! addEntry(pProfile, &pProfile->m_Sections[pProfile->m_NoSections - 1],
+ i, pStr, pChar - pStr))
+ {
+ SAL_WARN("sal.osl", "Adding entry => false");
+ continue;
+ }
+
+ }
+ else
+ {
+ /* new section */
+
+ if (! addSection(pProfile, i, pStr + 1, pChar - pStr - 1))
+ {
+ SAL_WARN("sal.osl", "Adding section => false");
+ continue;
+ }
+
+ }
+ }
+
+ return true;
+}
+
+static bool storeProfile(osl_TProfileImpl* pProfile, bool bCleanup)
+{
+ if (pProfile->m_Lines != nullptr)
+ {
+ if (pProfile->m_Flags & FLG_MODIFIED)
+ {
+ sal_uInt32 i;
+
+ osl_TFile* pTmpFile = osl_openTmpProfileImpl(pProfile);
+
+ if ( pTmpFile == nullptr )
+ {
+ return false;
+ }
+
+ OSL_VERIFY(OslProfile_rewindFile(pTmpFile, true));
+
+ for ( i = 0 ; i < pProfile->m_NoLines ; i++ )
+ {
+ OSL_VERIFY(OslProfile_putLine(pTmpFile, pProfile->m_Lines[i]));
+ }
+
+ if ( ! writeProfileImpl(pTmpFile) )
+ {
+ if ( pTmpFile->m_pWriteBuf != nullptr )
+ {
+ free(pTmpFile->m_pWriteBuf);
+ }
+
+ pTmpFile->m_pWriteBuf=nullptr;
+ pTmpFile->m_nWriteBufLen=0;
+ pTmpFile->m_nWriteBufFree=0;
+
+ closeFileImpl(pTmpFile,pProfile->m_Flags);
+
+ return false;
+ }
+
+ pProfile->m_Flags &= ~FLG_MODIFIED;
+
+ closeFileImpl(pProfile->m_pFile,pProfile->m_Flags);
+ closeFileImpl(pTmpFile,pProfile->m_Flags);
+
+ osl_ProfileSwapProfileNames(pProfile);
+
+ pProfile->m_pFile = openFileImpl(pProfile->m_FileName,pProfile->m_Flags);
+
+ }
+
+ if (bCleanup)
+ {
+ while (pProfile->m_NoLines > 0)
+ removeLine(pProfile, pProfile->m_NoLines - 1);
+
+ free(pProfile->m_Lines);
+ pProfile->m_Lines = nullptr;
+ pProfile->m_NoLines = 0;
+ pProfile->m_MaxLines = 0;
+
+ while (pProfile->m_NoSections > 0)
+ removeSection(pProfile, &pProfile->m_Sections[pProfile->m_NoSections - 1]);
+
+ free(pProfile->m_Sections);
+ pProfile->m_Sections = nullptr;
+ pProfile->m_NoSections = 0;
+ pProfile->m_MaxSections = 0;
+ }
+ }
+
+ return true;
+}
+
+static osl_TFile* osl_openTmpProfileImpl(osl_TProfileImpl* pProfile)
+{
+ osl_TFile* pFile=nullptr;
+ char const * const pszExtension = "tmp";
+ char pszTmpName[PATH_MAX];
+ oslProfileOption PFlags=0;
+
+ pszTmpName[0] = '\0';
+
+ /* generate tmp profilename */
+ osl_ProfileGenerateExtension(pProfile->m_FileName, pszExtension, pszTmpName, PATH_MAX);
+
+ if ( pszTmpName[0] == 0 )
+ {
+ return nullptr;
+ }
+
+ if ( ! ( pProfile->m_Flags & osl_Profile_READLOCK ) )
+ {
+ PFlags |= osl_Profile_WRITELOCK;
+ }
+
+ /* open this file */
+ pFile = openFileImpl(pszTmpName,pProfile->m_Flags | PFlags);
+
+ /* return new pFile */
+ return pFile;
+}
+
+static bool osl_ProfileSwapProfileNames(osl_TProfileImpl* pProfile)
+{
+ char pszBakFile[PATH_MAX];
+ char pszTmpFile[PATH_MAX];
+
+ pszBakFile[0] = '\0';
+ pszTmpFile[0] = '\0';
+
+ osl_ProfileGenerateExtension(pProfile->m_FileName, "bak", pszBakFile, PATH_MAX);
+ osl_ProfileGenerateExtension(pProfile->m_FileName, "tmp", pszTmpFile, PATH_MAX);
+
+ /* unlink bak */
+ unlink( pszBakFile );
+
+ // Rename ini -> bak, then tmp -> ini:
+ bool result = rename( pProfile->m_FileName, pszBakFile ) == 0;
+ if (!result)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "rename(" << pProfile->m_FileName << "," << pszBakFile << "): " << UnixErrnoString(e));
+ }
+ else
+ {
+ SAL_INFO("sal.file", "rename(" << pProfile->m_FileName << "," << pszBakFile << "): OK");
+ result = rename( pszTmpFile, pProfile->m_FileName ) == 0;
+ if (!result)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "rename(" << pszTmpFile << "," << pProfile->m_FileName << "): " << UnixErrnoString(e));
+ }
+ else
+ {
+ SAL_INFO("sal.file", "rename(" << pszTmpFile << "," << pProfile->m_FileName << "): OK");
+ }
+ }
+ return result;
+}
+
+static void osl_ProfileGenerateExtension(const char* pszFileName, const char* pszExtension, char* pszTmpName, int BufferMaxLen)
+{
+ char* cursor = pszTmpName;
+ int len;
+
+ /* concatenate filename + "." + extension, limited to the size of the
+ * output buffer; in case of overrun, data is truncated at the end...
+ * and the result is always 0-terminated.
+ */
+ len = strlen(pszFileName);
+ if(len < BufferMaxLen)
+ {
+ memcpy(cursor, pszFileName, len);
+ cursor += len;
+ BufferMaxLen -= len;
+ }
+ else
+ {
+ memcpy(cursor, pszFileName, BufferMaxLen - 1);
+ cursor += BufferMaxLen - 1;
+ BufferMaxLen = 1;
+ }
+ if(BufferMaxLen > 1)
+ {
+ *cursor++ = '.';
+ BufferMaxLen -= 1;
+ }
+ len = strlen(pszExtension);
+ if(len < BufferMaxLen)
+ {
+ memcpy(cursor, pszExtension, len);
+ cursor += len;
+ }
+ else
+ {
+ memcpy(cursor, pszExtension, BufferMaxLen - 1);
+ cursor += BufferMaxLen - 1;
+ }
+ *cursor = 0;
+}
+
+static osl_TProfileImpl* acquireProfile(oslProfile Profile, bool bWriteable)
+{
+ osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
+ oslProfileOption PFlags=0;
+
+ if ( bWriteable )
+ {
+ PFlags = osl_Profile_DEFAULT | osl_Profile_WRITELOCK;
+ }
+ else
+ {
+ PFlags = osl_Profile_DEFAULT;
+ }
+
+ if (pProfile == nullptr)
+ {
+ if ( ( pProfile = static_cast<osl_TProfileImpl*>(osl_openProfile(nullptr, PFlags )) ) != nullptr )
+ {
+ pProfile->m_Flags |= FLG_AUTOOPEN;
+ }
+ }
+ else
+ {
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ if (! (pProfile->m_Flags & (osl_Profile_READLOCK | osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE )))
+ {
+ osl_TStamp Stamp;
+
+ if (! (pProfile->m_pFile = openFileImpl(pProfile->m_FileName, pProfile->m_Flags | PFlags )))
+ return nullptr;
+
+ Stamp = OslProfile_getFileStamp(pProfile->m_pFile);
+
+ if (memcmp(&Stamp, &(pProfile->m_Stamp), sizeof(osl_TStamp)))
+ {
+ pProfile->m_Stamp = Stamp;
+ bool bRet = loadProfile(pProfile->m_pFile, pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "loadProfile(pProfile->m_pFile, pProfile) ==> false");
+ }
+ }
+ else
+ {
+ /* A readlock file could not be written */
+ if ((pProfile->m_Flags & osl_Profile_READLOCK) && bWriteable)
+ {
+ return nullptr;
+ }
+ }
+ }
+ }
+
+ return pProfile;
+}
+
+static bool releaseProfile(osl_TProfileImpl* pProfile)
+{
+ if ( pProfile == nullptr )
+ {
+ return false;
+ }
+
+ if (pProfile->m_Flags & FLG_AUTOOPEN)
+ {
+ return osl_closeProfile(static_cast<oslProfile>(pProfile));
+ }
+
+ if (! (pProfile->m_Flags & (osl_Profile_READLOCK | osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE )))
+ {
+ if (pProfile->m_Flags & FLG_MODIFIED)
+ {
+ bool bRet = storeProfile(pProfile, false);
+ SAL_WARN_IF(!bRet, "sal.osl", "storeProfile(pProfile, false) ==> false");
+ }
+
+ closeFileImpl(pProfile->m_pFile,pProfile->m_Flags);
+ pProfile->m_pFile = nullptr;
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/random.cxx b/sal/osl/unx/random.cxx
new file mode 100644
index 000000000..260aa61ba
--- /dev/null
+++ b/sal/osl/unx/random.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "system.hxx"
+#include <oslrandom.h>
+
+#include <assert.h>
+
+int osl_get_system_random_data(char* buffer, size_t desired_len)
+{
+ int fd;
+
+ assert(buffer);
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd != -1)
+ {
+ while (desired_len)
+ {
+ ssize_t nb_read;
+ if ((nb_read = read(fd, buffer, desired_len)) == -1)
+ {
+ if (errno != EINTR)
+ {
+ close(fd);
+ return false;
+ }
+ }
+ else
+ {
+ buffer += nb_read;
+ desired_len -= nb_read;
+ }
+ }
+ close(fd);
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/readwrite_helper.cxx b/sal/osl/unx/readwrite_helper.cxx
new file mode 100644
index 000000000..49c44c2c0
--- /dev/null
+++ b/sal/osl/unx/readwrite_helper.cxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <limits>
+
+#include "readwrite_helper.hxx"
+
+#include "system.hxx"
+
+namespace {
+
+std::size_t cap_ssize_t(std::size_t value) {
+ return std::min(value, std::size_t(std::numeric_limits<ssize_t>::max()));
+}
+
+}
+
+bool safeWrite(int fd, void* data, std::size_t dataSize)
+{
+ auto nToWrite = dataSize;
+ unsigned char* dataToWrite = static_cast<unsigned char *>(data);
+
+ while ( nToWrite ) {
+ auto nWritten = write(fd, dataToWrite, cap_ssize_t(nToWrite));
+ if ( nWritten < 0 ) {
+ if ( errno == EINTR )
+ continue;
+
+ return false;
+
+ }
+
+ assert(nWritten > 0);
+ nToWrite -= nWritten;
+ dataToWrite += nWritten;
+ }
+
+ return true;
+}
+
+bool safeRead( int fd, void* buffer, std::size_t count )
+{
+ auto nToRead = count;
+ unsigned char* bufferForReading = static_cast<unsigned char *>(buffer);
+
+ while ( nToRead ) {
+ auto nRead = read(fd, bufferForReading, cap_ssize_t(nToRead));
+ if ( nRead < 0 ) {
+ // We were interrupted before reading, retry.
+ if (errno == EINTR)
+ continue;
+
+ return false;
+ }
+
+ // If we reach the EOF, we consider this a partial transfer and thus
+ // an error.
+ if ( nRead == 0 )
+ return false;
+
+ nToRead -= nRead;
+ bufferForReading += nRead;
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/readwrite_helper.hxx b/sal/osl/unx/readwrite_helper.hxx
new file mode 100644
index 000000000..133ccd016
--- /dev/null
+++ b/sal/osl/unx/readwrite_helper.hxx
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_READWRITE_HELPER_HXX
+#define INCLUDED_SAL_OSL_UNX_READWRITE_HELPER_HXX
+
+#include <sal/config.h>
+
+#include <cstddef>
+
+bool safeWrite(int fd, void* data, std::size_t dataSize);
+
+// This function *will* read |count| bytes from |fd|, busy looping
+// if needed. Don't use it when you don't know if you can request enough
+// data. It will return sal_False for any partial transfer or error.
+bool safeRead(int fd, void* buffer, std::size_t count);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/salinit.cxx b/sal/osl/unx/salinit.cxx
new file mode 100644
index 000000000..c55cb2667
--- /dev/null
+++ b/sal/osl/unx/salinit.cxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <sal/config.h>
+
+#if defined MACOSX
+#include <cassert>
+#include <limits>
+#include <unistd.h>
+#include <sys/stat.h>
+#endif
+
+#include <config_global.h>
+#include <osl/process.h>
+#include <sal/main.h>
+#include <sal/types.h>
+
+#include "saltime.hxx"
+#include "soffice.hxx"
+#include <salusesyslog.hxx>
+
+#if HAVE_SYSLOG_H
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#endif
+
+extern "C" {
+
+void sal_detail_initialize(int argc, char ** argv) {
+ if (argc == sal::detail::InitializeSoffice)
+ {
+ sal::detail::setSoffice();
+ return;
+ }
+#if defined MACOSX && !HAVE_FEATURE_MACOSX_SANDBOX
+ // On macOS when not sandboxed, soffice can restart itself via exec (see
+ // restartOnMac in desktop/source/app/app.cxx), which leaves all file
+ // descriptors open, which in turn can have unwanted effects (see
+ // <https://bugs.libreoffice.org/show_bug.cgi?id=50603> "Unable to update
+ // LibreOffice without resetting user profile"). But closing fds in
+ // restartOnMac before calling exec does not work, as additional threads
+ // might still be running then, which can still use those fds and cause
+ // crashes. Therefore, the simplest solution is to close fds at process
+ // start (as early as possible, so that no other threads have been created
+ // yet that might already have opened some fds); this is done for all kinds
+ // of processes here, not just soffice, but hopefully none of our processes
+ // rely on being spawned with certain fds already open. Unfortunately, Mac
+ // macOS appears to have no better interface to close all fds (like
+ // closefrom):
+ long openMax = sysconf(_SC_OPEN_MAX);
+ // When LibreOffice restarts itself on macOS 11 beta on arm64, for
+ // some reason sysconf(_SC_OPEN_MAX) returns 0x7FFFFFFFFFFFFFFF,
+ // so use a sanity limit here.
+ if (openMax == -1 || openMax == std::numeric_limits<long>::max()) {
+ openMax = 100000;
+ }
+ assert(openMax >= 0 && openMax <= std::numeric_limits< int >::max());
+ for (int fd = 3; fd < int(openMax); ++fd) {
+ struct stat s;
+ if (fstat(fd, &s) != -1 && S_ISREG(s.st_mode))
+ close(fd);
+ }
+#endif
+ sal_initGlobalTimer();
+#if HAVE_SYSLOG_H
+ const char *use_syslog = getenv("SAL_LOG_SYSLOG");
+ sal_use_syslog = use_syslog != nullptr && !strcmp(use_syslog, "1");
+ if (sal_use_syslog)
+ openlog("libreoffice", 0, LOG_USER);
+#endif
+
+ osl_setCommandArgs(argc, argv);
+}
+
+void sal_detail_deinitialize() {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/saltime.hxx b/sal/osl/unx/saltime.hxx
new file mode 100644
index 000000000..03e8047e0
--- /dev/null
+++ b/sal/osl/unx/saltime.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_SALTIME_HXX
+#define INCLUDED_SAL_OSL_UNX_SALTIME_HXX
+
+#include <sal/config.h>
+
+void sal_initGlobalTimer();
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/secimpl.hxx b/sal/osl/unx/secimpl.hxx
new file mode 100644
index 000000000..1577d6609
--- /dev/null
+++ b/sal/osl/unx/secimpl.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_SECIMPL_HXX
+#define INCLUDED_SAL_OSL_UNX_SECIMPL_HXX
+
+#include <pwd.h>
+
+struct oslSecurityImpl
+{
+ struct passwd m_pPasswd;
+ char m_buffer[1]; /* should be a C99 flexible array member */
+};
+
+bool osl_psz_getUserIdent(oslSecurity Security, char* pszIdent, sal_uInt32 nMax);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/security.cxx b/sal/osl/unx/security.cxx
new file mode 100644
index 000000000..f541c9000
--- /dev/null
+++ b/sal/osl/unx/security.cxx
@@ -0,0 +1,537 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <limits>
+
+#ifdef IOS
+#include <premac.h>
+#import <Foundation/Foundation.h>
+#include <postmac.h>
+#endif
+
+#include "system.hxx"
+
+#include <o3tl/safeint.hxx>
+#include <osl/security.h>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+
+#include <osl/thread.h>
+#include <osl/file.h>
+
+#if defined LINUX || defined __sun
+#include <crypt.h>
+#endif
+
+#if defined HAIKU
+#include <fs_info.h>
+#include <FindDirectory.h>
+#endif
+
+#include "secimpl.hxx"
+
+#ifdef ANDROID
+#define getpwuid_r(uid, pwd, buf, buflen, result) (*(result) = getpwuid(uid), (*(result) ? (memcpy (buf, *(result), sizeof (struct passwd)), 0) : errno))
+#endif
+
+static bool osl_psz_getHomeDir(oslSecurity Security, OString* pszDirectory);
+static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory);
+
+static bool sysconf_SC_GETPW_R_SIZE_MAX(std::size_t * value) {
+#if defined _SC_GETPW_R_SIZE_MAX
+ long m;
+ errno = 0;
+ m = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (m == -1) {
+ /* _SC_GETPW_R_SIZE_MAX has no limit; some platforms like certain
+ FreeBSD versions support sysconf(_SC_GETPW_R_SIZE_MAX) in a broken
+ way and always set EINVAL, so be resilient here: */
+ return false;
+ }
+ SAL_WARN_IF( m < 0 || o3tl::make_unsigned(m) >= std::numeric_limits<std::size_t>::max(), "sal.osl",
+ "m < 0 || (unsigned long) m >= std::numeric_limits<std::size_t>::max()");
+ *value = static_cast<std::size_t>(m);
+ return true;
+#else
+ /* some platforms like macOS 1.3 do not define _SC_GETPW_R_SIZE_MAX: */
+ return false;
+#endif
+}
+
+static oslSecurityImpl * growSecurityImpl(
+ oslSecurityImpl * impl, std::size_t * bufSize)
+{
+ std::size_t n = 0;
+ oslSecurityImpl * p = nullptr;
+ if (impl == nullptr) {
+ if (!sysconf_SC_GETPW_R_SIZE_MAX(&n)) {
+ /* choose something sensible (the callers of growSecurityImpl will
+ detect it if the allocated buffer is too small: */
+ n = 1024;
+ }
+ } else if (*bufSize <= std::numeric_limits<std::size_t>::max() / 2) {
+ n = 2 * *bufSize;
+ }
+ if (n != 0) {
+ if (n <= std::numeric_limits<std::size_t>::max()
+ - offsetof(oslSecurityImpl, m_buffer))
+ {
+ *bufSize = n;
+ n += offsetof(oslSecurityImpl, m_buffer);
+ } else {
+ *bufSize = std::numeric_limits<std::size_t>::max()
+ - offsetof(oslSecurityImpl, m_buffer);
+ n = std::numeric_limits<std::size_t>::max();
+ }
+ p = static_cast<oslSecurityImpl *>(realloc(impl, n));
+ memset (p, 0, n);
+ }
+ if (p == nullptr) {
+ free(impl);
+ }
+ return p;
+}
+
+static void deleteSecurityImpl(oslSecurityImpl * impl) {
+ free(impl);
+}
+
+oslSecurity SAL_CALL osl_getCurrentSecurity()
+{
+ std::size_t n = 0;
+ oslSecurityImpl * p = nullptr;
+ for (;;) {
+ struct passwd * found;
+ p = growSecurityImpl(p, &n);
+ if (p == nullptr) {
+ return nullptr;
+ }
+#if (defined(IOS) && defined(X86_64)) || defined(EMSCRIPTEN)
+ // getpwuid_r() does not work in the iOS simulator
+ (void) found;
+ char * buffer = p->m_buffer;
+ assert(n >= 100);
+ strcpy(buffer, "mobile");
+ p->m_pPasswd.pw_name = buffer;
+ buffer += strlen(buffer) + 1;
+ strcpy(buffer, "*");
+ p->m_pPasswd.pw_passwd = buffer;
+ buffer += strlen(buffer) + 1;
+ p->m_pPasswd.pw_uid = geteuid();
+ p->m_pPasswd.pw_gid = getegid();
+#if !defined(EMSCRIPTEN)
+ p->m_pPasswd.pw_change = 0;
+ strcpy(buffer, "");
+ p->m_pPasswd.pw_class = buffer;
+ buffer += strlen(buffer) + 1;
+ p->m_pPasswd.pw_expire = 0;
+#endif
+ strcpy(buffer, "Mobile User");
+ p->m_pPasswd.pw_gecos = buffer;
+ buffer += strlen(buffer) + 1;
+ strcpy(buffer, "/var/mobile"); // ???
+ p->m_pPasswd.pw_dir = buffer;
+ buffer += strlen(buffer) + 1;
+ strcpy(buffer, "");
+ p->m_pPasswd.pw_shell = buffer;
+ buffer += strlen(buffer) + 1;
+ return p;
+#else
+ switch (getpwuid_r(getuid(), &p->m_pPasswd, p->m_buffer, n, &found)) {
+ case ERANGE:
+ break;
+ case 0:
+ if (found != nullptr) {
+ return p;
+ }
+ [[fallthrough]];
+ default:
+ deleteSecurityImpl(p);
+ return nullptr;
+ }
+#endif
+ }
+}
+
+oslSecurityError SAL_CALL osl_loginUser(
+ SAL_UNUSED_PARAMETER rtl_uString *,
+ SAL_UNUSED_PARAMETER rtl_uString *,
+ SAL_UNUSED_PARAMETER oslSecurity *
+ )
+{
+ return osl_Security_E_None;
+}
+
+oslSecurityError SAL_CALL osl_loginUserOnFileServer(
+ SAL_UNUSED_PARAMETER rtl_uString *,
+ SAL_UNUSED_PARAMETER rtl_uString *,
+ SAL_UNUSED_PARAMETER rtl_uString *,
+ SAL_UNUSED_PARAMETER oslSecurity *
+ )
+{
+ return osl_Security_E_UserUnknown;
+}
+
+sal_Bool SAL_CALL osl_getUserIdent(oslSecurity Security, rtl_uString **ustrIdent)
+{
+ bool bRet = false;
+ char pszIdent[1024];
+
+ pszIdent[0] = '\0';
+
+ bRet = osl_psz_getUserIdent(Security,pszIdent,sizeof(pszIdent));
+
+ rtl_string2UString( ustrIdent, pszIdent, rtl_str_getLength( pszIdent ), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
+ SAL_WARN_IF(*ustrIdent == nullptr, "sal.osl", "*ustrIdent == NULL");
+
+ return bRet;
+}
+
+bool osl_psz_getUserIdent(oslSecurity Security, char *pszIdent, sal_uInt32 nMax)
+{
+ char buffer[32];
+ sal_Int32 nChr;
+
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
+
+ if (pSecImpl == nullptr)
+ return false;
+
+ nChr = snprintf(buffer, sizeof(buffer), "%u", pSecImpl->m_pPasswd.pw_uid);
+ if ( nChr < 0 || sal::static_int_cast<sal_uInt32>(nChr) >= sizeof(buffer)
+ || sal::static_int_cast<sal_uInt32>(nChr) >= nMax )
+ return false; /* leave *pszIdent unmodified in case of failure */
+
+ memcpy(pszIdent, buffer, nChr+1);
+ return true;
+}
+
+sal_Bool SAL_CALL osl_getUserName(oslSecurity Security, rtl_uString **ustrName)
+{
+ bool bRet = false;
+ char * pszName;
+ sal_Int32 len;
+
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
+
+ if (pSecImpl != nullptr && pSecImpl->m_pPasswd.pw_name != nullptr) {
+ pszName = pSecImpl->m_pPasswd.pw_name;
+ auto const n = std::strlen(pszName);
+ if (n <= o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
+ len = n;
+ bRet = true;
+ }
+ }
+
+ if (!bRet) {
+ pszName = nullptr;
+ len = 0;
+ }
+
+ rtl_string2UString( ustrName, pszName, len, osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
+ SAL_WARN_IF(*ustrName == nullptr, "sal.osl", "ustrName == NULL");
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL osl_getShortUserName(oslSecurity Security, rtl_uString **ustrName)
+{
+ return osl_getUserName(Security, ustrName); // No domain name on unix
+}
+
+sal_Bool SAL_CALL osl_getHomeDir(oslSecurity Security, rtl_uString **pustrDirectory)
+{
+ bool bRet = false;
+ OString pszDirectory;
+
+ bRet = osl_psz_getHomeDir(Security,&pszDirectory);
+
+ if ( bRet )
+ {
+ rtl_string2UString( pustrDirectory, pszDirectory.getStr(), pszDirectory.getLength(), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
+ SAL_WARN_IF(*pustrDirectory == nullptr, "sal.osl", "*pustrDirectory == NULL");
+ osl_getFileURLFromSystemPath( *pustrDirectory, pustrDirectory );
+ }
+
+ return bRet;
+}
+
+static bool osl_psz_getHomeDir(oslSecurity Security, OString* pszDirectory)
+{
+ assert(pszDirectory != nullptr);
+
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
+
+ if (pSecImpl == nullptr)
+ return false;
+
+#ifdef HAIKU
+ dev_t volume = dev_for_path("/boot");
+ char homeDir[B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH];
+ status_t result = find_directory(B_USER_DIRECTORY, volume, false, homeDir,
+ sizeof(homeDir));
+ if (result == B_OK) {
+ static_assert(
+ B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH <= std::numeric_limits<sal_Int32>::max());
+ *pszDirectory = OString(homeDir, std::strlen(homeDir));
+ return true;
+ }
+ return false;
+#endif
+
+#ifdef ANDROID
+{
+ OUString pValue;
+
+ if (rtl::Bootstrap::get("HOME", pValue))
+ {
+ auto const pStrValue = OUStringToOString(pValue, RTL_TEXTENCODING_UTF8);
+ if (!pStrValue.isEmpty())
+ {
+ *pszDirectory = pStrValue;
+ return true;
+ }
+ }
+}
+#endif
+
+#ifdef IOS
+ {
+ // Let's pretend the app-specific "Documents" directory is the home directory for now
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *userDirectory = [paths objectAtIndex:0];
+ auto const len = [userDirectory length];
+ if (len <= std::numeric_limits<sal_Int32>::max())
+ {
+ *pszDirectory = OString([userDirectory UTF8String], len);
+ return sal_True;
+ }
+ }
+#endif
+
+ /* if current user, check also environment for HOME */
+ if (getuid() == pSecImpl->m_pPasswd.pw_uid)
+ {
+ char *pStr = nullptr;
+#ifdef __sun
+ char buffer[8192];
+
+ struct passwd pwd;
+ struct passwd *ppwd;
+
+#ifdef _POSIX_PTHREAD_SEMANTICS
+ if ( 0 != getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &ppwd ) )
+ ppwd = NULL;
+#else
+ ppwd = getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer) );
+#endif
+
+ if ( ppwd )
+ pStr = ppwd->pw_dir;
+#else
+ pStr = getenv("HOME");
+#endif
+
+ if (pStr != nullptr && pStr[0] != '\0' && access(pStr, 0) == 0)
+ {
+ auto const len = std::strlen(pStr);
+ if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
+ return false;
+ }
+ *pszDirectory = OString(pStr, len);
+ return true;
+ }
+ }
+ if (pSecImpl->m_pPasswd.pw_dir != nullptr)
+ {
+ auto const len = std::strlen(pSecImpl->m_pPasswd.pw_dir);
+ if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
+ return false;
+ }
+ *pszDirectory = OString(pSecImpl->m_pPasswd.pw_dir, len);
+ }
+ else
+ return false;
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_getConfigDir(oslSecurity Security, rtl_uString **pustrDirectory)
+{
+ bool bRet = false;
+ OString pszDirectory;
+
+ bRet = osl_psz_getConfigDir(Security,&pszDirectory);
+
+ if ( bRet )
+ {
+ rtl_string2UString( pustrDirectory, pszDirectory.getStr(), pszDirectory.getLength(), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
+ SAL_WARN_IF(*pustrDirectory == nullptr, "sal.osl", "*pustrDirectory == NULL");
+ osl_getFileURLFromSystemPath( *pustrDirectory, pustrDirectory );
+ }
+
+ return bRet;
+}
+
+#if defined HAIKU
+
+static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
+{
+ assert(pszDirectory != nullptr);
+ (void) Security;
+ dev_t volume = dev_for_path("/boot");
+ char configDir[B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH];
+ status_t result = find_directory(B_USER_SETTINGS_DIRECTORY, volume, false,
+ configDir, sizeof(configDir));
+ if (result == B_OK) {
+ auto const len = strlen(configDir);
+ if (len <= sal_uInt32(std::numeric_limits<sal_Int32>::max())) {
+ *pszDirectory = OString(configDir, len);
+ return true;
+ }
+ }
+ return false;
+}
+
+#elif !defined(MACOSX) && !defined(IOS)
+
+static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
+{
+ assert(pszDirectory != nullptr);
+
+ char *pStr = getenv("XDG_CONFIG_HOME");
+
+ if (pStr == nullptr || pStr[0] == '\0' || access(pStr, 0) != 0)
+ {
+ // a default equal to $HOME/.config should be used.
+ OString home;
+ if (!osl_psz_getHomeDir(Security, &home))
+ return false;
+ auto const config = OString(home + "/.config");
+
+ // try to create dir if not present
+ bool dirOK = true;
+ if (mkdir(config.getStr(), S_IRWXU) != 0)
+ {
+ int e = errno;
+ if (e != EEXIST)
+ {
+ SAL_WARN(
+ "sal.osl",
+ "mkdir(" << config << "): errno=" << e);
+ dirOK = false;
+ }
+ }
+ if (dirOK)
+ {
+ // check file type and permissions
+ struct stat st;
+ if (stat(config.getStr(), &st) != 0)
+ {
+ SAL_INFO("sal.osl","Could not stat $HOME/.config");
+ dirOK = false;
+ }
+ else
+ {
+ if (!S_ISDIR(st.st_mode))
+ {
+ SAL_INFO("sal.osl", "$HOME/.config is not a directory");
+ dirOK = false;
+ }
+ if (!(st.st_mode & S_IRUSR && st.st_mode & S_IWUSR && st.st_mode & S_IXUSR))
+ {
+ SAL_INFO("sal.osl", "$HOME/.config has bad permissions");
+ dirOK = false;
+ }
+ }
+ }
+
+ // if !dirOK, resort to HOME
+ if (dirOK)
+ home = config;
+ *pszDirectory = home;
+ }
+ else
+ {
+ auto const len = std::strlen(pStr);
+ if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
+ return false;
+ }
+ *pszDirectory = OString(pStr, len);
+ }
+
+ return true;
+}
+
+#else
+
+/*
+ * FIXME: rewrite to use more flexible
+ * NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)
+ * as soon as we can bump the baseline to Tiger (for NSApplicationSupportDirectory) and have
+ * support for Objective-C in the build environment
+ */
+
+static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
+{
+ assert(pszDirectory != nullptr);
+
+ OString home;
+ if( osl_psz_getHomeDir(Security, &home) )
+ {
+ *pszDirectory = home + "/Library/Application Support"; /* Used on iOS, too */
+ return true;
+ }
+
+ return false;
+}
+
+#endif
+
+sal_Bool SAL_CALL osl_isAdministrator(oslSecurity Security)
+{
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
+
+ if (pSecImpl == nullptr)
+ return false;
+
+ if (pSecImpl->m_pPasswd.pw_uid != 0)
+ return false;
+
+ return true;
+}
+
+void SAL_CALL osl_freeSecurityHandle(oslSecurity Security)
+{
+ deleteSecurityImpl(static_cast<oslSecurityImpl *>(Security));
+}
+
+sal_Bool SAL_CALL osl_loadUserProfile(SAL_UNUSED_PARAMETER oslSecurity)
+{
+ return false;
+}
+
+void SAL_CALL osl_unloadUserProfile(SAL_UNUSED_PARAMETER oslSecurity) {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/signal.cxx b/sal/osl/unx/signal.cxx
new file mode 100644
index 000000000..91cf59ff1
--- /dev/null
+++ b/sal/osl/unx/signal.cxx
@@ -0,0 +1,452 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <signalshared.hxx>
+
+#include <config_features.h>
+
+#include "soffice.hxx"
+/* system headers */
+#include "system.hxx"
+
+#include "backtrace.h"
+
+#define MAX_STACK_FRAMES 256
+
+#include <osl/diagnose.h>
+#include <osl/signal.h>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#include <rtl/bootstrap.h>
+#include <rtl/digest.h>
+
+#include "file_path_helper.hxx"
+#define ACT_IGNORE 1
+#define ACT_EXIT 2
+#define ACT_SYSTEM 3
+#define ACT_HIDE 4
+#define ACT_ABORT 5
+
+#if defined HAVE_VALGRIND_HEADERS
+#include <valgrind/memcheck.h>
+#endif
+
+namespace
+{
+extern "C" using Handler1 = void (*)(int);
+extern "C" using Handler2 = void (*)(int, siginfo_t *, void *);
+struct SignalAction
+{
+ int Signal;
+ int Action;
+ Handler1 Handler;
+ bool siginfo; // Handler's type is Handler2
+} Signals[] =
+{
+ { SIGHUP, ACT_HIDE, SIG_DFL, false }, /* hangup */
+ { SIGINT, ACT_EXIT, SIG_DFL, false }, /* interrupt (rubout) */
+ { SIGQUIT, ACT_EXIT, SIG_DFL, false }, /* quit (ASCII FS) */
+ { SIGILL, ACT_SYSTEM, SIG_DFL, false }, /* illegal instruction (not reset when caught) */
+/* changed from ACT_ABOUT to ACT_SYSTEM to try and get collector to run*/
+ { SIGTRAP, ACT_ABORT, SIG_DFL, false }, /* trace trap (not reset when caught) */
+#if ( SIGIOT != SIGABRT )
+ { SIGIOT, ACT_ABORT, SIG_DFL, false }, /* IOT instruction */
+#endif
+#if defined(FORCE_DEFAULT_SIGNAL)
+ { SIGABRT, ACT_SYSTEM, SIG_DFL, false }, /* used by abort, replace SIGIOT in the future */
+#else
+ { SIGABRT, ACT_ABORT, SIG_DFL, false }, /* used by abort, replace SIGIOT in the future */
+#endif
+#ifdef SIGEMT
+ { SIGEMT, ACT_SYSTEM, SIG_DFL, false }, /* EMT instruction */
+/* changed from ACT_ABORT to ACT_SYSTEM to remove handler*/
+/* SIGEMT may also be used by the profiler - so it is probably not a good
+plan to have the new handler use this signal*/
+#endif
+ { SIGFPE, ACT_ABORT, SIG_DFL, false }, /* floating point exception */
+ { SIGKILL, ACT_SYSTEM, SIG_DFL, false }, /* kill (cannot be caught or ignored) */
+ { SIGBUS, ACT_ABORT, SIG_DFL, false }, /* bus error */
+#if defined(FORCE_DEFAULT_SIGNAL)
+ { SIGSEGV, ACT_SYSTEM, SIG_DFL, false }, /* segmentation violation */
+#else
+ { SIGSEGV, ACT_ABORT, SIG_DFL, false }, /* segmentation violation */
+#endif
+#ifdef SIGSYS
+ { SIGSYS, ACT_ABORT, SIG_DFL, false }, /* bad argument to system call */
+#endif
+ { SIGPIPE, ACT_HIDE, SIG_DFL, false }, /* write on a pipe with no one to read it */
+#if defined(FORCE_DEFAULT_SIGNAL)
+ { SIGALRM, ACT_SYSTEM, SIG_DFL, false }, /* alarm clock */
+#else
+ { SIGALRM, ACT_EXIT, SIG_DFL, false }, /* alarm clock */
+#endif
+ { SIGTERM, ACT_EXIT, SIG_DFL, false }, /* software termination signal from kill */
+ { SIGUSR1, ACT_SYSTEM, SIG_DFL, false }, /* user defined signal 1 */
+ { SIGUSR2, ACT_SYSTEM, SIG_DFL, false }, /* user defined signal 2 */
+ { SIGCHLD, ACT_SYSTEM, SIG_DFL, false }, /* child status change */
+#ifdef SIGPWR
+ { SIGPWR, ACT_IGNORE, SIG_DFL, false }, /* power-fail restart */
+#endif
+ { SIGWINCH, ACT_IGNORE, SIG_DFL, false }, /* window size change */
+ { SIGURG, ACT_EXIT, SIG_DFL, false }, /* urgent socket condition */
+#ifdef SIGPOLL
+ { SIGPOLL, ACT_EXIT, SIG_DFL, false }, /* pollable event occurred */
+#endif
+ { SIGSTOP, ACT_SYSTEM, SIG_DFL, false }, /* stop (cannot be caught or ignored) */
+ { SIGTSTP, ACT_SYSTEM, SIG_DFL, false }, /* user stop requested from tty */
+ { SIGCONT, ACT_SYSTEM, SIG_DFL, false }, /* stopped process has been continued */
+ { SIGTTIN, ACT_SYSTEM, SIG_DFL, false }, /* background tty read attempted */
+ { SIGTTOU, ACT_SYSTEM, SIG_DFL, false }, /* background tty write attempted */
+ { SIGVTALRM, ACT_EXIT, SIG_DFL, false }, /* virtual timer expired */
+ { SIGPROF, ACT_SYSTEM, SIG_DFL, false }, /* profiling timer expired */
+/*Change from ACT_EXIT to ACT_SYSTEM for SIGPROF is so that profiling signals do
+not get taken by the new handler - the new handler does not pass on context
+information which causes 'collect' to crash. This is a way of avoiding
+what looks like a bug in the new handler*/
+ { SIGXCPU, ACT_ABORT, SIG_DFL, false }, /* exceeded cpu limit */
+ { SIGXFSZ, ACT_ABORT, SIG_DFL, false } /* exceeded file size limit */
+};
+const int NoSignals = SAL_N_ELEMENTS(Signals);
+
+bool bSetSEGVHandler = false;
+bool bSetWINCHHandler = false;
+bool bSetILLHandler = false;
+
+void signalHandlerFunction(int, siginfo_t *, void *);
+
+#if HAVE_FEATURE_BREAKPAD
+bool is_unset_signal(int signal)
+{
+#ifdef DBG_UTIL
+ return (!bSetSEGVHandler && signal == SIGSEGV) ||
+ (!bSetWINCHHandler && signal == SIGWINCH) ||
+ (!bSetILLHandler && signal == SIGILL);
+#else
+ (void) signal;
+ return false;
+#endif
+}
+#endif
+
+}
+
+bool onInitSignal()
+{
+ if (sal::detail::isSoffice())
+ {
+ // WORKAROUND FOR SEGV HANDLER CONFLICT
+ //
+ // the java jit needs SIGSEGV for proper work
+ // and we need SIGSEGV for the office crashguard
+ //
+ // TEMPORARY SOLUTION:
+ // the office sets the signal handler during startup
+ // java can than overwrite it, if needed
+ bSetSEGVHandler = true;
+
+ // WORKAROUND FOR WINCH HANDLER (SEE ABOVE)
+ bSetWINCHHandler = true;
+
+ // WORKAROUND FOR ILLEGAL INSTRUCTION HANDLER (SEE ABOVE)
+ bSetILLHandler = true;
+ }
+
+#ifdef DBG_UTIL
+ bSetSEGVHandler = bSetWINCHHandler = bSetILLHandler = false;
+#endif
+
+ struct sigaction act;
+ act.sa_sigaction = signalHandlerFunction;
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
+
+ sigfillset(&(act.sa_mask));
+
+ /* Initialize the rest of the signals */
+ for (SignalAction & rSignal : Signals)
+ {
+#if defined HAVE_VALGRIND_HEADERS
+ if (rSignal.Signal == SIGUSR2 && RUNNING_ON_VALGRIND)
+ rSignal.Action = ACT_IGNORE;
+#endif
+
+ /* hack: stomcatd is attaching JavaVM which does not work with an sigaction(SEGV) */
+ if ((bSetSEGVHandler || rSignal.Signal != SIGSEGV)
+ && (bSetWINCHHandler || rSignal.Signal != SIGWINCH)
+ && (bSetILLHandler || rSignal.Signal != SIGILL))
+ {
+ if (rSignal.Action != ACT_SYSTEM)
+ {
+ if (rSignal.Action == ACT_HIDE)
+ {
+ struct sigaction ign;
+
+ ign.sa_handler = SIG_IGN;
+ ign.sa_flags = 0;
+ sigemptyset(&ign.sa_mask);
+
+ struct sigaction oact;
+ if (sigaction(rSignal.Signal, &ign, &oact) == 0) {
+ rSignal.siginfo = (oact.sa_flags & SA_SIGINFO) != 0;
+ if (rSignal.siginfo) {
+ rSignal.Handler = reinterpret_cast<Handler1>(
+ oact.sa_sigaction);
+ } else {
+ rSignal.Handler = oact.sa_handler;
+ }
+ } else {
+ rSignal.Handler = SIG_DFL;
+ rSignal.siginfo = false;
+ }
+ }
+ else
+ {
+ struct sigaction oact;
+ if (sigaction(rSignal.Signal, &act, &oact) == 0) {
+ rSignal.siginfo = (oact.sa_flags & SA_SIGINFO) != 0;
+ if (rSignal.siginfo) {
+ rSignal.Handler = reinterpret_cast<Handler1>(
+ oact.sa_sigaction);
+ } else {
+ rSignal.Handler = oact.sa_handler;
+ }
+ } else {
+ rSignal.Handler = SIG_DFL;
+ rSignal.siginfo = false;
+ }
+ }
+ }
+ }
+ }
+
+ /* Clear signal mask inherited from parent process (on macOS, upon a
+ crash soffice re-execs itself from within the signal handler, so the
+ second soffice would have the guilty signal blocked and would freeze upon
+ encountering a similar crash again): */
+ sigset_t unset;
+ if (sigemptyset(&unset) < 0 ||
+ pthread_sigmask(SIG_SETMASK, &unset, nullptr) < 0)
+ {
+ SAL_WARN("sal.osl", "sigemptyset or pthread_sigmask failed");
+ }
+
+ return true;
+}
+
+bool onDeInitSignal()
+{
+ struct sigaction act;
+
+ sigemptyset(&(act.sa_mask));
+
+ /* Initialize the rest of the signals */
+ for (int i = NoSignals - 1; i >= 0; i--)
+ if (Signals[i].Action != ACT_SYSTEM
+ && ((bSetSEGVHandler || Signals[i].Signal != SIGSEGV)
+ && (bSetWINCHHandler || Signals[i].Signal != SIGWINCH)
+ && (bSetILLHandler || Signals[i].Signal != SIGILL)))
+ {
+ if (Signals[i].siginfo) {
+ act.sa_sigaction = reinterpret_cast<Handler2>(
+ Signals[i].Handler);
+ act.sa_flags = SA_SIGINFO;
+ } else {
+ act.sa_handler = Signals[i].Handler;
+ act.sa_flags = 0;
+ }
+
+ sigaction(Signals[i].Signal, &act, nullptr);
+ }
+
+ return false;
+}
+
+namespace
+{
+void printStack(int sig)
+{
+ void *buffer[MAX_STACK_FRAMES];
+ int size = backtrace( buffer, SAL_N_ELEMENTS(buffer) );
+
+ fprintf( stderr, "\n\nFatal exception: Signal %d\n", sig );
+
+#if ! HAVE_FEATURE_BACKTRACE && defined( MACOSX ) && !defined( INTEL )
+ fprintf( stderr, "Please turn on Enable Crash Reporting and\nAutomatic Display of Crashlogs in the Console application\n" );
+#endif
+
+ if ( size > 0 )
+ {
+ fputs( "Stack:\n", stderr );
+ backtrace_symbols_fd( buffer, size, fileno(stderr) );
+ }
+}
+
+void callSystemHandler(int signal, siginfo_t * info, void * context)
+{
+ int i;
+
+ for (i = 0; i < NoSignals; i++)
+ {
+ if (Signals[i].Signal == signal)
+ break;
+ }
+
+ if (i >= NoSignals)
+ return;
+
+ if ((Signals[i].Handler == SIG_DFL) ||
+ (Signals[i].Handler == SIG_IGN) ||
+ (Signals[i].Handler == SIG_ERR))
+ {
+ switch (Signals[i].Action)
+ {
+ case ACT_EXIT: /* terminate */
+ /* prevent dumping core on exit() */
+ _exit(255);
+ break;
+
+ case ACT_ABORT: /* terminate with core dump */
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = 0;
+ sigemptyset(&(act.sa_mask));
+ sigaction(SIGABRT, &act, nullptr);
+ printStack( signal );
+ abort();
+ break;
+
+ case ACT_IGNORE: /* ignore */
+ break;
+
+ default: /* should never happen */
+ OSL_ASSERT(false);
+ }
+ }
+ else if (Signals[i].siginfo) {
+ (*reinterpret_cast<Handler2>(Signals[i].Handler))(
+ signal, info, context);
+ } else {
+ (*Signals[i].Handler)(signal);
+ }
+}
+
+#if defined HAVE_VALGRIND_HEADERS
+void DUMPCURRENTALLOCS()
+{
+ VALGRIND_PRINTF( "=== start memcheck dump of active allocations ===\n" );
+
+#if __GNUC__ && !defined(__clang__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
+
+ VALGRIND_DO_LEAK_CHECK;
+
+#if __GNUC__ && !defined(__clang__)
+# pragma GCC diagnostic pop
+#endif
+
+ VALGRIND_PRINTF( "=== end memcheck dump of active allocations ===\n" );
+}
+#endif
+
+void signalHandlerFunction(int signal, siginfo_t * info, void * context)
+{
+ oslSignalInfo Info;
+
+ Info.UserSignal = signal;
+ Info.UserData = nullptr;
+
+ switch (signal)
+ {
+ case SIGBUS:
+ case SIGILL:
+ case SIGSEGV:
+ case SIGIOT:
+#if ( SIGIOT != SIGABRT )
+ case SIGABRT:
+#endif
+ Info.Signal = osl_Signal_AccessViolation;
+ break;
+
+ case -1:
+ Info.Signal = osl_Signal_IntegerDivideByZero;
+ break;
+
+ case SIGFPE:
+ Info.Signal = osl_Signal_FloatDivideByZero;
+ break;
+
+ case SIGINT:
+ case SIGTERM:
+ case SIGQUIT:
+ Info.Signal = osl_Signal_Terminate;
+ break;
+
+#if defined HAVE_VALGRIND_HEADERS
+ case SIGUSR2:
+ if (RUNNING_ON_VALGRIND)
+ DUMPCURRENTALLOCS();
+ Info.Signal = osl_Signal_System;
+ break;
+#endif
+
+ default:
+ Info.Signal = osl_Signal_System;
+ break;
+ }
+
+#if HAVE_FEATURE_BREAKPAD
+ if ((Info.Signal == osl_Signal_AccessViolation ||
+ Info.Signal == osl_Signal_IntegerDivideByZero ||
+ Info.Signal == osl_Signal_FloatDivideByZero) && !is_unset_signal(signal))
+ {
+ callSystemHandler(signal, info, context);
+ }
+#endif
+
+ switch (callSignalHandler(&Info))
+ {
+ case osl_Signal_ActCallNextHdl:
+ callSystemHandler(signal, info, context);
+ break;
+
+ case osl_Signal_ActAbortApp:
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = 0;
+ sigemptyset(&(act.sa_mask));
+ sigaction(SIGABRT, &act, nullptr);
+ printStack( signal );
+ abort();
+ break;
+
+ case osl_Signal_ActKillApp:
+ /* prevent dumping core on exit() */
+ _exit(255);
+ break;
+ default:
+ break;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/socket.cxx b/sal/osl/unx/socket.cxx
new file mode 100644
index 000000000..535c1727c
--- /dev/null
+++ b/sal/osl/unx/socket.cxx
@@ -0,0 +1,2046 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <utility>
+
+#include "system.hxx"
+
+#include <osl/socket.h>
+#include <osl/mutex.h>
+#include <osl/signal.h>
+
+#include <rtl/alloc.h>
+#include <rtl/byteseq.h>
+#include <rtl/ustring.hxx>
+#include <assert.h>
+#include <sal/types.h>
+#include <sal/log.hxx>
+
+#include "sockimpl.hxx"
+#include "unixerrnostring.hxx"
+#include <oslsocket.hxx>
+
+#include <poll.h>
+
+/* defines for shutdown */
+#define SD_RECEIVE 0
+#define SD_SEND 1
+#define SD_BOTH 2
+
+/*
+ oslSocketAddr is a pointer to a Berkeley struct sockaddr.
+ I refrained from using sockaddr_in because of possible further
+ extensions of this socket-interface (IP-NG?).
+ The intention was to hide all Berkeley data-structures from
+ direct access past the osl-interface.
+
+ The current implementation is internet (IP) centered. All
+ the constructor-functions (osl_create...) take parameters
+ that will probably make sense only in the IP-environment
+ (e.g. because of using the dotted-address-format).
+
+ If the interface will be extended to host other protocol-
+ families, I expect no externally visible changes in the
+ existing functions. You'll probably need only new
+ constructor-functions who take the different address
+ formats into consideration (maybe a long dotted address
+ or whatever).
+*/
+
+/* _Note_ that I rely on the fact that oslSocketAddr and struct sockaddr */
+/* are the same! I don't like it very much but see no other easy way to */
+/* conceal the struct sockaddr from the eyes of the user. */
+
+#define OSL_INVALID_SOCKET -1
+#define OSL_SOCKET_ERROR -1
+
+/* Buffer size for getnameinfo */
+#define MAX_HOSTBUFFER_SIZE 2048
+
+const unsigned long FamilyMap[]= {
+ AF_INET, /* osl_Socket_FamilyInet */
+ AF_IPX, /* osl_Socket_FamilyIpx */
+ 0 /* osl_Socket_FamilyInvalid */
+};
+
+static oslAddrFamily osl_AddrFamilyFromNative(sal_uInt32 nativeType)
+{
+ oslAddrFamily i= oslAddrFamily(0);
+
+ while(i != osl_Socket_FamilyInvalid)
+ {
+ if(FamilyMap[i] == nativeType)
+ return i;
+ i = static_cast<oslAddrFamily>( i + 1 );
+ }
+
+ return i;
+}
+
+#define FAMILY_FROM_NATIVE(y) osl_AddrFamilyFromNative(y)
+#define FAMILY_TO_NATIVE(x) static_cast<short>(FamilyMap[x])
+
+const sal_uInt32 ProtocolMap[]= {
+ 0, /* osl_Socket_ProtocolIp */
+ NSPROTO_IPX, /* osl_Socket_ProtocolIpx */
+ NSPROTO_SPX, /* osl_Socket_ProtocolSpx */
+ NSPROTO_SPXII, /* osl_Socket_ProtocolSpxII */
+ 0 /* osl_Socket_ProtocolInvalid */
+};
+
+#define PROTOCOL_TO_NATIVE(x) ProtocolMap[x]
+
+const sal_uInt32 TypeMap[]= {
+ SOCK_STREAM, /* osl_Socket_TypeStream */
+ SOCK_DGRAM, /* osl_Socket_TypeDgram */
+ SOCK_RAW, /* osl_Socket_TypeRaw */
+ SOCK_RDM, /* osl_Socket_TypeRdm */
+ SOCK_SEQPACKET, /* osl_Socket_TypeSeqPacket */
+ 0 /* osl_Socket_TypeInvalid */
+};
+
+static oslSocketType osl_SocketTypeFromNative(sal_uInt32 nativeType)
+{
+ oslSocketType i= oslSocketType(0);
+
+ while(i != osl_Socket_TypeInvalid)
+ {
+ if(TypeMap[i] == nativeType)
+ return i;
+ i = static_cast<oslSocketType>(i + 1);
+ }
+
+ return i;
+}
+
+#define TYPE_TO_NATIVE(x) TypeMap[x]
+#define TYPE_FROM_NATIVE(y) osl_SocketTypeFromNative(y)
+
+const sal_uInt32 OptionMap[]= {
+ SO_DEBUG, /* osl_Socket_OptionDebug */
+ SO_ACCEPTCONN, /* osl_Socket_OptionAcceptConn */
+ SO_REUSEADDR, /* osl_Socket_OptionReuseAddr */
+ SO_KEEPALIVE, /* osl_Socket_OptionKeepAlive */
+ SO_DONTROUTE, /* osl_Socket_OptionDontRoute */
+ SO_BROADCAST, /* osl_Socket_OptionBroadcast */
+ SO_USELOOPBACK, /* osl_Socket_OptionUseLoopback */
+ SO_LINGER, /* osl_Socket_OptionLinger */
+ SO_OOBINLINE, /* osl_Socket_OptionOOBinLine */
+ SO_SNDBUF, /* osl_Socket_OptionSndBuf */
+ SO_RCVBUF, /* osl_Socket_OptionRcvBuf */
+ SO_SNDLOWAT, /* osl_Socket_OptionSndLowat */
+ SO_RCVLOWAT, /* osl_Socket_OptionRcvLowat */
+ SO_SNDTIMEO, /* osl_Socket_OptionSndTimeo */
+ SO_RCVTIMEO, /* osl_Socket_OptionRcvTimeo */
+ SO_ERROR, /* osl_Socket_OptionError */
+ SO_TYPE, /* osl_Socket_OptionType */
+ TCP_NODELAY, /* osl_Socket_OptionTcpNoDelay */
+ 0 /* osl_Socket_OptionInvalid */
+};
+
+#define OPTION_TO_NATIVE(x) OptionMap[x]
+
+const sal_uInt32 OptionLevelMap[]= {
+ SOL_SOCKET, /* osl_Socket_LevelSocket */
+ IPPROTO_TCP, /* osl_Socket_LevelTcp */
+ 0 /* osl_Socket_LevelInvalid */
+};
+
+#define OPTION_LEVEL_TO_NATIVE(x) OptionLevelMap[x]
+
+const sal_uInt32 SocketMsgFlagMap[]= {
+ 0, /* osl_Socket_MsgNormal */
+ MSG_OOB, /* osl_Socket_MsgOOB */
+ MSG_PEEK, /* osl_Socket_MsgPeek */
+ MSG_DONTROUTE, /* osl_Socket_MsgDontRoute */
+ MSG_MAXIOVLEN, /* osl_Socket_MsgMaxIOVLen */
+ 0 /* osl_Socket_MsgInvalid */
+};
+
+#define MSG_FLAG_TO_NATIVE(x) SocketMsgFlagMap[x]
+
+const sal_uInt32 SocketDirection[]= {
+ SD_RECEIVE, /* osl_Socket_DirRead */
+ SD_SEND, /* osl_Socket_DirWrite */
+ SD_BOTH, /* osl_Socket_DirReadWrite */
+ 0 /* osl_Socket_DirInvalid */
+};
+
+#define DIRECTION_TO_NATIVE(x) SocketDirection[x]
+
+const struct
+{
+ int errcode;
+ oslSocketError error;
+} SocketError[]= {
+ { 0, osl_Socket_E_None }, /* no error */
+ { ENOTSOCK, osl_Socket_E_NotSocket }, /* Socket operation on non-socket */
+ { EDESTADDRREQ, osl_Socket_E_DestAddrReq }, /* Destination address required */
+ { EMSGSIZE, osl_Socket_E_MsgSize }, /* Message too long */
+ { EPROTOTYPE, osl_Socket_E_Prototype }, /* Protocol wrong type for socket */
+ { ENOPROTOOPT, osl_Socket_E_NoProtocol }, /* Protocol not available */
+ { EPROTONOSUPPORT, osl_Socket_E_ProtocolNoSupport }, /* Protocol not supported */
+#ifdef ESOCKTNOSUPPORT
+ { ESOCKTNOSUPPORT, osl_Socket_E_TypeNoSupport }, /* Socket type not supported */
+#endif
+ { EOPNOTSUPP, osl_Socket_E_OpNotSupport }, /* Operation not supported on socket */
+ { EPFNOSUPPORT, osl_Socket_E_PfNoSupport }, /* Protocol family not supported */
+ { EAFNOSUPPORT, osl_Socket_E_AfNoSupport }, /* Address family not supported by
+ protocol family */
+ { EADDRINUSE, osl_Socket_E_AddrInUse }, /* Address already in use */
+ { EADDRNOTAVAIL, osl_Socket_E_AddrNotAvail }, /* Can't assign requested address */
+ { ENETDOWN, osl_Socket_E_NetDown }, /* Network is down */
+ { ENETUNREACH, osl_Socket_E_NetUnreachable }, /* Network is unreachable */
+ { ENETRESET, osl_Socket_E_NetReset }, /* Network dropped connection because
+ of reset */
+ { ECONNABORTED, osl_Socket_E_ConnAborted }, /* Software caused connection abort */
+ { ECONNRESET, osl_Socket_E_ConnReset }, /* Connection reset by peer */
+ { ENOBUFS, osl_Socket_E_NoBufferSpace }, /* No buffer space available */
+ { EISCONN, osl_Socket_E_IsConnected }, /* Socket is already connected */
+ { ENOTCONN, osl_Socket_E_NotConnected }, /* Socket is not connected */
+ { ESHUTDOWN, osl_Socket_E_Shutdown }, /* Can't send after socket shutdown */
+#ifdef ETOOMANYREFS
+ { ETOOMANYREFS, osl_Socket_E_TooManyRefs }, /* Too many references: can't splice */
+#endif
+ { ETIMEDOUT, osl_Socket_E_TimedOut }, /* Connection timed out */
+ { ECONNREFUSED, osl_Socket_E_ConnRefused }, /* Connection refused */
+ { EHOSTDOWN, osl_Socket_E_HostDown }, /* Host is down */
+ { EHOSTUNREACH, osl_Socket_E_HostUnreachable }, /* No route to host */
+ { EWOULDBLOCK, osl_Socket_E_WouldBlock }, /* call would block on non-blocking socket */
+ { EALREADY, osl_Socket_E_Already }, /* operation already in progress */
+ { EINPROGRESS, osl_Socket_E_InProgress }, /* operation now in progress */
+ { EAGAIN, osl_Socket_E_WouldBlock }, /* same as EWOULDBLOCK */
+ { -1, osl_Socket_E_InvalidError }
+};
+
+static oslSocketError osl_SocketErrorFromNative(int nativeType)
+{
+ int i = 0;
+
+ while ((SocketError[i].error != osl_Socket_E_InvalidError) &&
+ (SocketError[i].errcode != nativeType)) i++;
+
+ return SocketError[i].error;
+}
+
+#define ERROR_FROM_NATIVE(y) osl_SocketErrorFromNative(y)
+
+static oslSocketAddr osl_psz_createInetSocketAddr (
+ const char* pszDottedAddr, sal_Int32 Port);
+
+static oslHostAddr osl_psz_createHostAddr (
+ const char *pszHostname, const oslSocketAddr Addr);
+
+static oslHostAddr osl_psz_createHostAddrByName (
+ const char *pszHostname);
+
+static const char* osl_psz_getHostnameOfHostAddr (
+ const oslHostAddr Addr);
+
+static oslSocketAddr osl_psz_resolveHostname (
+ const char* pszHostname);
+
+static sal_Int32 osl_psz_getServicePort (
+ const char* pszServicename, const char* pszProtocol);
+
+static void osl_psz_getLastSocketErrorDescription (
+ oslSocket Socket, char* pBuffer, sal_uInt32 BufferSize);
+
+namespace {
+
+int convertToMs(TimeValue const * value) {
+ return value->Seconds * 1000 + value->Nanosec / 1000000; //TODO: overflow
+}
+
+}
+
+static oslSocket createSocketImpl()
+{
+ oslSocket pSocket;
+
+ pSocket = static_cast<oslSocket>(calloc(1, sizeof(struct oslSocketImpl)));
+
+ pSocket->m_Socket = OSL_INVALID_SOCKET;
+ pSocket->m_nLastError = 0;
+ pSocket->m_nRefCount = 1;
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pSocket->m_bIsAccepting = false;
+#endif
+
+ return pSocket;
+}
+
+static void destroySocketImpl(oslSocket Socket)
+{
+ if ( Socket != nullptr)
+ free(Socket);
+}
+
+static oslSocketAddr createSocketAddr()
+{
+ oslSocketAddr pAddr = static_cast<oslSocketAddr>(rtl_allocateZeroMemory( sizeof( struct oslSocketAddrImpl )));
+ return pAddr;
+}
+
+static oslSocketAddr createSocketAddrWithFamily(
+ oslAddrFamily family, sal_Int32 port, sal_uInt32 nAddr )
+{
+ oslSocketAddr pAddr;
+
+ SAL_WARN_IF( family != osl_Socket_FamilyInet, "sal.osl", "creating socket for non-IP address family" );
+
+ pAddr = createSocketAddr();
+ switch( family )
+ {
+ case osl_Socket_FamilyInet:
+ {
+ struct sockaddr_in* pInetAddr= reinterpret_cast<sockaddr_in*>(&pAddr->m_sockaddr);
+
+ pInetAddr->sin_family = FAMILY_TO_NATIVE(osl_Socket_FamilyInet);
+ pInetAddr->sin_addr.s_addr = nAddr;
+ pInetAddr->sin_port = static_cast<sal_uInt16>(port&0xffff);
+ break;
+ }
+ default:
+ pAddr->m_sockaddr.sa_family = FAMILY_TO_NATIVE(family);
+ }
+ return pAddr;
+}
+
+static oslSocketAddr createSocketAddrFromSystem( struct sockaddr *pSystemSockAddr )
+{
+ oslSocketAddr pAddr = createSocketAddr();
+ memcpy( &(pAddr->m_sockaddr), pSystemSockAddr, sizeof( struct sockaddr ) );
+ return pAddr;
+}
+
+static void destroySocketAddr( oslSocketAddr addr )
+{
+ free( addr );
+}
+
+oslSocketAddr SAL_CALL osl_createEmptySocketAddr(oslAddrFamily Family)
+{
+ oslSocketAddr pAddr = nullptr;
+
+ /* is it an internet-Addr? */
+ if (Family == osl_Socket_FamilyInet)
+ {
+ pAddr = createSocketAddrWithFamily(Family, 0 , htonl(INADDR_ANY) );
+ }
+ else
+ {
+ pAddr = createSocketAddrWithFamily( Family , 0 , 0 );
+ }
+
+ return pAddr;
+}
+
+oslSocketAddr SAL_CALL osl_copySocketAddr(oslSocketAddr Addr)
+{
+ oslSocketAddr pCopy = nullptr;
+ if (Addr)
+ {
+ pCopy = createSocketAddr();
+
+ if (pCopy)
+ memcpy(&(pCopy->m_sockaddr),&(Addr->m_sockaddr), sizeof(struct sockaddr));
+ }
+ return pCopy;
+}
+
+sal_Bool SAL_CALL osl_isEqualSocketAddr (
+ oslSocketAddr Addr1,
+ oslSocketAddr Addr2)
+{
+ struct sockaddr* pAddr1 = nullptr;
+ struct sockaddr* pAddr2 = nullptr;
+
+ assert(Addr1 && Addr2);
+ pAddr1 = &(Addr1->m_sockaddr);
+ pAddr2 = &(Addr2->m_sockaddr);
+
+ if (pAddr1 == pAddr2)
+ {
+ return true;
+ }
+
+ if (pAddr1->sa_family == pAddr2->sa_family)
+ {
+ switch (pAddr1->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in* pInetAddr1= reinterpret_cast<sockaddr_in*>(pAddr1);
+ struct sockaddr_in* pInetAddr2= reinterpret_cast<sockaddr_in*>(pAddr2);
+
+ if ((pInetAddr1->sin_family == pInetAddr2->sin_family) &&
+ (pInetAddr1->sin_addr.s_addr == pInetAddr2->sin_addr.s_addr) &&
+ (pInetAddr1->sin_port == pInetAddr2->sin_port))
+ return true;
+ [[fallthrough]];
+ }
+
+ default:
+ {
+ return (memcmp(pAddr1, pAddr2, sizeof(struct sockaddr)) == 0);
+ }
+ }
+ }
+
+ return false;
+}
+
+oslSocketAddr SAL_CALL osl_createInetBroadcastAddr (
+ rtl_uString *strDottedAddr,
+ sal_Int32 Port)
+{
+ sal_uInt32 nAddr = OSL_INADDR_NONE;
+ oslSocketAddr pAddr;
+
+ if (strDottedAddr && strDottedAddr->length)
+ {
+ /* Dotted host address for limited broadcast */
+ rtl_String *pDottedAddr = nullptr;
+
+ rtl_uString2String (
+ &pDottedAddr, strDottedAddr->buffer, strDottedAddr->length,
+ RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);
+
+ in_addr buf;
+ if (inet_pton (AF_INET, pDottedAddr->buffer, &buf) == 1) {
+ nAddr = buf.s_addr;
+ }
+ rtl_string_release (pDottedAddr);
+ }
+
+ if (nAddr != OSL_INADDR_NONE)
+ {
+ /* Limited broadcast */
+ nAddr = ntohl(nAddr);
+ if (IN_CLASSA(nAddr))
+ {
+ nAddr &= IN_CLASSA_NET;
+ nAddr |= IN_CLASSA_HOST;
+ }
+ else if (IN_CLASSB(nAddr))
+ {
+ nAddr &= IN_CLASSB_NET;
+ nAddr |= IN_CLASSB_HOST;
+ }
+ else if (IN_CLASSC(nAddr))
+ {
+ nAddr &= IN_CLASSC_NET;
+ nAddr |= IN_CLASSC_HOST;
+ }
+ else
+ {
+ /* No broadcast in class D */
+ return nullptr;
+ }
+ nAddr = htonl(nAddr);
+ }
+
+ pAddr = createSocketAddrWithFamily( osl_Socket_FamilyInet, htons(Port), nAddr );
+ return pAddr;
+}
+
+oslSocketAddr SAL_CALL osl_createInetSocketAddr (
+ rtl_uString *ustrDottedAddr,
+ sal_Int32 Port)
+{
+ rtl_String* strDottedAddr=nullptr;
+ oslSocketAddr Addr;
+ char* pszDottedAddr=nullptr;
+
+ if ( ustrDottedAddr != nullptr )
+ {
+ rtl_uString2String( &strDottedAddr,
+ rtl_uString_getStr(ustrDottedAddr),
+ rtl_uString_getLength(ustrDottedAddr),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+ pszDottedAddr = rtl_string_getStr(strDottedAddr);
+ }
+
+ Addr = pszDottedAddr ? osl_psz_createInetSocketAddr(pszDottedAddr, Port) : nullptr;
+
+ if ( strDottedAddr != nullptr )
+ {
+ rtl_string_release(strDottedAddr);
+ }
+
+ return Addr;
+}
+
+oslSocketAddr osl_psz_createInetSocketAddr (
+ const char* pszDottedAddr,
+ sal_Int32 Port)
+{
+ oslSocketAddr pAddr = nullptr;
+ in_addr buf;
+ if(inet_pton(AF_INET, pszDottedAddr, &buf) == 1)
+ {
+ /* valid dotted addr */
+ pAddr = createSocketAddrWithFamily( osl_Socket_FamilyInet, htons(Port) , buf.s_addr );
+ }
+ return pAddr;
+}
+
+oslSocketResult SAL_CALL osl_setAddrOfSocketAddr( oslSocketAddr pAddr, sal_Sequence *pByteSeq )
+{
+ oslSocketResult res = osl_Socket_Error;
+
+ SAL_WARN_IF( !pAddr, "sal.osl", "setting address of undefined socket address" );
+ SAL_WARN_IF( !pByteSeq, "sal.osl", "setting undefined address for socket address" );
+
+ if( pAddr && pByteSeq )
+ {
+ struct sockaddr_in * pSystemInetAddr;
+
+ assert( pAddr->m_sockaddr.sa_family == FAMILY_TO_NATIVE( osl_Socket_FamilyInet ) );
+ assert( pByteSeq->nElements == 4 );
+
+ pSystemInetAddr = reinterpret_cast<sockaddr_in *>(&pAddr->m_sockaddr);
+ memcpy( &(pSystemInetAddr->sin_addr) , pByteSeq->elements , 4 );
+ res = osl_Socket_Ok;
+ }
+ return res;
+}
+
+oslSocketResult SAL_CALL osl_getAddrOfSocketAddr( oslSocketAddr pAddr, sal_Sequence **ppByteSeq )
+{
+ oslSocketResult res = osl_Socket_Error;
+
+ SAL_WARN_IF( !pAddr, "sal.osl", "getting address of undefined socket address" );
+ SAL_WARN_IF( !ppByteSeq, "sal.osl", "getting address to undefined address pointer" );
+
+ if( pAddr && ppByteSeq )
+ {
+ struct sockaddr_in * pSystemInetAddr = reinterpret_cast<sockaddr_in *>(&pAddr->m_sockaddr);
+ rtl_byte_sequence_constructFromArray( ppByteSeq, reinterpret_cast<sal_Int8 *>(&pSystemInetAddr->sin_addr), 4);
+ res = osl_Socket_Ok;
+ }
+ return res;
+}
+
+/** try to figure out a full-qualified hostname, by adding the current domain
+ as given by the domainname program to the given hostname.
+ This function MUST NOT call gethostbyname since pHostName already points
+ to data returned by gethostname and would be garbled: use gethostname_r
+ instead!
+ */
+
+namespace {
+
+struct oslAddrInfo
+{
+ addrinfo* pAddrInfoList = nullptr;
+ int nError;
+
+ oslAddrInfo(const char* name, bool isInet = false)
+ {
+ addrinfo aHints;
+ memset(&aHints, 0, sizeof(addrinfo));
+ if (isInet)
+ aHints.ai_family = AF_INET;
+ aHints.ai_flags = AI_CANONNAME;
+
+ nError = getaddrinfo(name, nullptr, &aHints, &pAddrInfoList);
+ }
+
+ ~oslAddrInfo()
+ {
+ if (nError == 0)
+ freeaddrinfo(pAddrInfoList);
+ }
+
+ const char* getHostName() const
+ {
+ assert(pAddrInfoList);
+ return pAddrInfoList->ai_canonname;
+ }
+};
+
+}
+static bool isFullQualifiedDomainName (const char *pHostName)
+{
+ /* a FQDN (aka 'hostname.domain.top_level_domain' )
+ * is a name which contains a dot '.' in it ( would
+ * match as well for 'hostname.' but is good enough
+ * for now )*/
+ return strchr( pHostName, int('.') ) != nullptr;
+}
+
+static char* getFullQualifiedDomainName (const char *pHostName)
+{
+ char *pFullQualifiedName = nullptr;
+
+ if (isFullQualifiedDomainName(pHostName))
+ {
+ pFullQualifiedName = strdup(pHostName);
+ }
+ else
+ {
+ oslAddrInfo aAddrInfo(pHostName);
+ if (aAddrInfo.nError == 0)
+ pFullQualifiedName = strdup(aAddrInfo.getHostName());
+ }
+
+ return pFullQualifiedName;
+}
+
+struct oslHostAddrImpl
+{
+ char *pHostName;
+ oslSocketAddr pSockAddr;
+};
+
+static oslHostAddr addrinfoToHostAddr (const addrinfo* ai)
+{
+ if (!ai || !ai->ai_canonname || !ai->ai_addr)
+ return nullptr;
+
+ char* cn = getFullQualifiedDomainName(ai->ai_canonname);
+ SAL_WARN_IF( !cn, "sal.osl", "couldn't get full qualified domain name" );
+ if (cn == nullptr)
+ return nullptr;
+
+ oslSocketAddr pSockAddr = createSocketAddr();
+ SAL_WARN_IF( !pSockAddr, "sal.osl", "insufficient memory" );
+ if (pSockAddr == nullptr)
+ {
+ free(cn);
+ return nullptr;
+ }
+
+ if (ai->ai_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ {
+ memcpy (
+ &(pSockAddr->m_sockaddr),
+ ai->ai_addr,
+ ai->ai_addrlen);
+ }
+ else
+ {
+ /* unknown address family */
+ /* future extensions for new families might be implemented here */
+
+ SAL_WARN( "sal.osl", "unknown address family" );
+
+ destroySocketAddr( pSockAddr );
+ free (cn);
+ return nullptr;
+ }
+
+ oslHostAddr pAddr = static_cast<oslHostAddr>(malloc(sizeof(struct oslHostAddrImpl)));
+ SAL_WARN_IF( !pAddr, "sal.osl", "allocation error" );
+ if (pAddr == nullptr)
+ {
+ destroySocketAddr( pSockAddr );
+ free (cn);
+ return nullptr;
+ }
+
+ pAddr->pHostName= cn;
+ pAddr->pSockAddr= pSockAddr;
+
+ return pAddr;
+}
+
+oslHostAddr SAL_CALL osl_createHostAddr (
+ rtl_uString *ustrHostname,
+ const oslSocketAddr Addr)
+{
+ oslHostAddr HostAddr;
+ rtl_String* strHostname=nullptr;
+ char* pszHostName=nullptr;
+
+ if ( ustrHostname != nullptr )
+ {
+ rtl_uString2String( &strHostname,
+ rtl_uString_getStr(ustrHostname),
+ rtl_uString_getLength(ustrHostname),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+ pszHostName = rtl_string_getStr(strHostname);
+ }
+
+ HostAddr = osl_psz_createHostAddr(pszHostName,Addr);
+
+ if ( strHostname != nullptr )
+ {
+ rtl_string_release(strHostname);
+ }
+
+ return HostAddr;
+}
+
+oslHostAddr osl_psz_createHostAddr (
+ const char *pszHostname,
+ const oslSocketAddr pAddr)
+{
+ oslHostAddr pHostAddr;
+ char *cn;
+
+ SAL_WARN_IF( !pszHostname, "sal.osl", "undefined hostname" );
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+ if ((pszHostname == nullptr) || (pAddr == nullptr))
+ return nullptr;
+
+ cn = strdup(pszHostname);
+ SAL_WARN_IF( !cn, "sal.osl", "insufficient memory" );
+ if (cn == nullptr)
+ return nullptr;
+
+ pHostAddr= static_cast<oslHostAddr>(malloc(sizeof(struct oslHostAddrImpl)));
+ SAL_WARN_IF( !pHostAddr, "sal.osl", "allocation error" );
+ if (pHostAddr == nullptr)
+ {
+ free (cn);
+ return nullptr;
+ }
+
+ pHostAddr->pHostName= cn;
+ pHostAddr->pSockAddr= osl_copySocketAddr( pAddr );
+
+ return pHostAddr;
+}
+
+oslHostAddr SAL_CALL osl_createHostAddrByName(rtl_uString *ustrHostname)
+{
+ oslHostAddr HostAddr;
+ rtl_String* strHostname=nullptr;
+ char* pszHostName=nullptr;
+
+ if ( ustrHostname != nullptr )
+ {
+ rtl_uString2String( &strHostname,
+ rtl_uString_getStr(ustrHostname),
+ rtl_uString_getLength(ustrHostname),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+ pszHostName=rtl_string_getStr(strHostname);
+ }
+
+ HostAddr = osl_psz_createHostAddrByName(pszHostName);
+
+ if ( strHostname != nullptr )
+ {
+ rtl_string_release(strHostname);
+ }
+
+ return HostAddr;
+}
+
+oslHostAddr osl_psz_createHostAddrByName (const char *pszHostname)
+{
+ oslAddrInfo aAddrInfo(pszHostname, /* isInet */ true);
+
+ return addrinfoToHostAddr (aAddrInfo.pAddrInfoList);
+}
+
+oslHostAddr SAL_CALL osl_createHostAddrByAddr (const oslSocketAddr pAddr)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if (pAddr == nullptr)
+ return nullptr;
+
+ if (pAddr->m_sockaddr.sa_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ {
+ const struct sockaddr_in *sin = reinterpret_cast<sockaddr_in *>(&pAddr->m_sockaddr);
+ if (sin->sin_addr.s_addr == htonl(INADDR_ANY))
+ return nullptr;
+
+ char host[MAX_HOSTBUFFER_SIZE];
+ int res = getnameinfo(&pAddr->m_sockaddr, sizeof(struct sockaddr_in),
+ host, sizeof(host), nullptr, 0, NI_NAMEREQD);
+ if (res != 0)
+ return nullptr;
+
+ char *cn = getFullQualifiedDomainName(host);
+ SAL_WARN_IF( !cn, "sal.osl", "couldn't get full qualified domain name" );
+ if (cn == nullptr)
+ return nullptr;
+
+ oslSocketAddr pSockAddr = createSocketAddr();
+ SAL_WARN_IF( !pSockAddr, "sal.osl", "insufficient memory" );
+ if (pSockAddr == nullptr)
+ {
+ free(cn);
+ return nullptr;
+ }
+
+ memcpy(&pSockAddr->m_sockaddr, &pAddr->m_sockaddr, sizeof(pAddr->m_sockaddr));
+
+ oslHostAddr pHostAddr = static_cast<oslHostAddr>(malloc(sizeof(struct oslHostAddrImpl)));
+ SAL_WARN_IF( !pAddr, "sal.osl", "allocation error" );
+ if (pHostAddr == nullptr)
+ {
+ destroySocketAddr(pSockAddr);
+ free(cn);
+ return nullptr;
+ }
+
+ pHostAddr->pHostName = cn;
+ pHostAddr->pSockAddr = pSockAddr;
+
+ return pHostAddr;
+ }
+
+ return nullptr;
+}
+
+oslHostAddr SAL_CALL osl_copyHostAddr (const oslHostAddr pAddr)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if (pAddr)
+ return osl_psz_createHostAddr (pAddr->pHostName, pAddr->pSockAddr);
+ return nullptr;
+}
+
+void SAL_CALL osl_getHostnameOfHostAddr (
+ const oslHostAddr Addr,
+ rtl_uString **ustrHostname)
+{
+ const char* pHostname = osl_psz_getHostnameOfHostAddr(Addr);
+
+ rtl_uString_newFromAscii (ustrHostname, pHostname);
+}
+
+const char* osl_psz_getHostnameOfHostAddr (const oslHostAddr pAddr)
+{
+ if (pAddr)
+ return pAddr->pHostName;
+ return nullptr;
+}
+
+oslSocketAddr SAL_CALL osl_getSocketAddrOfHostAddr (const oslHostAddr pAddr)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if (pAddr)
+ return pAddr->pSockAddr;
+ return nullptr;
+}
+
+void SAL_CALL osl_destroyHostAddr (oslHostAddr pAddr)
+{
+ if (pAddr)
+ {
+ if (pAddr->pHostName)
+ free (pAddr->pHostName);
+ if (pAddr->pSockAddr)
+ osl_destroySocketAddr (pAddr->pSockAddr);
+ free (pAddr);
+ }
+}
+
+namespace
+{
+oslSocketResult lcl_getLocalHostname(rtl_uString **ustrLocalHostname, bool bUseFQDN)
+{
+ static auto const init = [bUseFQDN]() -> std::pair<oslSocketResult, OUString> {
+ char LocalHostname[256] = "";
+
+#ifdef SYSV
+ struct utsname uts;
+
+ if (uname(&uts) < 0)
+ return {osl_Socket_Error, OUString()};
+
+ if ((strlen(uts.nodename) + 1) > nBufLen)
+ return {osl_Socket_Error, OUString()};
+
+ strncpy(LocalHostname, uts.nodename, sizeof( LocalHostname ));
+#else /* BSD compatible */
+ if (gethostname(LocalHostname, sizeof(LocalHostname)-1) != 0)
+ return {osl_Socket_Error, OUString()};
+#endif /* SYSV */
+ LocalHostname[sizeof(LocalHostname)-1] = 0;
+
+ /* check if we have an FQDN */
+ if (bUseFQDN && strchr(LocalHostname, '.') == nullptr)
+ {
+ oslHostAddr Addr;
+
+ /* no, determine it via dns */
+ Addr = osl_psz_createHostAddrByName(LocalHostname);
+
+ const char *pStr;
+ if ((pStr = osl_psz_getHostnameOfHostAddr(Addr)) != nullptr)
+ {
+ strncpy(LocalHostname, pStr, sizeof( LocalHostname ));
+ LocalHostname[sizeof(LocalHostname)-1] = 0;
+ }
+ osl_destroyHostAddr(Addr);
+ }
+
+ if (LocalHostname[0] != '\0')
+ {
+ return {osl_Socket_Ok, OUString::createFromAscii(LocalHostname)};
+ }
+
+ return {osl_Socket_Error, OUString()};
+ }();
+
+ rtl_uString_assign(ustrLocalHostname,init.second.pData);
+
+ return init.first;
+}
+}
+
+oslSocketResult SAL_CALL osl_getLocalHostname(rtl_uString **ustrLocalHostname)
+{
+ return lcl_getLocalHostname(ustrLocalHostname, false);
+}
+
+oslSocketResult osl_getLocalHostnameFQDN(rtl_uString **ustrLocalHostname)
+{
+ return lcl_getLocalHostname(ustrLocalHostname, true);
+}
+
+oslSocketAddr SAL_CALL osl_resolveHostname(rtl_uString *ustrHostname)
+{
+ oslSocketAddr Addr;
+ rtl_String* strHostname=nullptr;
+ char* pszHostName=nullptr;
+
+ if ( ustrHostname != nullptr )
+ {
+ rtl_uString2String( &strHostname,
+ rtl_uString_getStr(ustrHostname),
+ rtl_uString_getLength(ustrHostname),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+ pszHostName = rtl_string_getStr(strHostname);
+ }
+
+ Addr = osl_psz_resolveHostname(pszHostName);
+
+ if ( strHostname != nullptr )
+ {
+ rtl_string_release(strHostname);
+ }
+
+ return Addr;
+}
+
+oslSocketAddr osl_psz_resolveHostname(const char* pszHostname)
+{
+ struct oslHostAddrImpl *pAddr = osl_psz_createHostAddrByName(pszHostname);
+
+ if (pAddr)
+ {
+ oslSocketAddr SockAddr = osl_copySocketAddr(pAddr->pSockAddr);
+
+ osl_destroyHostAddr(pAddr);
+
+ return SockAddr;
+ }
+
+ return nullptr;
+}
+
+sal_Int32 SAL_CALL osl_getServicePort(rtl_uString *ustrServicename, rtl_uString *ustrProtocol)
+{
+ sal_Int32 nPort;
+ rtl_String* strServicename=nullptr;
+ rtl_String* strProtocol=nullptr;
+ char* pszServiceName=nullptr;
+ char* pszProtocol=nullptr;
+
+ if ( ustrServicename != nullptr )
+ {
+ rtl_uString2String( &strServicename,
+ rtl_uString_getStr(ustrServicename),
+ rtl_uString_getLength(ustrServicename),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+ pszServiceName = rtl_string_getStr(strServicename);
+ }
+
+ if ( ustrProtocol != nullptr )
+ {
+ rtl_uString2String( &strProtocol,
+ rtl_uString_getStr(ustrProtocol),
+ rtl_uString_getLength(ustrProtocol),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+ pszProtocol = rtl_string_getStr(strProtocol);
+ }
+
+ nPort = osl_psz_getServicePort(pszServiceName,pszProtocol);
+
+ if ( strServicename != nullptr )
+ {
+ rtl_string_release(strServicename);
+ }
+
+ if ( strProtocol != nullptr )
+ {
+ rtl_string_release(strProtocol);
+ }
+
+ return nPort;
+}
+
+sal_Int32 osl_psz_getServicePort(const char* pszServicename,
+ const char* pszProtocol)
+{
+ struct servent* ps;
+
+ ps= getservbyname(pszServicename, pszProtocol);
+
+ if (ps != nullptr)
+ return ntohs(ps->s_port);
+
+ return OSL_INVALID_PORT;
+}
+
+void SAL_CALL osl_destroySocketAddr(oslSocketAddr pAddr)
+{
+ destroySocketAddr( pAddr );
+}
+
+oslAddrFamily SAL_CALL osl_getFamilyOfSocketAddr(oslSocketAddr pAddr)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if (pAddr)
+ return FAMILY_FROM_NATIVE(pAddr->m_sockaddr.sa_family);
+ return osl_Socket_FamilyInvalid;
+}
+
+sal_Int32 SAL_CALL osl_getInetPortOfSocketAddr(oslSocketAddr pAddr)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if( pAddr )
+ {
+ struct sockaddr_in* pSystemInetAddr= reinterpret_cast<sockaddr_in*>(&pAddr->m_sockaddr);
+
+ if ( pSystemInetAddr->sin_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ return ntohs(pSystemInetAddr->sin_port);
+ }
+ return OSL_INVALID_PORT;
+}
+
+sal_Bool SAL_CALL osl_setInetPortOfSocketAddr(oslSocketAddr pAddr, sal_Int32 Port)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if( pAddr )
+ {
+ struct sockaddr_in* pSystemInetAddr= reinterpret_cast<sockaddr_in*>(&pAddr->m_sockaddr);
+ if ( pSystemInetAddr->sin_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ {
+ pSystemInetAddr->sin_port= htons(static_cast<short>(Port));
+ return true;
+ }
+ }
+
+ /* this is not a inet-addr => can't set port */
+ return false;
+}
+
+oslSocketResult SAL_CALL osl_getHostnameOfSocketAddr(oslSocketAddr Addr, rtl_uString **ustrHostname)
+{
+ oslHostAddr pHostAddr= osl_createHostAddrByAddr(Addr);
+
+ if (!pHostAddr)
+ {
+ return osl_Socket_Error;
+ }
+
+ rtl_uString_newFromAscii(ustrHostname,pHostAddr->pHostName);
+
+ osl_destroyHostAddr(pHostAddr);
+
+ return osl_Socket_Ok;
+}
+
+oslSocketResult SAL_CALL osl_getDottedInetAddrOfSocketAddr(oslSocketAddr Addr, rtl_uString **ustrDottedInetAddr)
+{
+ if( !Addr )
+ {
+ return osl_Socket_Error;
+ }
+
+ struct sockaddr_in* pSystemInetAddr = reinterpret_cast<sockaddr_in *>(&Addr->m_sockaddr);
+
+ if (pSystemInetAddr->sin_family != FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ {
+ return osl_Socket_Error;
+ }
+
+ char buf[INET_ADDRSTRLEN];
+ auto const text = inet_ntop(AF_INET, &pSystemInetAddr->sin_addr, buf, INET_ADDRSTRLEN);
+ assert(text != nullptr);
+ rtl_uString_newFromAscii(ustrDottedInetAddr,text);
+
+ return osl_Socket_Ok;
+
+}
+
+oslSocket SAL_CALL osl_createSocket(
+ oslAddrFamily Family,
+ oslSocketType Type,
+ oslProtocol Protocol)
+{
+ oslSocket pSocket;
+
+ /* alloc memory */
+ pSocket= createSocketImpl();
+
+ /* create socket */
+ pSocket->m_Socket= socket(FAMILY_TO_NATIVE(Family),
+ TYPE_TO_NATIVE(Type),
+ PROTOCOL_TO_NATIVE(Protocol));
+
+ /* creation failed => free memory */
+ if(pSocket->m_Socket == OSL_INVALID_SOCKET)
+ {
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "socket creation failed: " << UnixErrnoString(nErrno) );
+
+ destroySocketImpl(pSocket);
+ pSocket= nullptr;
+ }
+ else
+ {
+ sal_Int32 nFlags=0;
+ /* set close-on-exec flag */
+ if ((nFlags = fcntl(pSocket->m_Socket, F_GETFD, 0)) != -1)
+ {
+ nFlags |= FD_CLOEXEC;
+ if (fcntl(pSocket->m_Socket, F_SETFD, nFlags) == -1)
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "failed changing socket flags: " << UnixErrnoString(nErrno) );
+ }
+ }
+ else
+ {
+ pSocket->m_nLastError=errno;
+ }
+ }
+
+ return pSocket;
+}
+
+void SAL_CALL osl_acquireSocket(oslSocket pSocket)
+{
+ osl_atomic_increment(&(pSocket->m_nRefCount));
+}
+
+void SAL_CALL osl_releaseSocket(oslSocket pSocket)
+{
+ if (pSocket && osl_atomic_decrement(&(pSocket->m_nRefCount)) == 0)
+ {
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ if (pSocket->m_bIsAccepting)
+ {
+ SAL_WARN( "sal.osl", "attempt to destroy socket while accepting" );
+ return;
+ }
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+ osl_closeSocket(pSocket);
+ destroySocketImpl(pSocket);
+ }
+}
+
+void SAL_CALL osl_closeSocket(oslSocket pSocket)
+{
+ /* socket already invalid */
+ if (!pSocket)
+ return;
+
+ pSocket->m_nLastError=0;
+ sal_Int32 nFD = pSocket->m_Socket;
+
+ if (nFD == OSL_INVALID_SOCKET)
+ return;
+
+ pSocket->m_Socket = OSL_INVALID_SOCKET;
+
+ sal_Int32 nRet=0;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pSocket->m_bIsInShutdown = true;
+
+ if (pSocket->m_bIsAccepting)
+ {
+ union {
+ struct sockaddr aSockAddr;
+ struct sockaddr_in aSockAddrIn;
+ } s;
+ socklen_t nSockLen = sizeof(s.aSockAddr);
+
+ nRet = getsockname(nFD, &s.aSockAddr, &nSockLen);
+ if (nRet < 0)
+ {
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "getsockname call failed: " << UnixErrnoString(nErrno) );
+ }
+
+ if (s.aSockAddr.sa_family == AF_INET)
+ {
+ if (s.aSockAddrIn.sin_addr.s_addr == htonl(INADDR_ANY))
+ {
+ s.aSockAddrIn.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ }
+
+ int nConnFD = socket(AF_INET, SOCK_STREAM, 0);
+ if (nConnFD < 0)
+ {
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "socket call failed: " << UnixErrnoString(nErrno) );
+ }
+ else
+ {
+ nRet = connect(nConnFD, &s.aSockAddr, sizeof(s.aSockAddr));
+ if (nRet < 0)
+ {
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "connect call failed: " << UnixErrnoString(nErrno) );
+ }
+ close(nConnFD);
+ }
+ }
+ pSocket->m_bIsAccepting = false;
+ }
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+
+ nRet=close(nFD);
+ if (nRet != 0)
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "closeSocket close failed: " << UnixErrnoString(nErrno) );
+ }
+
+ pSocket->m_Socket = OSL_INVALID_SOCKET;
+}
+
+/* Note from function creator: I rely on the fact that oslSocketAddr and struct sockaddr
+ are the same! I don't like it very much but see no other easy way to conceal
+ the struct sockaddr from the eyes of the user. */
+oslSocketAddr SAL_CALL osl_getLocalAddrOfSocket(oslSocket pSocket)
+{
+ socklen_t AddrLen;
+ struct sockaddr Addr;
+ oslSocketAddr pAddr;
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return nullptr;
+
+ AddrLen= sizeof(struct sockaddr);
+
+ if (getsockname(pSocket->m_Socket, &Addr, &AddrLen) == OSL_SOCKET_ERROR)
+ return nullptr;
+
+ pAddr = createSocketAddrFromSystem( &Addr );
+ return pAddr;
+}
+
+oslSocketAddr SAL_CALL osl_getPeerAddrOfSocket(oslSocket pSocket)
+{
+ socklen_t AddrLen;
+ struct sockaddr Addr;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return nullptr;
+ }
+
+ pSocket->m_nLastError=0;
+ AddrLen= sizeof(struct sockaddr);
+
+ if(getpeername(pSocket->m_Socket, &Addr, &AddrLen) == OSL_SOCKET_ERROR)
+ {
+ pSocket->m_nLastError=errno;
+ return nullptr;
+ }
+ return createSocketAddrFromSystem( &Addr );
+}
+
+sal_Bool SAL_CALL osl_bindAddrToSocket(oslSocket pSocket,
+ oslSocketAddr pAddr)
+{
+ int nRet;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+ if ( pSocket == nullptr || pAddr == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ nRet = bind(pSocket->m_Socket, &(pAddr->m_sockaddr), sizeof(struct sockaddr));
+
+ if ( nRet == OSL_SOCKET_ERROR)
+ {
+ pSocket->m_nLastError=errno;
+ return false;
+ }
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_listenOnSocket(oslSocket pSocket,
+ sal_Int32 MaxPendingConnections)
+{
+ int nRet;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ nRet = listen(pSocket->m_Socket,
+ MaxPendingConnections == -1 ?
+ SOMAXCONN :
+ MaxPendingConnections);
+ if ( nRet == OSL_SOCKET_ERROR)
+ {
+ pSocket->m_nLastError=errno;
+ return false;
+ }
+
+ return true;
+}
+
+oslSocketResult SAL_CALL osl_connectSocketTo(oslSocket pSocket,
+ oslSocketAddr pAddr,
+ const TimeValue* pTimeout)
+{
+ int ReadyHandles;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+
+ if ( pSocket == nullptr )
+ {
+ return osl_Socket_Error;
+ }
+
+ pSocket->m_nLastError=0;
+
+ if (osl_isNonBlockingMode(pSocket))
+ {
+ if (connect(pSocket->m_Socket,
+ &(pAddr->m_sockaddr),
+ sizeof(struct sockaddr)) != OSL_SOCKET_ERROR)
+ return osl_Socket_Ok;
+
+ if (errno == EWOULDBLOCK || errno == EINPROGRESS)
+ {
+ pSocket->m_nLastError=EINPROGRESS;
+ return osl_Socket_InProgress;
+ }
+
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "connection failed: " << UnixErrnoString(nErrno) );
+ return osl_Socket_Error;
+ }
+
+ /* set socket temporarily to non-blocking */
+ if( !osl_enableNonBlockingMode(pSocket, true) )
+ SAL_WARN( "sal.osl", "failed to enable non-blocking mode" );
+
+ /* initiate connect */
+ if(connect(pSocket->m_Socket,
+ &(pAddr->m_sockaddr),
+ sizeof(struct sockaddr)) != OSL_SOCKET_ERROR)
+ {
+ /* immediate connection */
+ osl_enableNonBlockingMode(pSocket, false);
+
+ return osl_Socket_Ok;
+ }
+
+ /* really an error or just delayed? */
+ if (errno != EINPROGRESS)
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "connection failed: " << UnixErrnoString(nErrno) );
+
+ osl_enableNonBlockingMode(pSocket, false);
+ return osl_Socket_Error;
+ }
+
+ /* prepare poll set for socket */
+ pollfd Set = {pSocket->m_Socket, POLLPRI | POLLOUT, 0};
+
+ /* poll */
+ ReadyHandles= poll(&Set, 1,
+ pTimeout ? convertToMs(pTimeout) : -1);
+
+ if (ReadyHandles > 0) /* connected */
+ {
+ if ( (Set.revents & POLLOUT) != 0 )
+ {
+ int nErrorCode = 0;
+ socklen_t nErrorSize = sizeof( nErrorCode );
+
+ int nSockOpt;
+
+ nSockOpt = getsockopt ( pSocket->m_Socket, SOL_SOCKET, SO_ERROR,
+ &nErrorCode, &nErrorSize );
+ if ( (nSockOpt == 0) && (nErrorCode == 0))
+ {
+ osl_enableNonBlockingMode(pSocket, false);
+ return osl_Socket_Ok;
+ }
+ else
+ {
+ pSocket->m_nLastError = (nSockOpt == 0) ? nErrorCode : errno;
+ return osl_Socket_Error;
+ }
+ }
+ else
+ {
+ return osl_Socket_Error;
+ }
+ }
+ else if (ReadyHandles < 0) /* error */
+ {
+ if (errno == EBADF) /* most probably interrupted by close() */
+ {
+ /* do not access pSockImpl because it is about to be or */
+ /* already destroyed */
+ return osl_Socket_Interrupted;
+ }
+ pSocket->m_nLastError=errno;
+ return osl_Socket_Error;
+ }
+ else /* timeout */
+ {
+ pSocket->m_nLastError=errno;
+ return osl_Socket_TimedOut;
+ }
+}
+
+oslSocket SAL_CALL osl_acceptConnectionOnSocket(oslSocket pSocket,
+ oslSocketAddr* ppAddr)
+{
+ struct sockaddr Addr;
+ int Connection, Flags;
+ oslSocket pConnectionSockImpl;
+
+ socklen_t AddrLen = sizeof(struct sockaddr);
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return nullptr;
+ }
+
+ pSocket->m_nLastError=0;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pSocket->m_bIsAccepting = true;
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+
+ if( ppAddr && *ppAddr )
+ {
+ osl_destroySocketAddr( *ppAddr );
+ *ppAddr = nullptr;
+ }
+
+ /* prevent Linux EINTR behaviour */
+ do
+ {
+ Connection = accept(pSocket->m_Socket, &Addr, &AddrLen);
+ } while (Connection == -1 && errno == EINTR);
+
+ /* accept failed? */
+ if( Connection == OSL_SOCKET_ERROR )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "accept connection failed: " << UnixErrnoString(nErrno) );
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pSocket->m_bIsAccepting = false;
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+ return nullptr;
+ }
+
+ assert(AddrLen == sizeof(struct sockaddr));
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ if ( pSocket->m_bIsInShutdown )
+ {
+ close(Connection);
+ SAL_WARN( "sal.osl", "close while accept" );
+ return nullptr;
+ }
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+
+ if(ppAddr)
+ {
+ *ppAddr= createSocketAddrFromSystem(&Addr);
+ }
+
+ /* alloc memory */
+ pConnectionSockImpl= createSocketImpl();
+
+ /* set close-on-exec flag */
+ if ((Flags = fcntl(Connection, F_GETFD, 0)) != -1)
+ {
+ Flags |= FD_CLOEXEC;
+ if (fcntl(Connection, F_SETFD, Flags) == -1)
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "fcntl failed: " << UnixErrnoString(nErrno) );
+ }
+
+ }
+
+ pConnectionSockImpl->m_Socket = Connection;
+ pConnectionSockImpl->m_nLastError = 0;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pConnectionSockImpl->m_bIsAccepting = false;
+
+ pSocket->m_bIsAccepting = false;
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+ return pConnectionSockImpl;
+}
+
+sal_Int32 SAL_CALL osl_receiveSocket(oslSocket pSocket,
+ void* pBuffer,
+ sal_uInt32 BytesToRead,
+ oslSocketMsgFlag Flag)
+{
+ int nRead;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return -1;
+ }
+
+ pSocket->m_nLastError=0;
+
+ do
+ {
+ nRead = recv(pSocket->m_Socket,
+ pBuffer,
+ BytesToRead,
+ MSG_FLAG_TO_NATIVE(Flag));
+ } while ( nRead < 0 && errno == EINTR );
+
+ if ( nRead < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "receive socket [" << nRead << "] failed: " << UnixErrnoString(nErrno) );
+ }
+ else if ( nRead == 0 )
+ {
+ SAL_WARN( "sal.osl", "receive socket [" << nRead << "] failed: EOL" );
+ }
+
+ return nRead;
+}
+
+sal_Int32 SAL_CALL osl_receiveFromSocket(oslSocket pSocket,
+ oslSocketAddr pSenderAddr,
+ void* pBuffer,
+ sal_uInt32 BufferSize,
+ oslSocketMsgFlag Flag)
+{
+ int nRead;
+ struct sockaddr *pSystemSockAddr = nullptr;
+ socklen_t AddrLen = 0;
+ if( pSenderAddr )
+ {
+ AddrLen = sizeof( struct sockaddr );
+ pSystemSockAddr = &(pSenderAddr->m_sockaddr);
+ }
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return -1;
+ }
+
+ pSocket->m_nLastError=0;
+
+ nRead = recvfrom(pSocket->m_Socket,
+ pBuffer,
+ BufferSize,
+ MSG_FLAG_TO_NATIVE(Flag),
+ pSystemSockAddr,
+ &AddrLen);
+
+ if ( nRead < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "receive socket [" << nRead << "] failed: " << UnixErrnoString(nErrno) );
+ }
+ else if ( nRead == 0 )
+ {
+ SAL_WARN( "sal.osl", "receive socket [" << nRead << "] failed: EOL" );
+ }
+
+ return nRead;
+}
+
+sal_Int32 SAL_CALL osl_sendSocket(oslSocket pSocket,
+ const void* pBuffer,
+ sal_uInt32 BytesToSend,
+ oslSocketMsgFlag Flag)
+{
+ int nWritten;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return -1;
+ }
+
+ pSocket->m_nLastError=0;
+
+ do
+ {
+ nWritten = send(pSocket->m_Socket,
+ pBuffer,
+ BytesToSend,
+ MSG_FLAG_TO_NATIVE(Flag));
+ } while ( nWritten < 0 && errno == EINTR );
+
+ if ( nWritten < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "send socket [" << nWritten << "] failed: " << UnixErrnoString(nErrno) );
+ }
+ else if ( nWritten == 0 )
+ {
+ SAL_WARN( "sal.osl", "send socket [" << nWritten << "] failed: EOL" );
+ }
+
+ return nWritten;
+}
+
+sal_Int32 SAL_CALL osl_sendToSocket(oslSocket pSocket,
+ oslSocketAddr ReceiverAddr,
+ const void* pBuffer,
+ sal_uInt32 BytesToSend,
+ oslSocketMsgFlag Flag)
+{
+ int nWritten;
+
+ struct sockaddr *pSystemSockAddr = nullptr;
+ int AddrLen = 0;
+ if( ReceiverAddr )
+ {
+ pSystemSockAddr = &(ReceiverAddr->m_sockaddr);
+ AddrLen = sizeof( struct sockaddr );
+ }
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return -1;
+ }
+
+ pSocket->m_nLastError=0;
+
+ /* ReceiverAddr might be 0 when used on a connected socket. */
+ /* Then sendto should behave like send. */
+
+ nWritten = sendto(pSocket->m_Socket,
+ pBuffer,
+ BytesToSend,
+ MSG_FLAG_TO_NATIVE(Flag),
+ pSystemSockAddr,
+ AddrLen);
+
+ if ( nWritten < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "send socket [" << nWritten << "] failed: " << UnixErrnoString(nErrno) );
+ }
+ else if ( nWritten == 0 )
+ {
+ SAL_WARN( "sal.osl", "send socket [" << nWritten << "] failed: EOL" );
+ }
+
+ return nWritten;
+}
+
+sal_Int32 SAL_CALL osl_readSocket (
+ oslSocket pSocket, void *pBuffer, sal_Int32 n )
+{
+ sal_uInt8 * Ptr = static_cast<sal_uInt8 *>(pBuffer);
+ sal_uInt32 BytesRead= 0;
+ sal_uInt32 BytesToRead= n;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+
+ /* loop until all desired bytes were read or an error occurred */
+ while (BytesToRead > 0)
+ {
+ sal_Int32 RetVal;
+ RetVal= osl_receiveSocket(pSocket,
+ Ptr,
+ BytesToRead,
+ osl_Socket_MsgNormal);
+
+ /* error occurred? */
+ if(RetVal <= 0)
+ {
+ break;
+ }
+
+ BytesToRead -= RetVal;
+ BytesRead += RetVal;
+ Ptr += RetVal;
+ }
+
+ return BytesRead;
+}
+
+sal_Int32 SAL_CALL osl_writeSocket(
+ oslSocket pSocket, const void *pBuffer, sal_Int32 n )
+{
+ /* loop until all desired bytes were send or an error occurred */
+ sal_uInt32 BytesSend= 0;
+ sal_uInt32 BytesToSend= n;
+ sal_uInt8 const *Ptr = static_cast<sal_uInt8 const *>(pBuffer);
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+
+ while (BytesToSend > 0)
+ {
+ sal_Int32 RetVal;
+
+ RetVal= osl_sendSocket( pSocket,Ptr,BytesToSend,osl_Socket_MsgNormal);
+
+ /* error occurred? */
+ if(RetVal <= 0)
+ {
+ break;
+ }
+
+ BytesToSend -= RetVal;
+ BytesSend += RetVal;
+ Ptr += RetVal;
+
+ }
+ return BytesSend;
+}
+
+static bool socket_poll (
+ oslSocket pSocket,
+ const TimeValue* pTimeout,
+ short nEvent)
+{
+ struct pollfd fds;
+ int timeout;
+ int result;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if (pSocket == nullptr)
+ return false; /* EINVAL */
+
+ pSocket->m_nLastError = 0;
+
+ fds.fd = pSocket->m_Socket;
+ fds.events = nEvent;
+ fds.revents = 0;
+
+ timeout = -1;
+ if (pTimeout)
+ {
+ timeout = convertToMs(pTimeout);
+ }
+
+ result = poll (&fds, 1, timeout);
+ if (result < 0)
+ {
+ pSocket->m_nLastError = errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "poll failed: " << UnixErrnoString(nErrno) );
+ return false;
+ }
+ if (result == 0)
+ {
+ /* Timeout */
+ return false;
+ }
+
+ return ((fds.revents & nEvent) == nEvent);
+}
+
+sal_Bool SAL_CALL osl_isReceiveReady (
+ oslSocket pSocket, const TimeValue* pTimeout)
+{
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if (pSocket == nullptr)
+ {
+ /* ENOTSOCK */
+ return false;
+ }
+
+ return socket_poll (pSocket, pTimeout, POLLIN);
+}
+
+sal_Bool SAL_CALL osl_isSendReady (
+ oslSocket pSocket, const TimeValue* pTimeout)
+{
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if (pSocket == nullptr)
+ {
+ /* ENOTSOCK */
+ return false;
+ }
+
+ return socket_poll (pSocket, pTimeout, POLLOUT);
+}
+
+sal_Bool SAL_CALL osl_isExceptionPending (
+ oslSocket pSocket, const TimeValue* pTimeout)
+{
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if (pSocket == nullptr)
+ {
+ /* ENOTSOCK */
+ return false;
+ }
+
+ return socket_poll (pSocket, pTimeout, POLLPRI);
+}
+
+sal_Bool SAL_CALL osl_shutdownSocket(oslSocket pSocket,
+ oslSocketDirection Direction)
+{
+ int nRet;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ nRet=shutdown(pSocket->m_Socket, DIRECTION_TO_NATIVE(Direction));
+ if (nRet != 0 )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "shutdown failed: " << UnixErrnoString(nErrno) );
+ }
+ return (nRet==0);
+}
+
+sal_Int32 SAL_CALL osl_getSocketOption(oslSocket pSocket,
+ oslSocketOptionLevel Level,
+ oslSocketOption Option,
+ void* pBuffer,
+ sal_uInt32 BufferLen)
+{
+ socklen_t nOptLen = static_cast<socklen_t>(BufferLen);
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return -1;
+ }
+
+ pSocket->m_nLastError=0;
+
+ if(getsockopt(pSocket->m_Socket,
+ OPTION_LEVEL_TO_NATIVE(Level),
+ OPTION_TO_NATIVE(Option),
+ pBuffer,
+ &nOptLen) == -1)
+ {
+ pSocket->m_nLastError=errno;
+ return -1;
+ }
+
+ return nOptLen;
+}
+
+sal_Bool SAL_CALL osl_setSocketOption(oslSocket pSocket,
+ oslSocketOptionLevel Level,
+ oslSocketOption Option,
+ void* pBuffer,
+ sal_uInt32 BufferLen)
+{
+ int nRet;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ nRet = setsockopt(pSocket->m_Socket,
+ OPTION_LEVEL_TO_NATIVE(Level),
+ OPTION_TO_NATIVE(Option),
+ pBuffer,
+ BufferLen);
+
+ if ( nRet < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ return false;
+ }
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_enableNonBlockingMode(oslSocket pSocket,
+ sal_Bool On)
+{
+ int flags;
+ int nRet;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ flags = fcntl(pSocket->m_Socket, F_GETFL, 0);
+
+ if (On)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~(O_NONBLOCK);
+
+ nRet = fcntl(pSocket->m_Socket, F_SETFL, flags);
+
+ if ( nRet < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ return false;
+ }
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_isNonBlockingMode(oslSocket pSocket)
+{
+ int flags;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ flags = fcntl(pSocket->m_Socket, F_GETFL, 0);
+
+ if (flags == -1 || !(flags & O_NONBLOCK))
+ return false;
+
+ return true;
+}
+
+oslSocketType SAL_CALL osl_getSocketType(oslSocket pSocket)
+{
+ int Type=0;
+ socklen_t TypeSize= sizeof(Type);
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return osl_Socket_TypeInvalid;
+ }
+
+ pSocket->m_nLastError=0;
+
+ if(getsockopt(pSocket->m_Socket,
+ OPTION_LEVEL_TO_NATIVE(osl_Socket_LevelSocket),
+ OPTION_TO_NATIVE(osl_Socket_OptionType),
+ &Type,
+ &TypeSize) == -1)
+ {
+ /* error */
+ pSocket->m_nLastError=errno;
+ return osl_Socket_TypeInvalid;
+ }
+
+ return TYPE_FROM_NATIVE(Type);
+
+}
+
+void SAL_CALL osl_getLastSocketErrorDescription(oslSocket Socket, rtl_uString **ustrError)
+{
+ char pszError[1024];
+
+ pszError[0] = '\0';
+
+ osl_psz_getLastSocketErrorDescription(Socket,pszError,sizeof(pszError));
+
+ rtl_uString_newFromAscii(ustrError,pszError);
+}
+
+void osl_psz_getLastSocketErrorDescription(oslSocket pSocket, char* pBuffer, sal_uInt32 BufferSize)
+{
+ /* make sure pBuffer will be a zero-terminated string even when strncpy has to cut */
+ pBuffer[BufferSize-1]= '\0';
+
+ if ( pSocket == nullptr )
+ {
+ strncpy(pBuffer, strerror(EINVAL), BufferSize-1);
+ return;
+ }
+
+ strncpy(pBuffer, strerror(pSocket->m_nLastError), BufferSize-1);
+}
+
+oslSocketError SAL_CALL osl_getLastSocketError(oslSocket pSocket)
+{
+ if ( pSocket == nullptr )
+ {
+ return ERROR_FROM_NATIVE(EINVAL);
+ }
+
+ return ERROR_FROM_NATIVE(pSocket->m_nLastError);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/sockimpl.hxx b/sal/osl/unx/sockimpl.hxx
new file mode 100644
index 000000000..1a9bc1f9e
--- /dev/null
+++ b/sal/osl/unx/sockimpl.hxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_SOCKIMPL_HXX
+#define INCLUDED_SAL_OSL_UNX_SOCKIMPL_HXX
+
+#include <osl/pipe.h>
+#include <osl/socket.h>
+#include <osl/interlck.h>
+
+#include "system.hxx"
+
+#if defined(LINUX) || defined(FREEBSD) || defined(NETBSD)
+#define CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT 1
+#endif
+
+struct oslSocketImpl {
+ int m_Socket;
+ int m_nLastError;
+ oslInterlockedCount m_nRefCount;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ bool m_bIsAccepting;
+ bool m_bIsInShutdown;
+#endif
+};
+
+struct oslSocketAddrImpl
+{
+ struct sockaddr m_sockaddr;
+};
+
+struct oslPipeImpl {
+ int m_Socket;
+ char m_Name[sizeof sockaddr_un::sun_path];
+ oslInterlockedCount m_nRefCount;
+ bool m_bClosed;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ bool m_bIsAccepting;
+ bool m_bIsInShutdown;
+#endif
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/soffice.cxx b/sal/osl/unx/soffice.cxx
new file mode 100644
index 000000000..bcead388b
--- /dev/null
+++ b/sal/osl/unx/soffice.cxx
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <atomic>
+
+#include "soffice.hxx"
+
+namespace
+{
+std::atomic<bool> flag(false);
+}
+
+void sal::detail::setSoffice() { flag = true; }
+
+bool sal::detail::isSoffice() { return flag; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sal/osl/unx/soffice.hxx b/sal/osl/unx/soffice.hxx
new file mode 100644
index 000000000..da1b684af
--- /dev/null
+++ b/sal/osl/unx/soffice.hxx
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_SOFFICE_HXX
+#define INCLUDED_SAL_OSL_UNX_SOFFICE_HXX
+
+#include <sal/config.h>
+
+// Used to communicate special sal::detail::InitializeSoffice sal_detail_initialize call:
+
+namespace sal::detail
+{
+void setSoffice();
+
+bool isSoffice();
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sal/osl/unx/system.cxx b/sal/osl/unx/system.cxx
new file mode 100644
index 000000000..875f41016
--- /dev/null
+++ b/sal/osl/unx/system.cxx
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <config_features.h>
+
+#include "system.hxx"
+
+#ifdef NO_PTHREAD_RTL
+
+#if defined(MACOSX)
+
+#include <premac.h>
+#include <Foundation/Foundation.h>
+#include <postmac.h>
+
+/*
+ * Add support for resolving Mac native alias files (not the same as unix alias files)
+ * (what are "unix alias files"?)
+ * returns 0 on success.
+ */
+int macxp_resolveAlias(char *path, int buflen)
+{
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ /* Avoid unnecessary messages in the system.log:
+ *
+ * soffice(57342) deny file-read-data /Users/tml/Documents/b.odt/..namedfork/rsrc
+ * etc.
+ *
+ * Just don't bother with resolving aliases. I doubt its usefulness anyway.
+ */
+ (void) path;
+ (void) buflen;
+ return 0;
+#else
+ CFStringRef cfpath;
+ CFURLRef cfurl;
+ CFErrorRef cferror;
+ CFDataRef cfbookmark;
+
+ // Don't even try anything for files inside the app bundle. Just a
+ // waste of time.
+
+ static const char * const appBundle = [[[NSBundle mainBundle] bundlePath] UTF8String];
+
+ const size_t appBundleLen = strlen(appBundle);
+ if (strncmp(path, appBundle, appBundleLen) == 0 && path[appBundleLen] == '/')
+ return 0;
+
+ char *unprocessedPath = path;
+
+ if ( *unprocessedPath == '/' )
+ unprocessedPath++;
+
+ int nRet = 0;
+ while ( !nRet && unprocessedPath && *unprocessedPath )
+ {
+ unprocessedPath = strchr( unprocessedPath, '/' );
+ if ( unprocessedPath )
+ *unprocessedPath = '\0';
+
+ cfpath = CFStringCreateWithCString( nullptr, path, kCFStringEncodingUTF8 );
+ cfurl = CFURLCreateWithFileSystemPath( nullptr, cfpath, kCFURLPOSIXPathStyle, false );
+ CFRelease( cfpath );
+ cferror = nullptr;
+ cfbookmark = CFURLCreateBookmarkDataFromFile( nullptr, cfurl, &cferror );
+ CFRelease( cfurl );
+
+ if ( cfbookmark == nullptr )
+ {
+ if(cferror)
+ {
+ CFRelease( cferror );
+ }
+ }
+ else
+ {
+ Boolean isStale;
+ cfurl = CFURLCreateByResolvingBookmarkData( nullptr, cfbookmark, kCFBookmarkResolutionWithoutUIMask,
+ nullptr, nullptr, &isStale, &cferror );
+ CFRelease( cfbookmark );
+ if ( cfurl == nullptr )
+ {
+ CFRelease( cferror );
+ }
+ else
+ {
+ cfpath = CFURLCopyFileSystemPath( cfurl, kCFURLPOSIXPathStyle );
+ CFRelease( cfurl );
+ if ( cfpath != nullptr )
+ {
+ char tmpPath[ PATH_MAX ];
+ if ( CFStringGetCString( cfpath, tmpPath, PATH_MAX, kCFStringEncodingUTF8 ) )
+ {
+ int nLen = strlen( tmpPath ) + ( unprocessedPath ? strlen( unprocessedPath + 1 ) + 1 : 0 );
+ if ( nLen < buflen && nLen < PATH_MAX )
+ {
+ if ( unprocessedPath )
+ {
+ int nTmpPathLen = strlen( tmpPath );
+ strcat( tmpPath, "/" );
+ strcat( tmpPath, unprocessedPath + 1 );
+ strcpy( path, tmpPath);
+ unprocessedPath = path + nTmpPathLen;
+ }
+ else if ( !unprocessedPath )
+ {
+ strcpy( path, tmpPath );
+ }
+ }
+ else
+ {
+ errno = ENAMETOOLONG;
+ nRet = -1;
+ }
+ }
+ CFRelease( cfpath );
+ }
+ }
+ }
+
+ if ( unprocessedPath )
+ *unprocessedPath++ = '/';
+ }
+
+ return nRet;
+#endif
+}
+
+#endif /* defined MACOSX */
+
+#endif /* NO_PTHREAD_RTL */
+
+//might be useful on other platforms, but doesn't compiler under MACOSX anyway
+#if defined(__GNUC__) && defined(LINUX)
+//force the __data_start symbol to exist in any executables that link against
+//libuno_sal so that dlopening of the libgcj provided libjvm.so on some
+//platforms where it needs that symbol will succeed. e.g. Debian mips/lenny
+//with gcc 4.3. With this in place the smoketest succeeds with libgcj provided
+//java. Quite possibly also required/helpful for s390x/s390 and maybe some
+//others. Without it the dlopen of libjvm.so will fail with __data_start
+//not found
+extern int __data_start[] __attribute__((weak));
+extern int data_start[] __attribute__((weak));
+extern int _end[] __attribute__((weak));
+static void *dummy[] __attribute__((used)) = {__data_start, data_start, _end};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/system.hxx b/sal/osl/unx/system.hxx
new file mode 100644
index 000000000..8b1f1dccc
--- /dev/null
+++ b/sal/osl/unx/system.hxx
@@ -0,0 +1,406 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_SYSTEM_HXX
+#define INCLUDED_SAL_OSL_UNX_SYSTEM_HXX
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <signal.h>
+#include <utime.h>
+
+#include <pwd.h>
+
+#include <netdb.h>
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <sys/types.h>
+
+/* Make sockets of type AF_UNIX use underlying FS rights */
+#if defined(__sun) && !defined(_XOPEN_SOURCE)
+# define _XOPEN_SOURCE 500
+# include <sys/socket.h>
+# undef _XOPEN_SOURCE
+#else
+# include <sys/socket.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef SYSV
+# include <sys/utsname.h>
+#endif
+
+#ifdef LINUX
+# ifndef __USE_GNU
+# define __USE_GNU
+# endif
+
+# include <shadow.h>
+# include <pthread.h>
+# include <sys/file.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <dlfcn.h>
+# include <endian.h>
+# include <sys/time.h>
+# define IORESOURCE_TRANSFER_BSD
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define pthread_testcancel()
+# define NO_PTHREAD_PRIORITY
+# define PTHREAD_SIGACTION pthread_sigaction
+
+# ifndef ETIME
+# define ETIME ETIMEDOUT
+# endif
+
+#endif
+
+#ifdef HAIKU
+# include <shadow.h>
+# include <pthread.h>
+# include <sys/file.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <dlfcn.h>
+# include <endian.h>
+# include <sys/time.h>
+# define IORESOURCE_TRANSFER_BSD
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define pthread_testcancel()
+# define NO_PTHREAD_PRIORITY
+# define NO_PTHREAD_RTL
+# define PTHREAD_SIGACTION pthread_sigaction
+
+# ifndef ETIME
+# define ETIME ETIMEDOUT
+# endif
+# define SIGIOT SIGABRT
+# define SOCK_RDM 0
+// hack: Haiku defines SOL_SOCKET as -1, but this makes GCC complain about
+// narrowing conversion
+# undef SOL_SOCKET
+# define SOL_SOCKET (sal_uInt32) -1
+
+#endif
+
+#if defined(ANDROID)
+# include <pthread.h>
+# include <sys/file.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <dlfcn.h>
+# include <endian.h>
+# include <sys/time.h>
+# define IORESOURCE_TRANSFER_BSD
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define pthread_testcancel()
+# define NO_PTHREAD_PRIORITY
+#endif
+
+#ifdef NETBSD
+# define NO_PTHREAD_RTL
+#endif
+
+#ifdef FREEBSD
+# ifndef ETIME
+# define ETIME ETIMEDOUT
+# endif
+# include <pthread.h>
+# include <sys/sem.h>
+# include <dlfcn.h>
+# include <sys/filio.h>
+# include <sys/ioctl.h>
+# include <sys/param.h>
+# include <sys/time.h>
+# include <sys/uio.h>
+# include <sys/exec.h>
+# include <vm/vm.h>
+# include <vm/vm_param.h>
+# include <vm/pmap.h>
+# include <vm/swap_pager.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# define IORESOURCE_TRANSFER_BSD
+# include <machine/endian.h>
+# define NO_PTHREAD_RTL
+#endif
+
+#ifdef OPENBSD
+# define ETIME ETIMEDOUT
+# include <pthread.h>
+# include <sys/sem.h>
+# include <dlfcn.h>
+# include <sys/filio.h>
+# include <sys/ioctl.h>
+# include <sys/param.h>
+# include <sys/time.h>
+# include <sys/uio.h>
+# include <sys/exec.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# define IORESOURCE_TRANSFER_BSD
+# include <machine/endian.h>
+# define PTR_SIZE_T(s) ((size_t *)&(s))
+# define IORESOURCE_TRANSFER_BSD
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define pthread_testcancel()
+# define NO_PTHREAD_PRIORITY
+# define NO_PTHREAD_RTL
+# define PTHREAD_SIGACTION pthread_sigaction
+#endif
+
+#if defined(DRAGONFLY) || defined(NETBSD)
+# define ETIME ETIMEDOUT
+# include <pthread.h>
+# include <sys/sem.h>
+# include <dlfcn.h>
+# include <sys/filio.h>
+# include <sys/ioctl.h>
+# include <sys/param.h>
+# include <sys/time.h>
+# include <sys/uio.h>
+# include <sys/exec.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <machine/endian.h>
+# define IORESOURCE_TRANSFER_BSD
+# define IOCHANNEL_TRANSFER_BSD_RENO
+#endif
+
+#ifdef AIX
+# define AF_IPX -1
+# include <strings.h>
+# include <pthread.h>
+# include <dlfcn.h>
+# include <sys/time.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <sys/machine.h>
+# define SLEEP_TIMESPEC(timespec) nsleep(&timespec, 0)
+# define LIBPATH "LIBPATH"
+#endif
+
+#ifdef __sun
+# include <shadow.h>
+# include <sys/un.h>
+# include <stropts.h>
+# include <pthread.h>
+# include <netinet/tcp.h>
+# include <sys/filio.h>
+# include <dlfcn.h>
+# include <sys/isa_defs.h>
+# define IORESOURCE_TRANSFER_SYSV
+# define IOCHANNEL_TRANSFER_BSD
+# define LIBPATH "LD_LIBRARY_PATH"
+#endif
+
+#ifdef MACOSX
+#define TimeValue CFTimeValue // Do not conflict with TimeValue in sal/inc/osl/time.h
+#include <Carbon/Carbon.h>
+#undef TimeValue
+# ifndef ETIME
+# define ETIME ETIMEDOUT
+# endif
+# include <dlfcn.h>
+# include <pthread.h>
+# include <sys/file.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <machine/endian.h>
+# include <sys/time.h>
+# include <mach-o/dyld.h>
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define NO_PTHREAD_RTL
+/* for NSGetArgc/Argv/Environ */
+# include <crt_externs.h>
+int macxp_resolveAlias(char *path, int buflen);
+#endif
+
+#ifdef IOS
+# ifndef ETIME
+# define ETIME ETIMEDOUT
+# endif
+# include <dlfcn.h>
+# include <pthread.h>
+# include <sys/file.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <machine/endian.h>
+# include <sys/time.h>
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define NO_PTHREAD_RTL
+#endif
+
+#ifdef EMSCRIPTEN
+# ifndef ETIME
+# define ETIME ETIMEDOUT
+# endif
+# include <pthread.h>
+# include <sys/file.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <dlfcn.h>
+# include <endian.h>
+# include <sys/time.h>
+# define IORESOURCE_TRANSFER_BSD
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define pthread_testcancel()
+# define NO_PTHREAD_PRIORITY
+# define INIT_GROUPS(name, gid) false
+#endif
+
+#if !defined(_WIN32) && \
+ !defined(LINUX) && !defined(NETBSD) && !defined(FREEBSD) && \
+ !defined(AIX) && \
+ !defined(__sun) && !defined(MACOSX) && \
+ !defined(OPENBSD) && !defined(DRAGONFLY) && \
+ !defined(IOS) && !defined(ANDROID) && \
+ !defined(HAIKU) && !defined(EMSCRIPTEN)
+# error "Target platform not specified!"
+#endif
+
+#ifndef PTR_FD_SET
+# define PTR_FD_SET(s) (&(s))
+#endif
+
+#ifndef NORMALIZE_TIMESPEC
+# define NORMALIZE_TIMESPEC(timespec) \
+ timespec . tv_sec += timespec . tv_nsec / 1000000000; \
+ timespec . tv_nsec %= 1000000000;
+#endif
+
+#ifndef SET_TIMESPEC
+# define SET_TIMESPEC(timespec, sec, nsec) \
+ timespec . tv_sec = (sec); \
+ timespec . tv_nsec = (nsec); \
+ NORMALIZE_TIMESPEC(timespec);
+#endif
+
+#ifndef SLEEP_TIMESPEC
+# define SLEEP_TIMESPEC(timespec) nanosleep(&timespec, nullptr)
+#endif
+
+#ifndef INIT_GROUPS
+# define INIT_GROUPS(name, gid) ((setgid((gid)) == 0) && (initgroups((name), (gid)) == 0))
+#endif
+
+#ifndef PTHREAD_NONE
+# define PTHREAD_NONE _pthread_none_
+# ifndef PTHREAD_NONE_INIT
+# define PTHREAD_NONE_INIT ((pthread_t)-1)
+# endif
+#endif
+
+#ifndef PTHREAD_ATTR_DEFAULT
+# define PTHREAD_ATTR_DEFAULT nullptr
+#endif
+#ifndef PTHREAD_MUTEXATTR_DEFAULT
+# define PTHREAD_MUTEXATTR_DEFAULT nullptr
+#endif
+#ifndef PTHREAD_CONDATTR_DEFAULT
+# define PTHREAD_CONDATTR_DEFAULT nullptr
+#endif
+
+#ifndef PTHREAD_SIGACTION
+# define PTHREAD_SIGACTION sigaction
+#endif
+
+#ifndef STAT_PARENT
+# define STAT_PARENT lstat
+#endif
+
+/* socket options which might not be defined on all unx flavors */
+#ifndef SO_ACCEPTCONN
+# define SO_ACCEPTCONN 0
+#endif
+#ifndef SO_SNDLOWAT
+# define SO_SNDLOWAT 0
+#endif
+#ifndef SO_RCVLOWAT
+# define SO_RCVLOWAT 0
+#endif
+#ifndef SO_SNDTIMEO
+# define SO_SNDTIMEO 0
+#endif
+#ifndef SO_RCVTIMEO
+# define SO_RCVTIMEO 0
+#endif
+#ifndef SO_USELOOPBACK
+# define SO_USELOOPBACK 0
+#endif
+#ifndef MSG_MAXIOVLEN
+# define MSG_MAXIOVLEN 0
+#endif
+
+/* BEGIN HACK */
+/* dummy define and declarations for IPX should be replaced by */
+/* original ipx headers when these are available for this platform */
+
+#ifndef SA_FAMILY_DECL
+# define SA_FAMILY_DECL short sa_family
+#endif
+
+#define NSPROTO_IPX 1000
+#define NSPROTO_SPX 1256
+#define NSPROTO_SPXII 1257
+
+/* END HACK */
+
+#ifdef NO_PTHREAD_RTL
+#if !defined FREEBSD
+#if !defined NETBSD
+struct passwd *getpwent_r(struct passwd *pwd, char *buffer, int buflen);
+#endif
+extern struct spwd *getspnam_r(const char *name, struct spwd *result,
+ char *buffer, int buflen);
+
+#if !defined MACOSX
+struct tm *localtime_r(const time_t *timep, struct tm *buffer);
+struct tm *gmtime_r(const time_t *timep, struct tm *buffer);
+#endif
+#endif /* !defined(FREEBSD) */
+#endif
+
+#endif // INCLUDED_SAL_OSL_UNX_SYSTEM_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/system.mm b/sal/osl/unx/system.mm
new file mode 100644
index 000000000..7495a3e73
--- /dev/null
+++ b/sal/osl/unx/system.mm
@@ -0,0 +1 @@
+#include "system.cxx"
diff --git a/sal/osl/unx/tempfile.cxx b/sal/osl/unx/tempfile.cxx
new file mode 100644
index 000000000..29a4d453f
--- /dev/null
+++ b/sal/osl/unx/tempfile.cxx
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "system.hxx"
+#include <osl/file.h>
+#include <osl/thread.h>
+#include <rtl/ustrbuf.h>
+#include <osl/diagnose.h>
+#include <sal/macros.h>
+
+#include "file_url.hxx"
+#include "file_impl.hxx"
+
+#include <cassert>
+
+oslFileError SAL_CALL osl_getTempDirURL( rtl_uString** pustrTempDir )
+{
+ oslFileError error;
+ /* described in environ(7) */
+ const char *pValue = getenv( "TMPDIR" );
+ rtl_uString *ustrTempPath = nullptr;
+
+ if ( !pValue )
+ pValue = getenv( "TEMP" );
+
+ if ( !pValue )
+ pValue = getenv( "TMP" );
+
+ if ( !pValue )
+ pValue = "/tmp";
+
+ auto nLen = strlen(pValue);
+ while (nLen > 1 && pValue[nLen - 1] == '/') // Allow path consisting of single "/"
+ --nLen;
+ rtl_string2UString( &ustrTempPath, pValue, nLen, osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
+ assert(ustrTempPath);
+ error = osl_getFileURLFromSystemPath( ustrTempPath, pustrTempDir );
+ rtl_uString_release( ustrTempPath );
+
+ return error;
+}
+
+/******************************************************************
+ * Generates a random unique file name. We're using the scheme
+ * from the standard c-lib function mkstemp to generate a more
+ * or less random unique file name
+ *
+ * @param rand_name
+ * receives the random name
+ ******************************************************************/
+
+const char LETTERS[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+const int COUNT_OF_LETTERS = SAL_N_ELEMENTS(LETTERS) - 1;
+
+#define RAND_NAME_LENGTH 6
+
+static void osl_gen_random_name_impl_(rtl_uString** rand_name)
+{
+ static uint64_t value;
+
+ char buffer[RAND_NAME_LENGTH];
+ struct timeval tv;
+ uint64_t v;
+ int i;
+
+ gettimeofday(&tv, nullptr);
+
+ value += (static_cast<uint64_t>(tv.tv_usec) << 16) ^ tv.tv_sec ^ getpid();
+
+ v = value;
+
+ for (i = 0; i < RAND_NAME_LENGTH; i++)
+ {
+ buffer[i] = LETTERS[v % COUNT_OF_LETTERS];
+ v /= COUNT_OF_LETTERS;
+ }
+
+ rtl_string2UString(
+ rand_name,
+ buffer,
+ RAND_NAME_LENGTH,
+ RTL_TEXTENCODING_ASCII_US,
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+
+ assert(*rand_name);
+}
+
+/*****************************************************************
+ * Helper function
+ * Either use the directory provided or the result of
+ * osl_getTempDirUrl and return it as system path and file url
+ ****************************************************************/
+
+static oslFileError osl_setup_base_directory_impl_(
+ rtl_uString* pustrDirectoryURL,
+ rtl_uString** ppustr_base_dir)
+{
+ rtl_uString* dir_url = nullptr;
+ rtl_uString* dir = nullptr;
+ oslFileError error = osl_File_E_None;
+
+ if (pustrDirectoryURL)
+ rtl_uString_assign(&dir_url, pustrDirectoryURL);
+ else
+ error = osl_getTempDirURL(&dir_url);
+
+ if (error == osl_File_E_None)
+ {
+ error = getSystemPathFromFileURL_Ex(dir_url, &dir);
+ rtl_uString_release(dir_url);
+ }
+
+ if (error == osl_File_E_None)
+ {
+ rtl_uString_assign(ppustr_base_dir, dir);
+ rtl_uString_release(dir);
+ }
+
+ return error;
+}
+
+/*****************************************************************
+ * osl_setup_createTempFile_impl
+ * validate input parameter, setup variables
+ ****************************************************************/
+
+ static oslFileError osl_setup_createTempFile_impl_(
+ rtl_uString* pustrDirectoryURL,
+ oslFileHandle* pHandle,
+ rtl_uString** ppustrTempFileURL,
+ rtl_uString** ppustr_base_dir,
+ bool* b_delete_on_close)
+ {
+ oslFileError osl_error;
+
+ OSL_PRECOND(((nullptr != pHandle) || (nullptr != ppustrTempFileURL)), "Invalid parameter!");
+
+ if ((pHandle == nullptr) && (ppustrTempFileURL == nullptr))
+ {
+ osl_error = osl_File_E_INVAL;
+ }
+ else
+ {
+ osl_error = osl_setup_base_directory_impl_(
+ pustrDirectoryURL, ppustr_base_dir);
+
+ *b_delete_on_close = (ppustrTempFileURL == nullptr);
+ }
+
+ return osl_error;
+ }
+
+/*****************************************************************
+ * Create a unique file in the specified directory and return
+ * its name
+ ****************************************************************/
+
+static oslFileError osl_create_temp_file_impl_(
+ const rtl_uString* pustr_base_directory,
+ oslFileHandle* file_handle,
+ rtl_uString** ppustr_temp_file_name)
+{
+ rtl_uString* rand_name = nullptr;
+ sal_uInt32 len_base_dir = 0;
+ rtl_uString* tmp_file_path = nullptr;
+ rtl_uString* tmp_file_url = nullptr;
+ sal_Int32 capacity = 0;
+ oslFileError osl_error = osl_File_E_None;
+ sal_Int32 offset_file_name;
+ const sal_Unicode* puchr;
+
+ OSL_PRECOND(pustr_base_directory, "Invalid Parameter");
+ OSL_PRECOND(file_handle, "Invalid Parameter");
+ OSL_PRECOND(ppustr_temp_file_name, "Invalid Parameter");
+
+ len_base_dir = rtl_uString_getLength(pustr_base_directory);
+
+ rtl_uString_new_WithLength(
+ &tmp_file_path,
+ (len_base_dir + 1 + RAND_NAME_LENGTH));
+ capacity = len_base_dir + 1 + RAND_NAME_LENGTH;
+
+ rtl_uStringbuffer_insert(
+ &tmp_file_path,
+ &capacity,
+ 0,
+ rtl_uString_getStr(const_cast<rtl_uString*>(pustr_base_directory)),
+ len_base_dir);
+
+ offset_file_name = len_base_dir;
+
+ puchr = rtl_uString_getStr(tmp_file_path);
+
+ /* ensure that the last character is a '/' */
+
+ if (puchr[len_base_dir - 1] != '/')
+ {
+ rtl_uStringbuffer_insert_ascii(
+ &tmp_file_path,
+ &capacity,
+ len_base_dir,
+ "/",
+ 1);
+
+ offset_file_name++;
+ }
+
+ while(true) /* try until success */
+ {
+ osl_gen_random_name_impl_(&rand_name);
+
+ rtl_uStringbuffer_insert(
+ &tmp_file_path,
+ &capacity,
+ offset_file_name,
+ rtl_uString_getStr(rand_name),
+ rtl_uString_getLength(rand_name));
+
+ osl_error = osl_getFileURLFromSystemPath(
+ tmp_file_path, &tmp_file_url);
+
+ if (osl_error == osl_File_E_None)
+ {
+ osl_error = openFile(
+ tmp_file_url,
+ file_handle,
+ osl_File_OpenFlag_Read |
+ osl_File_OpenFlag_Write |
+ osl_File_OpenFlag_Create,
+ S_IRUSR | S_IWUSR);
+ }
+
+ /* in case of error osl_File_E_EXIST we simply try again else we give up */
+
+ if (osl_error != osl_File_E_EXIST)
+ {
+ rtl_uString_release(rand_name);
+
+ if (tmp_file_url)
+ rtl_uString_release(tmp_file_url);
+
+ break;
+ }
+ } /* while(1) */
+
+ if (osl_error == osl_File_E_None)
+ rtl_uString_assign(ppustr_temp_file_name, tmp_file_path);
+
+ rtl_uString_release(tmp_file_path);
+
+ return osl_error;
+}
+
+oslFileError SAL_CALL osl_createTempFile(
+ rtl_uString* pustrDirectoryURL,
+ oslFileHandle* pHandle,
+ rtl_uString** ppustrTempFileURL)
+{
+ rtl_uString* base_directory = nullptr;
+ oslFileHandle temp_file_handle = nullptr;
+ bool b_delete_on_close;
+ oslFileError osl_error;
+
+ osl_error = osl_setup_createTempFile_impl_(
+ pustrDirectoryURL,
+ pHandle,
+ ppustrTempFileURL,
+ &base_directory,
+ &b_delete_on_close);
+
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+
+ rtl_uString* temp_file_name = nullptr;
+ osl_error = osl_create_temp_file_impl_(
+ base_directory, &temp_file_handle, &temp_file_name);
+
+ rtl_uString* temp_file_url = nullptr;
+ if (osl_error == osl_File_E_None)
+ {
+ osl_error = osl_getFileURLFromSystemPath(temp_file_name, &temp_file_url);
+ rtl_uString_release(temp_file_name);
+ }
+
+ if (osl_error == osl_File_E_None)
+ {
+ if (b_delete_on_close)
+ {
+ osl_error = osl_removeFile(temp_file_url);
+
+ if (osl_error == osl_File_E_None)
+ {
+ *pHandle = temp_file_handle;
+ temp_file_handle = nullptr;
+ }
+ }
+ else
+ {
+ if (pHandle)
+ {
+ *pHandle = temp_file_handle;
+ temp_file_handle = nullptr;
+ }
+
+ rtl_uString_assign(ppustrTempFileURL, temp_file_url);
+ }
+
+ rtl_uString_release(temp_file_url);
+ }
+
+ if (temp_file_handle)
+ osl_closeFile(temp_file_handle);
+
+ rtl_uString_release(base_directory);
+
+ return osl_error;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/thread.cxx b/sal/osl/unx/thread.cxx
new file mode 100644
index 000000000..b122c5f31
--- /dev/null
+++ b/sal/osl/unx/thread.cxx
@@ -0,0 +1,1102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstddef>
+#include <limits>
+#include <functional>
+#include <mutex>
+
+#include "system.hxx"
+#include "unixerrnostring.hxx"
+#include <string.h>
+#if defined(OPENBSD)
+#include <sched.h>
+#endif
+#ifdef __FreeBSD__
+#if __FreeBSD_version <= 1201517
+#include <pthread_np.h>
+#define pthread_setname_np pthread_set_name_np
+#endif
+#endif
+#include <config_options.h>
+#include <o3tl/safeint.hxx>
+#include <osl/thread.h>
+#include <osl/nlsupport.h>
+#include <rtl/textenc.h>
+#include <rtl/alloc.h>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#ifdef ANDROID
+#include <jni.h>
+#include <android/log.h>
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+#if defined LINUX && ! defined __FreeBSD_kernel__
+#include <sys/syscall.h>
+#endif
+
+/****************************************************************************
+ * @@@ TODO @@@
+ *
+ * (1) 'osl_thread_priority_init_Impl()'
+ * - insane assumption that initializing caller is main thread
+ * - use _POSIX_THREAD_PRIORITY_SCHEDULING, not NO_PTHREAD_PRIORITY (?)
+ * - POSIX doesn't require defined prio's for SCHED_OTHER (!)
+ * - use SCHED_RR instead of SCHED_OTHER for defined behaviour (?)
+ * (2) 'oslThreadIdentifier' and '{insert|remove|lookup}ThreadId()'
+ * - cannot reliably be applied to 'alien' threads;
+ * - memory leak for 'alien' thread 'HashEntry's;
+ * - use 'reinterpret_cast<unsigned long>(pthread_t)' as identifier
+ * instead (?)
+ * - if yes, change 'oslThreadIdentifier' to 'intptr_t' or similar
+ * (3) 'oslSigAlarmHandler()' (#71232#)
+ * - [Under Solaris we get SIGALRM in e.g. pthread_join which terminates
+ * the process. So we initialize our signal handling module and do
+ * register a SIGALRM Handler which catches and ignores it]
+ * - should this still happen, 'signal.c' needs to be fixed instead.
+ *
+ ****************************************************************************/
+
+#define THREADIMPL_FLAGS_TERMINATE 0x00001
+#define THREADIMPL_FLAGS_STARTUP 0x00002
+#define THREADIMPL_FLAGS_SUSPENDED 0x00004
+#define THREADIMPL_FLAGS_ACTIVE 0x00008
+#define THREADIMPL_FLAGS_ATTACHED 0x00010
+#define THREADIMPL_FLAGS_DESTROYED 0x00020
+
+namespace {
+
+typedef struct osl_thread_impl_st
+{
+ pthread_t m_hThread;
+ oslThreadIdentifier m_Ident; /* @@@ see TODO @@@ */
+ short m_Flags;
+ oslWorkerFunction m_WorkerFunction;
+ void* m_pData;
+ pthread_mutex_t m_Lock;
+ pthread_cond_t m_Cond;
+} Thread_Impl;
+
+#if !defined NO_PTHREAD_PRIORITY
+struct osl_thread_priority_st
+{
+ int m_Highest;
+ int m_Above_Normal;
+ int m_Normal;
+ int m_Below_Normal;
+ int m_Lowest;
+};
+#endif
+
+}
+
+#if !defined NO_PTHREAD_PRIORITY
+#define OSL_THREAD_PRIORITY_INITIALIZER { 127, 96, 64, 32, 0 }
+#endif
+
+static void osl_thread_priority_init_Impl();
+
+namespace {
+
+struct osl_thread_textencoding_st
+{
+ pthread_key_t m_key; /* key to store thread local text encoding */
+ rtl_TextEncoding m_default; /* the default text encoding */
+};
+
+}
+
+#define OSL_THREAD_TEXTENCODING_INITIALIZER { 0, RTL_TEXTENCODING_DONTKNOW }
+static void osl_thread_textencoding_init_Impl();
+
+namespace {
+
+struct osl_thread_global_st
+{
+ pthread_once_t m_once;
+#if !defined NO_PTHREAD_PRIORITY
+ struct osl_thread_priority_st m_priority;
+#endif
+ struct osl_thread_textencoding_st m_textencoding;
+};
+
+}
+
+static struct osl_thread_global_st g_thread =
+{
+ PTHREAD_ONCE_INIT,
+#if !defined NO_PTHREAD_PRIORITY
+ OSL_THREAD_PRIORITY_INITIALIZER,
+#endif
+ OSL_THREAD_TEXTENCODING_INITIALIZER
+};
+
+static void osl_thread_init_Impl();
+
+static Thread_Impl* osl_thread_construct_Impl();
+static void osl_thread_destruct_Impl (Thread_Impl ** ppImpl);
+
+static void* osl_thread_start_Impl (void * pData);
+static void osl_thread_cleanup_Impl (Thread_Impl * pImpl);
+
+static oslThread osl_thread_create_Impl (
+ oslWorkerFunction pWorker, void * pThreadData, short nFlags);
+
+/* @@@ see TODO @@@ */
+static oslThreadIdentifier insertThreadId (pthread_t hThread);
+static oslThreadIdentifier lookupThreadId (pthread_t hThread);
+static void removeThreadId (pthread_t hThread);
+
+static void osl_thread_init_Impl()
+{
+ osl_thread_priority_init_Impl();
+ osl_thread_textencoding_init_Impl();
+}
+
+Thread_Impl* osl_thread_construct_Impl()
+{
+ Thread_Impl* pImpl = new Thread_Impl;
+ memset (pImpl, 0, sizeof(Thread_Impl));
+
+ pthread_mutex_init (&(pImpl->m_Lock), PTHREAD_MUTEXATTR_DEFAULT);
+ pthread_cond_init (&(pImpl->m_Cond), PTHREAD_CONDATTR_DEFAULT);
+ return pImpl;
+}
+
+static void osl_thread_destruct_Impl (Thread_Impl ** ppImpl)
+{
+ assert(ppImpl);
+ if (*ppImpl)
+ {
+ pthread_cond_destroy (&((*ppImpl)->m_Cond));
+ pthread_mutex_destroy (&((*ppImpl)->m_Lock));
+
+ delete *ppImpl;
+ (*ppImpl) = nullptr;
+ }
+}
+
+static void osl_thread_cleanup_Impl (Thread_Impl * pImpl)
+{
+ pthread_t thread;
+ bool attached;
+ bool destroyed;
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ thread = pImpl->m_hThread;
+ attached = (pImpl->m_Flags & THREADIMPL_FLAGS_ATTACHED) != 0;
+ destroyed = (pImpl->m_Flags & THREADIMPL_FLAGS_DESTROYED) != 0;
+ pImpl->m_Flags &= ~(THREADIMPL_FLAGS_ACTIVE | THREADIMPL_FLAGS_ATTACHED);
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+
+ /* release oslThreadIdentifier @@@ see TODO @@@ */
+ removeThreadId (thread);
+
+ if (attached)
+ {
+ pthread_detach (thread);
+ }
+
+ if (destroyed)
+ {
+ osl_thread_destruct_Impl (&pImpl);
+ }
+}
+
+static void* osl_thread_start_Impl (void* pData)
+{
+ bool terminate;
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(pData);
+
+ assert(pImpl);
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ /* request oslThreadIdentifier @@@ see TODO @@@ */
+ pImpl->m_Ident = insertThreadId (pImpl->m_hThread);
+
+ /* signal change from STARTUP to ACTIVE state */
+ pImpl->m_Flags &= ~THREADIMPL_FLAGS_STARTUP;
+ pImpl->m_Flags |= THREADIMPL_FLAGS_ACTIVE;
+ pthread_cond_signal (&(pImpl->m_Cond));
+
+ /* Check if thread is started in SUSPENDED state */
+ while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
+ {
+ /* wait until SUSPENDED flag is cleared */
+ pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
+ }
+
+ /* check for SUSPENDED to TERMINATE state change */
+ terminate = ((pImpl->m_Flags & THREADIMPL_FLAGS_TERMINATE) > 0);
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+
+ if (!terminate)
+ {
+#ifdef ANDROID
+ JNIEnv* env = 0;
+ int res = (*lo_get_javavm()).AttachCurrentThread(&env, NULL);
+ __android_log_print(ANDROID_LOG_INFO, "LibreOffice", "New sal thread started and attached res=%d", res);
+#endif
+ /* call worker function */
+ pImpl->m_WorkerFunction(pImpl->m_pData);
+
+#ifdef ANDROID
+ res = (*lo_get_javavm()).DetachCurrentThread();
+ __android_log_print(ANDROID_LOG_INFO, "LibreOffice", "Detached finished sal thread res=%d", res);
+#endif
+ }
+
+ osl_thread_cleanup_Impl (pImpl);
+ return nullptr;
+}
+
+static oslThread osl_thread_create_Impl (
+ oslWorkerFunction pWorker,
+ void* pThreadData,
+ short nFlags)
+{
+ Thread_Impl* pImpl;
+#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
+ pthread_attr_t attr;
+ size_t stacksize;
+#endif
+ int nRet=0;
+
+ pImpl = osl_thread_construct_Impl();
+ if (!pImpl)
+ return nullptr; /* ENOMEM */
+
+ pImpl->m_WorkerFunction = pWorker;
+ pImpl->m_pData = pThreadData;
+ pImpl->m_Flags = nFlags | THREADIMPL_FLAGS_STARTUP;
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
+ if (pthread_attr_init(&attr) != 0)
+ return nullptr;
+
+#if defined OPENBSD
+ stacksize = 262144;
+#elif !ENABLE_RUNTIME_OPTIMIZATIONS
+ stacksize = 12 * 1024 * 1024; // 8MB is not enough for ASAN on x86-64
+#else
+ stacksize = 1 * 1024 * 1024; // macOS default for non-main threads (512kB) is not enough...
+#endif
+ if (pthread_attr_setstacksize(&attr, stacksize) != 0) {
+ pthread_attr_destroy(&attr);
+ return nullptr;
+ }
+#endif
+
+ if ((nRet = pthread_create (
+ &(pImpl->m_hThread),
+#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
+ &attr,
+#else
+ PTHREAD_ATTR_DEFAULT,
+#endif
+ osl_thread_start_Impl,
+ static_cast<void*>(pImpl))) != 0)
+ {
+ SAL_WARN(
+ "sal.osl",
+ "pthread_create failed: " << UnixErrnoString(nRet));
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+ osl_thread_destruct_Impl (&pImpl);
+
+ return nullptr;
+ }
+
+#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
+ pthread_attr_destroy(&attr);
+#endif
+
+ /* wait for change from STARTUP to ACTIVE state */
+ while (pImpl->m_Flags & THREADIMPL_FLAGS_STARTUP)
+ {
+ /* wait until STARTUP flag is cleared */
+ pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
+ }
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+
+ return static_cast<oslThread>(pImpl);
+}
+
+oslThread osl_createThread (
+ oslWorkerFunction pWorker,
+ void * pThreadData)
+{
+ return osl_thread_create_Impl (
+ pWorker,
+ pThreadData,
+ THREADIMPL_FLAGS_ATTACHED);
+}
+
+oslThread osl_createSuspendedThread (
+ oslWorkerFunction pWorker,
+ void * pThreadData)
+{
+ return osl_thread_create_Impl (
+ pWorker,
+ pThreadData,
+ THREADIMPL_FLAGS_ATTACHED |
+ THREADIMPL_FLAGS_SUSPENDED );
+}
+
+void SAL_CALL osl_destroyThread(oslThread Thread)
+{
+ if (Thread != nullptr) {
+ Thread_Impl * impl = static_cast<Thread_Impl *>(Thread);
+ bool active;
+ pthread_mutex_lock(&impl->m_Lock);
+ active = (impl->m_Flags & THREADIMPL_FLAGS_ACTIVE) != 0;
+ impl->m_Flags |= THREADIMPL_FLAGS_DESTROYED;
+ pthread_mutex_unlock(&impl->m_Lock);
+ if (!active) {
+ osl_thread_destruct_Impl(&impl);
+ }
+ }
+}
+
+void SAL_CALL osl_resumeThread(oslThread Thread)
+{
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_resumeThread(nullptr) call");
+ return; /* EINVAL */
+ }
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ if (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
+ {
+ /* clear SUSPENDED flag */
+ pImpl->m_Flags &= ~THREADIMPL_FLAGS_SUSPENDED;
+ pthread_cond_signal (&(pImpl->m_Cond));
+ }
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+}
+
+void SAL_CALL osl_suspendThread(oslThread Thread)
+{
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_suspendThread(nullptr) call");
+ return; /* EINVAL */
+ }
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ pImpl->m_Flags |= THREADIMPL_FLAGS_SUSPENDED;
+
+ if (pthread_equal (pthread_self(), pImpl->m_hThread))
+ {
+ /* self suspend */
+ while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
+ {
+ /* wait until SUSPENDED flag is cleared */
+ pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
+ }
+ }
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+}
+
+sal_Bool SAL_CALL osl_isThreadRunning(const oslThread Thread)
+{
+ bool active;
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ return false;
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+ active = ((pImpl->m_Flags & THREADIMPL_FLAGS_ACTIVE) > 0);
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+
+ return active;
+}
+
+void SAL_CALL osl_joinWithThread(oslThread Thread)
+{
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ return;
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ pthread_t const thread = pImpl->m_hThread;
+ bool const attached = ((pImpl->m_Flags & THREADIMPL_FLAGS_ATTACHED) > 0);
+
+ /* check this only if *this* thread is still attached - if it's not,
+ then it could have terminated and another newly created thread could
+ have recycled the same id as m_hThread! */
+ if (attached && pthread_equal(pthread_self(), pImpl->m_hThread))
+ {
+ assert(false); /* Win32 implementation would deadlock here! */
+ /* self join */
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+ return; /* EDEADLK */
+ }
+
+ pImpl->m_Flags &= ~THREADIMPL_FLAGS_ATTACHED;
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+
+ if (attached)
+ {
+ pthread_join (thread, nullptr);
+ }
+}
+
+void SAL_CALL osl_terminateThread(oslThread Thread)
+{
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_terminateThread(nullptr) call");
+ return; /* EINVAL */
+ }
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ if (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
+ {
+ /* clear SUSPENDED flag */
+ pImpl->m_Flags &= ~THREADIMPL_FLAGS_SUSPENDED;
+ pthread_cond_signal (&(pImpl->m_Cond));
+ }
+
+ pImpl->m_Flags |= THREADIMPL_FLAGS_TERMINATE;
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+}
+
+sal_Bool SAL_CALL osl_scheduleThread(oslThread Thread)
+{
+ bool terminate;
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_scheduleThread(nullptr) call");
+ return false; /* EINVAL */
+ }
+
+ if (!(pthread_equal (pthread_self(), pImpl->m_hThread)))
+ {
+ SAL_WARN("sal.osl", "invalid osl_scheduleThread(non-self) call");
+ return false; /* EINVAL */
+ }
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
+ {
+ /* wait until SUSPENDED flag is cleared */
+ pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
+ }
+
+ terminate = ((pImpl->m_Flags & THREADIMPL_FLAGS_TERMINATE) > 0);
+
+ pthread_mutex_unlock(&(pImpl->m_Lock));
+
+ return !terminate;
+}
+
+void SAL_CALL osl_waitThread(const TimeValue* pDelay)
+{
+ if (pDelay)
+ {
+ struct timespec delay;
+
+ SET_TIMESPEC(delay, pDelay->Seconds, pDelay->Nanosec);
+
+ SLEEP_TIMESPEC(delay);
+ }
+}
+
+/** Yields thread
+
+ @attention Note that POSIX scheduling @em really requires threads to call this
+ function, since a thread only reschedules to other thread, when
+ it blocks (sleep, blocking I/O) OR calls sched_yield().
+*/
+void SAL_CALL osl_yieldThread()
+{
+ sched_yield();
+}
+
+void SAL_CALL osl_setThreadName(char const * name)
+{
+ assert( name );
+#if defined LINUX && ! defined __FreeBSD_kernel__
+ const int LINUX_THREAD_NAME_MAXLEN = 15;
+ if ( strlen( name ) > LINUX_THREAD_NAME_MAXLEN )
+ SAL_INFO( "sal.osl", "osl_setThreadName truncated thread name to "
+ << LINUX_THREAD_NAME_MAXLEN << " chars from name '"
+ << name << "'" );
+ char shortname[ LINUX_THREAD_NAME_MAXLEN + 1 ];
+ shortname[ LINUX_THREAD_NAME_MAXLEN ] = '\0';
+ strncpy( shortname, name, LINUX_THREAD_NAME_MAXLEN );
+ int err = pthread_setname_np( pthread_self(), shortname );
+ if ( 0 != err )
+ SAL_WARN("sal.osl", "pthread_setname_np failed with errno " << err);
+#elif defined __FreeBSD__
+ pthread_setname_np( pthread_self(), name );
+#elif defined MACOSX || defined IOS
+ pthread_setname_np( name );
+#else
+ (void) name;
+#endif
+}
+
+/* osl_getThreadIdentifier @@@ see TODO @@@ */
+
+namespace {
+
+struct HashEntry
+{
+ pthread_t Handle;
+ oslThreadIdentifier Ident;
+ HashEntry * Next;
+};
+
+}
+
+static HashEntry* HashTable[31];
+const int HashSize = SAL_N_ELEMENTS(HashTable);
+
+static std::mutex HashLock;
+
+#if ! ((defined LINUX && !defined __FreeBSD_kernel__) || defined MACOSX || defined IOS)
+static oslThreadIdentifier LastIdent = 0;
+#endif
+
+namespace {
+
+std::size_t HASHID(pthread_t x)
+{ return std::hash<pthread_t>()(x) % HashSize; }
+
+}
+
+static oslThreadIdentifier lookupThreadId (pthread_t hThread)
+{
+ HashEntry *pEntry;
+
+ std::unique_lock aGuard(HashLock);
+
+ pEntry = HashTable[HASHID(hThread)];
+ while (pEntry != nullptr)
+ {
+ if (pthread_equal(pEntry->Handle, hThread))
+ {
+ return pEntry->Ident;
+ }
+ pEntry = pEntry->Next;
+ }
+
+ return 0;
+}
+
+static oslThreadIdentifier insertThreadId (pthread_t hThread)
+{
+ HashEntry *pEntry, *pInsert = nullptr;
+
+ std::unique_lock aGuard(HashLock);
+
+ pEntry = HashTable[HASHID(hThread)];
+
+ while (pEntry != nullptr)
+ {
+ if (pthread_equal(pEntry->Handle, hThread))
+ break;
+
+ pInsert = pEntry;
+ pEntry = pEntry->Next;
+ }
+
+ if (pEntry == nullptr)
+ {
+ pEntry = static_cast<HashEntry*>(calloc(sizeof(HashEntry), 1));
+
+ pEntry->Handle = hThread;
+
+#if defined LINUX && ! defined __FreeBSD_kernel__
+#if defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30))
+ // gettid returns a pid_t, which POSIX defines to be a signed integer type; assume that all
+ // valid pid_t values on Linux are positive (zero is filtered out in the generic code
+ // below):
+ pid_t const tid = gettid();
+ assert(tid >= 0);
+#else
+ long const tid = syscall(SYS_gettid);
+ if (tid < 0 || o3tl::make_unsigned(tid) > std::numeric_limits<sal_uInt32>::max()) {
+ std::abort();
+ }
+#endif
+ pEntry->Ident = tid;
+#elif defined MACOSX || defined IOS
+ // currently the value of pthread_threadid_np is the same then
+ // syscall(SYS_thread_selfid), which returns an int as the TID.
+ // may change, as the syscall interface was deprecated.
+ uint64_t mac_tid;
+ pthread_threadid_np(nullptr, &mac_tid);
+ if (mac_tid > SAL_MAX_UINT32)
+ std::abort();
+ pEntry->Ident = mac_tid;
+#else
+ ++LastIdent;
+ if (0 == LastIdent)
+ LastIdent = 1;
+ pEntry->Ident = LastIdent;
+#endif
+ if (0 == pEntry->Ident)
+ std::abort();
+
+ if (pInsert)
+ pInsert->Next = pEntry;
+ else
+ HashTable[HASHID(hThread)] = pEntry;
+ }
+
+ return pEntry->Ident;
+}
+
+static void removeThreadId (pthread_t hThread)
+{
+ HashEntry *pEntry, *pRemove = nullptr;
+
+ std::unique_lock aGuard(HashLock);
+
+ pEntry = HashTable[HASHID(hThread)];
+ while (pEntry != nullptr)
+ {
+ if (pthread_equal(pEntry->Handle, hThread))
+ break;
+
+ pRemove = pEntry;
+ pEntry = pEntry->Next;
+ }
+
+ if (pEntry != nullptr)
+ {
+ if (pRemove)
+ pRemove->Next = pEntry->Next;
+ else
+ HashTable[HASHID(hThread)] = pEntry->Next;
+
+ free(pEntry);
+ }
+}
+
+oslThreadIdentifier SAL_CALL osl_getThreadIdentifier(oslThread Thread)
+{
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+ oslThreadIdentifier Ident;
+
+ if (pImpl)
+ Ident = pImpl->m_Ident;
+ else
+ {
+ /* current thread */
+ pthread_t current = pthread_self();
+
+ Ident = lookupThreadId (current);
+ if (Ident == 0)
+ /* @@@ see TODO: alien pthread_self() @@@ */
+ Ident = insertThreadId (current);
+ }
+
+ return Ident;
+}
+
+/*****************************************************************************
+ @@@ see TODO @@@
+ osl_thread_priority_init_Impl
+
+ set the base-priority of the main-thread to
+ oslThreadPriorityNormal (64) since 0 (lowest) is
+ the system default. This behaviour collides with
+ our enum-priority definition (highest..normal..lowest).
+ A normaluser will expect the main-thread of an app.
+ to have the "normal" priority.
+
+*****************************************************************************/
+static void osl_thread_priority_init_Impl()
+{
+#ifndef NO_PTHREAD_PRIORITY
+ struct sched_param param;
+ int policy=0;
+ int nRet=0;
+
+/* @@@ see TODO: calling thread may not be main thread @@@ */
+
+ if ((nRet = pthread_getschedparam(pthread_self(), &policy, &param)) != 0)
+ {
+ SAL_WARN(
+ "sal.osl",
+ "pthread_getschedparam failed: " << UnixErrnoString(nRet));
+ return;
+ }
+
+#if defined (__sun)
+ if ( policy >= _SCHED_NEXT)
+ {
+ /* mfe: pthread_getschedparam on Solaris has a possible Bug */
+ /* one gets 959917873 as the policy */
+ /* so set the policy to a default one */
+ policy=SCHED_OTHER;
+ }
+#endif /* __sun */
+
+ if ((nRet = sched_get_priority_min(policy) ) != -1)
+ {
+ SAL_INFO(
+ "sal.osl", "Min Prioriy for policy " << policy << " == " << nRet);
+ g_thread.m_priority.m_Lowest=nRet;
+ }
+ else
+ {
+ int e = errno;
+ SAL_WARN(
+ "sal.osl",
+ "sched_get_priority_min failed: " << UnixErrnoString(e));
+ }
+
+ if ((nRet = sched_get_priority_max(policy) ) != -1)
+ {
+ SAL_INFO(
+ "sal.osl", "Max Prioriy for policy " << policy << " == " << nRet);
+ g_thread.m_priority.m_Highest=nRet;
+ }
+ else
+ {
+ int e = errno;
+ SAL_WARN(
+ "sal.osl",
+ "sched_get_priority_max failed: " << UnixErrnoString(e));
+ }
+
+ g_thread.m_priority.m_Normal =
+ (g_thread.m_priority.m_Lowest + g_thread.m_priority.m_Highest) / 2;
+ g_thread.m_priority.m_Below_Normal =
+ (g_thread.m_priority.m_Lowest + g_thread.m_priority.m_Normal) / 2;
+ g_thread.m_priority.m_Above_Normal =
+ (g_thread.m_priority.m_Normal + g_thread.m_priority.m_Highest) / 2;
+
+/* @@@ set prio of calling (not main) thread (?) @@@ */
+
+ param.sched_priority= g_thread.m_priority.m_Normal;
+
+ if ((nRet = pthread_setschedparam(pthread_self(), policy, &param)) != 0)
+ {
+ SAL_WARN(
+ "sal.osl",
+ "pthread_setschedparam failed: " << UnixErrnoString(nRet));
+ SAL_INFO(
+ "sal.osl",
+ "Thread ID " << pthread_self() << ", Policy " << policy
+ << ", Priority " << param.sched_priority);
+ }
+
+#endif /* NO_PTHREAD_PRIORITY */
+}
+
+/**
+ Impl-Notes: contrary to solaris-docu, which claims
+ valid priority-levels from 0 .. INT_MAX, only the
+ range 0..127 is accepted. (0 lowest, 127 highest)
+*/
+void SAL_CALL osl_setThreadPriority (
+ oslThread Thread,
+ oslThreadPriority Priority)
+{
+#ifndef NO_PTHREAD_PRIORITY
+
+ struct sched_param Param;
+ int policy;
+ int nRet;
+
+#endif /* NO_PTHREAD_PRIORITY */
+
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_setThreadPriority(nullptr, ...) call");
+ return; /* EINVAL */
+ }
+
+#ifdef NO_PTHREAD_PRIORITY
+ (void) Priority; /* unused */
+#else /* NO_PTHREAD_PRIORITY */
+
+ if (pthread_getschedparam(pImpl->m_hThread, &policy, &Param) != 0)
+ return; /* ESRCH */
+
+#if defined (__sun)
+ if ( policy >= _SCHED_NEXT)
+ {
+ /* mfe: pthread_getschedparam on Solaris has a possible Bug */
+ /* one gets 959917873 as the policy */
+ /* so set the policy to a default one */
+ policy=SCHED_OTHER;
+ }
+#endif /* __sun */
+
+ pthread_once (&(g_thread.m_once), osl_thread_init_Impl);
+
+ switch(Priority)
+ {
+ case osl_Thread_PriorityHighest:
+ Param.sched_priority= g_thread.m_priority.m_Highest;
+ break;
+
+ case osl_Thread_PriorityAboveNormal:
+ Param.sched_priority= g_thread.m_priority.m_Above_Normal;
+ break;
+
+ case osl_Thread_PriorityNormal:
+ Param.sched_priority= g_thread.m_priority.m_Normal;
+ break;
+
+ case osl_Thread_PriorityBelowNormal:
+ Param.sched_priority= g_thread.m_priority.m_Below_Normal;
+ break;
+
+ case osl_Thread_PriorityLowest:
+ Param.sched_priority= g_thread.m_priority.m_Lowest;
+ break;
+
+ case osl_Thread_PriorityUnknown:
+ SAL_WARN(
+ "sal.osl",
+ "invalid osl_setThreadPriority(..., osl_Thread_PriorityUnknown)"
+ " call");
+ return;
+
+ default:
+ SAL_WARN(
+ "sal.osl",
+ "invalid osl_setThreadPriority(..., " << Priority << ") call");
+ return;
+ }
+
+ if ((nRet = pthread_setschedparam(pImpl->m_hThread, policy, &Param)) != 0)
+ {
+ SAL_WARN(
+ "sal.osl",
+ "pthread_setschedparam failed: " << UnixErrnoString(nRet));
+ }
+
+#endif /* NO_PTHREAD_PRIORITY */
+}
+
+oslThreadPriority SAL_CALL osl_getThreadPriority(const oslThread Thread)
+{
+#ifndef NO_PTHREAD_PRIORITY
+
+ struct sched_param Param;
+ int Policy;
+
+#endif /* NO_PTHREAD_PRIORITY */
+
+ oslThreadPriority Priority = osl_Thread_PriorityNormal;
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_getThreadPriority(nullptr) call");
+ return osl_Thread_PriorityUnknown; /* EINVAL */
+ }
+
+#ifndef NO_PTHREAD_PRIORITY
+
+ if (pthread_getschedparam(pImpl->m_hThread, &Policy, &Param) != 0)
+ return osl_Thread_PriorityUnknown; /* ESRCH */
+
+ pthread_once (&(g_thread.m_once), osl_thread_init_Impl);
+
+ /* map pthread priority to enum */
+ if (Param.sched_priority==g_thread.m_priority.m_Highest)
+ {
+ /* 127 - highest */
+ Priority= osl_Thread_PriorityHighest;
+ }
+ else if (Param.sched_priority > g_thread.m_priority.m_Normal)
+ {
+ /* 65..126 - above normal */
+ Priority= osl_Thread_PriorityAboveNormal;
+ }
+ else if (Param.sched_priority == g_thread.m_priority.m_Normal)
+ {
+ /* normal */
+ Priority= osl_Thread_PriorityNormal;
+ }
+ else if (Param.sched_priority > g_thread.m_priority.m_Lowest)
+ {
+ /* 63..1 -below normal */
+ Priority= osl_Thread_PriorityBelowNormal;
+ }
+ else if (Param.sched_priority == g_thread.m_priority.m_Lowest)
+ {
+ /* 0 - lowest */
+ Priority= osl_Thread_PriorityLowest;
+ }
+ else
+ {
+ /* unknown */
+ Priority= osl_Thread_PriorityUnknown;
+ }
+
+#endif /* NO_PTHREAD_PRIORITY */
+
+ return Priority;
+}
+
+namespace {
+
+struct wrapper_pthread_key
+{
+ pthread_key_t m_key;
+ oslThreadKeyCallbackFunction pfnCallback;
+};
+
+}
+
+oslThreadKey SAL_CALL osl_createThreadKey( oslThreadKeyCallbackFunction pCallback )
+{
+ wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(malloc(sizeof(wrapper_pthread_key)));
+
+ if (pKey)
+ {
+ pKey->pfnCallback = pCallback;
+
+ if (pthread_key_create(&(pKey->m_key), pKey->pfnCallback) != 0)
+ {
+ free(pKey);
+ pKey = nullptr;
+ }
+ }
+
+ return static_cast<oslThreadKey>(pKey);
+}
+
+void SAL_CALL osl_destroyThreadKey(oslThreadKey Key)
+{
+ wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
+ if (pKey)
+ {
+ pthread_key_delete(pKey->m_key);
+ free(pKey);
+ }
+}
+
+void* SAL_CALL osl_getThreadKeyData(oslThreadKey Key)
+{
+ wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
+ return pKey ? pthread_getspecific(pKey->m_key) : nullptr;
+}
+
+sal_Bool SAL_CALL osl_setThreadKeyData(oslThreadKey Key, void *pData)
+{
+ bool bRet;
+ void *pOldData = nullptr;
+ wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
+ if (!pKey)
+ return false;
+
+ if (pKey->pfnCallback)
+ pOldData = pthread_getspecific(pKey->m_key);
+
+ bRet = (pthread_setspecific(pKey->m_key, pData) == 0);
+
+ if (bRet && pKey->pfnCallback && pOldData)
+ pKey->pfnCallback(pOldData);
+
+ return bRet;
+}
+
+static void osl_thread_textencoding_init_Impl()
+{
+ rtl_TextEncoding defaultEncoding;
+
+ /* create thread specific data key */
+ pthread_key_create (&(g_thread.m_textencoding.m_key), nullptr);
+
+ /* determine default text encoding */
+ defaultEncoding = osl_getTextEncodingFromLocale(nullptr);
+ // Tools string functions call abort() on an unknown encoding so ASCII is a
+ // meaningful fallback:
+ if ( RTL_TEXTENCODING_DONTKNOW == defaultEncoding )
+ {
+ SAL_WARN("sal.osl", "RTL_TEXTENCODING_DONTKNOW -> _ASCII_US");
+ defaultEncoding = RTL_TEXTENCODING_ASCII_US;
+ }
+
+ g_thread.m_textencoding.m_default = defaultEncoding;
+}
+
+rtl_TextEncoding SAL_CALL osl_getThreadTextEncoding()
+{
+ rtl_TextEncoding threadEncoding;
+
+ pthread_once (&(g_thread.m_once), osl_thread_init_Impl);
+
+ /* check for thread specific encoding, use default if not set */
+ threadEncoding = static_cast<rtl_TextEncoding>(
+ reinterpret_cast<sal_uIntPtr>(pthread_getspecific(g_thread.m_textencoding.m_key)));
+ if (threadEncoding == 0)
+ threadEncoding = g_thread.m_textencoding.m_default;
+
+ return threadEncoding;
+}
+
+rtl_TextEncoding osl_setThreadTextEncoding(rtl_TextEncoding Encoding)
+{
+ rtl_TextEncoding oldThreadEncoding = osl_getThreadTextEncoding();
+
+ /* save encoding in thread local storage */
+ pthread_setspecific (
+ g_thread.m_textencoding.m_key,
+ reinterpret_cast<void*>(static_cast<sal_uIntPtr>(Encoding)));
+
+ return oldThreadEncoding;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/time.cxx b/sal/osl/unx/time.cxx
new file mode 100644
index 000000000..a77015614
--- /dev/null
+++ b/sal/osl/unx/time.cxx
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "saltime.hxx"
+#include "system.hxx"
+
+#include <osl/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef __MACH__
+#include <mach/clock.h>
+#include <mach/mach.h>
+#endif
+
+/* FIXME: detection should be done in configure script */
+#if defined(MACOSX) || defined(IOS) || defined(FREEBSD) || defined(NETBSD) || \
+ defined(LINUX) || defined(OPENBSD) || defined(DRAGONFLY)
+#define STRUCT_TM_HAS_GMTOFF 1
+
+#elif defined(__sun)
+#define HAS_ALTZONE 1
+#endif
+
+#ifdef __MACH__
+typedef mach_timespec_t osl_time_t;
+#else
+#if defined(_POSIX_TIMERS)
+#define USE_CLOCK_GETTIME
+typedef struct timespec osl_time_t;
+#else
+typedef struct timeval osl_time_t;
+#endif
+#endif
+static osl_time_t startTime;
+
+sal_Bool SAL_CALL osl_getSystemTime(TimeValue* tv)
+{
+#ifdef __MACH__
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ mach_port_deallocate(mach_task_self(), cclock);
+
+ tv->Seconds = mts.tv_sec;
+ tv->Nanosec = mts.tv_nsec;
+#else
+ int res;
+ osl_time_t tp;
+#if defined(USE_CLOCK_GETTIME)
+ res = clock_gettime(CLOCK_REALTIME, &tp);
+#else
+ res = gettimeofday(&tp, NULL);
+#endif
+
+ if (res != 0)
+ {
+ return false;
+ }
+
+ tv->Seconds = tp.tv_sec;
+ #if defined(USE_CLOCK_GETTIME)
+ tv->Nanosec = tp.tv_nsec;
+ #else
+ tv->Nanosec = tp.tv_usec * 1000;
+ #endif
+#endif
+ return true;
+}
+
+sal_Bool SAL_CALL osl_getDateTimeFromTimeValue( const TimeValue* pTimeVal, oslDateTime* pDateTime )
+{
+ struct tm *pSystemTime;
+ struct tm tmBuf;
+ time_t atime;
+
+ atime = static_cast<time_t>(pTimeVal->Seconds);
+
+ /* Convert time from type time_t to struct tm */
+ pSystemTime = gmtime_r( &atime, &tmBuf );
+
+ /* Convert struct tm to struct oslDateTime */
+ if ( pSystemTime != nullptr )
+ {
+ pDateTime->NanoSeconds = pTimeVal->Nanosec;
+ pDateTime->Seconds = pSystemTime->tm_sec;
+ pDateTime->Minutes = pSystemTime->tm_min;
+ pDateTime->Hours = pSystemTime->tm_hour;
+ pDateTime->Day = pSystemTime->tm_mday;
+ pDateTime->DayOfWeek = pSystemTime->tm_wday;
+ pDateTime->Month = pSystemTime->tm_mon + 1;
+ pDateTime->Year = pSystemTime->tm_year + 1900;
+
+ return true;
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL osl_getTimeValueFromDateTime( const oslDateTime* pDateTime, TimeValue* pTimeVal )
+{
+ struct tm aTime;
+ time_t nSeconds;
+
+ /* Convert struct oslDateTime to struct tm */
+ aTime.tm_sec = pDateTime->Seconds;
+ aTime.tm_min = pDateTime->Minutes;
+ aTime.tm_hour = pDateTime->Hours;
+ aTime.tm_mday = pDateTime->Day;
+
+ if ( pDateTime->Month > 0 )
+ aTime.tm_mon = pDateTime->Month - 1;
+ else
+ return false;
+
+ aTime.tm_year = pDateTime->Year - 1900;
+
+ aTime.tm_isdst = -1;
+ aTime.tm_wday = 0;
+ aTime.tm_yday = 0;
+
+#if defined(STRUCT_TM_HAS_GMTOFF)
+ aTime.tm_gmtoff = 0;
+#endif
+
+ /* Convert time to calendar value */
+ nSeconds = mktime( &aTime );
+
+ /*
+ * mktime expects the struct tm to be in local timezone, so we have to adjust
+ * the returned value to be timezone neutral.
+ */
+
+ if ( nSeconds != time_t(-1) )
+ {
+ time_t bias;
+
+ /* timezone corrections */
+ tzset();
+
+#if defined(STRUCT_TM_HAS_GMTOFF)
+ /* members of struct tm are corrected by mktime */
+ bias = 0 - aTime.tm_gmtoff;
+
+#elif defined(HAS_ALTZONE)
+ /* check if daylight saving time is in effect */
+ bias = aTime.tm_isdst > 0 ? altzone : timezone;
+#else
+ /* expect daylight saving time to be one hour */
+ bias = aTime.tm_isdst > 0 ? timezone - 3600 : timezone;
+#endif
+
+ pTimeVal->Seconds = nSeconds;
+ pTimeVal->Nanosec = pDateTime->NanoSeconds;
+
+ if ( nSeconds > bias )
+ pTimeVal->Seconds -= bias;
+
+ return true;
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL osl_getLocalTimeFromSystemTime( const TimeValue* pSystemTimeVal, TimeValue* pLocalTimeVal )
+{
+ struct tm *pLocalTime;
+ struct tm tmBuf;
+ time_t bias;
+ time_t atime;
+
+ atime = static_cast<time_t>(pSystemTimeVal->Seconds);
+ pLocalTime = localtime_r( &atime, &tmBuf );
+
+#if defined(STRUCT_TM_HAS_GMTOFF)
+ /* members of struct tm are corrected by mktime */
+ bias = -pLocalTime->tm_gmtoff;
+
+#elif defined(HAS_ALTZONE)
+ /* check if daylight saving time is in effect */
+ bias = pLocalTime->tm_isdst > 0 ? altzone : timezone;
+#else
+ /* expect daylight saving time to be one hour */
+ bias = pLocalTime->tm_isdst > 0 ? timezone - 3600 : timezone;
+#endif
+
+ if ( static_cast<sal_Int64>(pSystemTimeVal->Seconds) > bias )
+ {
+ pLocalTimeVal->Seconds = pSystemTimeVal->Seconds - bias;
+ pLocalTimeVal->Nanosec = pSystemTimeVal->Nanosec;
+
+ return true;
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL osl_getSystemTimeFromLocalTime( const TimeValue* pLocalTimeVal, TimeValue* pSystemTimeVal )
+{
+ struct tm *pLocalTime;
+ struct tm tmBuf;
+ time_t bias;
+ time_t atime;
+
+ atime = static_cast<time_t>(pLocalTimeVal->Seconds);
+
+ /* Convert atime, which is a local time, to its GMT equivalent. Then, get
+ * the timezone offset for the local time for the GMT equivalent time. Note
+ * that we cannot directly use local time to determine the timezone offset
+ * because GMT is the only reliable time that we can determine timezone
+ * offset from.
+ */
+
+ atime = mktime( gmtime_r( &atime, &tmBuf ) );
+ pLocalTime = localtime_r( &atime, &tmBuf );
+
+#if defined(STRUCT_TM_HAS_GMTOFF)
+ /* members of struct tm are corrected by mktime */
+ bias = 0 - pLocalTime->tm_gmtoff;
+
+#elif defined(HAS_ALTZONE)
+ /* check if daylight saving time is in effect */
+ bias = pLocalTime->tm_isdst > 0 ? altzone : timezone;
+#else
+ /* expect daylight saving time to be one hour */
+ bias = pLocalTime->tm_isdst > 0 ? timezone - 3600 : timezone;
+#endif
+
+ if ( static_cast<sal_Int64>(pLocalTimeVal->Seconds) + bias > 0 )
+ {
+ pSystemTimeVal->Seconds = pLocalTimeVal->Seconds + bias;
+ pSystemTimeVal->Nanosec = pLocalTimeVal->Nanosec;
+
+ return true;
+ }
+
+ return false;
+}
+
+void sal_initGlobalTimer()
+{
+#ifdef __MACH__
+ clock_serv_t cclock;
+
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &startTime);
+ mach_port_deallocate(mach_task_self(), cclock);
+#else /* ! (MACOSX || IOS) */
+#if defined(USE_CLOCK_GETTIME)
+ clock_gettime(CLOCK_REALTIME, &startTime);
+#else /* Ndef USE_CLOCK_GETTIME */
+ gettimeofday( &startTime, NULL );
+#endif /* NDef USE_CLOCK_GETTIME */
+#endif /* ! (MACOSX || IOS) */
+}
+
+sal_uInt32 SAL_CALL osl_getGlobalTimer()
+{
+ sal_uInt32 nSeconds;
+
+#ifdef __MACH__
+ clock_serv_t cclock;
+ mach_timespec_t currentTime;
+
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &currentTime);
+ mach_port_deallocate(mach_task_self(), cclock);
+
+ nSeconds = ( currentTime.tv_sec - startTime.tv_sec );
+ nSeconds = ( nSeconds * 1000 ) + static_cast<long>(( currentTime.tv_nsec - startTime.tv_nsec) / 1000000 );
+#else
+ osl_time_t currentTime;
+
+#if defined(USE_CLOCK_GETTIME)
+ clock_gettime(CLOCK_REALTIME, &currentTime);
+#else
+ gettimeofday( &currentTime, NULL );
+#endif
+
+ nSeconds = static_cast<sal_uInt32>( currentTime.tv_sec - startTime.tv_sec );
+#if defined(USE_CLOCK_GETTIME)
+ nSeconds = ( nSeconds * 1000 ) + static_cast<long>(( currentTime.tv_nsec - startTime.tv_nsec) / 1000000 );
+#else
+ nSeconds = ( nSeconds * 1000 ) + (long) (( currentTime.tv_usec - startTime.tv_usec) / 1000 );
+#endif
+#endif
+ return nSeconds;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/unixerrnostring.hxx b/sal/osl/unx/unixerrnostring.hxx
new file mode 100644
index 000000000..9e13b04e6
--- /dev/null
+++ b/sal/osl/unx/unixerrnostring.hxx
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_UNIXERRNOSTRING_HXX
+#define INCLUDED_SAL_OSL_UNX_UNIXERRNOSTRING_HXX
+
+#include <string>
+
+// Return the symbolic name of an errno value, like "ENOENT".
+
+// Rationale why to use this and not strerror(): This is intended to be used in SAL_INFO() and
+// SAL_WARN(). Such messages are intended to be read by developers, not end-users. Developers are
+// (or should be) familiar with symbolic errno names in code anyway. strerror() is localized and the
+// localised error strings might be less familiar to a developer that happens to run a localised
+// environment.
+
+std::string UnixErrnoString(int nErrno);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/uunxapi.cxx b/sal/osl/unx/uunxapi.cxx
new file mode 100644
index 000000000..5311bc719
--- /dev/null
+++ b/sal/osl/unx/uunxapi.cxx
@@ -0,0 +1,905 @@
+/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <config_features.h>
+
+#include "uunxapi.hxx"
+#include "system.hxx"
+#include "unixerrnostring.hxx"
+#include <limits.h>
+#include <rtl/ustring.hxx>
+#include <osl/thread.h>
+#include <sal/log.hxx>
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+OString osl::OUStringToOString(std::u16string_view s)
+{
+ return rtl::OUStringToOString(s, osl_getThreadTextEncoding());
+}
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+
+#include <Foundation/Foundation.h>
+#include <Security/Security.h>
+#include <mach-o/dyld.h>
+
+static NSUserDefaults *userDefaults = NULL;
+static bool isSandboxed = false;
+
+static void do_once()
+{
+ SecCodeRef code;
+ OSStatus rc = SecCodeCopySelf(kSecCSDefaultFlags, &code);
+
+ SecStaticCodeRef staticCode;
+ if (rc == errSecSuccess)
+ rc = SecCodeCopyStaticCode(code, kSecCSDefaultFlags, &staticCode);
+
+ CFDictionaryRef signingInformation;
+ if (rc == errSecSuccess)
+ rc = SecCodeCopySigningInformation(staticCode, kSecCSRequirementInformation, &signingInformation);
+
+ CFDictionaryRef entitlements = NULL;
+ if (rc == errSecSuccess)
+ entitlements = (CFDictionaryRef) CFDictionaryGetValue(signingInformation, kSecCodeInfoEntitlementsDict);
+
+ if (entitlements != NULL)
+ if (CFDictionaryGetValue(entitlements, CFSTR("com.apple.security.app-sandbox")) != NULL)
+ isSandboxed = true;
+
+ if (isSandboxed)
+ userDefaults = [NSUserDefaults standardUserDefaults];
+}
+
+typedef struct {
+ NSURL *scopeURL;
+ NSAutoreleasePool *pool;
+} accessFilePathState;
+
+static accessFilePathState *
+prepare_to_access_file_path( const char *cpFilePath )
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, &do_once);
+ NSURL *fileURL = nil;
+ NSData *data = nil;
+ BOOL stale;
+ accessFilePathState *state;
+
+ if (!isSandboxed)
+ return NULL;
+
+ // If malloc() fails we are screwed anyway
+ state = (accessFilePathState*) malloc(sizeof(accessFilePathState));
+
+ state->pool = [[NSAutoreleasePool alloc] init];
+ state->scopeURL = nil;
+
+ if (userDefaults != nil)
+ fileURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:cpFilePath]];
+
+ if (fileURL != nil)
+ data = [userDefaults dataForKey:[@"bookmarkFor:" stringByAppendingString:[fileURL absoluteString]]];
+
+ if (data != nil)
+ state->scopeURL = [NSURL URLByResolvingBookmarkData:data
+ options:NSURLBookmarkResolutionWithSecurityScope
+ relativeToURL:nil
+ bookmarkDataIsStale:&stale
+ error:nil];
+ if (state->scopeURL != nil)
+ [state->scopeURL startAccessingSecurityScopedResource];
+
+ return state;
+}
+
+static void
+done_accessing_file_path( const char * /*cpFilePath*/, accessFilePathState *state )
+{
+ if (!isSandboxed)
+ return;
+
+ int saved_errno = errno;
+
+ if (state->scopeURL != nil)
+ [state->scopeURL stopAccessingSecurityScopedResource];
+ [state->pool release];
+ free(state);
+
+ errno = saved_errno;
+}
+
+#else
+
+typedef void accessFilePathState;
+
+#define prepare_to_access_file_path( cpFilePath ) nullptr
+
+#define done_accessing_file_path( cpFilePath, state ) ((void) cpFilePath, (void) state)
+
+#endif
+
+#ifdef MACOSX
+/*
+ * Helper function for resolving Mac native alias files (not the same as unix alias files)
+ * and to return the resolved alias as OString
+ */
+static OString macxp_resolveAliasAndConvert(OString const & p)
+{
+ char path[PATH_MAX];
+ if (p.getLength() < PATH_MAX)
+ {
+ strcpy(path, p.getStr());
+ macxp_resolveAlias(path, PATH_MAX);
+ return OString(path);
+ }
+ return p;
+}
+#endif /* MACOSX */
+
+int osl::access(const OString& pstrPath, int mode)
+{
+ OString fn = pstrPath;
+#ifdef ANDROID
+ if (fn == "/assets" || fn.startsWith("/assets/"))
+ {
+ struct stat stat;
+ if (lo_apk_lstat(fn.getStr(), &stat) == -1)
+ return -1;
+ if (mode & W_OK)
+ {
+ errno = EACCES;
+ return -1;
+ }
+ return 0;
+ }
+#endif
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(fn.getStr());
+
+ int result = ::access(fn.getStr(), mode);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "access(" << fn << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "access(" << fn << ",0" << std::oct << mode << std::dec << "): OK");
+
+ done_accessing_file_path(fn.getStr(), state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+namespace {
+
+OString toOString(OString const & s) { return s; }
+
+OString toOString(std::u16string_view s) { return osl::OUStringToOString(s); }
+
+template<typename T> T fromOString(OString const &) = delete;
+
+template<> OString fromOString(OString const & s) { return s; }
+
+template<> OUString fromOString(OString const & s)
+{ return OStringToOUString(s, osl_getThreadTextEncoding()); }
+
+template<typename T> bool realpath_(const T& pstrFileName, T& ppstrResolvedName)
+{
+ OString fn = toOString(pstrFileName);
+#if defined ANDROID || defined(EMSCRIPTEN)
+#if defined ANDROID
+ if (fn == "/assets" || fn.startsWith("/assets/"))
+#else
+ if (fn == "/instdir" || fn.startsWith("/instdir/"))
+#endif
+ {
+ if (osl::access(fn, F_OK) == -1)
+ return false;
+
+ ppstrResolvedName = pstrFileName;
+
+ return true;
+ }
+#endif // ANDROID || EMSCRIPTEN
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(fn.getStr());
+
+ char rp[PATH_MAX];
+ bool bRet = realpath(fn.getStr(), rp);
+ int saved_errno = errno;
+ if (!bRet)
+ SAL_INFO("sal.file", "realpath(" << fn << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "realpath(" << fn << "): OK");
+
+ done_accessing_file_path(fn.getStr(), state);
+
+ if (bRet)
+ {
+ ppstrResolvedName = fromOString<T>(OString(rp));
+ }
+
+ errno = saved_errno;
+
+ return bRet;
+}
+
+}
+
+bool osl::realpath(const OUString& pustrFileName, OUString& ppustrResolvedName)
+{
+ return realpath_(pustrFileName, ppustrResolvedName);
+}
+
+bool osl::realpath(const OString& pstrFileName, OString& ppstrResolvedName)
+{
+ return realpath_(pstrFileName, ppstrResolvedName);
+}
+
+int stat_c(const char* cpPath, struct stat* buf)
+{
+#ifdef ANDROID
+ if (strncmp(cpPath, "/assets", sizeof("/assets")-1) == 0 &&
+ (cpPath[sizeof("/assets")-1] == '\0' ||
+ cpPath[sizeof("/assets")-1] == '/'))
+ return lo_apk_lstat(cpPath, buf);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(cpPath);
+
+ int result = stat(cpPath, buf);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "stat(" << cpPath << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "stat(" << cpPath << "): OK");
+
+ done_accessing_file_path(cpPath, state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+int lstat_c(const char* cpPath, struct stat* buf)
+{
+#ifdef ANDROID
+ if (strncmp(cpPath, "/assets", sizeof("/assets")-1) == 0 &&
+ (cpPath[sizeof("/assets")-1] == '\0' ||
+ cpPath[sizeof("/assets")-1] == '/'))
+ return lo_apk_lstat(cpPath, buf);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(cpPath);
+
+ int result = lstat(cpPath, buf);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "lstat(" << cpPath << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "lstat(" << cpPath << "): OK");
+
+ done_accessing_file_path(cpPath, state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+namespace {
+
+template<typename T> int lstat_(const T& pstrPath, struct stat& buf)
+{
+ OString fn = toOString(pstrPath);
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ return lstat_c(fn.getStr(), &buf);
+}
+
+}
+
+int osl::lstat(const OUString& pustrPath, struct stat& buf)
+{
+ return lstat_(pustrPath, buf);
+}
+
+int osl::lstat(const OString& pstrPath, struct stat& buf)
+{
+ return lstat_(pstrPath, buf);
+}
+
+int osl::mkdir(const OString& path, mode_t mode)
+{
+ accessFilePathState *state = prepare_to_access_file_path(path.getStr());
+
+ int result = ::mkdir(path.getStr(), mode);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "mkdir(" << path << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "mkdir(" << path << ",0" << std::oct << mode << std::dec << "): OK");
+
+ done_accessing_file_path(path.getStr(), state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+int open_c(const OString& path, int oflag, int mode)
+{
+ accessFilePathState *state = prepare_to_access_file_path(path.getStr());
+
+ int result = open(path.getStr(), oflag, mode);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "open(" << path << ",0" << std::oct << oflag << ",0" << mode << std::dec << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "open(" << path << ",0" << std::oct << oflag << ",0" << mode << std::dec << ") => " << result);
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ if (isSandboxed && result != -1 && (oflag & O_CREAT) && (oflag & O_EXCL))
+ {
+ // A new file was created. Check if it is outside the sandbox.
+ // (In that case it must be one the user selected as export or
+ // save destination in a file dialog, otherwise we wouldn't
+ // have been able to create it.) Create and store a security
+ // scoped bookmark for it so that we can access the file in
+ // the future, too. (For the "Recent Files" functionality.)
+ const char *sandbox = [NSHomeDirectory() UTF8String];
+ if (!(strncmp(sandbox, path.getStr(), strlen(sandbox)) == 0 &&
+ path[strlen(sandbox)] == '/'))
+ {
+ auto cpPath = path.getStr();
+ NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:cpPath]];
+ NSData *data = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
+ includingResourceValuesForKeys:nil
+ relativeToURL:nil
+ error:nil];
+ if (data != NULL)
+ {
+ [userDefaults setObject:data
+ forKey:[@"bookmarkFor:" stringByAppendingString:[url absoluteString]]];
+ }
+ }
+ }
+#endif
+
+ done_accessing_file_path(path.getStr(), state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+int utime_c(const char *cpPath, struct utimbuf *times)
+{
+ accessFilePathState *state = prepare_to_access_file_path(cpPath);
+
+ int result = utime(cpPath, times);
+
+ done_accessing_file_path(cpPath, state);
+
+ return result;
+}
+
+int ftruncate_with_name(int fd, sal_uInt64 uSize, const OString& path)
+{
+ /* When sandboxed on macOS, ftruncate(), even if it takes an
+ * already open file descriptor which was returned from an open()
+ * call already checked by the sandbox, still requires a security
+ * scope bookmark for the file to be active in case the file is
+ * one that the sandbox doesn't otherwise allow access to. Luckily
+ * LibreOffice usually calls ftruncate() through the helpful C++
+ * abstraction layer that keeps the pathname around.
+ */
+
+ OString fn(path);
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(fn.getStr());
+
+ int result = ftruncate(fd, uSize);
+ int saved_errno = errno;
+ if (result < 0)
+ SAL_INFO("sal.file", "ftruncate(" << fd << "," << uSize << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "ftruncate(" << fd << "," << uSize << "): OK");
+
+ done_accessing_file_path(fn.getStr(), state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+
+std::string UnixErrnoString(int nErrno)
+{
+ // Errnos from <asm-generic/errno-base.h> and <asm-generic/errno.h> on Linux and <sys/errno.h>
+ // on macOS.
+ switch (nErrno)
+ {
+ case EPERM:
+ return "EPERM";
+ case ENOENT:
+ return "ENOENT";
+ case ESRCH:
+ return "ESRCH";
+ case EINTR:
+ return "EINTR";
+ case EIO:
+ return "EIO";
+ case ENXIO:
+ return "ENXIO";
+ case E2BIG:
+ return "E2BIG";
+ case ENOEXEC:
+ return "ENOEXEC";
+ case EBADF:
+ return "EBADF";
+ case ECHILD:
+ return "ECHILD";
+ case EAGAIN:
+ return "EAGAIN";
+ case ENOMEM:
+ return "ENOMEM";
+ case EACCES:
+ return "EACCES";
+ case EFAULT:
+ return "EFAULT";
+#ifdef ENOTBLK
+ case ENOTBLK:
+ return "ENOTBLK";
+#endif
+ case EBUSY:
+ return "EBUSY";
+ case EEXIST:
+ return "EEXIST";
+ case EXDEV:
+ return "EXDEV";
+ case ENODEV:
+ return "ENODEV";
+ case ENOTDIR:
+ return "ENOTDIR";
+ case EISDIR:
+ return "EISDIR";
+ case EINVAL:
+ return "EINVAL";
+ case ENFILE:
+ return "ENFILE";
+ case EMFILE:
+ return "EMFILE";
+ case ENOTTY:
+ return "ENOTTY";
+ case ETXTBSY:
+ return "ETXTBSY";
+ case EFBIG:
+ return "EFBIG";
+ case ENOSPC:
+ return "ENOSPC";
+ case ESPIPE:
+ return "ESPIPE";
+ case EROFS:
+ return "EROFS";
+ case EMLINK:
+ return "EMLINK";
+ case EPIPE:
+ return "EPIPE";
+ case EDOM:
+ return "EDOM";
+ case ERANGE:
+ return "ERANGE";
+ case EDEADLK:
+ return "EDEADLK";
+ case ENAMETOOLONG:
+ return "ENAMETOOLONG";
+ case ENOLCK:
+ return "ENOLCK";
+ case ENOSYS:
+ return "ENOSYS";
+ case ENOTEMPTY:
+ return "ENOTEMPTY";
+ case ELOOP:
+ return "ELOOP";
+ case ENOMSG:
+ return "ENOMSG";
+ case EIDRM:
+ return "EIDRM";
+#ifdef ECHRNG
+ case ECHRNG:
+ return "ECHRNG";
+#endif
+#ifdef EL2NSYNC
+ case EL2NSYNC:
+ return "EL2NSYNC";
+#endif
+#ifdef EL3HLT
+ case EL3HLT:
+ return "EL3HLT";
+#endif
+#ifdef EL3RST
+ case EL3RST:
+ return "EL3RST";
+#endif
+#ifdef ELNRNG
+ case ELNRNG:
+ return "ELNRNG";
+#endif
+#ifdef EUNATCH
+ case EUNATCH:
+ return "EUNATCH";
+#endif
+#ifdef ENOCSI
+ case ENOCSI:
+ return "ENOCSI";
+#endif
+#ifdef EL2HLT
+ case EL2HLT:
+ return "EL2HLT";
+#endif
+#ifdef EBADE
+ case EBADE:
+ return "EBADE";
+#endif
+#ifdef EBADR
+ case EBADR:
+ return "EBADR";
+#endif
+#ifdef EXFULL
+ case EXFULL:
+ return "EXFULL";
+#endif
+#ifdef ENOANO
+ case ENOANO:
+ return "ENOANO";
+#endif
+#ifdef EBADRQC
+ case EBADRQC:
+ return "EBADRQC";
+#endif
+#ifdef EBADSLT
+ case EBADSLT:
+ return "EBADSLT";
+#endif
+#ifdef EBFONT
+ case EBFONT:
+ return "EBFONT";
+#endif
+ case ENOSTR:
+ return "ENOSTR";
+ case ENODATA:
+ return "ENODATA";
+ case ETIME:
+ return "ETIME";
+ case ENOSR:
+ return "ENOSR";
+#ifdef ENONET
+ case ENONET:
+ return "ENONET";
+#endif
+#ifdef ENOPKG
+ case ENOPKG:
+ return "ENOPKG";
+#endif
+#ifdef EREMOTE
+ case EREMOTE:
+ return "EREMOTE";
+#endif
+ case ENOLINK:
+ return "ENOLINK";
+#ifdef EADV
+ case EADV:
+ return "EADV";
+#endif
+#ifdef ESRMNT
+ case ESRMNT:
+ return "ESRMNT";
+#endif
+#ifdef ECOMM
+ case ECOMM:
+ return "ECOMM";
+#endif
+ case EPROTO:
+ return "EPROTO";
+ case EMULTIHOP:
+ return "EMULTIHOP";
+#ifdef EDOTDOT
+ case EDOTDOT:
+ return "EDOTDOT";
+#endif
+ case EBADMSG:
+ return "EBADMSG";
+ case EOVERFLOW:
+ return "EOVERFLOW";
+#ifdef ENOTUNIQ
+ case ENOTUNIQ:
+ return "ENOTUNIQ";
+#endif
+#ifdef EBADFD
+ case EBADFD:
+ return "EBADFD";
+#endif
+#ifdef EREMCHG
+ case EREMCHG:
+ return "EREMCHG";
+#endif
+#ifdef ELIBACC
+ case ELIBACC:
+ return "ELIBACC";
+#endif
+#ifdef ELIBBAD
+ case ELIBBAD:
+ return "ELIBBAD";
+#endif
+#ifdef ELIBSCN
+ case ELIBSCN:
+ return "ELIBSCN";
+#endif
+#ifdef ELIBMAX
+ case ELIBMAX:
+ return "ELIBMAX";
+#endif
+#ifdef ELIBEXEC
+ case ELIBEXEC:
+ return "ELIBEXEC";
+#endif
+ case EILSEQ:
+ return "EILSEQ";
+#ifdef ERESTART
+ case ERESTART:
+ return "ERESTART";
+#endif
+#ifdef ESTRPIPE
+ case ESTRPIPE:
+ return "ESTRPIPE";
+#endif
+#ifdef EUSERS
+ case EUSERS:
+ return "EUSERS";
+#endif
+ case ENOTSOCK:
+ return "ENOTSOCK";
+ case EDESTADDRREQ:
+ return "EDESTADDRREQ";
+ case EMSGSIZE:
+ return "EMSGSIZE";
+ case EPROTOTYPE:
+ return "EPROTOTYPE";
+ case ENOPROTOOPT:
+ return "ENOPROTOOPT";
+ case EPROTONOSUPPORT:
+ return "EPROTONOSUPPORT";
+#ifdef ESOCKTNOSUPPORT
+ case ESOCKTNOSUPPORT:
+ return "ESOCKTNOSUPPORT";
+#endif
+#ifdef EOPNOTSUPP
+ case EOPNOTSUPP:
+ return "EOPNOTSUPP";
+#endif
+ case EPFNOSUPPORT:
+ return "EPFNOSUPPORT";
+ case EAFNOSUPPORT:
+ return "EAFNOSUPPORT";
+ case EADDRINUSE:
+ return "EADDRINUSE";
+ case EADDRNOTAVAIL:
+ return "EADDRNOTAVAIL";
+ case ENETDOWN:
+ return "ENETDOWN";
+ case ENETUNREACH:
+ return "ENETUNREACH";
+ case ENETRESET:
+ return "ENETRESET";
+ case ECONNABORTED:
+ return "ECONNABORTED";
+ case ECONNRESET:
+ return "ECONNRESET";
+ case ENOBUFS:
+ return "ENOBUFS";
+ case EISCONN:
+ return "EISCONN";
+ case ENOTCONN:
+ return "ENOTCONN";
+#ifdef ESHUTDOWN
+ case ESHUTDOWN:
+ return "ESHUTDOWN";
+#endif
+#ifdef ETOOMANYREFS
+ case ETOOMANYREFS:
+ return "ETOOMANYREFS";
+#endif
+ case ETIMEDOUT:
+ return "ETIMEDOUT";
+ case ECONNREFUSED:
+ return "ECONNREFUSED";
+#ifdef EHOSTDOWN
+ case EHOSTDOWN:
+ return "EHOSTDOWN";
+#endif
+ case EHOSTUNREACH:
+ return "EHOSTUNREACH";
+ case EALREADY:
+ return "EALREADY";
+ case EINPROGRESS:
+ return "EINPROGRESS";
+ case ESTALE:
+ return "ESTALE";
+#ifdef EUCLEAN
+ case EUCLEAN:
+ return "EUCLEAN";
+#endif
+#ifdef ENOTNAM
+ case ENOTNAM:
+ return "ENOTNAM";
+#endif
+#ifdef ENAVAIL
+ case ENAVAIL:
+ return "ENAVAIL";
+#endif
+#ifdef EISNAM
+ case EISNAM:
+ return "EISNAM";
+#endif
+#ifdef EREMOTEIO
+ case EREMOTEIO:
+ return "EREMOTEIO";
+#endif
+ case EDQUOT:
+ return "EDQUOT";
+#ifdef ENOMEDIUM
+ case ENOMEDIUM:
+ return "ENOMEDIUM";
+#endif
+#ifdef EMEDIUMTYPE
+ case EMEDIUMTYPE:
+ return "EMEDIUMTYPE";
+#endif
+ case ECANCELED:
+ return "ECANCELED";
+#ifdef ENOKEY
+ case ENOKEY:
+ return "ENOKEY";
+#endif
+#ifdef EKEYEXPIRED
+ case EKEYEXPIRED:
+ return "EKEYEXPIRED";
+#endif
+#ifdef EKEYREVOKED
+ case EKEYREVOKED:
+ return "EKEYREVOKED";
+#endif
+#ifdef EKEYREJECTED
+ case EKEYREJECTED:
+ return "EKEYREJECTED";
+#endif
+#ifdef EOWNERDEAD
+ case EOWNERDEAD:
+ return "EOWNERDEAD";
+#endif
+#ifdef ENOTRECOVERABLE
+ case ENOTRECOVERABLE:
+ return "ENOTRECOVERABLE";
+#endif
+#ifdef ERFKILL
+ case ERFKILL:
+ return "ERFKILL";
+#endif
+#ifdef EHWPOISON
+ case EHWPOISON:
+ return "EHWPOISON";
+#endif
+#ifdef EPROCLIM
+ case EPROCLIM:
+ return "EPROCLIM";
+#endif
+#ifdef EBADRPC
+ case EBADRPC:
+ return "EBADRPC";
+#endif
+#ifdef ERPCMISMATCH
+ case ERPCMISMATCH:
+ return "ERPCMISMATCH";
+#endif
+#ifdef EPROGUNAVAIL
+ case EPROGUNAVAIL:
+ return "EPROGUNAVAIL";
+#endif
+#ifdef EPROGMISMATCH
+ case EPROGMISMATCH:
+ return "EPROGMISMATCH";
+#endif
+#ifdef EPROCUNAVAIL
+ case EPROCUNAVAIL:
+ return "EPROCUNAVAIL";
+#endif
+#ifdef EFTYPE
+ case EFTYPE:
+ return "EFTYPE";
+#endif
+#ifdef EAUTH
+ case EAUTH:
+ return "EAUTH";
+#endif
+#ifdef ENEEDAUTH
+ case ENEEDAUTH:
+ return "ENEEDAUTH";
+#endif
+#ifdef EPWROFF
+ case EPWROFF:
+ return "EPWROFF";
+#endif
+#ifdef EDEVERR
+ case EDEVERR:
+ return "EDEVERR";
+#endif
+#ifdef EBADEXEC
+ case EBADEXEC:
+ return "EBADEXEC";
+#endif
+#ifdef EBADARCH
+ case EBADARCH:
+ return "EBADARCH";
+#endif
+#ifdef ESHLIBVERS
+ case ESHLIBVERS:
+ return "ESHLIBVERS";
+#endif
+#ifdef EBADMACHO
+ case EBADMACHO:
+ return "EBADMACHO";
+#endif
+#ifdef ENOATTR
+ case ENOATTR:
+ return "ENOATTR";
+#endif
+#ifdef EQFULL
+ case EQFULL:
+ return "EQFULL";
+#endif
+ default:
+ char* str = strerror(nErrno);
+ return std::to_string(nErrno) + " (" + std::string(str) + ")";
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/uunxapi.hxx b/sal/osl/unx/uunxapi.hxx
new file mode 100644
index 000000000..9f792765c
--- /dev/null
+++ b/sal/osl/unx/uunxapi.hxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_UUNXAPI_HXX
+#define INCLUDED_SAL_OSL_UNX_UUNXAPI_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rtl/ustring.h>
+
+#include <rtl/ustring.hxx>
+
+int stat_c(const char *cpPath, struct stat* buf);
+
+int lstat_c(const char *cpPath, struct stat* buf);
+
+int mkdir_c(OString const & path, mode_t mode);
+
+int open_c(const OString& path, int oflag, int mode);
+
+int utime_c(const char *cpPath, struct utimbuf *times);
+
+int ftruncate_with_name(int fd, sal_uInt64 uSize, const OString& path);
+
+namespace osl
+{
+ OString OUStringToOString(std::u16string_view s);
+
+ int access(const OString& strPath, int mode);
+
+ /***********************************
+ osl::realpath
+
+ @descr
+ The return value differs from the
+ realpath function
+
+ @returns sal_True on success else
+ sal_False
+
+ @see realpath
+ **********************************/
+
+ bool realpath(
+ const OUString& ustrFileName,
+ OUString& ustrResolvedName);
+
+ bool realpath(
+ const OString& strFileName,
+ OString& strResolvedName);
+
+ int lstat(const OUString& ustrPath, struct stat& buf);
+
+ int lstat(const OString& strPath, struct stat& buf);
+
+ int mkdir(const OString& aPath, mode_t aMode);
+} // end namespace osl
+
+#endif // INCLUDED_SAL_OSL_UNX_UUNXAPI_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/uunxapi.mm b/sal/osl/unx/uunxapi.mm
new file mode 100644
index 000000000..5a7e17f34
--- /dev/null
+++ b/sal/osl/unx/uunxapi.mm
@@ -0,0 +1 @@
+#include "uunxapi.cxx"
diff --git a/sal/osl/w32/backtrace.cxx b/sal/osl/w32/backtrace.cxx
new file mode 100644
index 000000000..d9231de44
--- /dev/null
+++ b/sal/osl/w32/backtrace.cxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <limits>
+#include <memory>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <process.h>
+#include <iostream>
+#define OPTIONAL
+#include <DbgHelp.h>
+
+#include <rtl/ustrbuf.hxx>
+#include <sal/backtrace.hxx>
+
+#include <backtraceasstring.hxx>
+
+namespace {
+
+template<typename T> T clampToULONG(T n) {
+ auto const maxUlong = std::numeric_limits<ULONG>::max();
+ return n > maxUlong ? static_cast<T>(maxUlong) : n;
+}
+
+}
+
+OUString osl::detail::backtraceAsString(sal_uInt32 maxDepth)
+{
+ std::unique_ptr<sal::BacktraceState> backtrace = sal::backtrace_get( maxDepth );
+ return sal::backtrace_to_string( backtrace.get());
+}
+
+std::unique_ptr<sal::BacktraceState> sal::backtrace_get(sal_uInt32 maxDepth)
+{
+ assert(maxDepth != 0);
+ maxDepth = clampToULONG(maxDepth);
+
+ auto pStack = new void *[maxDepth];
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/bb204633.aspx
+ // "CaptureStackBackTrace function" claims that you "can capture up to
+ // MAXUSHORT frames", and on Windows Server 2003 and Windows XP it even
+ // "must be less than 63", but assume that a too large input value is
+ // clamped internally, instead of resulting in an error:
+ int nFrames = CaptureStackBackTrace( 0, static_cast<ULONG>(maxDepth), pStack, nullptr );
+
+ return std::unique_ptr<BacktraceState>(new BacktraceState{ pStack, nFrames });
+}
+
+OUString sal::backtrace_to_string(BacktraceState* backtraceState)
+{
+ HANDLE hProcess = GetCurrentProcess();
+ // https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-syminitialize
+ // says to not initialize more than once.
+ [[maybe_unused]] static bool bInitialized = SymInitialize(hProcess, nullptr, false);
+ SymRefreshModuleList(hProcess);
+ SYMBOL_INFO * pSymbol;
+ pSymbol = static_cast<SYMBOL_INFO *>(calloc( sizeof( SYMBOL_INFO ) + 1024 * sizeof( char ), 1 ));
+ assert(pSymbol);
+ pSymbol->MaxNameLen = 1024 - 1;
+ pSymbol->SizeOfStruct = sizeof( SYMBOL_INFO );
+
+ auto nFrames = backtraceState->nDepth;
+ OUStringBuffer aBuf;
+ for( int i = 0; i < nFrames; i++ )
+ {
+ SymFromAddr( hProcess, reinterpret_cast<DWORD64>(backtraceState->buffer[ i ]), nullptr, pSymbol );
+ aBuf.append( static_cast<sal_Int32>(nFrames - i - 1) );
+ aBuf.append( ": " );
+ aBuf.appendAscii( pSymbol->Name );
+ aBuf.append( " - 0x" );
+ aBuf.append( static_cast<sal_Int64>(pSymbol->Address), 16 );
+ aBuf.append( "\n" );
+ }
+
+ free( pSymbol );
+
+ return aBuf.makeStringAndClear();
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/conditn.cxx b/sal/osl/w32/conditn.cxx
new file mode 100644
index 000000000..6bc6d9d3e
--- /dev/null
+++ b/sal/osl/w32/conditn.cxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.h"
+
+#include <osl/conditn.h>
+#include <osl/diagnose.h>
+#include <osl/time.h>
+
+/*
+ under WIN32, we use the void* oslCondition
+ as a WIN32 HANDLE (which is also a 32-bit value)
+*/
+
+oslCondition SAL_CALL osl_createCondition(void)
+{
+ oslCondition Condition;
+
+ Condition= reinterpret_cast<oslCondition>(CreateEventW(nullptr, /* no security */
+ true, /* manual reset */
+ false, /* initial state not signaled */
+ nullptr)); /* automatic name */
+
+ return Condition;
+
+}
+
+void SAL_CALL osl_destroyCondition(oslCondition Condition)
+{
+ if(Condition)
+ OSL_VERIFY(CloseHandle(Condition));
+}
+
+sal_Bool SAL_CALL osl_setCondition(oslCondition Condition)
+{
+ OSL_ASSERT(Condition);
+
+ return SetEvent(reinterpret_cast<HANDLE>(Condition)) != FALSE;
+}
+
+sal_Bool SAL_CALL osl_resetCondition(oslCondition Condition)
+{
+ OSL_ASSERT(Condition);
+
+ return ResetEvent(reinterpret_cast<HANDLE>(Condition)) != FALSE;
+}
+
+oslConditionResult SAL_CALL osl_waitCondition(oslCondition Condition,
+ const TimeValue* pTimeout)
+{
+ DWORD timeout;
+
+ OSL_ASSERT(Condition);
+
+ if (pTimeout)
+ timeout = pTimeout->Seconds * 1000 + pTimeout->Nanosec / 1000000L;
+ else
+ timeout = INFINITE;
+
+ /* It's necessary to process SendMessage calls to the current thread to give other threads
+ access to COM objects instantiated in this thread */
+
+ while ( true )
+ {
+ /* Only wake up if a SendMessage call to the threads message loop is detected */
+ switch( MsgWaitForMultipleObjects( 1, reinterpret_cast<HANDLE *>(&Condition), FALSE, timeout, QS_SENDMESSAGE ) )
+ {
+ case WAIT_OBJECT_0 + 1:
+ {
+ MSG msg;
+
+ /* We Must not dispatch the message. PM_NOREMOVE leaves the message queue untouched
+ but dispatches SendMessage calls automatically */
+
+ PeekMessageW( &msg, nullptr, 0, 0, PM_NOREMOVE );
+ }
+ break;
+
+ case WAIT_OBJECT_0:
+ return osl_cond_result_ok;
+
+ case WAIT_TIMEOUT:
+ return osl_cond_result_timeout;
+
+ default:
+ return osl_cond_result_error;
+ }
+ }
+}
+
+sal_Bool SAL_CALL osl_checkCondition(oslCondition Condition)
+{
+ OSL_ASSERT(Condition);
+
+ return WaitForSingleObject(reinterpret_cast<HANDLE>(Condition), 0) == WAIT_OBJECT_0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/dllentry.cxx b/sal/osl/w32/dllentry.cxx
new file mode 100644
index 000000000..81139a058
--- /dev/null
+++ b/sal/osl/w32/dllentry.cxx
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <systools/win32/uwinapi.h>
+#include <tlhelp32.h>
+#include <rpc.h>
+#include <winsock.h>
+#ifdef _DEBUG
+#include <crtdbg.h>
+#endif
+#include <osl/diagnose.h>
+#include <sal/types.h>
+#include <float.h>
+
+#include <osl/mutex.h>
+#include <osl/thread.h>
+
+#include "file_url.hxx"
+#include <rtllifecycle.h>
+
+#include "thread.hxx"
+
+/*
+This is needed because DllMain is called after static constructors. A DLL's
+startup and shutdown sequence looks like this:
+
+_pRawDllMain()
+_CRT_INIT()
+DllMain()
+...
+DllMain()
+_CRT_INIT()
+_pRawDllMain()
+
+*/
+
+extern "C" {
+
+static BOOL WINAPI RawDllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved );
+BOOL (WINAPI *_pRawDllMain)(HINSTANCE, DWORD, LPVOID) = RawDllMain;
+
+}
+
+static BOOL WINAPI RawDllMain( HINSTANCE, DWORD fdwReason, LPVOID )
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+#ifdef _DEBUG
+ WCHAR buf[64];
+ DWORD const res = GetEnvironmentVariableW(L"SAL_NO_ASSERT_DIALOGS", buf, SAL_N_ELEMENTS(buf));
+ if (res && res < SAL_N_ELEMENTS(buf))
+ {
+ // disable the dialog on abort()
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_ERROR, (_CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE));
+ // not sure which assertions this affects
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_ASSERT, (_CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE));
+ // disable the dialog on assert(false)
+ _set_error_mode(_OUT_TO_STDERR);
+ }
+#endif
+
+#if OSL_DEBUG_LEVEL < 2
+ /* Suppress file error messages from system like "Floppy A: not inserted" */
+ SetErrorMode( SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS );
+#endif
+
+ //We disable floating point exceptions. This is the usual state at program startup
+ //but on Windows 98 and ME this is not always the case.
+ _control87(_MCW_EM, _MCW_EM);
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ WSACleanup( );
+
+ /*
+
+ On a product build memory management finalization might
+ cause a crash without assertion (assertions off) if heap is
+ corrupted. But a crash report won't help here because at
+ this point all other threads have been terminated and only
+ ntdll is on the stack. No chance to find the reason for the
+ corrupted heap if so.
+
+ So annoying the user with a crash report is completely useless.
+
+ */
+
+#ifndef DBG_UTIL
+ __try
+#endif
+ {
+ /* cleanup locale hashtable */
+ rtl_locale_fini();
+
+ /* finalize memory management */
+ rtl_cache_fini();
+ rtl_arena_fini();
+ }
+#ifndef DBG_UTIL
+ __except( EXCEPTION_EXECUTE_HANDLER )
+ {
+ }
+#endif
+ break;
+ }
+
+ return TRUE;
+}
+
+static DWORD GetParentProcessId()
+{
+ DWORD dwParentProcessId = 0;
+ HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
+
+ if ( IsValidHandle( hSnapshot ) )
+ {
+ PROCESSENTRY32 pe;
+ bool fSuccess;
+
+ ZeroMemory( &pe, sizeof(pe) );
+ pe.dwSize = sizeof(pe);
+ fSuccess = Process32First( hSnapshot, &pe );
+
+ while( fSuccess )
+ {
+ if ( GetCurrentProcessId() == pe.th32ProcessID )
+ {
+ dwParentProcessId = pe.th32ParentProcessID;
+ break;
+ }
+
+ fSuccess = Process32Next( hSnapshot, &pe );
+ }
+
+ CloseHandle( hSnapshot );
+ }
+
+ return dwParentProcessId;
+}
+
+static DWORD WINAPI ParentMonitorThreadProc( LPVOID lpParam )
+{
+ DWORD_PTR dwParentProcessId = reinterpret_cast<DWORD_PTR>(lpParam);
+
+ HANDLE hParentProcess = OpenProcess( SYNCHRONIZE, FALSE, dwParentProcessId );
+
+ osl_setThreadName("headless ParentMonitorThread");
+
+ if ( IsValidHandle( hParentProcess ) )
+ {
+ if ( WAIT_OBJECT_0 == WaitForSingleObject( hParentProcess, INFINITE ) )
+ {
+ TerminateProcess( GetCurrentProcess(), 0 );
+ }
+ CloseHandle( hParentProcess );
+ }
+ return 0;
+}
+
+extern "C"
+BOOL WINAPI DllMain( HINSTANCE, DWORD fdwReason, LPVOID )
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+ WCHAR szBuffer[64];
+
+ // This code will attach the process to its parent process
+ // if the parent process had set the environment variable.
+ // The corresponding code (setting the environment variable)
+ // is desktop/win32/source/officeloader.cxx
+
+ DWORD dwResult = GetEnvironmentVariableW( L"ATTACHED_PARENT_PROCESSID", szBuffer, SAL_N_ELEMENTS(szBuffer) );
+
+ if ( dwResult && dwResult < SAL_N_ELEMENTS(szBuffer) )
+ {
+ DWORD dwThreadId = 0;
+
+ DWORD_PTR dwParentProcessId = static_cast<DWORD_PTR>(_wtol( szBuffer ));
+
+ if ( dwParentProcessId && GetParentProcessId() == dwParentProcessId )
+ {
+ // No error check, it works or it does not
+ // Thread should only be started for headless mode, see desktop/win32/source/officeloader.cxx
+ HANDLE hThread
+ = CreateThread(nullptr, 0, ParentMonitorThreadProc,
+ reinterpret_cast<LPVOID>(dwParentProcessId), 0, &dwThreadId);
+ // Note: calling CreateThread in DllMain is discouraged
+ // but this is only done in the headless mode and in
+ // that case no other threads should be running at startup
+ // when sal3.dll is loaded; also there is no
+ // synchronization with the spawned thread, so there
+ // does not appear to be a real risk of deadlock here
+ if (hThread)
+ CloseHandle(hThread);
+ }
+ }
+
+ return TRUE;
+ }
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ osl_callThreadKeyCallbackOnThreadDetach( );
+ break;
+ }
+
+ return TRUE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/file-impl.hxx b/sal/osl/w32/file-impl.hxx
new file mode 100644
index 000000000..3f1d2136d
--- /dev/null
+++ b/sal/osl/w32/file-impl.hxx
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SAL_OSL_W32_FILE_IMPL_HXX
+#define INCLUDED_SAL_OSL_W32_FILE_IMPL_HXX
+
+#include <sal/config.h>
+
+#include <osl/file.h>
+#include <sal/types.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+extern "C" oslFileHandle osl_createFileHandleFromOSHandle(HANDLE hFile, sal_uInt32 uFlags);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sal/osl/w32/file.cxx b/sal/osl/w32/file.cxx
new file mode 100644
index 000000000..be921aede
--- /dev/null
+++ b/sal/osl/w32/file.cxx
@@ -0,0 +1,1121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <systools/win32/uwinapi.h>
+
+#include <osl/file.hxx>
+#include <rtl/alloc.h>
+#include <rtl/byteseq.h>
+#include <sal/log.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/typed_flags_set.hxx>
+
+#include "file-impl.hxx"
+#include "file_url.hxx"
+#include "file_error.hxx"
+
+#include <atomic>
+#include <cassert>
+#include <algorithm>
+#include <limits>
+
+#ifdef max /* conflict w/ std::numeric_limits<T>::max() */
+#undef max
+#endif
+#ifdef min
+#undef min
+#endif
+
+namespace {
+
+/** State
+ */
+enum class StateBits
+{
+ Seekable = 1, /*< open() sets, iff regular file */
+ Readable = 2, /*< open() sets, read() requires */
+ Writeable = 4, /*< open() sets, write() requires */
+ Modified = 8 /* write() sets, flush() resets */
+};
+
+}
+
+template<> struct o3tl::typed_flags<StateBits>: o3tl::is_typed_flags<StateBits, 0xF> {};
+
+namespace {
+
+/** File handle implementation.
+*/
+struct FileHandle_Impl
+{
+ CRITICAL_SECTION m_mutex;
+ HANDLE m_hFile;
+
+ StateBits m_state;
+
+ sal_uInt64 m_size; /*< file size */
+ LONGLONG m_offset; /*< physical offset from begin of file */
+ // m_filepos is hit hard in some situations, where the overhead of a mutex starts to show up, so use an atomic
+ std::atomic<LONGLONG> m_filepos; /*< logical offset from begin of file */
+
+ LONGLONG m_bufptr; /*< buffer offset from begin of file */
+ SIZE_T m_buflen; /*< buffer filled [0, m_bufsiz - 1] */
+
+ SIZE_T m_bufsiz;
+ sal_uInt8 * m_buffer;
+
+ explicit FileHandle_Impl (HANDLE hFile);
+ ~FileHandle_Impl();
+
+ static SIZE_T getpagesize();
+
+ sal_uInt64 getPos() const;
+ oslFileError setPos (sal_uInt64 uPos);
+
+ sal_uInt64 getSize() const;
+ oslFileError setSize (sal_uInt64 uPos);
+
+ oslFileError readAt(
+ LONGLONG nOffset,
+ void * pBuffer,
+ DWORD nBytesRequested,
+ sal_uInt64 * pBytesRead);
+
+ oslFileError writeAt(
+ LONGLONG nOffset,
+ void const * pBuffer,
+ DWORD nBytesToWrite,
+ sal_uInt64 * pBytesWritten);
+
+ oslFileError readFileAt(
+ LONGLONG nOffset,
+ void * pBuffer,
+ sal_uInt64 uBytesRequested,
+ sal_uInt64 * pBytesRead);
+
+ oslFileError writeFileAt(
+ LONGLONG nOffset,
+ void const * pBuffer,
+ sal_uInt64 uBytesToWrite,
+ sal_uInt64 * pBytesWritten);
+
+ oslFileError readLineAt(
+ LONGLONG nOffset,
+ sal_Sequence ** ppSequence,
+ sal_uInt64 * pBytesRead);
+
+ static oslFileError writeSequence_Impl (
+ sal_Sequence ** ppSequence,
+ SIZE_T * pnOffset,
+ const void * pBuffer,
+ SIZE_T nBytes);
+
+ oslFileError syncFile();
+
+ /** Guard.
+ */
+ class Guard
+ {
+ LPCRITICAL_SECTION m_mutex;
+
+ public:
+ explicit Guard(LPCRITICAL_SECTION pMutex);
+ ~Guard();
+ };
+};
+
+}
+
+FileHandle_Impl::Guard::Guard(LPCRITICAL_SECTION pMutex)
+ : m_mutex (pMutex)
+{
+ assert(pMutex);
+ ::EnterCriticalSection (m_mutex);
+}
+
+FileHandle_Impl::Guard::~Guard()
+{
+ ::LeaveCriticalSection (m_mutex);
+}
+
+FileHandle_Impl::FileHandle_Impl(HANDLE hFile)
+ : m_hFile (hFile),
+ m_state (StateBits::Readable | StateBits::Writeable),
+ m_size (0),
+ m_offset (0),
+ m_filepos (0),
+ m_bufptr (-1),
+ m_buflen (0),
+ m_bufsiz (getpagesize()),
+ m_buffer (nullptr)
+{
+ ::InitializeCriticalSection (&m_mutex);
+ m_buffer = static_cast<sal_uInt8 *>(calloc(m_bufsiz, 1));
+}
+
+FileHandle_Impl::~FileHandle_Impl()
+{
+ free(m_buffer);
+ m_buffer = nullptr;
+ ::DeleteCriticalSection (&m_mutex);
+}
+
+SIZE_T FileHandle_Impl::getpagesize()
+{
+ SYSTEM_INFO info;
+ ::GetSystemInfo(&info);
+ return sal::static_int_cast< SIZE_T >(info.dwPageSize);
+}
+
+sal_uInt64 FileHandle_Impl::getPos() const
+{
+ return sal::static_int_cast< sal_uInt64 >(m_filepos.load());
+}
+
+oslFileError FileHandle_Impl::setPos(sal_uInt64 uPos)
+{
+ m_filepos = sal::static_int_cast< LONGLONG >(uPos);
+ return osl_File_E_None;
+}
+
+sal_uInt64 FileHandle_Impl::getSize() const
+{
+ LONGLONG bufend = std::max(LONGLONG(0), m_bufptr) + m_buflen;
+ return std::max(m_size, sal::static_int_cast< sal_uInt64 >(bufend));
+}
+
+oslFileError FileHandle_Impl::setSize(sal_uInt64 uSize)
+{
+ LARGE_INTEGER nDstPos; nDstPos.QuadPart = sal::static_int_cast< LONGLONG >(uSize);
+ if (!::SetFilePointerEx(m_hFile, nDstPos, nullptr, FILE_BEGIN))
+ return oslTranslateFileError(GetLastError());
+
+ if (!::SetEndOfFile(m_hFile))
+ return oslTranslateFileError(GetLastError());
+ m_size = uSize;
+
+ nDstPos.QuadPart = m_offset;
+ if (!::SetFilePointerEx(m_hFile, nDstPos, nullptr, FILE_BEGIN))
+ return oslTranslateFileError(GetLastError());
+
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::readAt(
+ LONGLONG nOffset,
+ void * pBuffer,
+ DWORD nBytesRequested,
+ sal_uInt64 * pBytesRead)
+{
+ SAL_WARN_IF(!(m_state & StateBits::Seekable), "sal.osl", "FileHandle_Impl::readAt(): not seekable");
+ if (!(m_state & StateBits::Seekable))
+ return osl_File_E_SPIPE;
+
+ SAL_WARN_IF(!(m_state & StateBits::Readable), "sal.osl", "FileHandle_Impl::readAt(): not readable");
+ if (!(m_state & StateBits::Readable))
+ return osl_File_E_BADF;
+
+ if (nOffset != m_offset)
+ {
+ LARGE_INTEGER liOffset; liOffset.QuadPart = nOffset;
+ if (!::SetFilePointerEx(m_hFile, liOffset, nullptr, FILE_BEGIN))
+ return oslTranslateFileError(GetLastError());
+ m_offset = nOffset;
+ }
+
+ DWORD dwDone = 0;
+ if (!::ReadFile(m_hFile, pBuffer, nBytesRequested, &dwDone, nullptr))
+ return oslTranslateFileError(GetLastError());
+ m_offset += dwDone;
+
+ *pBytesRead = dwDone;
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::writeAt(
+ LONGLONG nOffset,
+ void const * pBuffer,
+ DWORD nBytesToWrite,
+ sal_uInt64 * pBytesWritten)
+{
+ SAL_WARN_IF(!(m_state & StateBits::Seekable), "sal.osl", "FileHandle_Impl::writeAt(): not seekable");
+ if (!(m_state & StateBits::Seekable))
+ return osl_File_E_SPIPE;
+
+ SAL_WARN_IF(!(m_state & StateBits::Writeable), "sal.osl", "FileHandle_Impl::writeAt(): not writeable");
+ if (!(m_state & StateBits::Writeable))
+ return osl_File_E_BADF;
+
+ if (nOffset != m_offset)
+ {
+ LARGE_INTEGER liOffset; liOffset.QuadPart = nOffset;
+ if (!::SetFilePointerEx (m_hFile, liOffset, nullptr, FILE_BEGIN))
+ return oslTranslateFileError(GetLastError());
+ m_offset = nOffset;
+ }
+
+ DWORD dwDone = 0;
+ if (!::WriteFile(m_hFile, pBuffer, nBytesToWrite, &dwDone, nullptr))
+ return oslTranslateFileError(GetLastError());
+ m_offset += dwDone;
+
+ m_size = std::max(m_size, sal::static_int_cast< sal_uInt64 >(m_offset));
+
+ *pBytesWritten = dwDone;
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::readFileAt(
+ LONGLONG nOffset,
+ void * pBuffer,
+ sal_uInt64 uBytesRequested,
+ sal_uInt64 * pBytesRead)
+{
+ static sal_uInt64 const g_limit_dword = std::numeric_limits< DWORD >::max();
+ if (g_limit_dword < uBytesRequested)
+ return osl_File_E_OVERFLOW;
+ DWORD nBytesRequested = sal::static_int_cast< DWORD >(uBytesRequested);
+
+ if (!(m_state & StateBits::Seekable))
+ {
+ // not seekable (pipe)
+ DWORD dwDone = 0;
+ if (!::ReadFile(m_hFile, pBuffer, nBytesRequested, &dwDone, nullptr))
+ return oslTranslateFileError(GetLastError());
+ *pBytesRead = dwDone;
+ return osl_File_E_None;
+ }
+ else if (!m_buffer)
+ {
+ // not buffered
+ return readAt (nOffset, pBuffer, nBytesRequested, pBytesRead);
+ }
+ else
+ {
+ sal_uInt8 * buffer = static_cast< sal_uInt8* >(pBuffer);
+ for (*pBytesRead = 0; nBytesRequested > 0;)
+ {
+ LONGLONG const bufptr = (nOffset / m_bufsiz) * m_bufsiz;
+ SIZE_T const bufpos = nOffset % m_bufsiz;
+
+ if (bufptr != m_bufptr)
+ {
+ // flush current buffer
+ oslFileError result = syncFile();
+ if (result != osl_File_E_None)
+ return result;
+ m_bufptr = -1;
+ m_buflen = 0;
+
+ if (nBytesRequested >= m_bufsiz)
+ {
+ // buffer too small, read through from file
+ sal_uInt64 uDone = 0;
+ result = readAt (nOffset, &(buffer[*pBytesRead]), nBytesRequested, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ nBytesRequested -= sal::static_int_cast< DWORD >(uDone);
+ *pBytesRead += uDone;
+ return osl_File_E_None;
+ }
+
+ // update buffer (pointer)
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+ m_bufptr = bufptr;
+ m_buflen = sal::static_int_cast< SIZE_T >(uDone);
+ }
+ if (bufpos >= m_buflen)
+ {
+ // end of file
+ return osl_File_E_None;
+ }
+
+ SIZE_T const bytes = std::min(m_buflen - bufpos, static_cast<SIZE_T>(nBytesRequested));
+ memcpy(&(buffer[*pBytesRead]), &(m_buffer[bufpos]), bytes);
+ nBytesRequested -= bytes;
+ *pBytesRead += bytes;
+ nOffset += bytes;
+ }
+ return osl_File_E_None;
+ }
+}
+
+oslFileError FileHandle_Impl::writeFileAt(
+ LONGLONG nOffset,
+ void const * pBuffer,
+ sal_uInt64 uBytesToWrite,
+ sal_uInt64 * pBytesWritten)
+{
+ static sal_uInt64 const g_limit_dword = std::numeric_limits< DWORD >::max();
+ if (g_limit_dword < uBytesToWrite)
+ return osl_File_E_OVERFLOW;
+ DWORD nBytesToWrite = sal::static_int_cast< DWORD >(uBytesToWrite);
+
+ if (!(m_state & StateBits::Seekable))
+ {
+ // not seekable (pipe)
+ DWORD dwDone = 0;
+ if (!::WriteFile(m_hFile, pBuffer, nBytesToWrite, &dwDone, nullptr))
+ return oslTranslateFileError(GetLastError());
+ *pBytesWritten = dwDone;
+ return osl_File_E_None;
+ }
+ else if (!m_buffer)
+ {
+ // not buffered
+ return writeAt(nOffset, pBuffer, nBytesToWrite, pBytesWritten);
+ }
+ else
+ {
+ sal_uInt8 const * buffer = static_cast< sal_uInt8 const* >(pBuffer);
+ for (*pBytesWritten = 0; nBytesToWrite > 0;)
+ {
+ LONGLONG const bufptr = (nOffset / m_bufsiz) * m_bufsiz;
+ SIZE_T const bufpos = nOffset % m_bufsiz;
+ if (bufptr != m_bufptr)
+ {
+ // flush current buffer
+ oslFileError result = syncFile();
+ if (result != osl_File_E_None)
+ return result;
+ m_bufptr = -1;
+ m_buflen = 0;
+
+ if (nBytesToWrite >= m_bufsiz)
+ {
+ // buffer too small, write through to file
+ sal_uInt64 uDone = 0;
+ result = writeAt(nOffset, &(buffer[*pBytesWritten]), nBytesToWrite, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+ if (uDone != nBytesToWrite)
+ return osl_File_E_IO;
+
+ nBytesToWrite -= sal::static_int_cast< DWORD >(uDone);
+ *pBytesWritten += uDone;
+ return osl_File_E_None;
+ }
+
+ // update buffer (pointer)
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+ m_bufptr = bufptr;
+ m_buflen = sal::static_int_cast< SIZE_T >(uDone);
+ }
+
+ SIZE_T const bytes = std::min(m_bufsiz - bufpos, static_cast<SIZE_T>(nBytesToWrite));
+ memcpy(&(m_buffer[bufpos]), &(buffer[*pBytesWritten]), bytes);
+ nBytesToWrite -= bytes;
+ *pBytesWritten += bytes;
+ nOffset += bytes;
+
+ m_buflen = std::max(m_buflen, bufpos + bytes);
+ m_state |= StateBits::Modified;
+ }
+ return osl_File_E_None;
+ }
+}
+
+oslFileError FileHandle_Impl::readLineAt(
+ LONGLONG nOffset,
+ sal_Sequence ** ppSequence,
+ sal_uInt64 * pBytesRead)
+{
+ oslFileError result = osl_File_E_None;
+
+ LONGLONG bufptr = (nOffset / m_bufsiz) * m_bufsiz;
+ if (bufptr != m_bufptr)
+ {
+ /* flush current buffer */
+ result = syncFile();
+ if (result != osl_File_E_None)
+ return result;
+
+ /* update buffer (pointer) */
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ m_bufptr = bufptr;
+ m_buflen = sal::static_int_cast< SIZE_T >(uDone);
+ }
+
+ static int const LINE_STATE_BEGIN = 0;
+ static int const LINE_STATE_CR = 1;
+ static int const LINE_STATE_LF = 2;
+
+ SIZE_T bufpos = sal::static_int_cast< SIZE_T >(nOffset - m_bufptr), curpos = bufpos, dstpos = 0;
+ int state = (bufpos >= m_buflen) ? LINE_STATE_LF : LINE_STATE_BEGIN;
+
+ for (; state != LINE_STATE_LF;)
+ {
+ if (curpos >= m_buflen)
+ {
+ /* buffer examined */
+ if (curpos > bufpos) // actually, curpos can't become less than bufpos, so != could do
+ {
+ /* flush buffer to sequence */
+ result = writeSequence_Impl(
+ ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos);
+ if (result != osl_File_E_None)
+ return result;
+ *pBytesRead += curpos - bufpos;
+ nOffset += curpos - bufpos;
+ }
+
+ bufptr = nOffset / m_bufsiz * m_bufsiz;
+ if (bufptr != m_bufptr)
+ {
+ /* update buffer (pointer) */
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+ m_bufptr = bufptr;
+ m_buflen = sal::static_int_cast< SIZE_T >(uDone);
+ }
+
+ bufpos = sal::static_int_cast< SIZE_T >(nOffset - m_bufptr);
+ curpos = bufpos;
+ if (bufpos >= m_buflen)
+ break;
+ }
+ switch (state)
+ {
+ case LINE_STATE_CR:
+ state = LINE_STATE_LF;
+ switch (m_buffer[curpos])
+ {
+ case 0x0A: /* CRLF */
+ /* eat current char */
+ curpos++;
+ break;
+ default: /* single CR */
+ /* keep current char */
+ break;
+ }
+ break;
+ default:
+ /* determine next state */
+ switch (m_buffer[curpos])
+ {
+ case 0x0A: /* single LF */
+ state = LINE_STATE_LF;
+ break;
+ case 0x0D: /* CR */
+ state = LINE_STATE_CR;
+ break;
+ default: /* advance to next char */
+ curpos++;
+ break;
+ }
+ if (state != LINE_STATE_BEGIN)
+ {
+ /* store (and eat) the newline char */
+ m_buffer[curpos] = 0x0A;
+ curpos++;
+
+ /* flush buffer to sequence */
+ result = writeSequence_Impl(
+ ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos - 1);
+ if (result != osl_File_E_None)
+ return result;
+ *pBytesRead += curpos - bufpos;
+ nOffset += curpos - bufpos;
+ }
+ break;
+ }
+ }
+
+ result = writeSequence_Impl(ppSequence, &dstpos, nullptr, 0);
+ if (result != osl_File_E_None)
+ return result;
+ if (dstpos > 0)
+ return osl_File_E_None;
+ if (bufpos >= m_buflen)
+ return osl_File_E_AGAIN;
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::writeSequence_Impl(
+ sal_Sequence ** ppSequence,
+ SIZE_T * pnOffset,
+ const void * pBuffer,
+ SIZE_T nBytes)
+{
+ sal_Int32 nElements = *pnOffset + nBytes;
+ if (!*ppSequence)
+ {
+ /* construct sequence */
+ rtl_byte_sequence_constructNoDefault(ppSequence, nElements);
+ }
+ else if (nElements != (*ppSequence)->nElements)
+ {
+ /* resize sequence */
+ rtl_byte_sequence_realloc(ppSequence, nElements);
+ }
+ if (*ppSequence)
+ {
+ /* fill sequence */
+ memcpy(&((*ppSequence)->elements[*pnOffset]), pBuffer, nBytes);
+ *pnOffset += nBytes;
+ }
+ return (*ppSequence) ? osl_File_E_None : osl_File_E_NOMEM;
+}
+
+oslFileError FileHandle_Impl::syncFile()
+{
+ oslFileError result = osl_File_E_None;
+ if (m_state & StateBits::Modified)
+ {
+ sal_uInt64 uDone = 0;
+ result = writeAt(m_bufptr, m_buffer, m_buflen, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+ if (uDone != m_buflen)
+ return osl_File_E_IO;
+ m_state &= ~StateBits::Modified;
+ }
+ return result;
+}
+
+extern "C" oslFileHandle osl_createFileHandleFromOSHandle(
+ HANDLE hFile,
+ sal_uInt32 uFlags)
+{
+ if (!IsValidHandle(hFile))
+ return nullptr; // EINVAL
+
+ FileHandle_Impl * pImpl = new FileHandle_Impl(hFile);
+
+ /* check for regular file */
+ if (GetFileType(hFile) == FILE_TYPE_DISK)
+ {
+ /* mark seekable */
+ pImpl->m_state |= StateBits::Seekable;
+
+ /* init current size */
+ LARGE_INTEGER uSize = { { 0, 0 } };
+ (void) ::GetFileSizeEx(hFile, &uSize);
+ pImpl->m_size = (sal::static_int_cast<sal_uInt64>(uSize.HighPart) << 32) + uSize.LowPart;
+ }
+
+ if (!(uFlags & osl_File_OpenFlag_Read))
+ pImpl->m_state &= ~StateBits::Readable;
+ if (!(uFlags & osl_File_OpenFlag_Write))
+ pImpl->m_state &= ~StateBits::Writeable;
+
+ SAL_WARN_IF(
+ !((uFlags & osl_File_OpenFlag_Read) || (uFlags & osl_File_OpenFlag_Write)),
+ "sal.osl",
+ "osl_createFileHandleFromOSHandle(): missing read/write access flags");
+ return static_cast<oslFileHandle>(pImpl);
+}
+
+oslFileError SAL_CALL osl_openFile(
+ rtl_uString * strPath,
+ oslFileHandle * pHandle,
+ sal_uInt32 uFlags)
+{
+ OUString strSysPath;
+ oslFileError result = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
+ if (result != osl_File_E_None)
+ return result;
+
+ // tdf126742 use FILE_SHARE_WRITE to get closer to non-Windows platform behaviour,
+ // for details and discussion see task please
+ DWORD dwAccess = GENERIC_READ, dwShare = FILE_SHARE_READ | FILE_SHARE_WRITE, dwCreation = 0;
+
+ if (uFlags & osl_File_OpenFlag_Write)
+ dwAccess |= GENERIC_WRITE;
+
+ if (uFlags & osl_File_OpenFlag_NoLock)
+ dwShare |= FILE_SHARE_DELETE;
+
+ if (uFlags & osl_File_OpenFlag_Create)
+ dwCreation |= CREATE_NEW;
+ else
+ dwCreation |= OPEN_EXISTING;
+
+ HANDLE hFile = CreateFileW(
+ o3tl::toW(strSysPath.getStr()),
+ dwAccess, dwShare, nullptr, dwCreation, 0, nullptr);
+
+ // @@@ ERROR HANDLING @@@
+ if (!IsValidHandle(hFile))
+ result = oslTranslateFileError(GetLastError());
+
+ *pHandle = osl_createFileHandleFromOSHandle(hFile, uFlags | osl_File_OpenFlag_Read);
+
+ return result;
+}
+
+oslFileError SAL_CALL osl_syncFile(oslFileHandle Handle)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
+ return osl_File_E_INVAL;
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+
+ oslFileError result = pImpl->syncFile();
+ if (result != osl_File_E_None)
+ return result;
+
+ if (!FlushFileBuffers(pImpl->m_hFile))
+ return oslTranslateFileError(GetLastError());
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_closeFile(oslFileHandle Handle)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
+ return osl_File_E_INVAL;
+
+ ::EnterCriticalSection(&(pImpl->m_mutex));
+
+ oslFileError result = pImpl->syncFile();
+ if (result != osl_File_E_None)
+ {
+ /* ignore double failure */
+ (void)::CloseHandle(pImpl->m_hFile);
+ }
+ else if (!::CloseHandle(pImpl->m_hFile))
+ {
+ /* translate error code */
+ result = oslTranslateFileError(GetLastError());
+ }
+
+ ::LeaveCriticalSection(&(pImpl->m_mutex));
+ delete pImpl;
+ return result;
+}
+
+namespace {
+
+// coverity[result_independent_of_operands] - crossplatform requirement
+template<typename T> bool exceedsMaxSIZE_T(T n)
+{ return n > std::numeric_limits< SIZE_T >::max(); }
+
+}
+
+oslFileError SAL_CALL osl_mapFile(
+ oslFileHandle Handle,
+ void** ppAddr,
+ sal_uInt64 uLength,
+ sal_uInt64 uOffset,
+ sal_uInt32 uFlags)
+{
+ struct FileMapping
+ {
+ HANDLE m_handle;
+
+ explicit FileMapping(HANDLE hMap)
+ : m_handle(hMap)
+ {}
+
+ ~FileMapping()
+ {
+ (void)::CloseHandle(m_handle);
+ }
+ };
+
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!ppAddr))
+ return osl_File_E_INVAL;
+ *ppAddr = nullptr;
+
+ if (exceedsMaxSIZE_T(uLength))
+ return osl_File_E_OVERFLOW;
+ SIZE_T const nLength = sal::static_int_cast< SIZE_T >(uLength);
+
+ FileMapping aMap(::CreateFileMappingW(pImpl->m_hFile, nullptr, SEC_COMMIT | PAGE_READONLY, 0, 0, nullptr));
+ if (!IsValidHandle(aMap.m_handle))
+ return oslTranslateFileError(GetLastError());
+
+ DWORD const dwOffsetHi = sal::static_int_cast<DWORD>(uOffset >> 32);
+ DWORD const dwOffsetLo = sal::static_int_cast<DWORD>(uOffset & 0xFFFFFFFF);
+
+ *ppAddr = ::MapViewOfFile(aMap.m_handle, FILE_MAP_READ, dwOffsetHi, dwOffsetLo, nLength);
+ if (!*ppAddr)
+ return oslTranslateFileError(GetLastError());
+
+ if (uFlags & osl_File_MapFlag_RandomAccess)
+ {
+ // Determine memory pagesize.
+ SYSTEM_INFO info;
+ ::GetSystemInfo(&info);
+ DWORD const dwPageSize = info.dwPageSize;
+
+ /*
+ * Pagein, touching first byte of each memory page.
+ * Note: volatile disables optimizing the loop away.
+ */
+ BYTE volatile * pData(static_cast<BYTE*>(*ppAddr));
+ SIZE_T nSize(nLength);
+
+ while (nSize > dwPageSize)
+ {
+ pData[0];
+ pData += dwPageSize;
+ nSize -= dwPageSize;
+ }
+ if (nSize > 0)
+ {
+ pData[0];
+ }
+ }
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_unmapFile(void* pAddr, sal_uInt64 /* uLength */)
+{
+ if (!pAddr)
+ return osl_File_E_INVAL;
+
+ if (!::UnmapViewOfFile(pAddr))
+ return oslTranslateFileError(GetLastError());
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_unmapMappedFile(oslFileHandle /* Handle */, void* pAddr, sal_uInt64 uLength)
+{
+ return osl_unmapFile(pAddr, uLength);
+}
+
+oslFileError
+SAL_CALL osl_readLine(
+ oslFileHandle Handle,
+ sal_Sequence ** ppSequence)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!ppSequence))
+ return osl_File_E_INVAL;
+ sal_uInt64 uBytesRead = 0;
+
+ // read at current filepos; filepos += uBytesRead;
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ oslFileError result = pImpl->readLineAt(
+ pImpl->m_filepos, ppSequence, &uBytesRead);
+ if (result == osl_File_E_None)
+ pImpl->m_filepos += uBytesRead;
+ return result;
+}
+
+oslFileError SAL_CALL osl_readFile(
+ oslFileHandle Handle,
+ void * pBuffer,
+ sal_uInt64 uBytesRequested,
+ sal_uInt64 * pBytesRead)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesRead))
+ return osl_File_E_INVAL;
+
+ // read at current filepos; filepos += *pBytesRead;
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ oslFileError result = pImpl->readFileAt(
+ pImpl->m_filepos, pBuffer, uBytesRequested, pBytesRead);
+ if (result == osl_File_E_None)
+ pImpl->m_filepos += *pBytesRead;
+ return result;
+}
+
+oslFileError SAL_CALL osl_writeFile(
+ oslFileHandle Handle,
+ const void * pBuffer,
+ sal_uInt64 uBytesToWrite,
+ sal_uInt64 * pBytesWritten)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesWritten))
+ return osl_File_E_INVAL;
+
+ // write at current filepos; filepos += *pBytesWritten;
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ oslFileError result = pImpl->writeFileAt(
+ pImpl->m_filepos, pBuffer, uBytesToWrite, pBytesWritten);
+ if (result == osl_File_E_None)
+ pImpl->m_filepos += *pBytesWritten;
+ return result;
+}
+
+LONGLONG const g_limit_longlong = std::numeric_limits< LONGLONG >::max();
+
+namespace {
+
+// coverity[result_independent_of_operands] - crossplatform requirement
+template<typename T> bool exceedsMaxLONGLONG(T n)
+{ return n > g_limit_longlong; }
+
+template<typename T> bool exceedsMinLONGLONG(T n)
+{ return n < std::numeric_limits<LONGLONG>::min(); }
+
+}
+
+oslFileError SAL_CALL osl_readFileAt(
+ oslFileHandle Handle,
+ sal_uInt64 uOffset,
+ void* pBuffer,
+ sal_uInt64 uBytesRequested,
+ sal_uInt64* pBytesRead)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesRead))
+ return osl_File_E_INVAL;
+ if (!(pImpl->m_state & StateBits::Seekable))
+ return osl_File_E_SPIPE;
+
+ if (exceedsMaxLONGLONG(uOffset))
+ return osl_File_E_OVERFLOW;
+ LONGLONG const nOffset = sal::static_int_cast< LONGLONG >(uOffset);
+
+ // read at specified fileptr
+ FileHandle_Impl::Guard lock (&(pImpl->m_mutex));
+ return pImpl->readFileAt(nOffset, pBuffer, uBytesRequested, pBytesRead);
+}
+
+oslFileError SAL_CALL osl_writeFileAt(
+ oslFileHandle Handle,
+ sal_uInt64 uOffset,
+ const void* pBuffer,
+ sal_uInt64 uBytesToWrite,
+ sal_uInt64* pBytesWritten)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pBuffer) || (!pBytesWritten))
+ return osl_File_E_INVAL;
+ if (!(pImpl->m_state & StateBits::Seekable))
+ return osl_File_E_SPIPE;
+
+ if (exceedsMaxLONGLONG(uOffset))
+ return osl_File_E_OVERFLOW;
+ LONGLONG const nOffset = sal::static_int_cast< LONGLONG >(uOffset);
+
+ // write at specified fileptr
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ return pImpl->writeFileAt(nOffset, pBuffer, uBytesToWrite, pBytesWritten);
+}
+
+oslFileError SAL_CALL osl_isEndOfFile(oslFileHandle Handle, sal_Bool *pIsEOF)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pIsEOF))
+ return osl_File_E_INVAL;
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ *pIsEOF = (pImpl->getPos() == pImpl->getSize());
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_getFilePos(oslFileHandle Handle, sal_uInt64 *pPos)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pPos))
+ return osl_File_E_INVAL;
+
+ // no need to lock because pos is atomic
+ *pPos = pImpl->getPos();
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_setFilePos(oslFileHandle Handle, sal_uInt32 uHow, sal_Int64 uOffset)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
+ return osl_File_E_INVAL;
+
+ if (exceedsMaxLONGLONG(uOffset) || exceedsMinLONGLONG(uOffset))
+ return osl_File_E_OVERFLOW;
+ LONGLONG nPos = 0, nOffset = sal::static_int_cast< LONGLONG >(uOffset);
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ switch (uHow)
+ {
+ case osl_Pos_Absolut:
+ if (nOffset < 0)
+ return osl_File_E_INVAL;
+ break;
+
+ case osl_Pos_Current:
+ nPos = sal::static_int_cast< LONGLONG >(pImpl->getPos());
+ if ((nOffset < 0) && (nPos < -1*nOffset))
+ return osl_File_E_INVAL;
+ assert(nPos >= 0);
+ if (nOffset > g_limit_longlong - nPos)
+ return osl_File_E_OVERFLOW;
+ break;
+
+ case osl_Pos_End:
+ nPos = sal::static_int_cast< LONGLONG >(pImpl->getSize());
+ if ((nOffset < 0) && (nPos < -1*nOffset))
+ return osl_File_E_INVAL;
+ assert(nPos >= 0);
+ if (nOffset > g_limit_longlong - nPos)
+ return osl_File_E_OVERFLOW;
+ break;
+
+ default:
+ return osl_File_E_INVAL;
+ }
+
+ return pImpl->setPos(nPos + nOffset);
+}
+
+oslFileError SAL_CALL osl_getFileSize(oslFileHandle Handle, sal_uInt64 *pSize)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile) || (!pSize))
+ return osl_File_E_INVAL;
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ *pSize = pImpl->getSize();
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_setFileSize(oslFileHandle Handle, sal_uInt64 uSize)
+{
+ FileHandle_Impl * pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || !IsValidHandle(pImpl->m_hFile))
+ return osl_File_E_INVAL;
+ if (!(pImpl->m_state & StateBits::Writeable))
+ return osl_File_E_BADF;
+
+ if (exceedsMaxLONGLONG(uSize))
+ return osl_File_E_OVERFLOW;
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ oslFileError result = pImpl->syncFile();
+ if (result != osl_File_E_None)
+ return result;
+ pImpl->m_bufptr = -1;
+ pImpl->m_buflen = 0;
+
+ return pImpl->setSize(uSize);
+}
+
+oslFileError SAL_CALL osl_removeFile(rtl_uString* strPath)
+{
+ OUString strSysPath;
+ oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
+
+ if (error == osl_File_E_None)
+ {
+ if (DeleteFileW(o3tl::toW(strSysPath.getStr())))
+ error = osl_File_E_None;
+ else
+ error = oslTranslateFileError(GetLastError());
+ }
+ return error;
+}
+
+oslFileError SAL_CALL osl_copyFile(rtl_uString* strPath, rtl_uString *strDestPath)
+{
+ OUString strSysPath, strSysDestPath;
+ oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
+
+ if (error == osl_File_E_None)
+ error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDestPath), &strSysDestPath.pData, false);
+
+ if (error == osl_File_E_None)
+ {
+ LPCWSTR src = o3tl::toW(strSysPath.getStr());
+ LPCWSTR dst = o3tl::toW(strSysDestPath.getStr());
+
+ if (CopyFileW(src, dst, FALSE))
+ error = osl_File_E_None;
+ else
+ error = oslTranslateFileError(GetLastError());
+ }
+
+ return error;
+}
+
+oslFileError SAL_CALL osl_moveFile(rtl_uString* strPath, rtl_uString *strDestPath)
+{
+ OUString strSysPath, strSysDestPath;
+ oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
+
+ if (error == osl_File_E_None)
+ error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDestPath), &strSysDestPath.pData, false);
+
+ if (error == osl_File_E_None)
+ {
+ LPCWSTR src = o3tl::toW(strSysPath.getStr());
+ LPCWSTR dst = o3tl::toW(strSysDestPath.getStr());
+
+ if (MoveFileExW(src, dst, MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING))
+ error = osl_File_E_None;
+ else
+ error = oslTranslateFileError(GetLastError());
+ }
+
+ return error;
+}
+
+oslFileError SAL_CALL osl_replaceFile(rtl_uString* strPath, rtl_uString* strDestPath)
+{
+ OUString strSysPath, strSysDestPath;
+ oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
+
+ if (error == osl_File_E_None)
+ error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDestPath), &strSysDestPath.pData, false);
+
+ if (error == osl_File_E_None)
+ {
+ LPCWSTR src = o3tl::toW(strSysPath.getStr());
+ LPCWSTR dst = o3tl::toW(strSysDestPath.getStr());
+
+ if (!ReplaceFileW(dst, src, nullptr,
+ REPLACEFILE_WRITE_THROUGH | REPLACEFILE_IGNORE_MERGE_ERRORS
+ | REPLACEFILE_IGNORE_ACL_ERRORS,
+ nullptr, nullptr))
+ {
+ DWORD dwError = GetLastError();
+ if (dwError == ERROR_FILE_NOT_FOUND // no strDestPath file?
+ || dwError == ERROR_UNABLE_TO_MOVE_REPLACEMENT // e.g., files on different volumes
+ || dwError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2
+ || dwError == ERROR_UNABLE_TO_REMOVE_REPLACED)
+ error = osl_moveFile(strPath, strDestPath);
+ else
+ error = oslTranslateFileError(dwError);
+ }
+ }
+
+ return error;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/file_dirvol.cxx b/sal/osl/w32/file_dirvol.cxx
new file mode 100644
index 000000000..f9d821605
--- /dev/null
+++ b/sal/osl/w32/file_dirvol.cxx
@@ -0,0 +1,1691 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <systools/win32/uwinapi.h>
+
+#include "file_url.hxx"
+#include "filetime.hxx"
+#include "file_error.hxx"
+
+#include "path_helper.hxx"
+
+#include <rtl/alloc.h>
+#include <rtl/ustring.hxx>
+#include <rtl/character.hxx>
+#include <sal/log.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+const wchar_t UNC_PREFIX[] = L"\\\\";
+const wchar_t BACKSLASH = '\\';
+const wchar_t SLASH = '/';
+
+BOOL TimeValueToFileTime(const TimeValue *cpTimeVal, FILETIME *pFTime)
+{
+ SYSTEMTIME BaseSysTime;
+ FILETIME BaseFileTime;
+ FILETIME FTime;
+ bool fSuccess = false;
+
+ BaseSysTime.wYear = 1970;
+ BaseSysTime.wMonth = 1;
+ BaseSysTime.wDayOfWeek = 0;
+ BaseSysTime.wDay = 1;
+ BaseSysTime.wHour = 0;
+ BaseSysTime.wMinute = 0;
+ BaseSysTime.wSecond = 0;
+ BaseSysTime.wMilliseconds = 0;
+
+ if (cpTimeVal==nullptr)
+ return fSuccess;
+
+ if ( SystemTimeToFileTime(&BaseSysTime, &BaseFileTime) )
+ {
+ __int64 timeValue;
+
+ __int64 localTime = cpTimeVal->Seconds*__int64(10000000)+cpTimeVal->Nanosec/100;
+ osl::detail::setFiletime(FTime, localTime);
+ fSuccess = 0 <= (timeValue= osl::detail::getFiletime(BaseFileTime) + osl::detail::getFiletime(FTime));
+ if (fSuccess)
+ osl::detail::setFiletime(*pFTime, timeValue);
+ }
+ return fSuccess;
+}
+
+BOOL FileTimeToTimeValue(const FILETIME *cpFTime, TimeValue *pTimeVal)
+{
+ SYSTEMTIME BaseSysTime;
+ FILETIME BaseFileTime;
+ bool fSuccess = false; /* Assume failure */
+
+ BaseSysTime.wYear = 1970;
+ BaseSysTime.wMonth = 1;
+ BaseSysTime.wDayOfWeek = 0;
+ BaseSysTime.wDay = 1;
+ BaseSysTime.wHour = 0;
+ BaseSysTime.wMinute = 0;
+ BaseSysTime.wSecond = 0;
+ BaseSysTime.wMilliseconds = 0;
+
+ if ( SystemTimeToFileTime(&BaseSysTime, &BaseFileTime) )
+ {
+ __int64 Value;
+
+ fSuccess = 0 <= (Value = osl::detail::getFiletime(*cpFTime) - osl::detail::getFiletime(BaseFileTime));
+
+ if ( fSuccess )
+ {
+ pTimeVal->Seconds = static_cast<unsigned long>(Value / 10000000L);
+ pTimeVal->Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100);
+ }
+ }
+ return fSuccess;
+}
+
+namespace
+{
+
+ struct Component
+ {
+ Component() :
+ begin_(nullptr), end_(nullptr)
+ {}
+
+ bool isPresent() const
+ { return (static_cast<sal_IntPtr>(end_ - begin_) > 0); }
+
+ const sal_Unicode* begin_;
+ const sal_Unicode* end_;
+ };
+
+ struct UNCComponents
+ {
+ Component server_;
+ Component share_;
+ Component resource_;
+ };
+
+ bool is_UNC_path(const sal_Unicode* path)
+ { return (0 == wcsncmp(UNC_PREFIX, o3tl::toW(path), SAL_N_ELEMENTS(UNC_PREFIX) - 1)); }
+
+ void parse_UNC_path(const sal_Unicode* path, UNCComponents* puncc)
+ {
+ OSL_PRECOND(is_UNC_path(path), "Precondition violated: No UNC path");
+ OSL_PRECOND(rtl_ustr_indexOfChar(path, SLASH) == -1, "Path must not contain slashes");
+
+ const sal_Unicode* pend = path + rtl_ustr_getLength(path);
+ const sal_Unicode* ppos = path + 2;
+
+ puncc->server_.begin_ = ppos;
+ while ((ppos < pend) && (*ppos != BACKSLASH))
+ ppos++;
+
+ puncc->server_.end_ = ppos;
+
+ if (BACKSLASH == *ppos)
+ {
+ puncc->share_.begin_ = ++ppos;
+ while ((ppos < pend) && (*ppos != BACKSLASH))
+ ppos++;
+
+ puncc->share_.end_ = ppos;
+
+ if (BACKSLASH == *ppos)
+ {
+ puncc->resource_.begin_ = ++ppos;
+ while (ppos < pend)
+ ppos++;
+
+ puncc->resource_.end_ = ppos;
+ }
+ }
+
+ SAL_WARN_IF(!puncc->server_.isPresent() || !puncc->share_.isPresent(),
+ "sal.osl",
+ "Postcondition violated: Invalid UNC path detected");
+ }
+
+ bool has_path_parent(const sal_Unicode* path)
+ {
+ // Has the given path a parent or are we already there,
+ // e.g. 'c:\' or '\\server\share\'?
+
+ bool has_parent = false;
+ if (is_UNC_path(path))
+ {
+ UNCComponents unc_comp;
+ parse_UNC_path(path, &unc_comp);
+ has_parent = unc_comp.resource_.isPresent();
+ }
+ else
+ {
+ has_parent = !osl::systemPathIsLogicalDrivePattern(OUString(path));
+ }
+ return has_parent;
+ }
+
+ bool has_path_parent(const OUString& path)
+ { return has_path_parent(path.getStr()); }
+
+}
+
+oslFileError SAL_CALL osl_acquireVolumeDeviceHandle( oslVolumeDeviceHandle Handle )
+{
+ if ( Handle )
+ {
+ rtl_uString_acquire( static_cast<rtl_uString *>(Handle) );
+ return osl_File_E_None;
+ }
+ else
+ return osl_File_E_INVAL;
+}
+
+oslFileError SAL_CALL osl_releaseVolumeDeviceHandle( oslVolumeDeviceHandle Handle )
+{
+ if ( Handle )
+ {
+ rtl_uString_release( static_cast<rtl_uString *>(Handle) );
+ return osl_File_E_None;
+ }
+ else
+ return osl_File_E_INVAL;
+}
+
+oslFileError SAL_CALL osl_getVolumeDeviceMountPath( oslVolumeDeviceHandle Handle, rtl_uString **pstrPath )
+{
+ if ( Handle && pstrPath )
+ {
+ rtl_uString_assign( pstrPath, static_cast<rtl_uString *>(Handle) );
+ return osl_File_E_None;
+ }
+ else
+ return osl_File_E_INVAL;
+}
+
+#define DIRECTORYITEM_DRIVE 0
+#define DIRECTORYITEM_FILE 1
+#define DIRECTORYITEM_SERVER 2
+
+namespace {
+
+struct DirectoryItem_Impl
+{
+ UINT uType = 0;
+ union {
+ WIN32_FIND_DATAW FindData;
+ WCHAR cDriveString[MAX_PATH];
+ };
+ OUString m_sFullPath;
+ bool bFullPathNormalized = false;
+ int nRefCount = 0;
+};
+
+}
+
+#define DIRECTORYTYPE_LOCALROOT 0
+#define DIRECTORYTYPE_NETROOT 1
+#define DIRECTORYTYPE_FILESYSTEM 3
+
+namespace {
+
+struct Directory_Impl
+{
+ UINT uType = 0;
+ union {
+ HANDLE hDirectory = nullptr;
+ HANDLE hEnumDrives;
+ };
+ OUString m_sDirectoryPath;
+};
+
+typedef struct tagDRIVEENUM
+{
+ LPCWSTR lpIdent;
+ WCHAR cBuffer[/*('Z' - 'A' + 1) * sizeof("A:\\") + 1*/256];
+ LPCWSTR lpCurrent;
+} DRIVEENUM, *PDRIVEENUM, *LPDRIVEENUM;
+
+}
+
+static HANDLE WINAPI OpenLogicalDrivesEnum()
+{
+ LPDRIVEENUM pEnum = static_cast<LPDRIVEENUM>(HeapAlloc( GetProcessHeap(), 0, sizeof(DRIVEENUM) ));
+ if ( pEnum )
+ {
+ DWORD dwNumCopied = GetLogicalDriveStringsW( SAL_N_ELEMENTS(pEnum->cBuffer) - 1, pEnum->cBuffer );
+
+ if ( dwNumCopied && dwNumCopied < SAL_N_ELEMENTS(pEnum->cBuffer) )
+ {
+ pEnum->lpCurrent = pEnum->cBuffer;
+ pEnum->lpIdent = L"tagDRIVEENUM";
+ }
+ else
+ {
+ HeapFree( GetProcessHeap(), 0, pEnum );
+ pEnum = nullptr;
+ }
+ }
+ return pEnum ? static_cast<HANDLE>(pEnum) : INVALID_HANDLE_VALUE;
+}
+
+static bool WINAPI EnumLogicalDrives(HANDLE hEnum, LPWSTR lpBuffer)
+{
+ LPDRIVEENUM pEnum = static_cast<LPDRIVEENUM>(hEnum);
+ if ( !pEnum )
+ {
+ SetLastError( ERROR_INVALID_HANDLE );
+ return false;
+ }
+
+ int nLen = wcslen( pEnum->lpCurrent );
+ if ( !nLen )
+ {
+ SetLastError( ERROR_NO_MORE_FILES );
+ return false;
+ }
+
+ CopyMemory( lpBuffer, pEnum->lpCurrent, (nLen + 1) * sizeof(WCHAR) );
+ pEnum->lpCurrent += nLen + 1;
+ return true;
+}
+
+static bool WINAPI CloseLogicalDrivesEnum(HANDLE hEnum)
+{
+ bool fSuccess = false;
+ LPDRIVEENUM pEnum = static_cast<LPDRIVEENUM>(hEnum);
+
+ if ( pEnum )
+ {
+ HeapFree( GetProcessHeap(), 0, pEnum );
+ fSuccess = true;
+ }
+ else
+ SetLastError( ERROR_INVALID_HANDLE );
+
+ return fSuccess;
+}
+
+namespace {
+
+typedef struct tagDIRECTORY
+{
+ HANDLE hFind;
+ WIN32_FIND_DATAW aFirstData;
+} DIRECTORY, *PDIRECTORY, *LPDIRECTORY;
+
+}
+
+static HANDLE WINAPI OpenDirectory( rtl_uString* pPath)
+{
+ if ( !pPath )
+ return nullptr;
+
+ sal_uInt32 nLen = rtl_uString_getLength( pPath );
+ if ( !nLen )
+ return nullptr;
+
+ const WCHAR* pSuffix = nullptr;
+ sal_uInt32 nSuffLen = 0;
+ if ( pPath->buffer[nLen - 1] != L'\\' )
+ {
+ pSuffix = L"\\*.*";
+ nSuffLen = 4;
+ }
+ else
+ {
+ pSuffix = L"*.*";
+ nSuffLen = 3;
+ }
+
+ WCHAR* szFileMask = static_cast< WCHAR* >( malloc( sizeof( WCHAR ) * ( nLen + nSuffLen + 1 ) ) );
+ assert(szFileMask); // Don't handle OOM conditions
+ wcscpy( szFileMask, o3tl::toW(rtl_uString_getStr( pPath )) );
+ wcscat( szFileMask, pSuffix );
+
+ LPDIRECTORY pDirectory = static_cast<LPDIRECTORY>(HeapAlloc(GetProcessHeap(), 0, sizeof(DIRECTORY)));
+ assert(pDirectory); // Don't handle OOM conditions
+ pDirectory->hFind = FindFirstFileW(szFileMask, &pDirectory->aFirstData);
+
+ if (!IsValidHandle(pDirectory->hFind))
+ {
+ if ( GetLastError() != ERROR_NO_MORE_FILES )
+ {
+ HeapFree(GetProcessHeap(), 0, pDirectory);
+ pDirectory = nullptr;
+ }
+ }
+ free(szFileMask);
+
+ return static_cast<HANDLE>(pDirectory);
+}
+
+static bool WINAPI EnumDirectory(HANDLE hDirectory, LPWIN32_FIND_DATAW pFindData)
+{
+ LPDIRECTORY pDirectory = static_cast<LPDIRECTORY>(hDirectory);
+ if ( !pDirectory )
+ {
+ SetLastError( ERROR_INVALID_HANDLE );
+ return false;
+ }
+
+ bool fSuccess = false;
+ bool fValid;
+ do
+ {
+ if ( pDirectory->aFirstData.cFileName[0] )
+ {
+ *pFindData = pDirectory->aFirstData;
+ fSuccess = true;
+ pDirectory->aFirstData.cFileName[0] = 0;
+ }
+ else if ( IsValidHandle( pDirectory->hFind ) )
+ fSuccess = FindNextFileW( pDirectory->hFind, pFindData );
+ else
+ {
+ fSuccess = false;
+ SetLastError( ERROR_NO_MORE_FILES );
+ }
+
+ fValid = fSuccess && wcscmp( L".", pFindData->cFileName ) != 0 && wcscmp( L"..", pFindData->cFileName ) != 0;
+
+ } while( fSuccess && !fValid );
+
+ return fSuccess;
+}
+
+static bool WINAPI CloseDirectory(HANDLE hDirectory)
+{
+ bool fSuccess = false;
+ LPDIRECTORY pDirectory = static_cast<LPDIRECTORY>(hDirectory);
+
+ if (pDirectory)
+ {
+ if (IsValidHandle(pDirectory->hFind))
+ fSuccess = FindClose(pDirectory->hFind);
+
+ fSuccess = HeapFree(GetProcessHeap(), 0, pDirectory) && fSuccess;
+ }
+ else
+ SetLastError(ERROR_INVALID_HANDLE);
+
+ return fSuccess;
+}
+
+static oslFileError osl_openLocalRoot(
+ rtl_uString *strDirectoryPath, oslDirectory *pDirectory)
+{
+ if ( !pDirectory )
+ return osl_File_E_INVAL;
+
+ *pDirectory = nullptr;
+
+ OUString strSysPath;
+ oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDirectoryPath), &strSysPath.pData, false);
+ if ( osl_File_E_None != error )
+ return error;
+
+ Directory_Impl* pDirImpl = new (std::nothrow) Directory_Impl;
+ assert(pDirImpl); // Don't handle OOM conditions
+ pDirImpl->m_sDirectoryPath = strSysPath;
+
+ /* Append backslash if necessary */
+
+ /* @@@ToDo
+ use function ensure backslash
+ */
+ sal_uInt32 nLen = pDirImpl->m_sDirectoryPath.getLength();
+ if ( nLen && pDirImpl->m_sDirectoryPath[nLen - 1] != L'\\' )
+ {
+ pDirImpl->m_sDirectoryPath += "\\";
+ }
+
+ pDirImpl->uType = DIRECTORYTYPE_LOCALROOT;
+ pDirImpl->hEnumDrives = OpenLogicalDrivesEnum();
+
+ /* @@@ToDo
+ Use IsValidHandle(...)
+ */
+ if ( pDirImpl->hEnumDrives != INVALID_HANDLE_VALUE )
+ {
+ *pDirectory = static_cast<oslDirectory>(pDirImpl);
+ error = osl_File_E_None;
+ }
+ else
+ {
+ if ( pDirImpl )
+ {
+ delete pDirImpl;
+ pDirImpl = nullptr;
+ }
+
+ error = oslTranslateFileError( GetLastError() );
+ }
+ return error;
+}
+
+static oslFileError osl_openFileDirectory(
+ rtl_uString *strDirectoryPath, oslDirectory *pDirectory)
+{
+ oslFileError error = osl_File_E_None;
+
+ if ( !pDirectory )
+ return osl_File_E_INVAL;
+ *pDirectory = nullptr;
+
+ Directory_Impl *pDirImpl = new (std::nothrow) Directory_Impl;
+ assert(pDirImpl); // Don't handle OOM conditions
+ pDirImpl->m_sDirectoryPath = strDirectoryPath;
+
+ /* Append backslash if necessary */
+
+ /* @@@ToDo
+ use function ensure backslash
+ */
+ sal_uInt32 nLen = pDirImpl->m_sDirectoryPath.getLength();
+ if ( nLen && pDirImpl->m_sDirectoryPath[nLen - 1] != '\\' )
+ pDirImpl->m_sDirectoryPath += "\\";
+
+ pDirImpl->uType = DIRECTORYTYPE_FILESYSTEM;
+ pDirImpl->hDirectory = OpenDirectory( pDirImpl->m_sDirectoryPath.pData );
+
+ if ( !pDirImpl->hDirectory )
+ {
+ error = oslTranslateFileError( GetLastError() );
+
+ delete pDirImpl;
+ pDirImpl = nullptr;
+ }
+
+ *pDirectory = static_cast<oslDirectory>(pDirImpl);
+ return error;
+}
+
+static oslFileError osl_openNetworkServer(
+ rtl_uString *strSysDirPath, oslDirectory *pDirectory)
+{
+ NETRESOURCEW aNetResource;
+ HANDLE hEnum;
+ DWORD dwError;
+
+ ZeroMemory( &aNetResource, sizeof(aNetResource) );
+
+ aNetResource.lpRemoteName = o3tl::toW(strSysDirPath->buffer);
+
+ dwError = WNetOpenEnumW(
+ RESOURCE_GLOBALNET,
+ RESOURCETYPE_DISK,
+ RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER,
+ &aNetResource,
+ &hEnum );
+
+ if ( ERROR_SUCCESS == dwError )
+ {
+ Directory_Impl *pDirImpl = new (std::nothrow) Directory_Impl;
+ assert(pDirImpl); // Don't handle OOM conditions
+ pDirImpl->uType = DIRECTORYTYPE_NETROOT;
+ pDirImpl->hDirectory = hEnum;
+ *pDirectory = static_cast<oslDirectory>(pDirImpl);
+ }
+ return oslTranslateFileError( dwError );
+}
+
+static DWORD create_dir_with_callback(
+ rtl_uString * dir_path,
+ oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
+ void* pData)
+{
+ // Create the specified directory and call the
+ // user specified callback function. On success
+ // the function returns ERROR_SUCCESS else a Win32 error code.
+
+ bool bCreated = CreateDirectoryW( o3tl::toW(rtl_uString_getStr( dir_path )), nullptr );
+
+ if ( bCreated )
+ {
+ if (aDirectoryCreationCallbackFunc)
+ {
+ OUString url;
+ osl_getFileURLFromSystemPath(dir_path, &(url.pData));
+ aDirectoryCreationCallbackFunc(pData, url.pData);
+ }
+ return ERROR_SUCCESS;
+ }
+ return GetLastError();
+}
+
+static int path_make_parent(sal_Unicode* path)
+{
+ /* Cut off the last part of the given path to
+ get the parent only, e.g. 'c:\dir\subdir' ->
+ 'c:\dir' or '\\share\sub\dir' -> '\\share\sub'
+ @return The position where the path has been cut
+ off (this is the position of the last backslash).
+ If there are no more parents 0 will be returned,
+ e.g. 'c:\' or '\\Share' have no more parents */
+
+ OSL_PRECOND(rtl_ustr_indexOfChar(path, SLASH) == -1, "Path must not contain slashes");
+ OSL_PRECOND(has_path_parent(path), "Path must have a parent");
+
+ sal_Unicode* pos_last_backslash = path + rtl_ustr_lastIndexOfChar(path, BACKSLASH);
+ *pos_last_backslash = 0;
+ return (pos_last_backslash - path);
+}
+
+static DWORD create_dir_recursively_(
+ rtl_uString * dir_path,
+ oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
+ void* pData)
+{
+ OSL_PRECOND(
+ rtl_ustr_lastIndexOfChar_WithLength(dir_path->buffer, dir_path->length, BACKSLASH) != dir_path->length,
+ "Path must not end with a backslash");
+
+ DWORD w32_error = create_dir_with_callback(
+ dir_path, aDirectoryCreationCallbackFunc, pData);
+ if (w32_error == ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ if ((w32_error != ERROR_PATH_NOT_FOUND) || !has_path_parent(dir_path->buffer))
+ return w32_error;
+
+ int pos = path_make_parent(dir_path->buffer); // dir_path->buffer[pos] = 0, restore below
+
+ w32_error = create_dir_recursively_(
+ dir_path, aDirectoryCreationCallbackFunc, pData);
+
+ dir_path->buffer[pos] = BACKSLASH; // restore
+
+ if (ERROR_SUCCESS != w32_error && ERROR_ALREADY_EXISTS != w32_error)
+ return w32_error;
+
+ return create_dir_recursively_(dir_path, aDirectoryCreationCallbackFunc, pData);
+}
+
+oslFileError SAL_CALL osl_createDirectoryPath(
+ rtl_uString* aDirectoryUrl,
+ oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
+ void* pData)
+{
+ if (aDirectoryUrl == nullptr)
+ return osl_File_E_INVAL;
+
+ OUString sys_path;
+ oslFileError osl_error =
+ osl_getSystemPathFromFileURL_(OUString::unacquired(&aDirectoryUrl), &sys_path.pData, false);
+
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+
+ osl::systemPathRemoveSeparator(sys_path);
+
+ // const_cast because sys_path is a local copy
+ // which we want to modify inplace instead of
+ // copy it into another buffer on the heap again
+ return oslTranslateFileError(create_dir_recursively_(
+ sys_path.pData, aDirectoryCreationCallbackFunc, pData));
+}
+
+oslFileError SAL_CALL osl_createDirectory(rtl_uString* strPath)
+{
+ return osl_createDirectoryWithFlags(
+ strPath, osl_File_OpenFlag_Read | osl_File_OpenFlag_Write);
+}
+
+oslFileError osl_createDirectoryWithFlags(rtl_uString * strPath, sal_uInt32)
+{
+ OUString strSysPath;
+ oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
+
+ if ( osl_File_E_None != error )
+ return error;
+
+ bool bCreated = CreateDirectoryW(o3tl::toW(strSysPath.getStr()), nullptr);
+ if ( !bCreated )
+ {
+ /*@@@ToDo
+ The following case is a hack because the ucb or the webtop had some
+ problems with the error code that CreateDirectory returns in
+ case the path is only a logical drive, should be removed!
+ */
+
+ if ((strSysPath.getLength() == 2 || (strSysPath.getLength() == 3 && strSysPath[2] == '\\'))
+ && rtl::isAsciiAlpha(strSysPath[0]) && strSysPath[1] == ':')
+ SetLastError( ERROR_ALREADY_EXISTS );
+
+ error = oslTranslateFileError( GetLastError() );
+ }
+
+ return error;
+}
+
+oslFileError SAL_CALL osl_removeDirectory(rtl_uString* strPath)
+{
+ OUString strSysPath;
+ oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strPath), &strSysPath.pData, false);
+
+ if ( osl_File_E_None == error )
+ {
+ if (RemoveDirectoryW(o3tl::toW(strSysPath.getStr())))
+ error = osl_File_E_None;
+ else
+ error = oslTranslateFileError( GetLastError() );
+ }
+ return error;
+}
+
+oslFileError SAL_CALL osl_openDirectory(rtl_uString *strDirectoryPath, oslDirectory *pDirectory)
+{
+ oslFileError error;
+
+ if ( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase( strDirectoryPath->buffer, "file:///" ) )
+ error = osl_openLocalRoot( strDirectoryPath, pDirectory );
+ else
+ {
+ OUString strSysDirectoryPath;
+ DWORD dwPathType;
+
+ error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strDirectoryPath), &strSysDirectoryPath.pData, false);
+
+ if ( osl_File_E_None != error )
+ return error;
+
+ dwPathType = IsValidFilePath(strSysDirectoryPath, VALIDATEPATH_NORMAL, nullptr);
+
+ if ( dwPathType & PATHTYPE_IS_SERVER )
+ error = osl_openNetworkServer(strSysDirectoryPath.pData, pDirectory);
+ else
+ error = osl_openFileDirectory(strSysDirectoryPath.pData, pDirectory);
+ }
+ return error;
+}
+
+static oslFileError osl_getNextNetResource(
+ oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 /*uHint*/ )
+{
+ Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory);
+ DirectoryItem_Impl *pItemImpl = nullptr;
+ BYTE buffer[16384];
+ LPNETRESOURCEW lpNetResource = reinterpret_cast<LPNETRESOURCEW>(buffer);
+ DWORD dwError, dwCount, dwBufSize;
+
+ if ( !pItem )
+ return osl_File_E_INVAL;
+ *pItem = nullptr;
+
+ if ( !pDirImpl )
+ return osl_File_E_INVAL;
+
+ dwCount = 1;
+ dwBufSize = sizeof(buffer);
+ dwError = WNetEnumResourceW( pDirImpl->hDirectory, &dwCount, lpNetResource, &dwBufSize );
+
+ switch ( dwError )
+ {
+ case NO_ERROR:
+ case ERROR_MORE_DATA:
+ {
+ pItemImpl = new (std::nothrow) DirectoryItem_Impl;
+ if ( !pItemImpl )
+ return osl_File_E_NOMEM;
+
+ pItemImpl->uType = DIRECTORYITEM_DRIVE;
+ osl_acquireDirectoryItem( static_cast<oslDirectoryItem>(pItemImpl) );
+
+ wcscpy( pItemImpl->cDriveString, lpNetResource->lpRemoteName );
+
+ *pItem = pItemImpl;
+ }
+ return osl_File_E_None;
+ case ERROR_NO_MORE_ITEMS:
+ return osl_File_E_NOENT;
+ default:
+ return oslTranslateFileError( dwError );
+ }
+}
+
+static oslFileError osl_getNextDrive(
+ oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 /*uHint*/ )
+{
+ Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory);
+ DirectoryItem_Impl *pItemImpl = nullptr;
+ bool fSuccess;
+
+ if ( !pItem )
+ return osl_File_E_INVAL;
+ *pItem = nullptr;
+
+ if ( !pDirImpl )
+ return osl_File_E_INVAL;
+
+ pItemImpl = new (std::nothrow) DirectoryItem_Impl;
+ if ( !pItemImpl )
+ return osl_File_E_NOMEM;
+
+ pItemImpl->uType = DIRECTORYITEM_DRIVE;
+ osl_acquireDirectoryItem( static_cast<oslDirectoryItem>(pItemImpl) );
+ fSuccess = EnumLogicalDrives( pDirImpl->hEnumDrives, pItemImpl->cDriveString );
+
+ if ( fSuccess )
+ {
+ *pItem = pItemImpl;
+ return osl_File_E_None;
+ }
+ else
+ {
+ delete pItemImpl;
+ return oslTranslateFileError( GetLastError() );
+ }
+}
+
+static oslFileError osl_getNextFileItem(
+ oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 /*uHint*/)
+{
+ Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory);
+ DirectoryItem_Impl *pItemImpl = nullptr;
+ bool fFound;
+
+ if ( !pItem )
+ return osl_File_E_INVAL;
+ *pItem = nullptr;
+
+ if ( !pDirImpl )
+ return osl_File_E_INVAL;
+
+ pItemImpl = new (std::nothrow) DirectoryItem_Impl;
+ if ( !pItemImpl )
+ return osl_File_E_NOMEM;
+
+ fFound = EnumDirectory( pDirImpl->hDirectory, &pItemImpl->FindData );
+ if ( !fFound )
+ {
+ delete pItemImpl;
+ return oslTranslateFileError( GetLastError() );
+ }
+
+ pItemImpl->uType = DIRECTORYITEM_FILE;
+ pItemImpl->nRefCount = 1;
+
+ pItemImpl->m_sFullPath = pDirImpl->m_sDirectoryPath + o3tl::toU(pItemImpl->FindData.cFileName);
+
+ pItemImpl->bFullPathNormalized = true;
+ *pItem = static_cast<oslDirectoryItem>(pItemImpl);
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_getNextDirectoryItem(
+ oslDirectory Directory, oslDirectoryItem *pItem, sal_uInt32 uHint)
+{
+ Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory);
+
+ /* Assume failure */
+
+ if ( !pItem )
+ return osl_File_E_INVAL;
+ *pItem = nullptr;
+
+ if ( !pDirImpl )
+ return osl_File_E_INVAL;
+
+ switch ( pDirImpl->uType )
+ {
+ case DIRECTORYTYPE_LOCALROOT:
+ return osl_getNextDrive( Directory, pItem, uHint );
+ case DIRECTORYTYPE_NETROOT:
+ return osl_getNextNetResource( Directory, pItem, uHint );
+ case DIRECTORYTYPE_FILESYSTEM:
+ return osl_getNextFileItem( Directory, pItem, uHint );
+ default:
+ return osl_File_E_INVAL;
+ }
+}
+
+oslFileError SAL_CALL osl_closeDirectory(oslDirectory Directory)
+{
+ Directory_Impl *pDirImpl = static_cast<Directory_Impl *>(Directory);
+ oslFileError eError = osl_File_E_INVAL;
+
+ if ( pDirImpl )
+ {
+ switch ( pDirImpl->uType )
+ {
+ case DIRECTORYTYPE_FILESYSTEM:
+ eError = CloseDirectory( pDirImpl->hDirectory ) ? osl_File_E_None : oslTranslateFileError( GetLastError() );
+ break;
+ case DIRECTORYTYPE_LOCALROOT:
+ eError = CloseLogicalDrivesEnum( pDirImpl->hEnumDrives ) ? osl_File_E_None : oslTranslateFileError( GetLastError() );
+ break;
+ case DIRECTORYTYPE_NETROOT:
+ {
+ DWORD err = WNetCloseEnum(pDirImpl->hDirectory);
+ eError = (err == NO_ERROR) ? osl_File_E_None : oslTranslateFileError(err);
+ }
+ break;
+ default:
+ OSL_FAIL( "Invalid directory type" );
+ break;
+ }
+
+ delete pDirImpl;
+ }
+ return eError;
+}
+
+namespace {
+
+/* Different types of paths */
+enum PATHTYPE
+{
+ PATHTYPE_SYNTAXERROR = 0,
+ PATHTYPE_NETROOT,
+ PATHTYPE_NETSERVER,
+ PATHTYPE_VOLUME,
+ PATHTYPE_FILE
+};
+
+}
+
+oslFileError SAL_CALL osl_getDirectoryItem(rtl_uString *strFilePath, oslDirectoryItem *pItem)
+{
+ oslFileError error = osl_File_E_None;
+ OUString strSysFilePath;
+ PATHTYPE type = PATHTYPE_FILE;
+ DWORD dwPathType;
+
+ /* Assume failure */
+
+ if ( !pItem )
+ return osl_File_E_INVAL;
+
+ *pItem = nullptr;
+
+ error = osl_getSystemPathFromFileURL_(OUString::unacquired(&strFilePath), &strSysFilePath.pData, false);
+
+ if ( osl_File_E_None != error )
+ return error;
+
+ dwPathType = IsValidFilePath( strSysFilePath, VALIDATEPATH_NORMAL, nullptr );
+
+ if ( dwPathType & PATHTYPE_IS_VOLUME )
+ type = PATHTYPE_VOLUME;
+ else if ( dwPathType & PATHTYPE_IS_SERVER )
+ type = PATHTYPE_NETSERVER;
+ else
+ type = PATHTYPE_FILE;
+
+ switch ( type )
+ {
+ case PATHTYPE_NETSERVER:
+ {
+ DirectoryItem_Impl* pItemImpl = new (std::nothrow) DirectoryItem_Impl;
+
+ if ( !pItemImpl )
+ error = osl_File_E_NOMEM;
+
+ if ( osl_File_E_None == error )
+ {
+ pItemImpl->uType = DIRECTORYITEM_SERVER;
+
+ osl_acquireDirectoryItem( static_cast<oslDirectoryItem>(pItemImpl) );
+ pItemImpl->m_sFullPath = strSysFilePath;
+
+ // Assign a title anyway
+ {
+ int iSrc = 2;
+ int iDst = 0;
+
+ while( iSrc < strSysFilePath.getLength() && strSysFilePath[iSrc] && strSysFilePath[iSrc] != '\\')
+ {
+ pItemImpl->FindData.cFileName[iDst++] = strSysFilePath[iSrc++];
+ }
+ }
+
+ *pItem = pItemImpl;
+ }
+ }
+ break;
+ case PATHTYPE_VOLUME:
+ {
+ DirectoryItem_Impl* pItemImpl = new (std::nothrow) DirectoryItem_Impl;
+
+ if ( !pItemImpl )
+ error = osl_File_E_NOMEM;
+
+ if ( osl_File_E_None == error )
+ {
+ pItemImpl->uType = DIRECTORYITEM_DRIVE;
+
+ osl_acquireDirectoryItem( static_cast<oslDirectoryItem>(pItemImpl) );
+
+ wcscpy( pItemImpl->cDriveString, o3tl::toW(strSysFilePath.getStr()) );
+ pItemImpl->cDriveString[0] = rtl::toAsciiUpperCase( pItemImpl->cDriveString[0] );
+
+ if ( pItemImpl->cDriveString[wcslen(pItemImpl->cDriveString) - 1] != '\\' )
+ wcscat( pItemImpl->cDriveString, L"\\" );
+
+ *pItem = pItemImpl;
+ }
+ }
+ break;
+ case PATHTYPE_SYNTAXERROR:
+ case PATHTYPE_NETROOT:
+ case PATHTYPE_FILE:
+ {
+ HANDLE hFind;
+ WIN32_FIND_DATAW aFindData;
+
+ if (!strSysFilePath.isEmpty() && strSysFilePath[strSysFilePath.getLength() - 1] == '\\')
+ strSysFilePath = strSysFilePath.copy(0, strSysFilePath.getLength() - 1);
+
+ hFind = FindFirstFileW( o3tl::toW(strSysFilePath.getStr()), &aFindData );
+
+ if ( hFind != INVALID_HANDLE_VALUE )
+ {
+ DirectoryItem_Impl *pItemImpl = new (std::nothrow) DirectoryItem_Impl;
+ if (!pItemImpl)
+ error = osl_File_E_NOMEM;
+
+ if (osl_File_E_None == error)
+ {
+ osl_acquireDirectoryItem(static_cast<oslDirectoryItem>(pItemImpl));
+
+ CopyMemory(&pItemImpl->FindData, &aFindData, sizeof(WIN32_FIND_DATAW));
+ pItemImpl->m_sFullPath = strSysFilePath;
+
+ pItemImpl->uType = DIRECTORYITEM_FILE;
+ *pItem = pItemImpl;
+ }
+
+ FindClose( hFind );
+ }
+ else
+ error = oslTranslateFileError( GetLastError() );
+ }
+ break;
+ }
+
+ return error;
+}
+
+oslFileError SAL_CALL osl_acquireDirectoryItem( oslDirectoryItem Item )
+{
+ DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item);
+
+ if ( !pItemImpl )
+ return osl_File_E_INVAL;
+
+ pItemImpl->nRefCount++;
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_releaseDirectoryItem( oslDirectoryItem Item )
+{
+ DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item);
+
+ if ( !pItemImpl )
+ return osl_File_E_INVAL;
+
+ if ( ! --pItemImpl->nRefCount )
+ delete pItemImpl;
+
+ return osl_File_E_None;
+}
+
+sal_Bool
+SAL_CALL osl_identicalDirectoryItem( oslDirectoryItem a, oslDirectoryItem b)
+{
+ DirectoryItem_Impl *pA = static_cast<DirectoryItem_Impl *>(a);
+ DirectoryItem_Impl *pB = static_cast<DirectoryItem_Impl *>(b);
+ if (a == b)
+ return true;
+ /* same name => same item, unless renaming / moving madness has occurred */
+ if (pA->m_sFullPath == pB->m_sFullPath)
+ return true;
+
+ // FIXME: as/when/if this is used in anger on Windows we could
+ // do better here.
+
+ return false;
+}
+
+static bool is_floppy_A_present()
+{ return (GetLogicalDrives() & 1); }
+
+static bool is_floppy_B_present()
+{ return (GetLogicalDrives() & 2); }
+
+static bool is_floppy_volume_mount_point(const OUString& path)
+{
+ // determines if a volume mount point shows to a floppy
+ // disk by comparing the unique volume names
+ static const LPCWSTR FLOPPY_A = L"A:\\";
+ static const LPCWSTR FLOPPY_B = L"B:\\";
+
+ OUString p(path);
+ osl::systemPathEnsureSeparator(p);
+
+ WCHAR vn[51];
+ if (GetVolumeNameForVolumeMountPointW(o3tl::toW(p.getStr()), vn, SAL_N_ELEMENTS(vn)))
+ {
+ WCHAR vnfloppy[51];
+ if (is_floppy_A_present() &&
+ GetVolumeNameForVolumeMountPointW(FLOPPY_A, vnfloppy, SAL_N_ELEMENTS(vnfloppy)) &&
+ (0 == wcscmp(vn, vnfloppy)))
+ return true;
+
+ if (is_floppy_B_present() &&
+ GetVolumeNameForVolumeMountPointW(FLOPPY_B, vnfloppy, SAL_N_ELEMENTS(vnfloppy)) &&
+ (0 == wcscmp(vn, vnfloppy)))
+ return true;
+ }
+ return false;
+}
+
+static bool is_floppy_drive(const OUString& path)
+{
+ static const LPCWSTR FLOPPY_DRV_LETTERS = L"AaBb";
+
+ // we must take into account that even a floppy
+ // drive may be mounted to a directory so checking
+ // for the drive letter alone is not sufficient
+ // we must compare the unique volume name with
+ // that of the available floppy disks
+
+ const sal_Unicode* pszPath = path.getStr();
+ return ((wcschr(FLOPPY_DRV_LETTERS, pszPath[0]) && (L':' == pszPath[1])) || is_floppy_volume_mount_point(path));
+}
+
+static bool is_volume_mount_point(const OUString& path)
+{
+ OUString p(path);
+ osl::systemPathRemoveSeparator(p);
+
+ if (is_floppy_drive(p))
+ return false;
+
+ DWORD fattr = GetFileAttributesW(o3tl::toW(p.getStr()));
+ if ((INVALID_FILE_ATTRIBUTES == fattr) ||
+ !(FILE_ATTRIBUTE_REPARSE_POINT & fattr))
+ return false;
+
+ bool is_volume_root = false;
+ WIN32_FIND_DATAW find_data;
+ HANDLE h_find = FindFirstFileW(o3tl::toW(p.getStr()), &find_data);
+
+ if (IsValidHandle(h_find) &&
+ (FILE_ATTRIBUTE_REPARSE_POINT & find_data.dwFileAttributes) &&
+ (IO_REPARSE_TAG_MOUNT_POINT == find_data.dwReserved0))
+ {
+ is_volume_root = true;
+ }
+ if (IsValidHandle(h_find))
+ FindClose(h_find);
+ return is_volume_root;
+}
+
+static UINT get_volume_mount_point_drive_type(const OUString& path)
+{
+ if (0 == path.getLength())
+ return GetDriveTypeW(nullptr);
+
+ OUString p(path);
+ osl::systemPathEnsureSeparator(p);
+
+ WCHAR vn[51];
+ if (GetVolumeNameForVolumeMountPointW(o3tl::toW(p.getStr()), vn, SAL_N_ELEMENTS(vn)))
+ return GetDriveTypeW(vn);
+
+ return DRIVE_NO_ROOT_DIR;
+}
+
+static bool is_drivetype_request(sal_uInt32 field_mask)
+{
+ return (field_mask & osl_VolumeInfo_Mask_Attributes);
+}
+
+static oslFileError osl_get_drive_type(
+ const OUString& path, oslVolumeInfo* pInfo)
+{
+ // GetDriveType fails on empty volume mount points
+ // see Knowledge Base Q244089
+ UINT drive_type;
+ if (is_volume_mount_point(path))
+ drive_type = get_volume_mount_point_drive_type(path);
+ else
+ drive_type = GetDriveTypeW(o3tl::toW(path.getStr()));
+
+ if (DRIVE_NO_ROOT_DIR == drive_type)
+ return oslTranslateFileError(ERROR_INVALID_DRIVE);
+
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes;
+
+ switch (drive_type)
+ {
+ case DRIVE_CDROM:
+ pInfo->uAttributes |= osl_Volume_Attribute_CompactDisc | osl_Volume_Attribute_Removeable;
+ break;
+ case DRIVE_REMOVABLE:
+ pInfo->uAttributes |= osl_Volume_Attribute_Removeable;
+ if (is_floppy_drive(path))
+ pInfo->uAttributes |= osl_Volume_Attribute_FloppyDisk;
+ break;
+ case DRIVE_FIXED:
+ pInfo->uAttributes |= osl_Volume_Attribute_FixedDisk;
+ break;
+ case DRIVE_RAMDISK:
+ pInfo->uAttributes |= osl_Volume_Attribute_RAMDisk;
+ break;
+ case DRIVE_REMOTE:
+ pInfo->uAttributes |= osl_Volume_Attribute_Remote;
+ break;
+ case DRIVE_UNKNOWN:
+ pInfo->uAttributes = 0;
+ break;
+ default:
+ pInfo->uValidFields &= ~osl_VolumeInfo_Mask_Attributes;
+ pInfo->uAttributes = 0;
+ break;
+ }
+ return osl_File_E_None;
+}
+
+static bool is_volume_space_info_request(sal_uInt32 field_mask)
+{
+ return (field_mask &
+ (osl_VolumeInfo_Mask_TotalSpace |
+ osl_VolumeInfo_Mask_UsedSpace |
+ osl_VolumeInfo_Mask_FreeSpace));
+}
+
+static void get_volume_space_information(
+ const OUString& path, oslVolumeInfo *pInfo)
+{
+ bool ret = GetDiskFreeSpaceExW(
+ o3tl::toW(path.getStr()),
+ reinterpret_cast<PULARGE_INTEGER>(&pInfo->uFreeSpace),
+ reinterpret_cast<PULARGE_INTEGER>(&pInfo->uTotalSpace),
+ nullptr);
+
+ if (ret)
+ {
+ pInfo->uUsedSpace = pInfo->uTotalSpace - pInfo->uFreeSpace;
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_TotalSpace |
+ osl_VolumeInfo_Mask_UsedSpace |
+ osl_VolumeInfo_Mask_FreeSpace;
+ }
+}
+
+static bool is_filesystem_attributes_request(sal_uInt32 field_mask)
+{
+ return (field_mask &
+ (osl_VolumeInfo_Mask_MaxNameLength |
+ osl_VolumeInfo_Mask_MaxPathLength |
+ osl_VolumeInfo_Mask_FileSystemName |
+ osl_VolumeInfo_Mask_FileSystemCaseHandling));
+}
+
+static oslFileError get_filesystem_attributes(
+ const OUString& path, sal_uInt32 field_mask, oslVolumeInfo* pInfo)
+{
+ pInfo->uAttributes = 0;
+
+ // osl_get_drive_type must be called first because
+ // this function resets osl_VolumeInfo_Mask_Attributes
+ // on failure
+ if (is_drivetype_request(field_mask))
+ {
+ oslFileError osl_error = osl_get_drive_type(path, pInfo);
+ if (osl_File_E_None != osl_error)
+ return osl_error;
+ }
+ if (is_filesystem_attributes_request(field_mask))
+ {
+ /* the following two parameters can not be longer than MAX_PATH+1 */
+ WCHAR vn[MAX_PATH+1];
+ WCHAR fsn[MAX_PATH+1];
+
+ DWORD serial;
+ DWORD mcl;
+ DWORD flags;
+
+ LPCWSTR pszPath = o3tl::toW(path.getStr());
+ if (GetVolumeInformationW(pszPath, vn, MAX_PATH+1, &serial, &mcl, &flags, fsn, MAX_PATH+1))
+ {
+ // Currently sal does not use this value, instead MAX_PATH is used
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_MaxNameLength;
+ pInfo->uMaxNameLength = mcl;
+
+ // Should the uMaxPathLength be set to 32767, "\\?\" prefix allows it
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_MaxPathLength;
+ pInfo->uMaxPathLength = MAX_PATH;
+
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_FileSystemName;
+ rtl_uString_newFromStr(&pInfo->ustrFileSystemName, o3tl::toU(fsn));
+
+ // volumes (even NTFS) will always be considered case
+ // insensitive because the Win32 API is not able to
+ // deal with case sensitive volumes see M$ Knowledge Base
+ // article 100625 that's why we never set the attribute
+ // osl_Volume_Attribute_Case_Sensitive
+
+ if (flags & FS_CASE_IS_PRESERVED)
+ pInfo->uAttributes |= osl_Volume_Attribute_Case_Is_Preserved;
+
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes;
+ }
+ }
+ return osl_File_E_None;
+}
+
+static bool path_get_parent(OUString& path)
+{
+ OSL_PRECOND(path.lastIndexOf(SLASH) == -1, "Path must not have slashes");
+
+ if (!has_path_parent(path))
+ {
+ sal_Int32 i = path.lastIndexOf(BACKSLASH);
+ if (-1 < i)
+ {
+ path = OUString(path.getStr(), i);
+ return true;
+ }
+ }
+ return false;
+}
+
+static void path_travel_to_volume_root(const OUString& system_path, OUString& volume_root)
+{
+ OUString sys_path(system_path);
+
+ while(!is_volume_mount_point(sys_path) && path_get_parent(sys_path))
+ /**/;
+
+ volume_root = sys_path;
+ osl::systemPathEnsureSeparator(volume_root);
+}
+
+oslFileError SAL_CALL osl_getVolumeInformation(
+ rtl_uString *ustrURL, oslVolumeInfo *pInfo, sal_uInt32 uFieldMask )
+{
+ if (!pInfo)
+ return osl_File_E_INVAL;
+
+ OUString system_path;
+ oslFileError error = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrURL), &system_path.pData, false);
+
+ if (osl_File_E_None != error)
+ return error;
+
+ OUString volume_root;
+ path_travel_to_volume_root(system_path, volume_root);
+
+ pInfo->uValidFields = 0;
+
+ if ((error = get_filesystem_attributes(volume_root, uFieldMask, pInfo)) != osl_File_E_None)
+ return error;
+
+ if (is_volume_space_info_request(uFieldMask))
+ get_volume_space_information(volume_root, pInfo);
+
+ if (uFieldMask & osl_VolumeInfo_Mask_DeviceHandle)
+ {
+ error = osl_getFileURLFromSystemPath(volume_root.pData, reinterpret_cast<rtl_uString**>(&pInfo->pDeviceHandle));
+ if (error != osl_File_E_None)
+ return error;
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_DeviceHandle;
+ }
+
+ return osl_File_E_None;
+}
+
+static oslFileError osl_getDriveInfo(
+ oslDirectoryItem Item, oslFileStatus *pStatus, sal_uInt32 uFieldMask)
+{
+ DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item);
+ WCHAR cDrive[3] = L"A:";
+ WCHAR cRoot[4] = L"A:\\";
+
+ if ( !pItemImpl )
+ return osl_File_E_INVAL;
+
+ pStatus->uValidFields = 0;
+
+ cDrive[0] = pItemImpl->cDriveString[0];
+ cRoot[0] = pItemImpl->cDriveString[0];
+
+ if ( uFieldMask & osl_FileStatus_Mask_FileName )
+ {
+ if ( pItemImpl->cDriveString[0] == '\\' && pItemImpl->cDriveString[1] == '\\' )
+ {
+ LPCWSTR lpFirstBkSlash = wcschr( &pItemImpl->cDriveString[2], '\\' );
+
+ if ( lpFirstBkSlash && lpFirstBkSlash[1] )
+ {
+ LPCWSTR lpLastBkSlash = wcschr( &lpFirstBkSlash[1], '\\' );
+
+ if ( lpLastBkSlash )
+ rtl_uString_newFromStr_WithLength( &pStatus->ustrFileName, o3tl::toU(&lpFirstBkSlash[1]), lpLastBkSlash - lpFirstBkSlash - 1 );
+ else
+ rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(&lpFirstBkSlash[1]) );
+ pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
+ }
+ }
+ else switch ( GetDriveTypeW( cRoot ) )
+ {
+ case DRIVE_REMOTE:
+ {
+ WCHAR szBuffer[1024];
+ DWORD const dwBufsizeConst = SAL_N_ELEMENTS(szBuffer);
+ DWORD dwBufsize = dwBufsizeConst;
+
+ DWORD dwResult = WNetGetConnectionW( cDrive, szBuffer, &dwBufsize );
+ if ( NO_ERROR == dwResult )
+ {
+ WCHAR szFileName[dwBufsizeConst + 16];
+
+ swprintf( szFileName, L"%s [%s]", cDrive, szBuffer );
+ rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(szFileName) );
+ }
+ else
+ rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(cDrive) );
+ }
+ pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
+ break;
+ case DRIVE_FIXED:
+ {
+ WCHAR szVolumeNameBuffer[1024];
+ DWORD const dwBufsizeConst = SAL_N_ELEMENTS(szVolumeNameBuffer);
+
+ if ( GetVolumeInformationW( cRoot, szVolumeNameBuffer, dwBufsizeConst, nullptr, nullptr, nullptr, nullptr, 0 ) )
+ {
+ WCHAR szFileName[dwBufsizeConst + 16];
+
+ swprintf( szFileName, L"%s [%s]", cDrive, szVolumeNameBuffer );
+ rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(szFileName) );
+ }
+ else
+ rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(cDrive) );
+ }
+ pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
+ break;
+ case DRIVE_CDROM:
+ case DRIVE_REMOVABLE:
+ pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
+ rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(cRoot) );
+ break;
+ case DRIVE_UNKNOWN:
+ default:
+ break;
+ }
+ }
+
+ pStatus->eType = osl_File_Type_Volume;
+ pStatus->uValidFields |= osl_FileStatus_Mask_Type;
+
+ if ( uFieldMask & osl_FileStatus_Mask_FileURL )
+ {
+ rtl_uString *ustrSystemPath = nullptr;
+
+ rtl_uString_newFromStr( &ustrSystemPath, o3tl::toU(pItemImpl->cDriveString) );
+ oslFileError error = osl_getFileURLFromSystemPath( ustrSystemPath, &pStatus->ustrFileURL );
+ rtl_uString_release( ustrSystemPath );
+ if (error != osl_File_E_None)
+ return error;
+ pStatus->uValidFields |= osl_FileStatus_Mask_FileURL;
+ }
+ return osl_File_E_None;
+}
+
+static oslFileError osl_getServerInfo(
+ oslDirectoryItem Item, oslFileStatus *pStatus, sal_uInt32 uFieldMask )
+{
+ DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item);
+ if ( !pItemImpl )
+ return osl_File_E_INVAL;
+
+ pStatus->uValidFields = 0;
+ pStatus->eType = osl_File_Type_Directory;
+ pStatus->uValidFields |= osl_FileStatus_Mask_Type;
+
+ if ( uFieldMask & osl_FileStatus_Mask_FileURL )
+ {
+ oslFileError error = osl_getFileURLFromSystemPath( pItemImpl->m_sFullPath.pData, &pStatus->ustrFileURL );
+ if (error != osl_File_E_None)
+ return error;
+ pStatus->uValidFields |= osl_FileStatus_Mask_FileURL;
+ }
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_getFileStatus(
+ oslDirectoryItem Item,
+ oslFileStatus *pStatus,
+ sal_uInt32 uFieldMask )
+{
+ DirectoryItem_Impl *pItemImpl = static_cast<DirectoryItem_Impl *>(Item);
+
+ if ( !pItemImpl )
+ return osl_File_E_INVAL;
+
+ switch ( pItemImpl->uType )
+ {
+ case DIRECTORYITEM_DRIVE:
+ return osl_getDriveInfo( Item, pStatus, uFieldMask );
+ case DIRECTORYITEM_SERVER:
+ return osl_getServerInfo( Item, pStatus, uFieldMask );
+ default:
+ break;
+ }
+
+ OUString sFullPath(pItemImpl->m_sFullPath);
+
+ // Prefix long paths, windows API calls expect this prefix
+ // (only local paths starting with something like C: or D:)
+ if (sFullPath.getLength() >= MAX_PATH && isalpha(sFullPath[0]) && sFullPath[1] == ':')
+ sFullPath = "\\\\?\\" + sFullPath;
+
+ if ( uFieldMask & osl_FileStatus_Mask_Validate )
+ {
+ HANDLE hFind = FindFirstFileW( o3tl::toW(sFullPath.getStr() ), &pItemImpl->FindData );
+
+ if ( hFind != INVALID_HANDLE_VALUE )
+ FindClose( hFind );
+ else
+ return oslTranslateFileError( GetLastError() );
+
+ uFieldMask &= ~ osl_FileStatus_Mask_Validate;
+ }
+
+ /* If no fields to retrieve left ignore pStatus */
+ if ( !uFieldMask )
+ return osl_File_E_None;
+
+ /* Otherwise, this must be a valid pointer */
+ if ( !pStatus )
+ return osl_File_E_INVAL;
+
+ if ( pStatus->uStructSize != sizeof(oslFileStatus) )
+ return osl_File_E_INVAL;
+
+ pStatus->uValidFields = 0;
+
+ /* File time stamps */
+
+ if ( (uFieldMask & osl_FileStatus_Mask_ModifyTime) &&
+ FileTimeToTimeValue( &pItemImpl->FindData.ftLastWriteTime, &pStatus->aModifyTime ) )
+ pStatus->uValidFields |= osl_FileStatus_Mask_ModifyTime;
+
+ if ( (uFieldMask & osl_FileStatus_Mask_AccessTime) &&
+ FileTimeToTimeValue( &pItemImpl->FindData.ftLastAccessTime, &pStatus->aAccessTime ) )
+ pStatus->uValidFields |= osl_FileStatus_Mask_AccessTime;
+
+ if ( (uFieldMask & osl_FileStatus_Mask_CreationTime) &&
+ FileTimeToTimeValue( &pItemImpl->FindData.ftCreationTime, &pStatus->aCreationTime ) )
+ pStatus->uValidFields |= osl_FileStatus_Mask_CreationTime;
+
+ /* Most of the fields are already set, regardless of required fields */
+
+ rtl_uString_newFromStr( &pStatus->ustrFileName, o3tl::toU(pItemImpl->FindData.cFileName) );
+ pStatus->uValidFields |= osl_FileStatus_Mask_FileName;
+
+ if ((FILE_ATTRIBUTE_REPARSE_POINT & pItemImpl->FindData.dwFileAttributes) &&
+ (IO_REPARSE_TAG_MOUNT_POINT == pItemImpl->FindData.dwReserved0))
+ pStatus->eType = osl_File_Type_Volume;
+ else if (pItemImpl->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ pStatus->eType = osl_File_Type_Directory;
+ else
+ pStatus->eType = osl_File_Type_Regular;
+
+ pStatus->uValidFields |= osl_FileStatus_Mask_Type;
+
+ pStatus->uAttributes = pItemImpl->FindData.dwFileAttributes;
+ pStatus->uValidFields |= osl_FileStatus_Mask_Attributes;
+
+ pStatus->uFileSize = static_cast<sal_uInt64>(pItemImpl->FindData.nFileSizeLow) + (static_cast<sal_uInt64>(pItemImpl->FindData.nFileSizeHigh) << 32);
+ pStatus->uValidFields |= osl_FileStatus_Mask_FileSize;
+
+ if ( uFieldMask & osl_FileStatus_Mask_LinkTargetURL )
+ {
+ oslFileError error = osl_getFileURLFromSystemPath( sFullPath.pData, &pStatus->ustrLinkTargetURL );
+ if (error != osl_File_E_None)
+ return error;
+
+ pStatus->uValidFields |= osl_FileStatus_Mask_LinkTargetURL;
+ }
+
+ if ( uFieldMask & osl_FileStatus_Mask_FileURL )
+ {
+ if ( !pItemImpl->bFullPathNormalized )
+ {
+ ::osl::LongPathBuffer<sal_Unicode> aBuffer(MAX_LONG_PATH);
+ sal_uInt32 nNewLen = GetLongPathNameW(o3tl::toW(sFullPath.getStr()), o3tl::toW(aBuffer),
+ aBuffer.getBufSizeInSymbols());
+
+ if ( nNewLen )
+ {
+ /* Capitalizes drive name (single letter). Windows file paths are processed
+ case-sensitively. While parsing a path, function osl_DirectoryItem has case
+ PATHTYPE_VOLUME for drives, and capitalizes them. That can be overwritten by
+ function osl_getFileStatus, in it win32 api GetLongPathNameW does no
+ capitalization. Thus it needs to be postprocessed.*/
+ sal_Int32 nIndex = rtl_ustr_indexOfChar(aBuffer, ':');
+ if (nIndex > 0) {
+ aBuffer[nIndex - 1] = rtl::toAsciiUpperCase(aBuffer[nIndex - 1]);
+ }
+
+ pItemImpl->m_sFullPath = OUString(&*aBuffer, nNewLen);
+ sFullPath = pItemImpl->m_sFullPath;
+ pItemImpl->bFullPathNormalized = true;
+ }
+ }
+
+ oslFileError error = osl_getFileURLFromSystemPath( sFullPath.pData, &pStatus->ustrFileURL );
+ if (error != osl_File_E_None)
+ return error;
+ pStatus->uValidFields |= osl_FileStatus_Mask_FileURL;
+ }
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_setFileAttributes(
+ rtl_uString *ustrFileURL,
+ sal_uInt64 uAttributes )
+{
+ oslFileError error;
+ OUString ustrSysPath;
+ DWORD dwFileAttributes;
+ bool fSuccess;
+
+ // Converts the normalized path into a systempath
+ error = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrFileURL), &ustrSysPath.pData, false);
+
+ if ( osl_File_E_None != error )
+ return error;
+
+ dwFileAttributes = GetFileAttributesW(o3tl::toW(ustrSysPath.getStr()));
+
+ if ( DWORD(-1) != dwFileAttributes )
+ {
+ dwFileAttributes &= ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
+
+ if ( uAttributes & osl_File_Attribute_ReadOnly )
+ dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
+
+ if ( uAttributes & osl_File_Attribute_Hidden )
+ dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
+
+ fSuccess = SetFileAttributesW(o3tl::toW(ustrSysPath.getStr()), dwFileAttributes);
+ }
+ else
+ {
+ fSuccess = false;
+ }
+
+ if ( !fSuccess )
+ error = oslTranslateFileError( GetLastError() );
+
+ return error;
+}
+
+oslFileError SAL_CALL osl_setFileTime(
+ rtl_uString *filePath,
+ const TimeValue *aCreationTime,
+ const TimeValue *aLastAccessTime,
+ const TimeValue *aLastWriteTime)
+{
+ oslFileError error;
+ OUString sysPath;
+ FILETIME *lpCreationTime=nullptr;
+ FILETIME *lpLastAccessTime=nullptr;
+ FILETIME *lpLastWriteTime=nullptr;
+ FILETIME ftCreationTime;
+ FILETIME ftLastAccessTime;
+ FILETIME ftLastWriteTime;
+ HANDLE hFile;
+ bool fSuccess;
+
+ error=osl_getSystemPathFromFileURL_(OUString::unacquired(&filePath), &sysPath.pData, false);
+
+ if (error==osl_File_E_INVAL)
+ return error;
+
+ hFile=CreateFileW(o3tl::toW(sysPath.getStr()), GENERIC_WRITE, 0, nullptr , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+
+ if (hFile==INVALID_HANDLE_VALUE)
+ return osl_File_E_NOENT;
+
+ if (TimeValueToFileTime(aCreationTime, &ftCreationTime))
+ lpCreationTime=&ftCreationTime;
+
+ if (TimeValueToFileTime(aLastAccessTime, &ftLastAccessTime))
+ lpLastAccessTime=&ftLastAccessTime;
+
+ if (TimeValueToFileTime(aLastWriteTime, &ftLastWriteTime))
+ lpLastWriteTime=&ftLastWriteTime;
+
+ fSuccess=SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
+
+ CloseHandle(hFile);
+
+ if (!fSuccess)
+ return osl_File_E_INVAL;
+ else
+ return osl_File_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/file_error.cxx b/sal/osl/w32/file_error.cxx
new file mode 100644
index 000000000..feefaca38
--- /dev/null
+++ b/sal/osl/w32/file_error.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "file_error.hxx"
+#include <winerror.h>
+
+namespace {
+
+/* OS error to oslFileError values mapping table */
+struct osl_file_error_entry
+{
+ unsigned long oscode; /* OS return value */
+ int errnocode; /* oslFileError code */
+};
+
+}
+
+const struct osl_file_error_entry errtable[] = {
+ { ERROR_SUCCESS, osl_File_E_None }, /* 0 */
+ { ERROR_INVALID_FUNCTION, osl_File_E_INVAL }, /* 1 */
+ { ERROR_FILE_NOT_FOUND, osl_File_E_NOENT }, /* 2 */
+ { ERROR_PATH_NOT_FOUND, osl_File_E_NOENT }, /* 3 */
+ { ERROR_TOO_MANY_OPEN_FILES, osl_File_E_MFILE }, /* 4 */
+ { ERROR_ACCESS_DENIED, osl_File_E_ACCES }, /* 5 */
+ { ERROR_INVALID_HANDLE, osl_File_E_BADF }, /* 6 */
+ { ERROR_ARENA_TRASHED, osl_File_E_NOMEM }, /* 7 */
+ { ERROR_NOT_ENOUGH_MEMORY, osl_File_E_NOMEM }, /* 8 */
+ { ERROR_INVALID_BLOCK, osl_File_E_NOMEM }, /* 9 */
+ { ERROR_BAD_ENVIRONMENT, osl_File_E_2BIG }, /* 10 */
+ { ERROR_BAD_FORMAT, osl_File_E_NOEXEC }, /* 11 */
+ { ERROR_INVALID_ACCESS, osl_File_E_INVAL }, /* 12 */
+ { ERROR_INVALID_DATA, osl_File_E_INVAL }, /* 13 */
+ { ERROR_INVALID_DRIVE, osl_File_E_NOENT }, /* 15 */
+ { ERROR_CURRENT_DIRECTORY, osl_File_E_ACCES }, /* 16 */
+ { ERROR_NOT_SAME_DEVICE, osl_File_E_XDEV }, /* 17 */
+ { ERROR_NO_MORE_FILES, osl_File_E_NOENT }, /* 18 */
+ { ERROR_NOT_READY, osl_File_E_NOTREADY }, /* 21 */
+ { ERROR_SHARING_VIOLATION, osl_File_E_ACCES }, /* 32 */
+ { ERROR_LOCK_VIOLATION, osl_File_E_ACCES }, /* 33 */
+ { ERROR_BAD_NETPATH, osl_File_E_NOENT }, /* 53 */
+ { ERROR_NETWORK_ACCESS_DENIED, osl_File_E_ACCES }, /* 65 */
+ { ERROR_BAD_NET_NAME, osl_File_E_NOENT }, /* 67 */
+ { ERROR_FILE_EXISTS, osl_File_E_EXIST }, /* 80 */
+ { ERROR_CANNOT_MAKE, osl_File_E_ACCES }, /* 82 */
+ { ERROR_FAIL_I24, osl_File_E_ACCES }, /* 83 */
+ { ERROR_INVALID_PARAMETER, osl_File_E_INVAL }, /* 87 */
+ { ERROR_NO_PROC_SLOTS, osl_File_E_AGAIN }, /* 89 */
+ { ERROR_DRIVE_LOCKED, osl_File_E_ACCES }, /* 108 */
+ { ERROR_BROKEN_PIPE, osl_File_E_PIPE }, /* 109 */
+ { ERROR_DISK_FULL, osl_File_E_NOSPC }, /* 112 */
+ { ERROR_INVALID_TARGET_HANDLE, osl_File_E_BADF }, /* 114 */
+ { ERROR_INVALID_NAME, osl_File_E_NOENT }, /* 123 */
+ { ERROR_INVALID_HANDLE, osl_File_E_INVAL }, /* 124 */
+ { ERROR_WAIT_NO_CHILDREN, osl_File_E_CHILD }, /* 128 */
+ { ERROR_CHILD_NOT_COMPLETE, osl_File_E_CHILD }, /* 129 */
+ { ERROR_DIRECT_ACCESS_HANDLE, osl_File_E_BADF }, /* 130 */
+ { ERROR_NEGATIVE_SEEK, osl_File_E_INVAL }, /* 131 */
+ { ERROR_SEEK_ON_DEVICE, osl_File_E_ACCES }, /* 132 */
+ { ERROR_DIR_NOT_EMPTY, osl_File_E_NOTEMPTY }, /* 145 */
+ { ERROR_NOT_LOCKED, osl_File_E_ACCES }, /* 158 */
+ { ERROR_BAD_PATHNAME, osl_File_E_NOENT }, /* 161 */
+ { ERROR_MAX_THRDS_REACHED, osl_File_E_AGAIN }, /* 164 */
+ { ERROR_LOCK_FAILED, osl_File_E_ACCES }, /* 167 */
+ { ERROR_ALREADY_EXISTS, osl_File_E_EXIST }, /* 183 */
+ { ERROR_FILENAME_EXCED_RANGE, osl_File_E_NOENT }, /* 206 */
+ { ERROR_NESTING_NOT_ALLOWED, osl_File_E_AGAIN }, /* 215 */
+ { ERROR_FILE_CHECKED_OUT, osl_File_E_ACCES }, /* 220 */
+ { ERROR_DIRECTORY, osl_File_E_NOTDIR }, /* 267 */
+ { ERROR_NOT_ENOUGH_QUOTA, osl_File_E_NOMEM }, /* 1816 */
+ { ERROR_CANT_ACCESS_FILE, osl_File_E_ACCES }, /* 1920 */
+ { ERROR_UNEXP_NET_ERR, osl_File_E_NETWORK } /* 59 */
+};
+
+/* The following two constants must be the minimum and maximum
+ values in the (contiguous) range of osl_File_E_xec Failure errors.
+*/
+#define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG
+#define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN
+
+/* These are the low and high value in the range of errors that are
+ access violations
+*/
+#define MIN_EACCES_RANGE ERROR_WRITE_PROTECT
+#define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED
+
+oslFileError oslTranslateFileError (/*DWORD*/ unsigned long dwError)
+{
+ static const int n = SAL_N_ELEMENTS(errtable);
+
+ int i;
+ for (i = 0; i < n; ++i )
+ {
+ if (dwError == errtable[i].oscode)
+ return static_cast<oslFileError>(errtable[i].errnocode);
+ }
+
+ /* The error code wasn't in the table. We check for a range of
+ osl_File_E_ACCES errors or exec failure errors (ENOEXEC).
+ Otherwise osl_File_E_INVAL is returned.
+ */
+ if ( (dwError >= MIN_EACCES_RANGE) && (dwError <= MAX_EACCES_RANGE) )
+ return osl_File_E_ACCES;
+ else if ( (dwError >= MIN_EXEC_ERROR) && (dwError <= MAX_EXEC_ERROR) )
+ return osl_File_E_NOEXEC;
+ else
+ return osl_File_E_INVAL;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/file_error.hxx b/sal/osl/w32/file_error.hxx
new file mode 100644
index 000000000..4da87c132
--- /dev/null
+++ b/sal/osl/w32/file_error.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_W32_FILE_ERROR_HXX
+#define INCLUDED_SAL_OSL_W32_FILE_ERROR_HXX
+
+#include <osl/file.h>
+
+oslFileError oslTranslateFileError(/*DWORD*/ unsigned long dwError);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/file_url.cxx b/sal/osl/w32/file_url.cxx
new file mode 100644
index 000000000..8c525042d
--- /dev/null
+++ b/sal/osl/w32/file_url.cxx
@@ -0,0 +1,990 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <optional>
+#include <stack>
+#include <string_view>
+
+#include <systools/win32/uwinapi.h>
+
+#include "file_url.hxx"
+#include "file_error.hxx"
+
+#include <rtl/alloc.h>
+#include <rtl/character.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/mutex.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/string_view.hxx>
+
+#include "path_helper.hxx"
+
+// FileURL functions
+
+namespace
+{
+constexpr std::u16string_view WSTR_SYSTEM_ROOT_PATH = u"\\\\.\\";
+constexpr std::u16string_view WSTR_LONG_PATH_PREFIX = u"\\\\?\\";
+constexpr std::u16string_view WSTR_LONG_PATH_PREFIX_UNC = u"\\\\?\\UNC\\";
+
+// Internal functions that expect only backslashes as path separators
+
+bool startsWithDriveColon(std::u16string_view s)
+{
+ return s.length() >= 2 && rtl::isAsciiAlpha(s[0]) && s[1] == ':';
+}
+
+bool startsWithDriveColonSlash(std::u16string_view s)
+{
+ return s.length() >= 3 && startsWithDriveColon(s) && s[2] == '\\';
+}
+
+bool startsWithSlashSlash(std::u16string_view s) { return o3tl::starts_with(s, u"\\\\"); }
+
+// An absolute path starts either with \\ (an UNC or device path like \\.\ or \\?\)
+// or with a ASCII alpha character followed by colon followed by backslash.
+bool isAbsolute(std::u16string_view s)
+{
+ return startsWithSlashSlash(s) || startsWithDriveColonSlash(s);
+}
+
+bool onSameDrive(std::u16string_view s1, std::u16string_view s2)
+{
+ assert(startsWithDriveColon(s1) && startsWithDriveColon(s2));
+ return rtl::toAsciiUpperCase(s1[0]) == rtl::toAsciiUpperCase(s2[0]) && s1[1] == s2[1];
+}
+
+sal_Int32 getRootLength(std::u16string_view path)
+{
+ assert(isAbsolute(path));
+ size_t nResult = 0;
+ if (startsWithSlashSlash(path))
+ {
+ // Cases:
+ // 1. Device UNC: \\?\UNC\server\share or \\.\UNC\server\share
+ // 2. Non-device UNC: \\server\share
+ // 3. Device non-UNC: \\?\C: or \\.\C:
+ bool bUNC = false;
+ if (path.length() > 3 && (path[2] == '.' || path[2] == '?') && path[3] == '\\')
+ {
+ if (path.substr(4, 4) == u"UNC\\")
+ {
+ // \\?\UNC\server\share or \\.\UNC\server\share
+ nResult = 8;
+ bUNC = true;
+ }
+ else
+ {
+ // \\?\C: or \\.\C:
+ assert(startsWithDriveColon(path.substr(4)));
+ nResult = 6;
+ }
+ }
+ else
+ {
+ // \\server\share
+ nResult = 2;
+ bUNC = true;
+ }
+ if (bUNC)
+ {
+ // \\?\UNC\server\share or \\.\UNC\server\share or \\server\share
+ assert(nResult < path.length() && path[nResult] != '\\');
+ // Skip server name and share name
+ for (int nSlashes = 0; nResult < path.length(); ++nResult)
+ {
+ if (path[nResult] == '\\' && ++nSlashes == 2)
+ break;
+ }
+ }
+ }
+ else
+ {
+ // C:
+ assert(startsWithDriveColon(path));
+ nResult = 2;
+ }
+ return std::min(nResult, path.length());
+}
+
+std::u16string_view pathView(std::u16string_view path, bool bOnlyRoot)
+{
+ return bOnlyRoot ? path.substr(0, getRootLength(path)) : path;
+}
+
+OUString combinePath(std::u16string_view basePath, std::u16string_view relPath)
+{
+ const bool needSep = !o3tl::ends_with(basePath, u'\\');
+ const auto sSeparator = needSep ? std::u16string_view(u"\\") : std::u16string_view();
+ if (o3tl::starts_with(relPath, u'\\'))
+ relPath.remove_prefix(1); // avoid two adjacent backslashes
+ return OUString::Concat(basePath) + sSeparator + relPath;
+}
+
+OUString removeRelativeParts(const OUString& p)
+{
+ const sal_Int32 rootPos = getRootLength(p);
+ OUStringBuffer buf(p.getLength());
+ buf.append(p.subView(0, rootPos));
+ std::stack<sal_Int32> partPositions;
+ bool bAfterSlash = false;
+ for (sal_Int32 i = rootPos; i < p.getLength(); ++i)
+ {
+ sal_Unicode c = p[i];
+ if (c == '\\')
+ {
+ if (i + 1 < p.getLength() && p[i + 1] == '.')
+ {
+ if (i + 2 == p.getLength() || p[i + 2] == '\\')
+ {
+ // 1. Skip current directory (\.\ or trailing \.)
+ ++i; // process next slash: it may start another "\.\"
+ }
+ else if (p[i + 2] == '.' && (i + 3 == p.getLength() || p[i + 3] == '\\'))
+ {
+ // 2. For parent directory (\..\), drop previous part and skip
+ if (bAfterSlash && partPositions.size())
+ partPositions.pop();
+ sal_Int32 nParentPos = partPositions.size() ? partPositions.top() : rootPos;
+ if (partPositions.size())
+ partPositions.pop();
+ buf.truncate(nParentPos);
+ bAfterSlash = false; // we have just removed slash after parent part
+ i += 2; // process next slash: it may start another "\.\"
+ }
+ }
+ if (bAfterSlash)
+ continue; // 3. Skip double backslashes (\\)
+ partPositions.push(buf.getLength());
+ bAfterSlash = true;
+ }
+ else
+ bAfterSlash = false;
+
+ buf.append(c);
+ }
+ return buf.makeStringAndClear();
+}
+}
+
+static bool IsValidFilePathComponent(
+ std::optional<std::u16string_view>& roComponent,
+ DWORD dwFlags)
+{
+ assert(roComponent);
+ auto lpComponentEnd = roComponent->end();
+ auto lpCurrent = roComponent->begin();
+ bool bValid = lpCurrent != lpComponentEnd; // Empty components are not allowed
+ sal_Unicode cLast = 0;
+
+ while (bValid)
+ {
+ /* Path component length must not exceed MAX_PATH even if long path with "\\?\" prefix is used */
+ if (lpCurrent - roComponent->begin() >= MAX_PATH)
+ {
+ bValid = false;
+ break;
+ }
+
+ switch ( *lpCurrent )
+ {
+ /* Both backslash and slash determine the end of a path component */
+ case '\0':
+ case '/':
+ case '\\':
+ switch ( cLast )
+ {
+ /* Component must not end with '.' or blank and can't be empty */
+
+ case '.':
+ if ( dwFlags & VALIDATEPATH_ALLOW_ELLIPSE )
+ {
+ if ( (dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD) ||
+ 1 == lpCurrent - roComponent->begin() )
+ {
+ /* Either do allow periods anywhere, or current directory */
+ lpComponentEnd = lpCurrent;
+ break;
+ }
+ else if ( 2 == lpCurrent - roComponent->begin() && '.' == roComponent->front() )
+ {
+ /* Parent directory is O.K. */
+ lpComponentEnd = lpCurrent;
+ break;
+ }
+ }
+ [[fallthrough]];
+ case 0:
+ case ' ':
+ if ( dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD )
+ lpComponentEnd = lpCurrent;
+ else
+ bValid = false;
+ break;
+ default:
+ lpComponentEnd = lpCurrent;
+ break;
+ }
+ break;
+ /* The following characters are reserved */
+ case '?':
+ case '*':
+ case '<':
+ case '>':
+ case '\"':
+ case '|':
+ case ':':
+ bValid = false;
+ break;
+ default:
+ /* Characters below ASCII 32 are not allowed */
+ if ( *lpCurrent < ' ' )
+ bValid = false;
+ break;
+ }
+
+ if (lpCurrent != lpComponentEnd)
+ cLast = *lpCurrent++;
+
+ if (lpCurrent == lpComponentEnd)
+ break;
+ }
+
+ if ( bValid )
+ {
+ // Empty components are not allowed
+ if (lpComponentEnd - roComponent->begin() < 1)
+ bValid = false;
+ // If we reached the end of the string nullopt is returned
+ else if (lpComponentEnd == roComponent->end())
+ roComponent.reset();
+ else
+ roComponent->remove_prefix(lpComponentEnd - roComponent->begin());
+ }
+
+ return bValid;
+}
+
+static sal_Int32 countInitialSeparators(std::u16string_view path) {
+ size_t n = 0;
+ while (n < path.length() && (path[n] == '\\' || path[n] == '/'))
+ ++n;
+ return n;
+}
+
+DWORD IsValidFilePath(const OUString& path, DWORD dwFlags, OUString* corrected)
+{
+ std::optional<std::u16string_view> oComponent = path;
+ bool bValid = true;
+ DWORD dwPathType = PATHTYPE_ERROR;
+
+ if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
+ dwFlags |= VALIDATEPATH_ALLOW_ELLIPSE;
+
+ DWORD dwCandidatPathType = PATHTYPE_ERROR;
+
+ if (path.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX_UNC))
+ {
+ /* This is long path in UNC notation */
+ oComponent = path.subView(WSTR_LONG_PATH_PREFIX_UNC.size());
+ dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC | PATHTYPE_IS_LONGPATH;
+ }
+ else if (path.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX))
+ {
+ /* This is long path */
+ oComponent = path.subView(WSTR_LONG_PATH_PREFIX.size());
+
+ if (startsWithDriveColon(*oComponent))
+ {
+ oComponent->remove_prefix(2);
+ dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL | PATHTYPE_IS_LONGPATH;
+ }
+ }
+ else if ( 2 == countInitialSeparators(path) )
+ {
+ /* The UNC path notation */
+ oComponent = path.subView(2);
+ dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC;
+ }
+ else if (startsWithDriveColon(path))
+ {
+ /* Local path verification. Must start with <drive>: */
+ oComponent = path.subView(2);
+ dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL;
+ }
+
+ if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_UNC )
+ {
+ bValid = IsValidFilePathComponent(oComponent, VALIDATEPATH_ALLOW_ELLIPSE);
+
+ /* So far we have a valid servername. Now let's see if we also have a network resource */
+
+ dwPathType = dwCandidatPathType;
+
+ if ( bValid )
+ {
+ if (oComponent)
+ {
+ oComponent->remove_prefix(1);
+ if (oComponent->empty())
+ oComponent.reset();
+ }
+
+ if (!oComponent)
+ {
+ dwPathType |= PATHTYPE_IS_SERVER;
+ }
+ else
+ {
+ /* Now test the network resource */
+
+ bValid = IsValidFilePathComponent(oComponent, 0);
+
+ /* If we now reached the end of the path, everything is O.K. */
+
+ if (bValid)
+ {
+ if (oComponent)
+ {
+ oComponent->remove_prefix(1);
+ if (oComponent->empty())
+ oComponent.reset();
+ }
+ if (!oComponent)
+ dwPathType |= PATHTYPE_IS_VOLUME;
+ }
+ }
+ }
+ }
+ else if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_LOCAL )
+ {
+ if (1 == countInitialSeparators(*oComponent))
+ oComponent->remove_prefix(1);
+ else if (!oComponent->empty())
+ bValid = false;
+
+ dwPathType = dwCandidatPathType;
+
+ /* Now we are behind the backslash or it was a simple drive without backslash */
+
+ if (bValid && oComponent->empty())
+ {
+ oComponent.reset();
+ dwPathType |= PATHTYPE_IS_VOLUME;
+ }
+ }
+ else if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
+ {
+ /* Can be a relative path */
+ oComponent = path;
+
+ /* Relative path can start with a backslash */
+
+ if (1 == countInitialSeparators(*oComponent))
+ {
+ oComponent->remove_prefix(1);
+ if (oComponent->empty())
+ oComponent.reset();
+ }
+
+ dwPathType = PATHTYPE_RELATIVE;
+ }
+ else
+ {
+ /* Anything else is an error */
+ bValid = false;
+ }
+
+ /* Now validate each component of the path */
+ OUString lastCorrected = path;
+ while (bValid && oComponent)
+ {
+ // Correct path by merging consecutive slashes:
+ if (o3tl::starts_with(*oComponent, u"\\") && corrected != nullptr) {
+ sal_Int32 i = oComponent->data() - lastCorrected.getStr();
+ *corrected = lastCorrected.replaceAt(i, 1, {});
+ //TODO: handle out-of-memory
+ lastCorrected = *corrected;
+ oComponent = lastCorrected.subView(i);
+ }
+
+ bValid = IsValidFilePathComponent(oComponent, dwFlags | VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD);
+
+ if (bValid && oComponent)
+ {
+ oComponent->remove_prefix(1);
+
+ /* If the string behind the backslash is empty, we've done */
+
+ if (oComponent->empty())
+ oComponent.reset();
+ }
+ }
+
+ /* The path can be longer than MAX_PATH only in case it has the longpath prefix */
+ if (bValid && !(dwPathType & PATHTYPE_IS_LONGPATH) && path.getLength() >= MAX_PATH)
+ {
+ bValid = false;
+ }
+
+ return bValid ? dwPathType : PATHTYPE_ERROR;
+}
+
+static std::optional<OUString> osl_decodeURL_(const OString& sUTF8)
+{
+ const char *pSrcEnd;
+ const char *pSrc;
+ bool bValidEncoded = true; /* Assume success */
+
+ /* The resulting decoded string length is shorter or equal to the source length */
+
+ const sal_Int32 nSrcLen = sUTF8.getLength();
+ OStringBuffer aBuffer(nSrcLen + 1);
+
+ pSrc = sUTF8.getStr();
+ pSrcEnd = pSrc + nSrcLen;
+
+ /* Now decode the URL what should result in a UTF-8 string */
+ while ( bValidEncoded && pSrc < pSrcEnd )
+ {
+ switch ( *pSrc )
+ {
+ case '%':
+ {
+ char aToken[3];
+ char aChar;
+
+ pSrc++;
+ aToken[0] = *pSrc++;
+ aToken[1] = *pSrc++;
+ aToken[2] = 0;
+
+ aChar = static_cast<char>(strtoul( aToken, nullptr, 16 ));
+
+ /* The chars are path delimiters and must not be encoded */
+
+ if ( 0 == aChar || '\\' == aChar || '/' == aChar || ':' == aChar )
+ bValidEncoded = false;
+ else
+ aBuffer.append(aChar);
+ }
+ break;
+ case '\0':
+ case '#':
+ case '?':
+ bValidEncoded = false;
+ break;
+ default:
+ aBuffer.append(*pSrc++);
+ break;
+ }
+ }
+
+ return bValidEncoded ? OUString(aBuffer.getStr(), aBuffer.getLength(), RTL_TEXTENCODING_UTF8)
+ : std::optional<OUString>();
+}
+
+static OUString osl_encodeURL_(std::u16string_view sURL)
+{
+ /* Encode non ascii characters within the URL */
+
+ const char *pURLScan;
+ sal_Int32 nURLScanLen;
+ sal_Int32 nURLScanCount;
+
+ OString sUTF8 = OUStringToOString(sURL, RTL_TEXTENCODING_UTF8);
+
+ OUStringBuffer sEncodedURL(sUTF8.getLength() * 3 + 1);
+ pURLScan = sUTF8.getStr();
+ nURLScanLen = sUTF8.getLength();
+ nURLScanCount = 0;
+
+ while ( nURLScanCount < nURLScanLen )
+ {
+ char cCurrent = *pURLScan;
+ switch ( cCurrent )
+ {
+ default:
+ if (!( ( cCurrent >= 'a' && cCurrent <= 'z' ) || ( cCurrent >= 'A' && cCurrent <= 'Z' ) || ( cCurrent >= '0' && cCurrent <= '9' ) ) )
+ {
+ char buf[3];
+ sprintf( buf, "%02X", static_cast<unsigned char>(cCurrent) );
+ sEncodedURL.append('%').appendAscii(buf, 2);
+ break;
+ }
+ [[fallthrough]];
+ case '!':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ case '$':
+ case '&':
+ case '+':
+ case ',':
+ case '=':
+ case '@':
+ case ':':
+ case '/':
+ case '\\':
+ case '|':
+ sEncodedURL.appendAscii(&cCurrent, 1);
+ break;
+ case 0:
+ break;
+ }
+
+ pURLScan++;
+ nURLScanCount++;
+ }
+
+ return sEncodedURL.makeStringAndClear();
+}
+
+// A helper that makes sure that for existing part of the path, the case is correct.
+// Unlike GetLongPathNameW that it wraps, this function does not require the path to exist.
+static OUString GetCaseCorrectPathName(std::u16string_view sysPath)
+{
+ // Prepare a null-terminated string first.
+ // Neither OUString, nor u16string_view are guaranteed to be null-terminated
+ osl::LongPathBuffer<wchar_t> szPath(sysPath.size() + WSTR_LONG_PATH_PREFIX_UNC.size() + 1);
+ wchar_t* const pPath = szPath;
+ wchar_t* pEnd = pPath;
+ size_t sysPathOffset = 0;
+ if (sysPath.size() >= MAX_PATH && isAbsolute(sysPath)
+ && !o3tl::starts_with(sysPath, WSTR_LONG_PATH_PREFIX))
+ {
+ // Allow GetLongPathNameW consume long paths
+ std::u16string_view prefix = WSTR_LONG_PATH_PREFIX;
+ if (startsWithSlashSlash(sysPath))
+ {
+ sysPathOffset = 2; // skip leading "\\"
+ prefix = WSTR_LONG_PATH_PREFIX_UNC;
+ }
+ pEnd = std::copy(prefix.begin(), prefix.end(), pEnd);
+ }
+ wchar_t* const pStart = pEnd;
+ pEnd = std::copy(sysPath.begin() + sysPathOffset, sysPath.end(), pStart);
+ *pEnd = 0;
+ osl::LongPathBuffer<wchar_t> aBuf(MAX_LONG_PATH);
+ while (pEnd > pStart)
+ {
+ std::u16string_view curPath(o3tl::toU(pPath), pEnd - pPath);
+ if (curPath == u"\\\\" || curPath == WSTR_SYSTEM_ROOT_PATH
+ || curPath == WSTR_LONG_PATH_PREFIX
+ || o3tl::equalsIgnoreAsciiCase(curPath, WSTR_LONG_PATH_PREFIX_UNC))
+ break; // Do not check if the special path prefix exists itself
+
+ DWORD nNewLen = GetLongPathNameW(pPath, aBuf, aBuf.getBufSizeInSymbols());
+ if (nNewLen == 0)
+ {
+ // Error?
+ const DWORD err = GetLastError();
+ if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
+ {
+ // Check the base path; skip possible trailing separator
+ size_t sepPos = curPath.substr(0, curPath.size() - 1).rfind(u'\\');
+ if (sepPos != std::u16string_view::npos)
+ {
+ pEnd = pPath + sepPos;
+ *pEnd = 0;
+ continue;
+ }
+ }
+ else
+ {
+ SAL_WARN("sal.osl", "GetLongPathNameW: Windows error code "
+ << err << " processing path " << OUString(curPath));
+ }
+ break; // All other errors, or no separators left
+ }
+ assert(nNewLen < aBuf.getBufSizeInSymbols());
+ // Combine the case-correct leading part with the non-existing trailing part
+ return OUString::Concat(std::u16string_view(o3tl::toU(aBuf), nNewLen))
+ + sysPath.substr(pEnd - pStart + sysPathOffset);
+ };
+ return OUString(sysPath); // We found no existing parts - just assume it's OK
+}
+
+oslFileError osl_getSystemPathFromFileURL_(const OUString& strURL, rtl_uString **pustrPath, bool bAllowRelative)
+{
+ OUString sTempPath;
+ oslFileError nError = osl_File_E_INVAL; /* Assume failure */
+
+ /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from
+ having a mixed encoded URL later */
+
+ OString sUTF8 = OUStringToOString(strURL, RTL_TEXTENCODING_UTF8);
+
+ /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */
+
+ SAL_WARN_IF(
+ sUTF8.getLength() != strURL.getLength() &&
+ strURL.matchIgnoreAsciiCase("file:\\")
+ , "sal.osl"
+ ,"osl_getSystemPathFromFileURL: \"" << strURL << "\" is not encoded !!!");
+
+ if (auto sDecodedURL = osl_decodeURL_(sUTF8))
+ {
+ /* Replace backslashes and pipes */
+
+ sDecodedURL = sDecodedURL->replace('/', '\\').replace('|', ':');
+
+ /* Must start with "file:/" */
+ if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\") )
+ {
+ sal_uInt32 nSkip;
+
+ if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\\\") )
+ nSkip = 8;
+ else if (
+ sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\localhost\\") ||
+ sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\127.0.0.1\\")
+ )
+ nSkip = 17;
+ else if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\") )
+ nSkip = 5;
+ else
+ nSkip = 6;
+
+ const sal_uInt32 nDecodedLen = sDecodedURL->getLength();
+
+ /* Indicates local root */
+ if ( nDecodedLen == nSkip )
+ sTempPath = WSTR_SYSTEM_ROOT_PATH;
+ else
+ {
+ /* do not separate the directory and file case, so the maximal path length without prefix is MAX_PATH-12 */
+ if ( nDecodedLen - nSkip <= MAX_PATH - 12 )
+ {
+ sTempPath = sDecodedURL->subView(nSkip);
+ }
+ else
+ {
+ sDecodedURL = GetCaseCorrectPathName(sDecodedURL->subView(nSkip));
+ if (sDecodedURL->getLength() <= MAX_PATH - 12
+ || sDecodedURL->startsWith(WSTR_SYSTEM_ROOT_PATH)
+ || sDecodedURL->startsWith(WSTR_LONG_PATH_PREFIX))
+ {
+ sTempPath = *sDecodedURL;
+ }
+ else if (sDecodedURL->startsWith("\\\\"))
+ {
+ /* it should be an UNC path, use the according prefix */
+ sTempPath = OUString::Concat(WSTR_LONG_PATH_PREFIX_UNC) + sDecodedURL->subView(2);
+ }
+ else
+ {
+ sTempPath = WSTR_LONG_PATH_PREFIX + *sDecodedURL;
+ }
+ }
+ }
+
+ if (IsValidFilePath(sTempPath, VALIDATEPATH_ALLOW_ELLIPSE, &sTempPath))
+ nError = osl_File_E_None;
+ }
+ else if ( bAllowRelative ) /* This maybe a relative file URL */
+ {
+ /* In future the relative path could be converted to absolute if it is too long */
+ sTempPath = *sDecodedURL;
+
+ if (IsValidFilePath(sTempPath, VALIDATEPATH_ALLOW_RELATIVE | VALIDATEPATH_ALLOW_ELLIPSE, &sTempPath))
+ nError = osl_File_E_None;
+ }
+ else
+ SAL_INFO_IF(nError, "sal.osl",
+ "osl_getSystemPathFromFileURL: \"" << strURL << "\" is not an absolute FileURL");
+
+ }
+
+ if ( osl_File_E_None == nError )
+ rtl_uString_assign(pustrPath, sTempPath.pData);
+
+ SAL_INFO_IF(nError, "sal.osl",
+ "osl_getSystemPathFromFileURL: \"" << strURL << "\" is not a FileURL");
+
+ return nError;
+}
+
+oslFileError osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** pstrURL )
+{
+ oslFileError nError = osl_File_E_INVAL; /* Assume failure */
+ OUString sTempURL;
+ DWORD dwPathType = PATHTYPE_ERROR;
+
+ if (strPath)
+ dwPathType = IsValidFilePath(OUString::unacquired(&strPath), VALIDATEPATH_ALLOW_RELATIVE, nullptr);
+
+ if (dwPathType)
+ {
+ OUString sTempPath;
+ const OUString& sPath = OUString::unacquired(&strPath);
+
+ if ( dwPathType & PATHTYPE_IS_LONGPATH )
+ {
+ /* the path has the longpath prefix, lets remove it */
+ switch ( dwPathType & PATHTYPE_MASK_TYPE )
+ {
+ case PATHTYPE_ABSOLUTE_UNC:
+ static_assert(WSTR_LONG_PATH_PREFIX_UNC.size() == 8,
+ "Unexpected long path UNC prefix!");
+
+ /* generate the normal UNC path */
+ sTempPath = "\\\\" + sPath.copy(8).replace('\\', '/');
+ break;
+
+ case PATHTYPE_ABSOLUTE_LOCAL:
+ static_assert(WSTR_LONG_PATH_PREFIX.size() == 4,
+ "Unexpected long path prefix!");
+
+ /* generate the normal path */
+ sTempPath = sPath.copy(4).replace('\\', '/');
+ break;
+
+ default:
+ OSL_FAIL( "Unexpected long path format!" );
+ sTempPath = sPath.replace('\\', '/');
+ break;
+ }
+ }
+ else
+ {
+ /* Replace backslashes */
+ sTempPath = sPath.replace('\\', '/');
+ }
+
+ switch ( dwPathType & PATHTYPE_MASK_TYPE )
+ {
+ case PATHTYPE_RELATIVE:
+ sTempURL = sTempPath;
+ nError = osl_File_E_None;
+ break;
+ case PATHTYPE_ABSOLUTE_UNC:
+ sTempURL = "file:" + sTempPath;
+ nError = osl_File_E_None;
+ break;
+ case PATHTYPE_ABSOLUTE_LOCAL:
+ sTempURL = "file:///" + sTempPath;
+ nError = osl_File_E_None;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ( osl_File_E_None == nError )
+ {
+ /* Encode the URL */
+ rtl_uString_assign(pstrURL, osl_encodeURL_(sTempURL).pData);
+ OSL_ASSERT(*pstrURL != nullptr);
+ }
+
+ SAL_INFO_IF(nError, "sal.osl",
+ "osl_getFileURLFromSystemPath: \"" << OUString::unacquired(&strPath) << "\" is not a systemPath");
+ return nError;
+}
+
+oslFileError SAL_CALL osl_getSystemPathFromFileURL(
+ rtl_uString *ustrURL, rtl_uString **pustrPath)
+{
+ return osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrURL), pustrPath, true);
+}
+
+oslFileError SAL_CALL osl_searchFileURL(
+ rtl_uString *ustrFileName,
+ rtl_uString *ustrSystemSearchPath,
+ rtl_uString **pustrPath)
+{
+ OUString ustrUNCPath;
+ OUString ustrSysPath;
+ oslFileError error;
+
+ /* First try to interpret the file name as a URL even a relative one */
+ error = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrFileName), &ustrUNCPath.pData, true);
+
+ /* So far we either have an UNC path or something invalid
+ Now create a system path */
+ if ( osl_File_E_None == error )
+ error = osl_getSystemPathFromFileURL_(ustrUNCPath, &ustrSysPath.pData, true);
+
+ if ( osl_File_E_None == error )
+ {
+ DWORD nBufferLength;
+ DWORD dwResult;
+ LPWSTR lpBuffer = nullptr;
+ LPWSTR lpszFilePart;
+
+ /* Repeat calling SearchPath ...
+ Start with MAX_PATH for the buffer. In most cases this
+ will be enough and does not force the loop to run twice */
+ dwResult = MAX_PATH;
+
+ do
+ {
+ /* If search path is empty use a nullptr pointer instead according to MSDN documentation of SearchPath */
+ LPCWSTR lpszSearchPath = ustrSystemSearchPath && ustrSystemSearchPath->length ? o3tl::toW(ustrSystemSearchPath->buffer) : nullptr;
+ LPCWSTR lpszSearchFile = o3tl::toW(ustrSysPath.getStr());
+
+ /* Allocate space for buffer according to previous returned count of required chars */
+ /* +1 is not necessary if we follow MSDN documentation but for robustness we do so */
+ nBufferLength = dwResult + 1;
+ lpBuffer = lpBuffer ?
+ static_cast<LPWSTR>(realloc(lpBuffer, nBufferLength * sizeof(WCHAR))) :
+ static_cast<LPWSTR>(malloc(nBufferLength * sizeof(WCHAR)));
+
+ dwResult = SearchPathW( lpszSearchPath, lpszSearchFile, nullptr, nBufferLength, lpBuffer, &lpszFilePart );
+ } while ( dwResult && dwResult >= nBufferLength );
+
+ /* ... until an error occurs or buffer is large enough.
+ dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
+
+ if ( dwResult )
+ {
+ ustrSysPath = o3tl::toU(lpBuffer);
+ error = osl_getFileURLFromSystemPath(ustrSysPath.pData, pustrPath);
+ }
+ else
+ {
+ WIN32_FIND_DATAW aFindFileData;
+ HANDLE hFind;
+
+ /* something went wrong, perhaps the path was absolute */
+ error = oslTranslateFileError( GetLastError() );
+
+ hFind = FindFirstFileW(o3tl::toW(ustrSysPath.getStr()), &aFindFileData);
+
+ if ( IsValidHandle(hFind) )
+ {
+ error = osl_getFileURLFromSystemPath(ustrSysPath.pData, pustrPath);
+ FindClose( hFind );
+ }
+ }
+
+ free( lpBuffer );
+ }
+
+ return error;
+}
+
+oslFileError SAL_CALL osl_getAbsoluteFileURL( rtl_uString* ustrBaseURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL )
+{
+ oslFileError eError = osl_File_E_None;
+ OUString ustrRelSysPath;
+ OUString ustrBaseSysPath;
+
+ if ( ustrBaseURL && ustrBaseURL->length )
+ {
+ eError = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrBaseURL), &ustrBaseSysPath.pData, false);
+ OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with relative or invalid base URL" );
+ }
+ if (eError == osl_File_E_None)
+ {
+ eError = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrRelativeURL), &ustrRelSysPath.pData,
+ !ustrBaseSysPath.isEmpty());
+ OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" );
+ }
+
+ if ( !eError )
+ {
+ OUString sResultPath;
+/*@@@ToDo
+ The whole FileURL implementation should be merged
+ with the rtl/uri class.
+*/
+ // If ustrRelSysPath is absolute, we don't need ustrBaseSysPath.
+ if (!ustrBaseSysPath.isEmpty() && !isAbsolute(ustrRelSysPath))
+ {
+ // ustrBaseSysPath is known here to be a valid absolute path -> its first two characters
+ // are ASCII (either alpha + colon, or double backslashes)
+
+ // Don't use SetCurrentDirectoryW together with GetFullPathNameW, because:
+ // (a) it needs synchronization and may affect threads that may access relative paths;
+ // (b) it would give wrong results for non-existing base path (allowed by RFC2396).
+
+ if (startsWithDriveColon(ustrRelSysPath))
+ {
+ // Special case: a path relative to a specific drive's current directory.
+ // Should we error out here?
+
+ // If ustrBaseSysPath is on the same drive as ustrRelSysPath, then take base path
+ // as is; otherwise, use current directory on ustrRelSysPath's drive as base path
+ if (onSameDrive(ustrRelSysPath, ustrBaseSysPath))
+ {
+ sResultPath = combinePath(ustrBaseSysPath, ustrRelSysPath.subView(2));
+ }
+ else
+ {
+ // Call GetFullPathNameW to get current directory on ustrRelSysPath's drive
+ wchar_t baseDrive[3] = { ustrRelSysPath[0], ':' }; // just "C:"
+ osl::LongPathBuffer<wchar_t> aBuf(MAX_LONG_PATH);
+ DWORD dwResult
+ = GetFullPathNameW(baseDrive, aBuf.getBufSizeInSymbols(), aBuf, nullptr);
+ if (dwResult)
+ {
+ if (dwResult >= aBuf.getBufSizeInSymbols())
+ eError = osl_File_E_INVAL;
+ else
+ sResultPath = combinePath(o3tl::toU(aBuf), ustrRelSysPath.subView(2));
+ }
+ else
+ eError = oslTranslateFileError(GetLastError());
+ }
+ }
+ else
+ {
+ // Is this a rooted relative path (starting with a backslash)?
+ // Then we need only root from base. E.g.,
+ // ustrBaseSysPath is "\\server\share\path1\" and ustrRelSysPath is "\path2\to\file"
+ // => \\server\share\path2\to\file
+ // ustrBaseSysPath is "D:\path1\" and ustrRelSysPath is "\path2\to\file"
+ // => D:\path2\to\file
+ auto sBaseView(pathView(ustrBaseSysPath, ustrRelSysPath.startsWith("\\")));
+ sResultPath = combinePath(sBaseView, ustrRelSysPath);
+ }
+ }
+ else
+ sResultPath = ustrRelSysPath;
+
+ if (eError == osl_File_E_None)
+ {
+ sResultPath = removeRelativeParts(sResultPath);
+ eError = osl_getFileURLFromSystemPath(sResultPath.pData, pustrAbsoluteURL);
+ }
+ }
+
+ return eError;
+}
+
+oslFileError SAL_CALL osl_getCanonicalName( rtl_uString *strRequested, rtl_uString **strValid )
+{
+ rtl_uString_newFromString(strValid, strRequested);
+ return osl_File_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/file_url.hxx b/sal/osl/w32/file_url.hxx
new file mode 100644
index 000000000..86ce27060
--- /dev/null
+++ b/sal/osl/w32/file_url.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_W32_FILE_URL_HXX
+#define INCLUDED_SAL_OSL_W32_FILE_URL_HXX
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+#include <osl/file.h>
+#include <osl/mutex.hxx>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#define PATHTYPE_ERROR 0
+#define PATHTYPE_RELATIVE 1
+#define PATHTYPE_ABSOLUTE_UNC 2
+#define PATHTYPE_ABSOLUTE_LOCAL 3
+#define PATHTYPE_MASK_TYPE 0xFF
+#define PATHTYPE_IS_VOLUME 0x0100
+#define PATHTYPE_IS_SERVER 0x0200
+#define PATHTYPE_IS_LONGPATH 0x0400
+
+#define VALIDATEPATH_NORMAL 0x0000
+#define VALIDATEPATH_ALLOW_ELLIPSE 0x0002
+#define VALIDATEPATH_ALLOW_RELATIVE 0x0004
+#define VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD 0x0010
+
+#define MAX_LONG_PATH 32767
+
+DWORD IsValidFilePath (
+ const OUString& path,
+ DWORD dwFlags,
+ OUString* corrected
+);
+
+oslFileError osl_getSystemPathFromFileURL_ (
+ const OUString& strURL,
+ rtl_uString** pustrPath,
+ bool bAllowRelative
+);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/filetime.hxx b/sal/osl/w32/filetime.hxx
new file mode 100644
index 000000000..dc355591a
--- /dev/null
+++ b/sal/osl/w32/filetime.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SAL_OSL_W32_FILETIME_HXX
+#define INCLUDED_SAL_OSL_W32_FILETIME_HXX
+
+#include <sal/config.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <osl/time.h>
+
+BOOL TimeValueToFileTime(TimeValue const* cpTimeVal, FILETIME* pFTime);
+
+BOOL FileTimeToTimeValue(FILETIME const* cpFTime, TimeValue* pTimeVal);
+
+namespace osl::detail
+{
+inline __int64 getFiletime(FILETIME const& ft)
+{
+ return (DWORD64(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+}
+
+inline void setFiletime(FILETIME& ft, __int64 value)
+{
+ ft.dwHighDateTime = value >> 32;
+ ft.dwLowDateTime = value & 0xFFFFFFFF;
+}
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sal/osl/w32/interlck.cxx b/sal/osl/w32/interlck.cxx
new file mode 100644
index 000000000..b9ecf6d78
--- /dev/null
+++ b/sal/osl/w32/interlck.cxx
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.h"
+
+#include <osl/interlck.h>
+
+oslInterlockedCount SAL_CALL osl_incrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ return InterlockedIncrement(pCount);
+}
+
+oslInterlockedCount SAL_CALL osl_decrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ return InterlockedDecrement(pCount);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/memory.cxx b/sal/osl/w32/memory.cxx
new file mode 100644
index 000000000..1f1a5b516
--- /dev/null
+++ b/sal/osl/w32/memory.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <oslmemory.h>
+
+#include <malloc.h>
+
+void* osl_aligned_alloc( sal_Size align, sal_Size size )
+{
+ return _aligned_malloc(size, align);
+}
+
+void osl_aligned_free( void* p )
+{
+ _aligned_free(p);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/module.cxx b/sal/osl/w32/module.cxx
new file mode 100644
index 000000000..8379e14b2
--- /dev/null
+++ b/sal/osl/w32/module.cxx
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.h"
+
+#include "file_url.hxx"
+#include "path_helper.hxx"
+
+#include <osl/module.h>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/file.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <vector>
+
+/*
+ under WIN32, we use the void* oslModule
+ as a WIN32 HANDLE (which is also a 32-bit value)
+*/
+
+oslModule SAL_CALL osl_loadModule(rtl_uString *strModuleName, sal_Int32 /*nRtldMode*/ )
+{
+ HMODULE h;
+#if OSL_DEBUG_LEVEL < 2
+ UINT errorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
+#endif
+ rtl_uString* Module = nullptr;
+ oslModule ret = nullptr;
+ oslFileError nError;
+
+ SAL_INFO( "sal.osl", "osl_loadModule: " << OUString(strModuleName) );
+ OSL_ASSERT(strModuleName);
+
+ nError = osl_getSystemPathFromFileURL(strModuleName, &Module);
+
+ if ( osl_File_E_None != nError )
+ rtl_uString_assign(&Module, strModuleName);
+
+ h = LoadLibraryW(o3tl::toW(Module->buffer));
+
+ if (h == nullptr)
+ h = LoadLibraryExW(o3tl::toW(Module->buffer), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
+
+ // In case of long path names (\\?\c:\...) try to shorten the filename.
+ // LoadLibrary cannot handle file names which exceed 260 letters.
+ // In case the path is too long, the function will fail. However, the error
+ // code can be different. For example, it returned ERROR_FILENAME_EXCED_RANGE
+ // on Windows XP and ERROR_INSUFFICIENT_BUFFER on Windows 7 (64bit)
+ if (h == nullptr && Module->length > 260)
+ {
+ std::vector<WCHAR> vec(Module->length + 1);
+ DWORD len = GetShortPathNameW(o3tl::toW(Module->buffer), vec.data(), Module->length + 1);
+ if (len )
+ {
+ h = LoadLibraryW(vec.data());
+
+ if (h == nullptr)
+ h = LoadLibraryExW(vec.data(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
+ }
+ }
+
+ ret = static_cast<oslModule>(h);
+ rtl_uString_release(Module);
+#if OSL_DEBUG_LEVEL < 2
+ SetErrorMode(errorMode);
+#endif
+
+ return ret;
+}
+
+oslModule SAL_CALL osl_loadModuleAscii(const char *pModuleName, sal_Int32 )
+{
+ HMODULE h;
+ UINT errorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
+ oslModule ret = nullptr;
+
+ SAL_INFO( "sal.osl", "osl_loadModule: " << pModuleName );
+ OSL_ASSERT(pModuleName);
+
+ h = LoadLibraryA(pModuleName);
+ if (h == nullptr)
+ h = LoadLibraryExA(pModuleName, nullptr,
+ LOAD_WITH_ALTERED_SEARCH_PATH);
+
+ ret = static_cast<oslModule>(h);
+ SetErrorMode(errorMode);
+
+ return ret;
+}
+
+oslModule osl_loadModuleRelativeAscii(
+ oslGenericFunction baseModule, char const * relativePath, sal_Int32 mode)
+{
+ return osl_loadModuleRelative(baseModule, OUString::createFromAscii(relativePath).pData, mode);
+}
+
+sal_Bool SAL_CALL
+osl_getModuleHandle(rtl_uString *pModuleName, oslModule *pResult)
+{
+ LPCWSTR pName = pModuleName ? o3tl::toW(pModuleName->buffer) : nullptr;
+ HMODULE h = GetModuleHandleW(pName);
+ if( h )
+ {
+ *pResult = static_cast<oslModule>(h);
+ return true;
+ }
+
+ return false;
+}
+
+void SAL_CALL osl_unloadModule(oslModule Module)
+{
+ FreeLibrary(static_cast<HMODULE>(Module));
+}
+
+void* SAL_CALL osl_getSymbol(oslModule Module, rtl_uString *strSymbolName)
+{
+ /* casting from a function pointer to a data pointer is invalid
+ be in this case unavoidable because the API has to stay
+ compatible. We need to keep this function which returns a
+ void* by definition */
+ return reinterpret_cast<void*>(osl_getFunctionSymbol(Module, strSymbolName));
+}
+
+oslGenericFunction SAL_CALL osl_getFunctionSymbol( oslModule Module, rtl_uString *strSymbolName )
+{
+ rtl_String *symbolName = nullptr;
+ oslGenericFunction address;
+
+ OSL_ASSERT(Module);
+ OSL_ASSERT(strSymbolName);
+
+ rtl_uString2String(
+ &symbolName,
+ strSymbolName->buffer,
+ strSymbolName->length,
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS
+ );
+
+ address=osl_getAsciiFunctionSymbol(Module, rtl_string_getStr(symbolName));
+ rtl_string_release(symbolName);
+
+ return address;
+}
+
+oslGenericFunction SAL_CALL
+osl_getAsciiFunctionSymbol( oslModule Module, const char *pSymbol )
+{
+ oslGenericFunction fncAddr = nullptr;
+
+ if( pSymbol )
+ fncAddr=reinterpret_cast<oslGenericFunction>(GetProcAddress(static_cast<HMODULE>(Module), pSymbol));
+
+ return fncAddr;
+}
+
+sal_Bool SAL_CALL osl_getModuleURLFromAddress( void *pv, rtl_uString **pustrURL )
+{
+ HMODULE hModule{};
+ GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
+ | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ static_cast<LPCWSTR>(pv), &hModule);
+ if (!hModule)
+ return false;
+
+ ::osl::LongPathBuffer<sal_Unicode> aBuffer(MAX_LONG_PATH);
+
+ DWORD nch = GetModuleFileNameW(hModule, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols());
+
+ OUString ustrSysPath(aBuffer, nch);
+ return osl_getFileURLFromSystemPath(ustrSysPath.pData, pustrURL) == osl_File_E_None;
+}
+
+sal_Bool SAL_CALL osl_getModuleURLFromFunctionAddress( oslGenericFunction addr, rtl_uString ** ppLibraryUrl )
+{
+ /* casting a function pointer to a data pointer (void*) is
+ not allowed according to the C/C++ standards. In this case
+ it is unavoidable because we have to stay compatible we
+ cannot remove any function. */
+ return osl_getModuleURLFromAddress(reinterpret_cast<void*>(addr), ppLibraryUrl);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/mutex.cxx b/sal/osl/w32/mutex.cxx
new file mode 100644
index 000000000..7de4c41ee
--- /dev/null
+++ b/sal/osl/w32/mutex.cxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "system.h"
+
+#include <osl/mutex.h>
+#include <osl/diagnose.h>
+
+/*
+ Implementation notes:
+ The void* hidden by oslMutex points to a WIN32
+ CRITICAL_SECTION structure.
+*/
+
+oslMutex SAL_CALL osl_createMutex(void)
+{
+ CRITICAL_SECTION* pMutexImpl;
+
+ pMutexImpl = static_cast<CRITICAL_SECTION*>(calloc(sizeof(CRITICAL_SECTION), 1));
+
+ OSL_ASSERT(pMutexImpl); /* alloc successful? */
+
+ InitializeCriticalSection(pMutexImpl);
+
+ return reinterpret_cast<oslMutex>(pMutexImpl);
+}
+
+void SAL_CALL osl_destroyMutex(oslMutex Mutex)
+{
+ CRITICAL_SECTION* pMutexImpl = reinterpret_cast<CRITICAL_SECTION*>(Mutex);
+
+ if (pMutexImpl)
+ {
+ DeleteCriticalSection(pMutexImpl);
+ free(pMutexImpl);
+ }
+}
+
+sal_Bool SAL_CALL osl_acquireMutex(oslMutex Mutex)
+{
+ CRITICAL_SECTION* pMutexImpl = reinterpret_cast<CRITICAL_SECTION*>(Mutex);
+
+ OSL_ASSERT(Mutex);
+
+ EnterCriticalSection(pMutexImpl);
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_tryToAcquireMutex(oslMutex Mutex)
+{
+ CRITICAL_SECTION* pMutexImpl = reinterpret_cast<CRITICAL_SECTION*>(Mutex);
+
+ OSL_ASSERT(Mutex);
+
+ return TryEnterCriticalSection(pMutexImpl) != FALSE;
+}
+
+sal_Bool SAL_CALL osl_releaseMutex(oslMutex Mutex)
+{
+ CRITICAL_SECTION* pMutexImpl = reinterpret_cast<CRITICAL_SECTION*>(Mutex);
+
+ OSL_ASSERT(Mutex);
+
+ LeaveCriticalSection(pMutexImpl);
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/nlsupport.cxx b/sal/osl/w32/nlsupport.cxx
new file mode 100644
index 000000000..f27b97aa7
--- /dev/null
+++ b/sal/osl/w32/nlsupport.cxx
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <wchar.h>
+
+#include "nlsupport.hxx"
+
+#include <osl/mutex.h>
+#include <osl/nlsupport.h>
+#include <osl/diagnose.h>
+#include <osl/process.h>
+#include <rtl/tencinfo.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+/* XXX NOTE:
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/dd373848.aspx
+ * (retrieved 2013-02-13) has some weird description for the LOCALE_SISO*
+ * constants: "The maximum number of characters allowed for this string is
+ * nine, including a terminating null character." NINE?!? In ISO 639 and ISO
+ * 3166?
+ */
+#define ELP_LANGUAGE_FIELD_LENGTH 4
+#define ELP_COUNTRY_FIELD_LENGTH 3
+
+namespace {
+
+/** Struct used in EnumLocalesProcW() called via EnumSystemLocalesW() to obtain
+ available locales.
+*/
+struct EnumLocalesParams
+{
+ WCHAR Language[ELP_LANGUAGE_FIELD_LENGTH];
+ WCHAR Country[ELP_COUNTRY_FIELD_LENGTH];
+ LCID Locale;
+};
+
+}
+
+static DWORD g_dwTLSLocaleEncId = DWORD(-1);
+
+/*****************************************************************************
+ * callback function test
+ *****************************************************************************/
+
+static BOOL CALLBACK EnumLocalesProcW( LPWSTR lpLocaleStringW )
+{
+ /* check params received via TLS */
+ EnumLocalesParams * params = static_cast<EnumLocalesParams *>(TlsGetValue( g_dwTLSLocaleEncId ));
+ if( nullptr == params || '\0' == params->Language[0] )
+ return FALSE;
+
+ LPWSTR pszEnd;
+ WCHAR langCode[ELP_LANGUAGE_FIELD_LENGTH];
+
+ /* convert hex-string to LCID */
+ LCID localeId = wcstol(lpLocaleStringW, &pszEnd, 16);
+
+ /*
+ get the ISO language code for this locale
+ */
+ if( !GetLocaleInfoW( localeId, LOCALE_SISO639LANGNAME , langCode, ELP_LANGUAGE_FIELD_LENGTH ) )
+ /* retry by going on */
+ return TRUE;
+
+ WCHAR ctryCode[ELP_COUNTRY_FIELD_LENGTH];
+
+ /* continue if language code does not match */
+ if( 0 != wcscmp( langCode, params->Language ) )
+ return TRUE;
+
+ /* check if country code is set and equals the current locale */
+ if( '\0' != params->Country[0] && GetLocaleInfoW( localeId,
+ LOCALE_SISO3166CTRYNAME , ctryCode, ELP_COUNTRY_FIELD_LENGTH ) )
+ {
+ /* save return value in TLS and break if found desired locale */
+ if( 0 == wcscmp( ctryCode, params->Country ) )
+ {
+ params->Locale = localeId;
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* fill with default values for that language */
+ LANGID langId = LANGIDFROMLCID( localeId );
+
+ /* exchange sublanguage with SUBLANG_NEUTRAL */
+ langId = MAKELANGID( PRIMARYLANGID( langId ), SUBLANG_NEUTRAL );
+
+ /* and use default sorting order */
+ params->Locale = MAKELCID( langId, SORT_DEFAULT );
+
+ return FALSE;
+ }
+
+ /* retry by going on */
+ return TRUE;
+}
+
+static rtl_TextEncoding GetTextEncodingFromLCID( LCID localeId )
+{
+ /* query ansi codepage for given locale */
+ WCHAR ansiCP[6];
+ if( !localeId || !GetLocaleInfoW( localeId, LOCALE_IDEFAULTANSICODEPAGE, ansiCP, 6 ) )
+ return RTL_TEXTENCODING_DONTKNOW;
+
+ /* if GetLocaleInfo returns "0", it is a UNICODE only locale */
+ if( 0 == wcscmp( ansiCP, L"0" ) )
+ return RTL_TEXTENCODING_UNICODE;
+
+ /* values returned from GetLocaleInfo are decimal based */
+ WCHAR *pwcEnd;
+ UINT codepage = wcstol( ansiCP, &pwcEnd, 10 );
+
+ /* find matching rtl encoding */
+ return rtl_getTextEncodingFromWindowsCodePage( codepage );
+}
+
+rtl_TextEncoding SAL_CALL osl_getTextEncodingFromLocale( rtl_Locale * pLocale )
+{
+ struct EnumLocalesParams params = { L"", L"", 0 };
+
+ /* initialise global TLS id */
+ if( DWORD(-1) == g_dwTLSLocaleEncId )
+ {
+ oslMutex globalMutex = * osl_getGlobalMutex();
+
+ /* initializing must be thread save */
+ osl_acquireMutex( globalMutex );
+
+ if( DWORD(-1) == g_dwTLSLocaleEncId )
+ g_dwTLSLocaleEncId = TlsAlloc();
+
+ osl_releaseMutex( globalMutex );
+ }
+
+ /* if pLocale is NULL, use process locale as default */
+ if( nullptr == pLocale )
+ osl_getProcessLocale( &pLocale );
+
+ /* copy in parameters to structure */
+ if( !pLocale || !pLocale->Language || pLocale->Language->length >= ELP_LANGUAGE_FIELD_LENGTH )
+ return RTL_TEXTENCODING_DONTKNOW;
+
+ wcscpy( params.Language, o3tl::toW(pLocale->Language->buffer) );
+
+ if( pLocale->Country && pLocale->Country->length < ELP_COUNTRY_FIELD_LENGTH )
+ wcscpy( params.Country, o3tl::toW(pLocale->Country->buffer) );
+
+ /* save pointer to local structure in TLS */
+ TlsSetValue( g_dwTLSLocaleEncId, &params );
+
+ /* enum all locales known to Windows */
+ EnumSystemLocalesW( EnumLocalesProcW, LCID_SUPPORTED );
+
+ /* use the LCID found in iteration */
+ return GetTextEncodingFromLCID( params.Locale );
+}
+
+void imp_getProcessLocale( rtl_Locale ** ppLocale )
+{
+ WCHAR langCode[ELP_LANGUAGE_FIELD_LENGTH];
+ WCHAR ctryCode[ELP_COUNTRY_FIELD_LENGTH];
+ LCID localeId;
+
+ OSL_ASSERT( ppLocale );
+
+ /* get the LCID to retrieve information from */
+ localeId = GetUserDefaultLCID();
+
+ /* call GetLocaleInfo to retrieve the iso codes */
+ if( GetLocaleInfoW( localeId, LOCALE_SISO639LANGNAME , langCode, ELP_LANGUAGE_FIELD_LENGTH ) &&
+ GetLocaleInfoW( localeId, LOCALE_SISO3166CTRYNAME , ctryCode, ELP_COUNTRY_FIELD_LENGTH ) )
+ {
+ *ppLocale = rtl_locale_register( o3tl::toU(langCode), o3tl::toU(ctryCode), u"" );
+ }
+ else
+ {
+ *ppLocale = rtl_locale_register( u"C", u"", u"" );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/nlsupport.hxx b/sal/osl/w32/nlsupport.hxx
new file mode 100644
index 000000000..2d818c378
--- /dev/null
+++ b/sal/osl/w32/nlsupport.hxx
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SAL_OSL_W32_NLSUPPORT_HXX
+#define INCLUDED_SAL_OSL_W32_NLSUPPORT_HXX
+
+#include <sal/config.h>
+
+#include <rtl/locale.h>
+
+void imp_getProcessLocale(rtl_Locale** ppLocale);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sal/osl/w32/path_helper.cxx b/sal/osl/w32/path_helper.cxx
new file mode 100644
index 000000000..a2dd134fd
--- /dev/null
+++ b/sal/osl/w32/path_helper.cxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "path_helper.hxx"
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <wchar.h>
+
+constexpr OUStringLiteral BACKSLASH (u"\\");
+constexpr OUStringLiteral SLASH (u"/");
+
+void osl_systemPathEnsureSeparator(/*inout*/ rtl_uString** ppustrPath)
+{
+ OSL_PRECOND(ppustrPath && (nullptr != *ppustrPath),
+ "osl_systemPathEnsureSeparator: Invalid parameter");
+
+ OUString path(*ppustrPath);
+ sal_Int32 i = std::max<sal_Int32>(path.lastIndexOf(BACKSLASH), path.lastIndexOf(SLASH));
+
+ if (i < (path.getLength()-1))
+ {
+ path += BACKSLASH;
+ rtl_uString_assign(ppustrPath, path.pData);
+ }
+
+ SAL_WARN_IF( !path.endsWith(BACKSLASH),
+ "sal.osl",
+ "osl_systemPathEnsureSeparator: Post condition failed");
+}
+
+void osl_systemPathRemoveSeparator(/*inout*/ rtl_uString** ppustrPath)
+{
+ OUString path(*ppustrPath);
+
+ if (!osl::systemPathIsLogicalDrivePattern(path))
+ {
+ sal_Int32 i = std::max<sal_Int32>(path.lastIndexOf(BACKSLASH), path.lastIndexOf(SLASH));
+
+ if (i > -1 && (i == (path.getLength() - 1)))
+ {
+ path = OUString(path.getStr(), path.getLength() - 1);
+ rtl_uString_assign(ppustrPath, path.pData);
+ }
+ }
+}
+
+// is [A-Za-z]:[/|\]\0
+const char* const LDP = ":";
+const char* const LDP_WITH_BACKSLASH = ":\\";
+const char* const LDP_WITH_SLASH = ":/";
+
+// degenerated case returned by the Windows FileOpen dialog
+// when someone enters for instance "x:filename", the Win32
+// API accepts this case
+const char* const LDP_WITH_DOT_BACKSLASH = ":.\\";
+
+bool osl_systemPathIsLogicalDrivePattern(/*in*/ const rtl_uString* pustrPath)
+{
+ const sal_Unicode* p = rtl_uString_getStr(const_cast<rtl_uString*>(pustrPath));
+ if (iswalpha(*p++))
+ {
+ return ((0 == rtl_ustr_ascii_compare(p, LDP)) ||
+ (0 == rtl_ustr_ascii_compare(p, LDP_WITH_BACKSLASH)) ||
+ (0 == rtl_ustr_ascii_compare(p, LDP_WITH_SLASH)) ||
+ (0 == rtl_ustr_ascii_compare(p, LDP_WITH_DOT_BACKSLASH)));
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/path_helper.hxx b/sal/osl/w32/path_helper.hxx
new file mode 100644
index 000000000..bcb4f83af
--- /dev/null
+++ b/sal/osl/w32/path_helper.hxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_W32_PATH_HELPER_HXX
+#define INCLUDED_SAL_OSL_W32_PATH_HELPER_HXX
+
+#include <sal/config.h>
+
+#include <osl/diagnose.h>
+#include <osl/file.h>
+#include <rtl/alloc.h>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+/**
+ Adds a trailing path separator to the given system path if not
+ already there and if the path is not the root path or a logical
+ drive alone
+*/
+
+void osl_systemPathEnsureSeparator(/*inout*/ rtl_uString** ppustrPath);
+
+/**
+ Removes the last separator from the given system path if any and
+ if the path is not the root path '\'
+*/
+
+void osl_systemPathRemoveSeparator(/*inout*/ rtl_uString** ppustrPath);
+
+/**
+ Returns whether a given path is only a logical drive pattern or not.
+ A logical drive pattern is something like "a:\", "c:\".
+ No logical drive pattern is something like "c:\test"
+*/
+
+bool osl_systemPathIsLogicalDrivePattern(/*in*/ const rtl_uString* pustrPath);
+
+namespace osl
+{
+
+/**
+ Adds a trailing path separator to the given system path if not
+ already there and if the path is not the root path or a logical
+ drive alone
+*/
+
+inline void systemPathEnsureSeparator(/*inout*/ OUString& Path)
+{
+ osl_systemPathEnsureSeparator(&Path.pData);
+}
+
+/**
+ Removes the last separator from the given system path if any and
+ if the path is not the root path '\'
+*/
+
+inline void systemPathRemoveSeparator(/*inout*/ OUString& Path)
+{
+ osl_systemPathRemoveSeparator(&Path.pData);
+}
+
+inline bool systemPathIsLogicalDrivePattern(/*in*/ const OUString& path)
+{
+ return osl_systemPathIsLogicalDrivePattern(path.pData);
+}
+
+template< class T >
+class LongPathBuffer
+{
+ T* m_pBuffer;
+ sal_uInt32 m_nCharNum;
+
+ LongPathBuffer();
+ LongPathBuffer( const LongPathBuffer& );
+ LongPathBuffer& operator=( const LongPathBuffer& );
+
+public:
+ explicit LongPathBuffer( sal_uInt32 nCharNum )
+ : m_pBuffer( static_cast<T*>( malloc( nCharNum * sizeof( T ) ) ) )
+ , m_nCharNum( nCharNum )
+ {
+ OSL_ENSURE( m_pBuffer, "Can not allocate the buffer!" );
+ }
+
+ ~LongPathBuffer()
+ {
+ if ( m_pBuffer )
+ free( m_pBuffer );
+ m_pBuffer = nullptr;
+ }
+
+ sal_uInt32 getBufSizeInSymbols()
+ {
+ return m_nCharNum;
+ }
+
+ operator T* ()
+ {
+ return m_pBuffer;
+ }
+
+};
+
+} // end namespace osl
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/pipe.cxx b/sal/osl/w32/pipe.cxx
new file mode 100644
index 000000000..c94441e63
--- /dev/null
+++ b/sal/osl/w32/pipe.cxx
@@ -0,0 +1,489 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.h"
+
+#include <osl/pipe.h>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/mutex.h>
+#include <osl/conditn.h>
+#include <osl/interlck.h>
+#include <osl/process.h>
+#include <rtl/alloc.h>
+#include <sal/log.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <cassert>
+#include <string.h>
+
+#define PIPESYSTEM "\\\\.\\pipe\\"
+#define PIPEPREFIX "OSL_PIPE_"
+
+struct oslPipeImpl
+{
+ oslInterlockedCount m_Reference;
+ HANDLE m_File;
+ HANDLE m_NamedObject;
+ PSECURITY_ATTRIBUTES m_Security;
+ HANDLE m_ReadEvent;
+ HANDLE m_WriteEvent;
+ HANDLE m_AcceptEvent;
+ rtl_uString* m_Name;
+ oslPipeError m_Error;
+ bool m_bClosed;
+};
+
+static oslPipe osl_createPipeImpl(void)
+{
+ oslPipe pPipe;
+
+ pPipe = static_cast< oslPipe >(rtl_allocateZeroMemory(sizeof(struct oslPipeImpl)));
+
+ pPipe->m_bClosed = false;
+ pPipe->m_Reference = 0;
+ pPipe->m_Name = nullptr;
+ pPipe->m_File = INVALID_HANDLE_VALUE;
+ pPipe->m_NamedObject = nullptr;
+
+ pPipe->m_ReadEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ pPipe->m_WriteEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ pPipe->m_AcceptEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
+
+ return pPipe;
+}
+
+static void osl_destroyPipeImpl(oslPipe pPipe)
+{
+ if (!pPipe)
+ return;
+
+ if (pPipe->m_NamedObject)
+ CloseHandle(pPipe->m_NamedObject);
+
+ if (pPipe->m_Security)
+ {
+ free(pPipe->m_Security->lpSecurityDescriptor);
+ free(pPipe->m_Security);
+ }
+
+ CloseHandle(pPipe->m_ReadEvent);
+ CloseHandle(pPipe->m_WriteEvent);
+ CloseHandle(pPipe->m_AcceptEvent);
+
+ if (pPipe->m_Name)
+ rtl_uString_release(pPipe->m_Name);
+
+ free(pPipe);
+}
+
+oslPipe SAL_CALL osl_createPipe(rtl_uString *strPipeName, oslPipeOptions Options,
+ oslSecurity Security)
+{
+ rtl_uString* name = nullptr;
+ rtl_uString* path = nullptr;
+ rtl_uString* temp = nullptr;
+ oslPipe pPipe;
+
+ PSECURITY_ATTRIBUTES pSecAttr = nullptr;
+
+ rtl_uString_newFromAscii(&path, PIPESYSTEM);
+ rtl_uString_newFromAscii(&name, PIPEPREFIX);
+
+ if (Security)
+ {
+ rtl_uString *Ident = nullptr;
+ rtl_uString *Delim = nullptr;
+
+ OSL_VERIFY(osl_getUserIdent(Security, &Ident));
+ rtl_uString_newFromAscii(&Delim, "_");
+
+ rtl_uString_newConcat(&temp, name, Ident);
+ rtl_uString_newConcat(&name, temp, Delim);
+
+ rtl_uString_release(Ident);
+ rtl_uString_release(Delim);
+ }
+ else
+ {
+ if (Options & osl_Pipe_CREATE)
+ {
+ PSECURITY_DESCRIPTOR pSecDesc;
+
+ pSecDesc = static_cast< PSECURITY_DESCRIPTOR >(malloc(SECURITY_DESCRIPTOR_MIN_LENGTH));
+ assert(pSecDesc); // Don't handle OOM conditions
+
+ /* add a NULL disc. ACL to the security descriptor */
+ OSL_VERIFY(InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION));
+ OSL_VERIFY(SetSecurityDescriptorDacl(pSecDesc, TRUE, nullptr, FALSE));
+
+ pSecAttr = static_cast< PSECURITY_ATTRIBUTES >(malloc(sizeof(SECURITY_ATTRIBUTES)));
+ assert(pSecAttr); // Don't handle OOM conditions
+ pSecAttr->nLength = sizeof(SECURITY_ATTRIBUTES);
+ pSecAttr->lpSecurityDescriptor = pSecDesc;
+ pSecAttr->bInheritHandle = TRUE;
+ }
+ }
+
+ rtl_uString_assign(&temp, name);
+ rtl_uString_newConcat(&name, temp, strPipeName);
+
+ /* alloc memory */
+ pPipe = osl_createPipeImpl();
+
+ assert(pPipe); // if osl_createPipeImpl() cannot init. a new pipe, this is a failure
+
+ osl_atomic_increment(&(pPipe->m_Reference));
+
+ /* build system pipe name */
+ rtl_uString_assign(&temp, path);
+ rtl_uString_newConcat(&path, temp, name);
+ rtl_uString_release(temp);
+ temp = nullptr;
+
+ if (Options & osl_Pipe_CREATE)
+ {
+ SetLastError(ERROR_SUCCESS);
+
+ pPipe->m_NamedObject = CreateMutexW(nullptr, FALSE, o3tl::toW(name->buffer));
+
+ if (pPipe->m_NamedObject)
+ {
+ if (GetLastError() != ERROR_ALREADY_EXISTS)
+ {
+ pPipe->m_Security = pSecAttr;
+ rtl_uString_assign(&pPipe->m_Name, name);
+
+ /* try to open system pipe */
+ pPipe->m_File = CreateNamedPipeW(
+ o3tl::toW(path->buffer),
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,
+ PIPE_UNLIMITED_INSTANCES,
+ 4096, 4096,
+ NMPWAIT_WAIT_FOREVER,
+ pPipe->m_Security);
+
+ if (pPipe->m_File != INVALID_HANDLE_VALUE)
+ {
+ rtl_uString_release( name );
+ rtl_uString_release( path );
+
+ return pPipe;
+ }
+ }
+ else
+ {
+ CloseHandle(pPipe->m_NamedObject);
+ pPipe->m_NamedObject = nullptr;
+ }
+ }
+ }
+ else
+ {
+ bool bPipeAvailable;
+
+ do
+ {
+ /* free instance should be available first */
+ bPipeAvailable = WaitNamedPipeW(o3tl::toW(path->buffer), NMPWAIT_WAIT_FOREVER);
+
+ /* first try to open system pipe */
+ if (bPipeAvailable)
+ {
+ pPipe->m_File = CreateFileW(
+ o3tl::toW(path->buffer),
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ nullptr);
+
+ if (pPipe->m_File != INVALID_HANDLE_VALUE)
+ {
+ // We got it !
+ rtl_uString_release(name);
+ rtl_uString_release(path);
+
+ // We should try to transfer our privilege to become foreground process
+ // to the other process, so that it may show popups (otherwise, they might
+ // be blocked by SPI_GETFOREGROUNDLOCKTIMEOUT setting -
+ // see SystemParametersInfo function at MSDN
+ ULONG ServerProcessId = 0;
+ if (GetNamedPipeServerProcessId(pPipe->m_File, &ServerProcessId))
+ AllowSetForegroundWindow(ServerProcessId);
+
+ return pPipe;
+ }
+ else
+ {
+ // Pipe instance maybe caught by another client -> try again
+ }
+ }
+ } while (bPipeAvailable);
+ }
+
+ /* if we reach here something went wrong */
+ osl_destroyPipeImpl(pPipe);
+
+ return nullptr;
+}
+
+void SAL_CALL osl_acquirePipe(oslPipe pPipe)
+{
+ osl_atomic_increment(&(pPipe->m_Reference));
+}
+
+void SAL_CALL osl_releasePipe(oslPipe pPipe)
+{
+ if (!pPipe)
+ return;
+
+ if (osl_atomic_decrement(&(pPipe->m_Reference)) == 0)
+ {
+ if (!pPipe->m_bClosed)
+ osl_closePipe(pPipe);
+
+ osl_destroyPipeImpl(pPipe);
+ }
+}
+
+void SAL_CALL osl_closePipe(oslPipe pPipe)
+{
+ if (pPipe && !pPipe->m_bClosed)
+ {
+ pPipe->m_bClosed = true;
+ /* if we have a system pipe close it */
+ if (pPipe->m_File != INVALID_HANDLE_VALUE)
+ {
+ DisconnectNamedPipe(pPipe->m_File);
+ CloseHandle(pPipe->m_File);
+ }
+ }
+}
+
+oslPipe SAL_CALL osl_acceptPipe(oslPipe pPipe)
+{
+ oslPipe pAcceptedPipe = nullptr;
+
+ OVERLAPPED os = {};
+
+ DWORD nBytesTransferred;
+ rtl_uString* path = nullptr;
+ rtl_uString* temp = nullptr;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_acceptPipe: invalid pipe");
+ if (!pPipe)
+ return nullptr;
+
+ SAL_WARN_IF(pPipe->m_File == INVALID_HANDLE_VALUE, "sal.osl.pipe", "osl_acceptPipe: invalid handle");
+
+ os.hEvent = pPipe->m_AcceptEvent;
+ ResetEvent(pPipe->m_AcceptEvent);
+
+ if (!ConnectNamedPipe(pPipe->m_File, &os))
+ {
+ switch (GetLastError())
+ {
+ case ERROR_PIPE_CONNECTED: // Client already connected to pipe
+ case ERROR_NO_DATA: // Client was connected but has already closed pipe end
+ // should only appear in nonblocking mode but in fact does
+ // in blocking asynchronous mode.
+ break;
+ case ERROR_PIPE_LISTENING: // Only for nonblocking mode but see ERROR_NO_DATA
+ case ERROR_IO_PENDING: // This is normal if not client is connected yet
+ case ERROR_MORE_DATA: // Should not happen
+ // blocking call to accept
+ if( !GetOverlappedResult(pPipe->m_File, &os, &nBytesTransferred, TRUE))
+ {
+ // Possible error could be that between ConnectNamedPipe and
+ // GetOverlappedResult a connect took place.
+
+ switch (GetLastError())
+ {
+ case ERROR_PIPE_CONNECTED: // Pipe was already connected
+ case ERROR_NO_DATA: // Pipe was connected but client has already closed -> ver fast client ;-)
+ break; // Everything's fine !!!
+ default:
+ // Something went wrong
+ return nullptr;
+ }
+ }
+ break;
+ default: // All other error say that somethings going wrong.
+ return nullptr;
+ }
+ }
+
+ pAcceptedPipe = osl_createPipeImpl();
+ assert(pAcceptedPipe); // should never be the case that an oslPipe cannot be initialized
+
+ osl_atomic_increment(&(pAcceptedPipe->m_Reference));
+ rtl_uString_assign(&pAcceptedPipe->m_Name, pPipe->m_Name);
+ pAcceptedPipe->m_File = pPipe->m_File;
+
+ rtl_uString_newFromAscii(&temp, PIPESYSTEM);
+ rtl_uString_newConcat(&path, temp, pPipe->m_Name);
+ rtl_uString_release(temp);
+
+ // prepare for next accept
+ pPipe->m_File =
+ CreateNamedPipeW(o3tl::toW(path->buffer),
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,
+ PIPE_UNLIMITED_INSTANCES,
+ 4096, 4096,
+ NMPWAIT_WAIT_FOREVER,
+ pAcceptedPipe->m_Security);
+ rtl_uString_release(path);
+
+ return pAcceptedPipe;
+}
+
+sal_Int32 SAL_CALL osl_receivePipe(oslPipe pPipe,
+ void* pBuffer,
+ sal_Int32 BytesToRead)
+{
+ DWORD nBytes;
+ OVERLAPPED os = {};
+
+ assert(pPipe);
+
+ os.hEvent = pPipe->m_ReadEvent;
+
+ ResetEvent(pPipe->m_ReadEvent);
+
+ if (!ReadFile(pPipe->m_File, pBuffer, BytesToRead, &nBytes, &os) &&
+ ((GetLastError() != ERROR_IO_PENDING) ||
+ !GetOverlappedResult(pPipe->m_File, &os, &nBytes, TRUE)))
+ {
+ DWORD lastError = GetLastError();
+
+ if (lastError == ERROR_MORE_DATA)
+ {
+ nBytes = BytesToRead;
+ }
+ else
+ {
+ if (lastError == ERROR_PIPE_NOT_CONNECTED)
+ nBytes = 0;
+ else
+ nBytes = DWORD(-1);
+
+ pPipe->m_Error = osl_Pipe_E_ConnectionAbort;
+ }
+ }
+
+ return nBytes;
+}
+
+sal_Int32 SAL_CALL osl_sendPipe(oslPipe pPipe,
+ const void* pBuffer,
+ sal_Int32 BytesToSend)
+{
+ DWORD nBytes;
+ OVERLAPPED os = {};
+
+ assert(pPipe);
+
+ os.hEvent = pPipe->m_WriteEvent;
+ ResetEvent(pPipe->m_WriteEvent);
+
+ if (!WriteFile(pPipe->m_File, pBuffer, BytesToSend, &nBytes, &os) &&
+ ((GetLastError() != ERROR_IO_PENDING) ||
+ !GetOverlappedResult(pPipe->m_File, &os, &nBytes, TRUE)))
+ {
+ if (GetLastError() == ERROR_PIPE_NOT_CONNECTED)
+ nBytes = 0;
+ else
+ nBytes = DWORD(-1);
+
+ pPipe->m_Error = osl_Pipe_E_ConnectionAbort;
+ }
+
+ return nBytes;
+}
+
+sal_Int32 SAL_CALL osl_writePipe(oslPipe pPipe, const void *pBuffer , sal_Int32 n)
+{
+ /* loop until all desired bytes were send or an error occurred */
+ sal_Int32 BytesSend = 0;
+ sal_Int32 BytesToSend = n;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_writePipe: invalid pipe");
+ while (BytesToSend > 0)
+ {
+ sal_Int32 RetVal;
+
+ RetVal= osl_sendPipe(pPipe, pBuffer, BytesToSend);
+
+ /* error occurred? */
+ if (RetVal <= 0)
+ break;
+
+ BytesToSend -= RetVal;
+ BytesSend += RetVal;
+ pBuffer= static_cast< char const* >(pBuffer) + RetVal;
+ }
+
+ return BytesSend;
+}
+
+sal_Int32 SAL_CALL osl_readPipe(oslPipe pPipe, void *pBuffer, sal_Int32 n)
+{
+ /* loop until all desired bytes were read or an error occurred */
+ sal_Int32 BytesRead = 0;
+ sal_Int32 BytesToRead = n;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_readPipe: invalid pipe");
+ while (BytesToRead > 0)
+ {
+ sal_Int32 RetVal;
+ RetVal= osl_receivePipe(pPipe, pBuffer, BytesToRead);
+
+ /* error occurred? */
+ if(RetVal <= 0)
+ break;
+
+ BytesToRead -= RetVal;
+ BytesRead += RetVal;
+ pBuffer= static_cast< char* >(pBuffer) + RetVal;
+ }
+ return BytesRead;
+}
+
+oslPipeError SAL_CALL osl_getLastPipeError(oslPipe pPipe)
+{
+ oslPipeError Error;
+
+ if (pPipe)
+ {
+ Error = pPipe->m_Error;
+ pPipe->m_Error = osl_Pipe_E_None;
+ }
+ else
+ {
+ Error = osl_Pipe_E_NotFound;
+ }
+
+ return Error;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/process.cxx b/sal/osl/w32/process.cxx
new file mode 100644
index 000000000..065415f2c
--- /dev/null
+++ b/sal/osl/w32/process.cxx
@@ -0,0 +1,525 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.h"
+#include <string.h>
+
+#include <shellapi.h>
+
+#include <cassert>
+
+#include <osl/mutex.hxx>
+#include <osl/nlsupport.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include "filetime.hxx"
+#include "nlsupport.hxx"
+#include "procimpl.hxx"
+#include "file_url.hxx"
+#include "path_helper.hxx"
+#include <rtl/alloc.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process)
+{
+ if (Process == nullptr)
+ return osl_Process_E_Unknown;
+
+ HANDLE hProcess = static_cast<oslProcessImpl*>(Process)->m_hProcess;
+ DWORD dwPID = GetProcessId(hProcess);
+
+ // cannot be System Process (0x00000000)
+ if (dwPID == 0x0)
+ return osl_Process_E_InvalidError;
+
+ // Test to see if we can create a thread in a process... adapted from:
+ // * https://support.microsoft.com/en-us/help/178893/how-to-terminate-an-application-cleanly-in-win32
+ // * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
+
+ // TODO: we really should firstly check to see if we have access to create threads and only
+ // duplicate the handle with elevated access if we don't have access... this can be done, but
+ // it's not exactly easy - an example can be found here:
+ // http://windowsitpro.com/site-files/windowsitpro.com/files/archive/windowsitpro.com/content/content/15989/listing_01.txt
+
+ HANDLE hDupProcess = nullptr;
+
+
+ // we need to make sure we can create a thread in the remote process, if the handle was created
+ // in something that doesn't give us appropriate levels of access then we will need to give it the
+ // desired level of access - if the process handle was grabbed from OpenProcess it's quite possible
+ // that the handle doesn't have the appropriate level of access...
+
+ // see https://msdn.microsoft.com/en-au/library/windows/desktop/ms684880(v=vs.85).aspx
+ DWORD const dwAccessFlags = (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION
+ | PROCESS_VM_WRITE | PROCESS_VM_READ);
+
+ bool bHaveDuplHdl = DuplicateHandle(GetCurrentProcess(), // handle to process that has handle
+ hProcess, // handle to be duplicated
+ GetCurrentProcess(), // process that will get the dup handle
+ &hDupProcess, // store duplicate process handle here
+ dwAccessFlags, // desired access
+ FALSE, // handle can't be inherited
+ 0); // zero means no additional action needed
+
+ if (bHaveDuplHdl)
+ hProcess = hDupProcess; // so we were able to duplicate the handle, all good...
+ else
+ SAL_WARN("sal.osl", "Could not duplicate process handle, let's hope for the best...");
+
+ DWORD dwProcessStatus = 0;
+ HANDLE hRemoteThread = nullptr;
+
+ if (GetExitCodeProcess(hProcess, &dwProcessStatus) && (dwProcessStatus == STILL_ACTIVE))
+ {
+ // We need to get the address of the Win32 procedure ExitProcess, can't call it
+ // directly because we'll be calling the thunk and that will probably lead to an
+ // access violation. Once we have the address, then we need to create a new
+ // thread in the process (which we might need to run in the address space of
+ // another process) and then call on ExitProcess to try to cleanly terminate that
+ // process
+
+ DWORD dwTID = 0; // dummy variable as we don't need to track the thread ID
+
+ // Note: we want to call on ExitProcess() and not TerminateProcess() - this is
+ // because with ExitProcess() Windows notifies all attached dlls that the process
+ // is detaching from the dll, but TerminateProcess() terminates all threads
+ // immediately, doesn't call any termination handlers and doesn't notify any dlls
+ // that it is detaching from them
+
+ HINSTANCE hKernel = GetModuleHandleW(L"kernel32.dll");
+ FARPROC pfnExitProc = GetProcAddress(hKernel, "ExitProcess");
+ hRemoteThread = CreateRemoteThread(
+ hProcess, /* process handle */
+ nullptr, /* default security descriptor */
+ 0, /* initial size of stack in bytes is default
+ size for executable */
+ reinterpret_cast<LPTHREAD_START_ROUTINE>(pfnExitProc), /* Win32 ExitProcess() */
+ reinterpret_cast<PVOID>(UINT(0)), /* ExitProcess(UINT uExitCode) argument */
+ 0, /* value of 0 tells thread to run immediately
+ after creation */
+ &dwTID); /* new remote thread's identifier */
+
+ }
+
+ bool bHasExited = false;
+
+ if (hRemoteThread)
+ {
+ WaitForSingleObject(hProcess, INFINITE); // wait for process to terminate, never stop waiting...
+ CloseHandle(hRemoteThread); // close the thread handle to allow the process to exit
+ bHasExited = true;
+ }
+
+ // need to close this duplicated process handle...
+ if (bHaveDuplHdl)
+ CloseHandle(hProcess);
+
+ if (bHasExited)
+ return osl_Process_E_None;
+
+ // fallback - given that we wait for an infinite time on WaitForSingleObject, this should
+ // never occur... unless CreateRemoteThread failed
+ SAL_WARN("sal.osl", "TerminateProcess(hProcess, 0) called - we should never get here!");
+ return (TerminateProcess(hProcess, 0) == FALSE) ? osl_Process_E_Unknown : osl_Process_E_None;
+}
+
+oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident)
+{
+ oslProcessImpl* pProcImpl;
+ HANDLE hProcess = OpenProcess(
+ STANDARD_RIGHTS_REQUIRED | PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, static_cast<DWORD>(Ident));
+
+ if (hProcess)
+ {
+ pProcImpl = static_cast< oslProcessImpl*>( malloc(sizeof(oslProcessImpl)) );
+ pProcImpl->m_hProcess = hProcess;
+ pProcImpl->m_IdProcess = Ident;
+ }
+ else
+ pProcImpl = nullptr;
+
+ return pProcImpl;
+}
+
+void SAL_CALL osl_freeProcessHandle(oslProcess Process)
+{
+ if (Process != nullptr)
+ {
+ CloseHandle(static_cast<oslProcessImpl*>(Process)->m_hProcess);
+
+ free(Process);
+ }
+}
+
+oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields,
+ oslProcessInfo* pInfo)
+{
+ HANDLE hProcess;
+ DWORD IdProcess;
+
+ if (Process == nullptr)
+ {
+ hProcess = GetCurrentProcess();
+ IdProcess = GetCurrentProcessId();
+ }
+ else
+ {
+ hProcess = static_cast<oslProcessImpl*>(Process)->m_hProcess;
+ IdProcess = static_cast<oslProcessImpl*>(Process)->m_IdProcess;
+ }
+
+ if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo)))
+ return osl_Process_E_Unknown;
+
+ pInfo->Fields = 0;
+
+ if (Fields & osl_Process_IDENTIFIER)
+ {
+ pInfo->Ident = IdProcess;
+ pInfo->Fields |= osl_Process_IDENTIFIER;
+ }
+
+ if (Fields & osl_Process_EXITCODE)
+ {
+ if (GetExitCodeProcess(hProcess, &(pInfo->Code)) && (pInfo->Code != STILL_ACTIVE))
+ pInfo->Fields |= osl_Process_EXITCODE;
+ }
+
+ if (Fields & osl_Process_HEAPUSAGE)
+ {
+ void* lpAddress=nullptr;
+ MEMORY_BASIC_INFORMATION Info;
+
+ pInfo->HeapUsage = 0;
+
+ do
+ {
+ if (VirtualQueryEx(hProcess, lpAddress, &Info, sizeof(Info)) == 0)
+ break;
+
+ if ((Info.State == MEM_COMMIT) && (Info.Type == MEM_PRIVATE))
+ pInfo->HeapUsage += Info.RegionSize;
+
+ lpAddress = static_cast<LPBYTE>(lpAddress) + Info.RegionSize;
+ }
+ while (reinterpret_cast<uintptr_t>(lpAddress) <= 0x7FFFFFFFU); // 2GB address space
+
+ pInfo->Fields |= osl_Process_HEAPUSAGE;
+ }
+
+ if (Fields & osl_Process_CPUTIMES)
+ {
+ FILETIME CreationTime, ExitTime, KernelTime, UserTime;
+
+ if (GetProcessTimes(hProcess, &CreationTime, &ExitTime,
+ &KernelTime, &UserTime))
+ {
+ __int64 Value;
+
+ Value = osl::detail::getFiletime(UserTime);
+ pInfo->UserTime.Seconds = static_cast<unsigned long>(Value / 10000000L);
+ pInfo->UserTime.Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100);
+
+ Value = osl::detail::getFiletime(KernelTime);
+ pInfo->SystemTime.Seconds = static_cast<unsigned long>(Value / 10000000L);
+ pInfo->SystemTime.Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100);
+
+ pInfo->Fields |= osl_Process_CPUTIMES;
+ }
+ }
+
+ return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
+}
+
+oslProcessError SAL_CALL osl_joinProcess(oslProcess Process)
+{
+ return osl_joinProcessWithTimeout(Process, nullptr);
+}
+
+oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout)
+{
+ DWORD timeout = INFINITE;
+ oslProcessError osl_error = osl_Process_E_None;
+ DWORD ret;
+
+ if (nullptr == Process)
+ return osl_Process_E_Unknown;
+
+ if (pTimeout)
+ timeout = pTimeout->Seconds * 1000 + pTimeout->Nanosec / 1000000L;
+
+ ret = WaitForSingleObject(static_cast<oslProcessImpl*>(Process)->m_hProcess, timeout);
+
+ if (WAIT_FAILED == ret)
+ osl_error = osl_Process_E_Unknown;
+ else if (WAIT_TIMEOUT == ret)
+ osl_error = osl_Process_E_TimedOut;
+
+ return osl_error;
+}
+
+namespace {
+
+oslProcessError bootstrap_getExecutableFile(rtl_uString ** ppFileURL)
+{
+ oslProcessError result = osl_Process_E_NotFound;
+
+ ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
+ DWORD buflen = 0;
+
+ if ((buflen = GetModuleFileNameW (nullptr, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols())) > 0)
+ {
+ rtl_uString * pAbsPath = nullptr;
+ rtl_uString_newFromStr_WithLength (&pAbsPath, aBuffer, buflen);
+ if (pAbsPath)
+ {
+ /* Convert from path to url. */
+ if (osl_getFileURLFromSystemPath (pAbsPath, ppFileURL) == osl_File_E_None)
+ {
+ /* Success. */
+ result = osl_Process_E_None;
+ }
+ rtl_uString_release (pAbsPath);
+ }
+ }
+
+ return result;
+}
+
+struct CommandArgs_Impl
+{
+ sal_uInt32 m_nCount;
+ rtl_uString ** m_ppArgs;
+};
+
+}
+
+static struct CommandArgs_Impl g_command_args =
+{
+ 0,
+ nullptr
+};
+
+static rtl_uString ** osl_createCommandArgs_Impl (int argc, char **)
+{
+ rtl_uString ** ppArgs =
+ static_cast<rtl_uString**>(rtl_allocateZeroMemory (argc * sizeof(rtl_uString*)));
+ if (ppArgs != nullptr)
+ {
+ int i;
+ int nArgs;
+ LPWSTR *wargv = CommandLineToArgvW( GetCommandLineW(), &nArgs );
+ assert( nArgs == argc );
+ for (i = 0; i < nArgs; i++)
+ {
+ /* Convert to unicode */
+ rtl_uString_newFromStr( &(ppArgs[i]), o3tl::toU(wargv[i]) );
+ }
+ if (ppArgs[0] != nullptr)
+ {
+ /* Ensure absolute path */
+ ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
+ DWORD dwResult
+ = GetModuleFileNameW(nullptr, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols());
+ if ((0 < dwResult) && (dwResult < aBuffer.getBufSizeInSymbols()))
+ {
+ /* Replace argv[0] with its absolute path */
+ rtl_uString_newFromStr_WithLength(
+ &(ppArgs[0]), aBuffer, dwResult);
+ }
+ }
+ if (ppArgs[0] != nullptr)
+ {
+ /* Convert to FileURL, see @ osl_getExecutableFile() */
+ rtl_uString * pResult = nullptr;
+ osl_getFileURLFromSystemPath (ppArgs[0], &pResult);
+ if (pResult != nullptr)
+ {
+ rtl_uString_assign (&(ppArgs[0]), pResult);
+ rtl_uString_release (pResult);
+ }
+ }
+ }
+ return ppArgs;
+
+}
+
+oslProcessError SAL_CALL osl_getExecutableFile( rtl_uString **ppustrFile )
+{
+ {
+ osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
+ if (g_command_args.m_nCount > 0)
+ {
+ /* CommandArgs set. Obtain arv[0]. */
+ rtl_uString_assign(ppustrFile, g_command_args.m_ppArgs[0]);
+ return osl_Process_E_None;
+ }
+ }
+ return bootstrap_getExecutableFile(ppustrFile);
+}
+
+sal_uInt32 SAL_CALL osl_getCommandArgCount()
+{
+ sal_uInt32 result = 0;
+
+ osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
+ SAL_INFO_IF(
+ g_command_args.m_nCount == 0, "sal.osl",
+ "osl_getCommandArgCount w/o prior call to osl_setCommandArgs");
+ if (g_command_args.m_nCount > 0)
+ {
+ /* We're not counting argv[0] here. */
+ result = g_command_args.m_nCount - 1;
+ }
+
+ return result;
+}
+
+oslProcessError SAL_CALL osl_getCommandArg( sal_uInt32 nArg, rtl_uString **strCommandArg)
+{
+ oslProcessError result = osl_Process_E_NotFound;
+
+ osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
+ assert(g_command_args.m_nCount > 0);
+ if (g_command_args.m_nCount > (nArg + 1))
+ {
+ /* We're not counting argv[0] here. */
+ rtl_uString_assign (strCommandArg, g_command_args.m_ppArgs[nArg + 1]);
+ result = osl_Process_E_None;
+ }
+
+ return result;
+}
+
+void SAL_CALL osl_setCommandArgs (int argc, char ** argv)
+{
+ assert(argc > 0);
+ osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
+ SAL_WARN_IF(g_command_args.m_nCount != 0, "sal.osl", "args already set");
+ if (g_command_args.m_nCount == 0)
+ {
+ rtl_uString** ppArgs = osl_createCommandArgs_Impl (argc, argv);
+ if (ppArgs != nullptr)
+ {
+ g_command_args.m_nCount = argc;
+ g_command_args.m_ppArgs = ppArgs;
+ }
+ }
+}
+
+/* TODO because of an issue with GetEnvironmentVariableW we have to
+ allocate a buffer large enough to hold the requested environment
+ variable instead of testing for the required size. This wastes
+ some stack space, maybe we should revoke this work around if
+ this is no longer a problem */
+#define ENV_BUFFER_SIZE (32*1024-1)
+
+oslProcessError SAL_CALL osl_getEnvironment(rtl_uString *ustrVar, rtl_uString **ustrValue)
+{
+ WCHAR buff[ENV_BUFFER_SIZE];
+
+ if (GetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), buff, ENV_BUFFER_SIZE) > 0)
+ {
+ rtl_uString_newFromStr(ustrValue, o3tl::toU(buff));
+ return osl_Process_E_None;
+ }
+ return osl_Process_E_Unknown;
+}
+
+oslProcessError SAL_CALL osl_setEnvironment(rtl_uString *ustrVar, rtl_uString *ustrValue)
+{
+ // set Windows environment variable
+ if (SetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), o3tl::toW(ustrValue->buffer)))
+ {
+ OUString sAssign = OUString::unacquired(&ustrVar) + "=" + OUString::unacquired(&ustrValue);
+ _wputenv(o3tl::toW(sAssign.getStr()));
+ return osl_Process_E_None;
+ }
+ return osl_Process_E_Unknown;
+}
+
+oslProcessError SAL_CALL osl_clearEnvironment(rtl_uString *ustrVar)
+{
+ // delete the variable from the current process environment
+ // by setting SetEnvironmentVariable's second parameter to NULL
+ if (SetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), nullptr))
+ {
+ OUString sAssign = OUString::unacquired(&ustrVar) + "=";
+ _wputenv(o3tl::toW(sAssign.getStr()));
+ return osl_Process_E_None;
+ }
+ return osl_Process_E_Unknown;
+}
+
+oslProcessError SAL_CALL osl_getProcessWorkingDir( rtl_uString **pustrWorkingDir )
+{
+ ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
+ DWORD dwLen = GetCurrentDirectoryW(aBuffer.getBufSizeInSymbols(), o3tl::toW(aBuffer));
+
+ if ( dwLen && dwLen < aBuffer.getBufSizeInSymbols() )
+ {
+ oslFileError eError;
+ rtl_uString *ustrTemp = nullptr;
+
+ rtl_uString_newFromStr_WithLength( &ustrTemp, aBuffer, dwLen );
+ eError = osl_getFileURLFromSystemPath( ustrTemp, pustrWorkingDir );
+
+ rtl_uString_release( ustrTemp );
+
+ if ( osl_File_E_None != eError )
+ return osl_Process_E_Unknown;
+ else
+ return osl_Process_E_None;
+ }
+ else
+ return osl_Process_E_Unknown;
+}
+
+static rtl_Locale * g_theProcessLocale = nullptr;
+
+oslProcessError SAL_CALL osl_getProcessLocale( rtl_Locale ** ppLocale )
+{
+ osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
+
+ /* determine the users default locale */
+ if( nullptr == g_theProcessLocale )
+ imp_getProcessLocale( &g_theProcessLocale );
+
+ /* or return the cached value */
+ *ppLocale = g_theProcessLocale;
+
+ return osl_Process_E_None;
+}
+
+oslProcessError SAL_CALL osl_setProcessLocale( rtl_Locale * pLocale )
+{
+ osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
+
+ /* check if locale is supported */
+ if( RTL_TEXTENCODING_DONTKNOW == osl_getTextEncodingFromLocale( pLocale ) )
+ return osl_Process_E_Unknown;
+
+ /* just remember the locale here */
+ g_theProcessLocale = pLocale;
+
+ return osl_Process_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/procimpl.cxx b/sal/osl/w32/procimpl.cxx
new file mode 100644
index 000000000..5fb9a4634
--- /dev/null
+++ b/sal/osl/w32/procimpl.cxx
@@ -0,0 +1,596 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# undef WIN32_LEAN_AND_MEAN
+#endif
+
+#include "file-impl.hxx"
+#include "procimpl.hxx"
+#include <rtl/alloc.h>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include "secimpl.hxx"
+#include <osl/file.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <vector>
+#include <algorithm>
+
+namespace /* private */
+{
+ /* Function object that compares two strings that are
+ expected to be environment variables in the form
+ "name=value". Only the 'name' part will be compared.
+ The comparison is in upper case and returns true
+ if the first of both strings is less than the
+ second one. */
+ struct less_environment_variable
+ {
+ bool operator() (const OUString& lhs, const OUString& rhs) const
+ {
+ OSL_ENSURE((lhs.indexOf(L'=') > -1) &&
+ (rhs.indexOf(L'=') > -1),
+ "Malformed environment variable");
+
+ // Windows compares environment variables uppercase
+ // so we do it, too
+ return (rtl_ustr_compare_WithLength(
+ lhs.toAsciiUpperCase().pData->buffer,
+ lhs.indexOf(L'='),
+ rhs.toAsciiUpperCase().pData->buffer,
+ rhs.indexOf(L'=')) < 0);
+ }
+ };
+
+ /* Function object used by for_each algorithm to
+ calculate the sum of the length of all strings
+ in a string container. */
+ class sum_of_string_lengths
+ {
+ public:
+
+ sum_of_string_lengths() : sum_(0) {}
+
+ void operator() (const OUString& string)
+ {
+ OSL_ASSERT(string.getLength());
+
+ // always include the terminating '\0'
+ if (string.getLength())
+ sum_ += string.getLength() + 1;
+ }
+
+ operator size_t () const
+ {
+ return sum_;
+ }
+ private:
+ size_t sum_;
+ };
+
+ size_t calc_sum_of_string_lengths(const std::vector<OUString>& string_cont)
+ {
+ return std::for_each(
+ string_cont.begin(), string_cont.end(), sum_of_string_lengths());
+ }
+
+ void read_environment(/*out*/ std::vector<OUString>* environment)
+ {
+ // GetEnvironmentStrings returns a sorted list, Windows
+ // sorts environment variables upper case
+ LPWSTR env = GetEnvironmentStringsW();
+ LPWSTR p = env;
+
+ while (size_t l = wcslen(p))
+ {
+ environment->push_back(OUString(o3tl::toU(p)));
+ p += l + 1;
+ }
+ FreeEnvironmentStringsW(env);
+
+ // it is apparently possible that the environment is not completely
+ // sorted; Cygwin may append entries, which breaks the equal_range
+ std::stable_sort(environment->begin(), environment->end(),
+ less_environment_variable());
+ }
+
+ /* the environment list must be sorted, new values
+ should either replace existing ones or should be
+ added to the list, environment variables will
+ be handled case-insensitive */
+ bool create_merged_environment(
+ rtl_uString* env_vars[],
+ sal_uInt32 env_vars_count,
+ /*in|out*/ std::vector<OUString>* merged_env)
+ {
+ OSL_ASSERT(env_vars && env_vars_count > 0 && merged_env);
+
+ read_environment(merged_env);
+
+ for (sal_uInt32 i = 0; i < env_vars_count; i++)
+ {
+ OUString env_var(env_vars[i]);
+
+ if (env_var.getLength() == 0)
+ return false;
+
+ auto iter_pair = std::equal_range(
+ merged_env->begin(),
+ merged_env->end(),
+ env_var,
+ less_environment_variable());
+
+ if (env_var.indexOf(L'=') == -1)
+ {
+ merged_env->erase(iter_pair.first, iter_pair.second);
+ }
+ else
+ {
+ if (iter_pair.first != iter_pair.second) // found
+ *iter_pair.first = env_var;
+ else // not found
+ merged_env->insert(iter_pair.first, env_var);
+ }
+ }
+ return true;
+ }
+
+ /* Create a merged environment */
+ bool setup_process_environment(
+ rtl_uString* environment_vars[],
+ sal_uInt32 n_environment_vars,
+ /*in|out*/ std::vector<sal_Unicode>& environment)
+ {
+ std::vector<OUString> merged_env;
+ if (!create_merged_environment(environment_vars, n_environment_vars, &merged_env))
+ return false;
+
+ // allocate enough space for the '\0'-separated environment strings and
+ // a final '\0'
+ environment.resize(calc_sum_of_string_lengths(merged_env) + 1);
+
+ sal_uInt32 pos = 0;
+ for (auto& envv : merged_env)
+ {
+ OSL_ASSERT(envv.getLength());
+
+ sal_uInt32 n = envv.getLength() + 1; // copy the final '\0', too
+ memcpy(&environment[pos], envv.getStr(), n * sizeof(sal_Unicode));
+ pos += n;
+ }
+ environment[pos] = 0; // append a final '\0'
+
+ return true;
+ }
+
+ /* In contrast to the Win32 API function CreatePipe with
+ this function the caller is able to determine separately
+ which handle of the pipe is inheritable. */
+ bool create_pipe(
+ PHANDLE p_read_pipe,
+ bool b_read_pipe_inheritable,
+ PHANDLE p_write_pipe,
+ bool b_write_pipe_inheritable,
+ LPVOID p_security_descriptor = nullptr,
+ DWORD pipe_size = 0)
+ {
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = p_security_descriptor;
+ sa.bInheritHandle = b_read_pipe_inheritable || b_write_pipe_inheritable;
+
+ bool bRet = false;
+ HANDLE hTemp = nullptr;
+
+ if (!b_read_pipe_inheritable && b_write_pipe_inheritable)
+ {
+ bRet = CreatePipe(&hTemp, p_write_pipe, &sa, pipe_size);
+
+ if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp,
+ GetCurrentProcess(), p_read_pipe, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
+ {
+ CloseHandle(hTemp);
+ CloseHandle(*p_read_pipe);
+ return false;
+ }
+ }
+ else if (b_read_pipe_inheritable && !b_write_pipe_inheritable)
+ {
+ bRet = CreatePipe(p_read_pipe, &hTemp, &sa, pipe_size);
+
+ if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp,
+ GetCurrentProcess(), p_write_pipe, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
+ {
+ CloseHandle(hTemp);
+ CloseHandle(*p_write_pipe);
+ return false;
+ }
+ }
+ else
+ {
+ bRet = CreatePipe(p_read_pipe, p_write_pipe, &sa, pipe_size);
+ }
+ return bRet;
+ }
+
+ // Add a quote sign to the start and the end of a string
+ // if not already present
+ OUString quote_string(const OUString& string)
+ {
+ OUStringBuffer quoted;
+ if (string.indexOf(L'"') != 0)
+ quoted.append('"');
+
+ quoted.append(string);
+
+ if (string.lastIndexOf(L'"') != (string.getLength() - 1))
+ quoted.append('"');
+
+ return quoted.makeStringAndClear();
+ }
+
+ // The parameter path must be a system path. If it is longer than 260 characters
+ // then it is shortened using the GetShortPathName function. This function only
+ // works if the path exists. Because "path" can be the path to an executable, it
+ // may not have the file extension ".exe". However, if the file on disk has the
+ // ".exe" extension, then the function will fail. In this case a second attempt
+ // is started by adding the parameter "extension" to "path".
+ OUString getShortPath(OUString const & path, OUString const & extension)
+ {
+ OUString ret(path);
+ if (path.getLength() > 260)
+ {
+ std::vector<sal_Unicode> vec(path.getLength() + 1);
+ //GetShortPathNameW only works if the file can be found!
+ const DWORD len = GetShortPathNameW(
+ o3tl::toW(path.getStr()), o3tl::toW(vec.data()), path.getLength() + 1);
+
+ if (!len && GetLastError() == ERROR_FILE_NOT_FOUND
+ && extension.getLength())
+ {
+ const OUString extPath(path + extension);
+ std::vector<sal_Unicode> vec2(
+ extPath.getLength() + 1);
+ const DWORD len2 = GetShortPathNameW(
+ o3tl::toW(extPath.getStr()), o3tl::toW(vec2.data()), extPath.getLength() + 1);
+ ret = OUString(vec2.data(), len2);
+ }
+ else
+ {
+ ret = OUString(vec.data(), len);
+ }
+ }
+ return ret;
+ }
+
+ // Returns the system path of the executable which can either
+ // be provided via the strImageName parameter or as first
+ // element of the strArguments list.
+ // The returned path will be quoted if it contains spaces.
+ OUString get_executable_path(
+ rtl_uString* image_name,
+ rtl_uString* cmdline_args[],
+ sal_uInt32 n_cmdline_args,
+ bool search_path)
+ {
+ OUString exe_name;
+
+ if (image_name)
+ exe_name = image_name;
+ else if (n_cmdline_args)
+ exe_name = OUString(cmdline_args[0]);
+
+ OUString exe_url = exe_name;
+ if (search_path)
+ osl_searchFileURL(exe_name.pData, nullptr, &exe_url.pData);
+
+ OUString exe_path;
+ if (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(exe_url, exe_path))
+ return OUString();
+
+ exe_path = getShortPath(exe_path, ".exe");
+
+ if (exe_path.indexOf(' ') != -1)
+ exe_path = quote_string(exe_path);
+
+ return exe_path;
+ }
+
+ OUString get_file_extension(const OUString& file_name)
+ {
+ // Quoted file name
+ if ((file_name.indexOf(L'"') == 0) && (file_name.lastIndexOf(L'"') == (file_name.getLength() - 1)))
+ {
+ sal_Int32 index = file_name.lastIndexOf('.');
+ if ((index != -1) && ((index + 2) < file_name.getLength()))
+ return file_name.copy(index + 1, file_name.getLength() - (index + 2));
+ }
+ // Unquoted file name
+ else
+ {
+ sal_Int32 index = file_name.lastIndexOf('.');
+ if ((index != -1) && ((index + 1) < file_name.getLength()))
+ return file_name.copy(index + 1);
+ }
+ return OUString();
+ }
+
+ bool is_batch_file(const OUString& file_name)
+ {
+ OUString ext = get_file_extension(file_name);
+ return (ext.equalsIgnoreAsciiCase("bat") ||
+ ext.equalsIgnoreAsciiCase("cmd") ||
+ ext.equalsIgnoreAsciiCase("btm"));
+ }
+
+ const OUString ENV_COMSPEC ("COMSPEC");
+ OUString get_batch_processor()
+ {
+ OUString comspec;
+ osl_getEnvironment(ENV_COMSPEC.pData, &comspec.pData);
+
+ OSL_ASSERT(comspec.getLength());
+
+ /* check if comspec path contains blanks and quote it if any */
+ if (comspec.indexOf(' ') != -1)
+ comspec = quote_string(comspec);
+
+ return comspec;
+ }
+
+} // namespace private
+
+oslProcessError SAL_CALL osl_executeProcess(
+ rtl_uString *strImageName,
+ rtl_uString *strArguments[],
+ sal_uInt32 nArguments,
+ oslProcessOption Options,
+ oslSecurity Security,
+ rtl_uString *strDirectory,
+ rtl_uString *strEnvironmentVars[],
+ sal_uInt32 nEnvironmentVars,
+ oslProcess *pProcess
+)
+{
+ return osl_executeProcess_WithRedirectedIO(
+ strImageName,
+ strArguments,
+ nArguments,
+ Options,
+ Security,
+ strDirectory,
+ strEnvironmentVars,
+ nEnvironmentVars,
+ pProcess,
+ nullptr, nullptr, nullptr );
+}
+
+oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO(
+ rtl_uString *ustrImageName,
+ rtl_uString *ustrArguments[],
+ sal_uInt32 nArguments,
+ oslProcessOption Options,
+ oslSecurity Security,
+ rtl_uString *ustrDirectory,
+ rtl_uString *ustrEnvironmentVars[],
+ sal_uInt32 nEnvironmentVars,
+ oslProcess *pProcess,
+ oslFileHandle *pProcessInputWrite,
+ oslFileHandle *pProcessOutputRead,
+ oslFileHandle *pProcessErrorRead)
+{
+ OUString exe_path = get_executable_path(
+ ustrImageName, ustrArguments, nArguments, (Options & osl_Process_SEARCHPATH) != 0);
+
+ if (0 == exe_path.getLength())
+ return osl_Process_E_NotFound;
+
+ if (pProcess == nullptr)
+ return osl_Process_E_InvalidError;
+
+ DWORD flags = NORMAL_PRIORITY_CLASS;
+ OUStringBuffer command_line;
+
+ if (is_batch_file(exe_path))
+ {
+ OUString batch_processor = get_batch_processor();
+
+ if (batch_processor.getLength())
+ {
+ /* cmd.exe does not work without a console window */
+ if (!(Options & osl_Process_WAIT) || (Options & osl_Process_DETACHED))
+ flags |= CREATE_NEW_CONSOLE;
+
+ command_line.append(batch_processor);
+ command_line.append(" /c ");
+ }
+ else
+ // should we return here in case of error?
+ return osl_Process_E_Unknown;
+ }
+
+ command_line.append(exe_path);
+
+ /* Add remaining arguments to command line. If ustrImageName is nullptr
+ the first parameter is the name of the executable so we have to
+ start at 1 instead of 0 */
+ for (sal_uInt32 n = (nullptr != ustrImageName) ? 0 : 1; n < nArguments; n++)
+ {
+ command_line.append(" ");
+
+ /* Quote arguments containing blanks */
+ if (OUString(ustrArguments[n]).indexOf(' ') != -1)
+ command_line.append(quote_string(ustrArguments[n]));
+ else
+ command_line.append(ustrArguments[n]);
+ }
+
+ std::vector<sal_Unicode> environment;
+ LPVOID p_environment = nullptr;
+
+ if (nEnvironmentVars && ustrEnvironmentVars)
+ {
+ if (!setup_process_environment(
+ ustrEnvironmentVars, nEnvironmentVars, environment))
+ return osl_Process_E_InvalidError;
+
+ flags |= CREATE_UNICODE_ENVIRONMENT;
+ p_environment = environment.data();
+ }
+
+ OUString cwd;
+ if (ustrDirectory && ustrDirectory->length && (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(ustrDirectory, cwd)))
+ return osl_Process_E_InvalidError;
+
+ LPCWSTR p_cwd = (cwd.getLength()) ? o3tl::toW(cwd.getStr()) : nullptr;
+
+ if ((Options & osl_Process_DETACHED) && !(flags & CREATE_NEW_CONSOLE))
+ flags |= DETACHED_PROCESS;
+
+ STARTUPINFOW startup_info = {};
+ startup_info.cb = sizeof(startup_info);
+ startup_info.dwFlags = STARTF_USESHOWWINDOW;
+ startup_info.lpDesktop = const_cast<LPWSTR>(L"");
+
+ /* Create pipes for redirected IO */
+ HANDLE hInputRead = nullptr;
+ HANDLE hInputWrite = nullptr;
+ if (pProcessInputWrite && create_pipe(&hInputRead, true, &hInputWrite, false))
+ startup_info.hStdInput = hInputRead;
+
+ HANDLE hOutputRead = nullptr;
+ HANDLE hOutputWrite = nullptr;
+ if (pProcessOutputRead && create_pipe(&hOutputRead, false, &hOutputWrite, true))
+ startup_info.hStdOutput = hOutputWrite;
+
+ HANDLE hErrorRead = nullptr;
+ HANDLE hErrorWrite = nullptr;
+ if (pProcessErrorRead && create_pipe(&hErrorRead, false, &hErrorWrite, true))
+ startup_info.hStdError = hErrorWrite;
+
+ bool b_inherit_handles = false;
+ if (pProcessInputWrite || pProcessOutputRead || pProcessErrorRead)
+ {
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+ b_inherit_handles = true;
+ }
+
+ switch(Options & (osl_Process_NORMAL | osl_Process_HIDDEN | osl_Process_MINIMIZED | osl_Process_MAXIMIZED | osl_Process_FULLSCREEN))
+ {
+ case osl_Process_HIDDEN:
+ startup_info.wShowWindow = SW_HIDE;
+ flags |= CREATE_NO_WINDOW; // ignored for non-console
+ // applications; ignored on
+ // Win9x
+ break;
+
+ case osl_Process_MINIMIZED:
+ startup_info.wShowWindow = SW_MINIMIZE;
+ break;
+
+ case osl_Process_MAXIMIZED:
+ case osl_Process_FULLSCREEN:
+ startup_info.wShowWindow = SW_MAXIMIZE;
+ break;
+
+ default:
+ startup_info.wShowWindow = SW_NORMAL;
+ }
+
+ OUString cmdline = command_line.makeStringAndClear();
+ PROCESS_INFORMATION process_info;
+ bool bRet = false;
+
+ if ((Security != nullptr) && (static_cast<oslSecurityImpl*>(Security)->m_hToken != nullptr))
+ {
+ bRet = CreateProcessAsUserW(
+ static_cast<oslSecurityImpl*>(Security)->m_hToken,
+ nullptr, const_cast<LPWSTR>(o3tl::toW(cmdline.getStr())), nullptr, nullptr,
+ b_inherit_handles, flags, p_environment, p_cwd,
+ &startup_info, &process_info);
+ }
+ else
+ {
+ bRet = CreateProcessW(
+ nullptr, const_cast<LPWSTR>(o3tl::toW(cmdline.getStr())), nullptr, nullptr,
+ b_inherit_handles, flags, p_environment, p_cwd,
+ &startup_info, &process_info);
+ }
+
+ /* Now we can close the pipe ends that are used by the child process */
+
+ if (hInputRead)
+ CloseHandle(hInputRead);
+
+ if (hOutputWrite)
+ CloseHandle(hOutputWrite);
+
+ if (hErrorWrite)
+ CloseHandle(hErrorWrite);
+
+ if (bRet)
+ {
+ CloseHandle(process_info.hThread);
+
+ oslProcessImpl* pProcImpl = static_cast<oslProcessImpl*>(
+ malloc(sizeof(oslProcessImpl)));
+
+ if (pProcImpl != nullptr)
+ {
+ pProcImpl->m_hProcess = process_info.hProcess;
+ pProcImpl->m_IdProcess = process_info.dwProcessId;
+
+ *pProcess = static_cast<oslProcess>(pProcImpl);
+
+ if (Options & osl_Process_WAIT)
+ WaitForSingleObject(pProcImpl->m_hProcess, INFINITE);
+
+ if (pProcessInputWrite)
+ *pProcessInputWrite = osl_createFileHandleFromOSHandle(hInputWrite, osl_File_OpenFlag_Write);
+
+ if (pProcessOutputRead)
+ *pProcessOutputRead = osl_createFileHandleFromOSHandle(hOutputRead, osl_File_OpenFlag_Read);
+
+ if (pProcessErrorRead)
+ *pProcessErrorRead = osl_createFileHandleFromOSHandle(hErrorRead, osl_File_OpenFlag_Read);
+
+ return osl_Process_E_None;
+ }
+ }
+
+ /* if an error occurred we have to close the server side pipe ends too */
+
+ if (hInputWrite)
+ CloseHandle(hInputWrite);
+
+ if (hOutputRead)
+ CloseHandle(hOutputRead);
+
+ if (hErrorRead)
+ CloseHandle(hErrorRead);
+
+ return osl_Process_E_Unknown;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/procimpl.hxx b/sal/osl/w32/procimpl.hxx
new file mode 100644
index 000000000..fb1263fa7
--- /dev/null
+++ b/sal/osl/w32/procimpl.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_W32_PROCIMPL_HXX
+#define INCLUDED_SAL_OSL_W32_PROCIMPL_HXX
+
+#include <osl/process.h>
+
+struct oslProcessImpl
+{
+ HANDLE m_hProcess;
+ DWORD m_IdProcess;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/profile.cxx b/sal/osl/w32/profile.cxx
new file mode 100644
index 000000000..a0790c3de
--- /dev/null
+++ b/sal/osl/w32/profile.cxx
@@ -0,0 +1,2341 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.h"
+
+#include "file_url.hxx"
+#include "path_helper.hxx"
+
+#include <string.h>
+#include <osl/diagnose.h>
+#include <osl/profile.h>
+#include <osl/process.h>
+#include <osl/thread.h>
+#include <osl/file.h>
+#include <rtl/alloc.h>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <algorithm>
+#include <vector>
+using std::min;
+static void copy_ustr_n( void *dest, const void *source, size_t length ) { memcpy(dest, source, length*sizeof(sal_Unicode)); }
+
+#define LINES_INI 32
+#define LINES_ADD 10
+#define SECTIONS_INI 5
+#define SECTIONS_ADD 3
+#define ENTRIES_INI 5
+#define ENTRIES_ADD 3
+
+#define STR_INI_EXTENSION L".ini"
+#define STR_INI_METAHOME "?~"
+#define STR_INI_METASYS "?$"
+#define STR_INI_METACFG "?^"
+#define STR_INI_METAINS "?#"
+
+#define STR_INI_BOOLYES "yes"
+#define STR_INI_BOOLON "on"
+#define STR_INI_BOOLONE "1"
+#define STR_INI_BOOLNO "no"
+#define STR_INI_BOOLOFF "off"
+#define STR_INI_BOOLZERO "0"
+
+#define FLG_USER 0x00FF
+#define FLG_AUTOOPEN 0x0100
+#define FLG_MODIFIED 0x0200
+
+#define SVERSION_LOCATION STR_INI_METACFG
+#define SVERSION_FALLBACK STR_INI_METASYS
+#define SVERSION_NAME "sversion"
+#define SVERSION_SECTION "Versions"
+#define SVERSION_SOFFICE "StarOffice"
+#define SVERSION_PROFILE "soffice.ini"
+#define SVERSION_DIRS { "bin", "program" }
+#define SVERSION_USER "user"
+
+/*#define DEBUG_OSL_PROFILE 1*/
+
+typedef FILETIME osl_TStamp;
+
+namespace {
+
+enum osl_TLockMode
+{
+ un_lock, read_lock, write_lock
+};
+
+struct osl_TFile
+{
+ HANDLE m_Handle;
+ char* m_pReadPtr;
+ char m_ReadBuf[512];
+ char* m_pWriteBuf;
+ sal_uInt32 m_nWriteBufLen;
+ sal_uInt32 m_nWriteBufFree;
+};
+
+struct osl_TProfileEntry
+{
+ sal_uInt32 m_Line;
+ sal_uInt32 m_Offset;
+ sal_uInt32 m_Len;
+};
+
+struct osl_TProfileSection
+{
+ sal_uInt32 m_Line;
+ sal_uInt32 m_Offset;
+ sal_uInt32 m_Len;
+ sal_uInt32 m_NoEntries;
+ sal_uInt32 m_MaxEntries;
+ osl_TProfileEntry* m_Entries;
+};
+
+/*
+ Profile-data structure hidden behind oslProfile:
+*/
+struct osl_TProfileImpl
+{
+ sal_uInt32 m_Flags;
+ osl_TFile* m_pFile;
+ osl_TStamp m_Stamp;
+ sal_uInt32 m_NoLines;
+ sal_uInt32 m_MaxLines;
+ sal_uInt32 m_NoSections;
+ sal_uInt32 m_MaxSections;
+ char** m_Lines;
+ rtl_uString *m_strFileName;
+ osl_TProfileSection* m_Sections;
+};
+
+}
+
+static osl_TFile* openFileImpl(rtl_uString * strFileName, oslProfileOption ProfileFlags );
+static osl_TStamp closeFileImpl(osl_TFile* pFile);
+static bool lockFile(const osl_TFile* pFile, osl_TLockMode eMode);
+static bool rewindFile(osl_TFile* pFile, bool bTruncate);
+static osl_TStamp getFileStamp(osl_TFile* pFile);
+
+static bool getLine(osl_TFile* pFile, char *pszLine, int MaxLen);
+static bool putLine(osl_TFile* pFile, const char *pszLine);
+static const char* stripBlanks(const char* String, sal_uInt32* pLen);
+static const char* addLine(osl_TProfileImpl* pProfile, const char* Line);
+static const char* insertLine(osl_TProfileImpl* pProfile, const char* Line, sal_uInt32 LineNo);
+static void removeLine(osl_TProfileImpl* pProfile, sal_uInt32 LineNo);
+static void setEntry(osl_TProfileImpl* pProfile, osl_TProfileSection* pSection,
+ sal_uInt32 NoEntry, sal_uInt32 Line,
+ const char* Entry, sal_uInt32 Len);
+static bool addEntry(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection,
+ int Line, const char* Entry, sal_uInt32 Len);
+static void removeEntry(osl_TProfileSection *pSection, sal_uInt32 NoEntry);
+static bool addSection(osl_TProfileImpl* pProfile, int Line, const char* Section, sal_uInt32 Len);
+static void removeSection(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection);
+static osl_TProfileSection* findEntry(osl_TProfileImpl* pProfile, const char* Section,
+ const char* Entry, sal_uInt32 *pNoEntry);
+static bool loadProfile(osl_TFile* pFile, osl_TProfileImpl* pProfile);
+static bool storeProfile(osl_TProfileImpl* pProfile, bool bCleanup);
+static osl_TProfileImpl* acquireProfile(oslProfile Profile, bool bWriteable);
+static bool releaseProfile(osl_TProfileImpl* pProfile);
+static bool lookupProfile(const sal_Unicode *strPath, const sal_Unicode *strFile, sal_Unicode *strProfile);
+
+static bool writeProfileImpl (osl_TFile* pFile);
+static osl_TFile* osl_openTmpProfileImpl(osl_TProfileImpl*);
+static bool osl_ProfileSwapProfileNames(osl_TProfileImpl*);
+static rtl_uString* osl_ProfileGenerateExtension(rtl_uString* ustrFileName, rtl_uString* ustrExtension);
+
+static bool osl_getProfileName(rtl_uString* strPath, rtl_uString* strName, rtl_uString** strProfileName);
+
+oslProfile SAL_CALL osl_openProfile(rtl_uString *strProfileName, oslProfileOption Flags)
+{
+ osl_TFile* pFile = nullptr;
+ osl_TProfileImpl* pProfile;
+ rtl_uString *FileName=nullptr;
+
+ OSL_VERIFY(strProfileName);
+
+ if (rtl_uString_getLength(strProfileName) == 0 )
+ {
+ OSL_VERIFY(osl_getProfileName(nullptr, nullptr, &FileName));
+ }
+ else
+ {
+ rtl_uString_assign(&FileName, strProfileName);
+ }
+
+ osl_getSystemPathFromFileURL(FileName, &FileName);
+
+#ifdef DEBUG_OSL_PROFILE
+ Flags=osl_Profile_FLUSHWRITE;
+
+ if ( Flags == osl_Profile_DEFAULT )
+ {
+ SAL_INFO("sal.osl", "with osl_Profile_DEFAULT");
+ }
+ if ( Flags & osl_Profile_SYSTEM )
+ {
+ SAL_INFO("sal.osl", "with osl_Profile_SYSTEM");
+ }
+ if ( Flags & osl_Profile_READLOCK )
+ {
+ SAL_INFO("sal.osl", "with osl_Profile_READLOCK");
+ }
+ if ( Flags & osl_Profile_WRITELOCK )
+ {
+ SAL_INFO("sal.osl", "with osl_Profile_WRITELOCK");
+ }
+ if ( Flags & osl_Profile_FLUSHWRITE )
+ {
+ SAL_INFO("sal.osl", "with osl_Profile_FLUSHWRITE");
+ }
+#endif
+
+ if ( (! (Flags & osl_Profile_SYSTEM)) && ( (pFile = openFileImpl(FileName, Flags) ) == nullptr ) )
+ {
+ if( FileName)
+ rtl_uString_release( FileName);
+
+ return nullptr;
+ }
+
+ pProfile = static_cast<osl_TProfileImpl*>(calloc(1, sizeof(osl_TProfileImpl)));
+ if (!pProfile)
+ return nullptr;
+
+ pProfile->m_Flags = Flags & FLG_USER;
+ osl_getSystemPathFromFileURL(strProfileName, &pProfile->m_strFileName);
+
+ if (Flags & (osl_Profile_READLOCK | osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE ))
+ pProfile->m_pFile = pFile;
+
+ pProfile->m_Stamp = getFileStamp(pFile);
+
+ loadProfile(pFile, pProfile);
+
+ if (pProfile->m_pFile == nullptr)
+ closeFileImpl(pFile);
+
+ if( FileName)
+ rtl_uString_release( FileName);
+
+ return pProfile;
+}
+
+sal_Bool SAL_CALL osl_closeProfile(oslProfile Profile)
+{
+ osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
+
+
+ if ( Profile == nullptr )
+ {
+ return false;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ pProfile = acquireProfile(Profile,true);
+
+ if ( pProfile != nullptr )
+ {
+ if ( !( pProfile->m_Flags & osl_Profile_READLOCK ) && ( pProfile->m_Flags & FLG_MODIFIED ) )
+ {
+ storeProfile(pProfile, false);
+ }
+ }
+ else
+ {
+ pProfile = acquireProfile(Profile,false);
+ }
+
+ if ( pProfile == nullptr )
+ {
+ return false;
+ }
+
+ if (pProfile->m_pFile != nullptr)
+ closeFileImpl(pProfile->m_pFile);
+ }
+
+ pProfile->m_pFile = nullptr;
+ rtl_uString_release(pProfile->m_strFileName);
+ pProfile->m_strFileName = nullptr;
+
+ /* release whole profile data types memory */
+ if ( pProfile->m_NoLines > 0)
+ {
+ unsigned int index=0;
+ if ( pProfile->m_Lines != nullptr )
+ {
+ for ( index = 0 ; index < pProfile->m_NoLines ; ++index)
+ {
+ if ( pProfile->m_Lines[index] != nullptr )
+ {
+ free(pProfile->m_Lines[index]);
+ }
+ }
+ free(pProfile->m_Lines);
+ }
+ if ( pProfile->m_Sections != nullptr )
+ {
+ for ( index = 0 ; index < pProfile->m_NoSections ; ++index )
+ {
+ if ( pProfile->m_Sections[index].m_Entries != nullptr )
+ free(pProfile->m_Sections[index].m_Entries);
+ }
+ free(pProfile->m_Sections);
+ }
+
+ }
+ free(pProfile);
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_flushProfile(oslProfile Profile)
+{
+ osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
+ osl_TFile* pFile;
+ bool bRet = false;
+
+
+ if ( pProfile == nullptr )
+ {
+ return false;
+ }
+
+ pFile = pProfile->m_pFile;
+ if ( pFile == nullptr || pFile->m_Handle == INVALID_HANDLE_VALUE )
+ {
+ return false;
+ }
+
+ if ( pProfile->m_Flags & FLG_MODIFIED )
+ {
+#ifdef DEBUG_OSL_PROFILE
+ SAL_INFO("sal.osl", "swapping to storeprofile");
+#endif
+ bRet = storeProfile(pProfile,false);
+ }
+
+ return bRet;
+}
+
+static bool writeProfileImpl(osl_TFile* pFile)
+{
+ DWORD BytesWritten=0;
+ bool bRet;
+
+ if ( pFile == nullptr || pFile->m_Handle == INVALID_HANDLE_VALUE || ( pFile->m_pWriteBuf == nullptr ) )
+ {
+ return false;
+ }
+
+ bRet=WriteFile(pFile->m_Handle, pFile->m_pWriteBuf, pFile->m_nWriteBufLen - pFile->m_nWriteBufFree,&BytesWritten,nullptr);
+
+ if ( !bRet || BytesWritten == 0 )
+ {
+ OSL_ENSURE(bRet,"WriteFile failed!!!");
+ SAL_WARN("sal.osl", "write failed " << strerror(errno));
+
+ return false;
+ }
+
+ free(pFile->m_pWriteBuf);
+ pFile->m_pWriteBuf=nullptr;
+ pFile->m_nWriteBufLen=0;
+ pFile->m_nWriteBufFree=0;
+
+ return true;
+}
+
+namespace {
+// Use Unicode version of GetPrivateProfileString, to work with Multi-language paths
+DWORD GetPrivateProfileStringWrapper(const osl_TProfileImpl* pProfile,
+ const char* pszSection, const char* pszEntry,
+ char* pszString, sal_uInt32 MaxLen,
+ const char* pszDefault)
+{
+ OSL_ASSERT(pProfile && (!MaxLen || pszString));
+
+ rtl_uString *pSection = nullptr, *pEntry = nullptr, *pDefault = nullptr;
+ if (pszSection)
+ {
+ rtl_string2UString(&pSection, pszSection, strlen(pszSection), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS);
+ OSL_ASSERT(pSection);
+ }
+ if (pszEntry)
+ {
+ rtl_string2UString(&pEntry, pszEntry, strlen(pszEntry), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS);
+ OSL_ASSERT(pEntry);
+ }
+ if (pszDefault)
+ {
+ rtl_string2UString(&pDefault, pszDefault, strlen(pszDefault), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS);
+ OSL_ASSERT(pDefault);
+ }
+
+ LPCWSTR pWSection = (pSection ? o3tl::toW(rtl_uString_getStr(pSection)) : nullptr),
+ pWEntry = (pEntry ? o3tl::toW(rtl_uString_getStr(pEntry)) : nullptr),
+ pWDefault = (pDefault ? o3tl::toW(rtl_uString_getStr(pDefault)) : nullptr);
+
+ std::vector<wchar_t> aBuf(MaxLen + 1);
+ GetPrivateProfileStringW(pWSection, pWEntry, pWDefault, aBuf.data(), MaxLen, o3tl::toW(rtl_uString_getStr(pProfile->m_strFileName)));
+
+ if (pDefault)
+ rtl_uString_release(pDefault);
+ if (pEntry)
+ rtl_uString_release(pEntry);
+ if (pSection)
+ rtl_uString_release(pSection);
+
+ return WideCharToMultiByte(CP_ACP, 0, aBuf.data(), -1, pszString, MaxLen, nullptr, nullptr);
+}
+
+// Use Unicode version of WritePrivateProfileString, to work with Multi-language paths
+bool WritePrivateProfileStringWrapper(const osl_TProfileImpl* pProfile,
+ const char* pszSection, const char* pszEntry,
+ const char* pszString)
+{
+ OSL_ASSERT(pProfile && pszSection);
+ rtl_uString *pSection, *pEntry = nullptr, *pString = nullptr;
+ rtl_string2UString(&pSection, pszSection, strlen(pszSection), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS);
+ OSL_ASSERT(pSection);
+ if (pszEntry)
+ {
+ rtl_string2UString(&pEntry, pszEntry, strlen(pszEntry), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS);
+ OSL_ASSERT(pEntry);
+ }
+ if (pszString)
+ {
+ rtl_string2UString(&pString, pszString, strlen(pszString), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS);
+ OSL_ASSERT(pString);
+ }
+
+ LPCWSTR pWSection = o3tl::toW(pSection->buffer),
+ pWEntry = (pEntry ? o3tl::toW(rtl_uString_getStr(pEntry)) : nullptr),
+ pWString = (pString ? o3tl::toW(rtl_uString_getStr(pString)) : nullptr);
+
+ bool bResult = WritePrivateProfileStringW(pWSection, pWEntry, pWString, o3tl::toW(rtl_uString_getStr(pProfile->m_strFileName)));
+
+ if (pString)
+ rtl_uString_release(pString);
+ if (pEntry)
+ rtl_uString_release(pEntry);
+ rtl_uString_release(pSection);
+
+ return bResult;
+}
+}
+
+sal_Bool SAL_CALL osl_readProfileString(oslProfile Profile,
+ const char* pszSection, const char* pszEntry,
+ char* pszString, sal_uInt32 MaxLen,
+ const char* pszDefault)
+{
+ sal_uInt32 NoEntry;
+ const char* pStr = nullptr;
+ osl_TProfileImpl* pProfile = nullptr;
+
+ pProfile = acquireProfile(Profile, false);
+
+ if (pProfile == nullptr)
+ {
+ return false;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ osl_TProfileSection* pSec;
+ if (((pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry)) != nullptr) &&
+ (NoEntry < pSec->m_NoEntries) &&
+ ((pStr = strchr(pProfile->m_Lines[pSec->m_Entries[NoEntry].m_Line],
+ '=')) != nullptr))
+ pStr++;
+ else
+ pStr = pszDefault;
+
+ if ( pStr != nullptr )
+ {
+ pStr = stripBlanks(pStr, nullptr);
+ MaxLen = (MaxLen - 1 < strlen(pStr)) ? (MaxLen - 1) : strlen(pStr);
+ pStr = stripBlanks(pStr, &MaxLen);
+ strncpy(pszString, pStr, MaxLen);
+ pszString[MaxLen] = '\0';
+ }
+ }
+ else
+ {
+ if (GetPrivateProfileStringWrapper(pProfile, pszSection, pszEntry, pszString, MaxLen, pszDefault) > 0)
+ pStr = pszString; // required to return true below
+ }
+
+ releaseProfile(pProfile);
+
+ if ( pStr == nullptr )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_readProfileBool(oslProfile Profile,
+ const char* pszSection, const char* pszEntry,
+ sal_Bool Default)
+{
+ char Line[32];
+
+ if (osl_readProfileString(Profile, pszSection, pszEntry, Line, sizeof(Line), ""))
+ {
+ if ((stricmp(Line, STR_INI_BOOLYES) == 0) ||
+ (stricmp(Line, STR_INI_BOOLON) == 0) ||
+ (stricmp(Line, STR_INI_BOOLONE) == 0))
+ Default = true;
+ else
+ if ((stricmp(Line, STR_INI_BOOLNO) == 0) ||
+ (stricmp(Line, STR_INI_BOOLOFF) == 0) ||
+ (stricmp(Line, STR_INI_BOOLZERO) == 0))
+ Default = false;
+ }
+
+ return Default;
+}
+
+sal_uInt32 SAL_CALL osl_readProfileIdent(oslProfile Profile,
+ const char* pszSection, const char* pszEntry,
+ sal_uInt32 FirstId, const char* Strings[],
+ sal_uInt32 Default)
+{
+ sal_uInt32 i;
+ char Line[256];
+
+ if (osl_readProfileString(Profile, pszSection, pszEntry, Line, sizeof(Line), ""))
+ {
+ i = 0;
+ while (Strings[i] != nullptr)
+ {
+ if (stricmp(Line, Strings[i]) == 0)
+ {
+ Default = i + FirstId;
+ break;
+ }
+ i++;
+ }
+ }
+
+ return Default;
+}
+
+sal_Bool SAL_CALL osl_writeProfileString(oslProfile Profile,
+ const char* pszSection, const char* pszEntry,
+ const char* pszString)
+{
+ sal_uInt32 i;
+ bool bRet = false;
+ sal_uInt32 NoEntry;
+ const char* pStr;
+ char Line[4096];
+ osl_TProfileSection* pSec;
+ osl_TProfileImpl* pProfile = nullptr;
+
+ pProfile = acquireProfile(Profile, true);
+
+ if (pProfile == nullptr)
+ {
+ return false;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ if ((pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry)) == nullptr)
+ {
+ Line[0] = '\0';
+ addLine(pProfile, Line);
+
+ Line[0] = '[';
+ strcpy(&Line[1], pszSection);
+ Line[1 + strlen(pszSection)] = ']';
+ Line[2 + strlen(pszSection)] = '\0';
+
+ if (((pStr = addLine(pProfile, Line)) == nullptr) ||
+ (! addSection(pProfile, pProfile->m_NoLines - 1, &pStr[1], strlen(pszSection))))
+ {
+ releaseProfile(pProfile);
+ return false;
+ }
+
+ pSec = &pProfile->m_Sections[pProfile->m_NoSections - 1];
+ NoEntry = pSec->m_NoEntries;
+ }
+
+ Line[0] = '\0';
+ strcpy(&Line[0], pszEntry);
+ Line[0 + strlen(pszEntry)] = '=';
+ strcpy(&Line[1 + strlen(pszEntry)], pszString);
+
+ if (NoEntry >= pSec->m_NoEntries)
+ {
+ if (pSec->m_NoEntries > 0)
+ i = pSec->m_Entries[pSec->m_NoEntries - 1].m_Line + 1;
+ else
+ i = pSec->m_Line + 1;
+
+ if (((pStr = insertLine(pProfile, Line, i)) == nullptr) ||
+ (! addEntry(pProfile, pSec, i, pStr, strlen(pszEntry))))
+ {
+ releaseProfile(pProfile);
+ return false;
+ }
+
+ pProfile->m_Flags |= FLG_MODIFIED;
+ }
+ else
+ {
+ i = pSec->m_Entries[NoEntry].m_Line;
+ free(pProfile->m_Lines[i]);
+ pProfile->m_Lines[i] = strdup(Line);
+ setEntry(pProfile, pSec, NoEntry, i, pProfile->m_Lines[i], strlen(pszEntry));
+
+ pProfile->m_Flags |= FLG_MODIFIED;
+ }
+ }
+ else
+ {
+ WritePrivateProfileStringWrapper(pProfile, pszSection, pszEntry, pszString);
+ }
+
+ bRet = releaseProfile(pProfile);
+ return bRet;
+}
+
+sal_Bool SAL_CALL osl_writeProfileBool(oslProfile Profile,
+ const char* pszSection, const char* pszEntry,
+ sal_Bool Value)
+{
+ bool bRet = false;
+
+ if (Value)
+ bRet=osl_writeProfileString(Profile, pszSection, pszEntry, STR_INI_BOOLONE);
+ else
+ bRet=osl_writeProfileString(Profile, pszSection, pszEntry, STR_INI_BOOLZERO);
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL osl_writeProfileIdent(oslProfile Profile,
+ const char* pszSection, const char* pszEntry,
+ sal_uInt32 FirstId, const char* Strings[],
+ sal_uInt32 Value)
+{
+ int i, n;
+ bool bRet = false;
+
+ for (n = 0; Strings[n] != nullptr; n++);
+
+ if ((i = Value - FirstId) >= n)
+ bRet=false;
+ else
+ bRet=osl_writeProfileString(Profile, pszSection, pszEntry, Strings[i]);
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL osl_removeProfileEntry(oslProfile Profile,
+ const char *pszSection, const char *pszEntry)
+{
+ sal_uInt32 NoEntry;
+ osl_TProfileImpl* pProfile = nullptr;
+ bool bRet = false;
+
+ pProfile = acquireProfile(Profile, true);
+
+ if (pProfile == nullptr)
+ return false;
+
+ if (!(pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ osl_TProfileSection* pSec;
+ if (((pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry)) != nullptr) &&
+ (NoEntry < pSec->m_NoEntries))
+ {
+ removeLine(pProfile, pSec->m_Entries[NoEntry].m_Line);
+ removeEntry(pSec, NoEntry);
+ if (pSec->m_NoEntries == 0)
+ {
+ removeLine(pProfile, pSec->m_Line);
+
+ /* remove any empty separation line */
+ if ((pSec->m_Line > 0) && (pProfile->m_Lines[pSec->m_Line - 1][0] == '\0'))
+ removeLine(pProfile, pSec->m_Line - 1);
+
+ removeSection(pProfile, pSec);
+ }
+
+ pProfile->m_Flags |= FLG_MODIFIED;
+ }
+ }
+ else
+ {
+ WritePrivateProfileStringWrapper(pProfile, pszSection, pszEntry, nullptr);
+ }
+
+ bRet = releaseProfile(pProfile);
+ return bRet;
+}
+
+sal_uInt32 SAL_CALL osl_getProfileSectionEntries(oslProfile Profile, const char *pszSection,
+ char* pszBuffer, sal_uInt32 MaxLen)
+{
+ sal_uInt32 i, n = 0;
+ sal_uInt32 NoEntry;
+ osl_TProfileImpl* pProfile = nullptr;
+
+ pProfile = acquireProfile(Profile, false);
+
+ if (pProfile == nullptr)
+ return 0;
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ osl_TProfileSection* pSec;
+ if ((pSec = findEntry(pProfile, pszSection, "", &NoEntry)) != nullptr)
+ {
+ if (MaxLen != 0)
+ {
+ for (i = 0; i < pSec->m_NoEntries; i++)
+ {
+ if ((n + pSec->m_Entries[i].m_Len + 1) < MaxLen)
+ {
+ strncpy(&pszBuffer[n], &pProfile->m_Lines[pSec->m_Entries[i].m_Line]
+ [pSec->m_Entries[i].m_Offset], pSec->m_Entries[i].m_Len);
+ n += pSec->m_Entries[i].m_Len;
+ pszBuffer[n++] = '\0';
+ }
+ else
+ {
+ break;
+ }
+
+ }
+
+ pszBuffer[n++] = '\0';
+ }
+ else
+ {
+ for (i = 0; i < pSec->m_NoEntries; i++)
+ {
+ n += pSec->m_Entries[i].m_Len + 1;
+ }
+
+ n += 1;
+ }
+ }
+ else
+ {
+ n = 0;
+ }
+ }
+ else
+ {
+ n = GetPrivateProfileStringWrapper(pProfile, pszSection, nullptr, pszBuffer, MaxLen, nullptr);
+ }
+
+ releaseProfile(pProfile);
+
+ return n;
+}
+
+bool osl_getProfileName(rtl_uString* strPath, rtl_uString* strName, rtl_uString** strProfileName)
+{
+ bool bFailed;
+ ::osl::LongPathBuffer< sal_Unicode > aFile( MAX_LONG_PATH );
+ ::osl::LongPathBuffer< sal_Unicode > aPath( MAX_LONG_PATH );
+ sal_uInt32 nFileLen = 0;
+ sal_uInt32 nPathLen = 0;
+
+ rtl_uString * strTmp = nullptr;
+ oslFileError nError;
+
+ /* build file name */
+ if (strName && strName->length)
+ {
+ if( ::sal::static_int_cast< sal_uInt32 >( strName->length ) >= aFile.getBufSizeInSymbols() )
+ return false;
+
+ copy_ustr_n( aFile, strName->buffer, strName->length+1);
+ nFileLen = strName->length;
+
+ if (rtl_ustr_indexOfChar( aFile, L'.' ) == -1)
+ {
+ if (nFileLen + wcslen(STR_INI_EXTENSION) >= aFile.getBufSizeInSymbols())
+ return false;
+
+ /* add default extension */
+ copy_ustr_n( aFile + nFileLen, STR_INI_EXTENSION, wcslen(STR_INI_EXTENSION)+1 );
+ nFileLen += wcslen(STR_INI_EXTENSION);
+ }
+ }
+ else
+ {
+ rtl_uString *strProgName = nullptr;
+ sal_Unicode *pProgName;
+ sal_Int32 nOffset = 0;
+ sal_Int32 nLen;
+ sal_Int32 nPos;
+
+ if (osl_getExecutableFile(&strProgName) != osl_Process_E_None)
+ return false;
+
+ /* remove path and extension from filename */
+ pProgName = strProgName->buffer;
+ nLen = strProgName->length ;
+
+ if ((nPos = rtl_ustr_lastIndexOfChar( pProgName, L'/' )) != -1)
+ nOffset = nPos + 1;
+ else if ((nPos = rtl_ustr_lastIndexOfChar( pProgName, L':' )) != -1)
+ nOffset = nPos + 1;
+
+ if ((nPos = rtl_ustr_lastIndexOfChar( pProgName, L'.' )) != -1 )
+ nLen -= 4;
+
+ if ((nFileLen = nLen - nOffset) >= aFile.getBufSizeInSymbols())
+ return false;
+
+ copy_ustr_n(aFile, pProgName + nOffset, nFileLen);
+
+ if (nFileLen + wcslen(STR_INI_EXTENSION) >= aFile.getBufSizeInSymbols())
+ return false;
+
+ /* add default extension */
+ copy_ustr_n(aFile + nFileLen, STR_INI_EXTENSION, wcslen(STR_INI_EXTENSION)+1);
+ nFileLen += wcslen(STR_INI_EXTENSION);
+
+ rtl_uString_release( strProgName );
+ }
+
+ if (aFile[0] == 0)
+ return false;
+
+ /* build directory path */
+ if (strPath && strPath->length)
+ {
+ sal_Unicode *pPath = rtl_uString_getStr(strPath);
+ sal_Int32 nLen = rtl_uString_getLength(strPath);
+
+ if ((rtl_ustr_ascii_compare_WithLength(pPath, RTL_CONSTASCII_LENGTH(STR_INI_METAHOME) , STR_INI_METAHOME) == 0) &&
+ ((nLen == RTL_CONSTASCII_LENGTH(STR_INI_METAHOME)) || (pPath[RTL_CONSTASCII_LENGTH(STR_INI_METAHOME)] == '/')))
+ {
+ rtl_uString * strHome = nullptr;
+ oslSecurity security = osl_getCurrentSecurity();
+
+ bFailed = ! osl_getHomeDir(security, &strHome);
+ osl_freeSecurityHandle(security);
+
+ if (bFailed) return false;
+
+ if ( ::sal::static_int_cast< sal_uInt32 >( strHome->length ) >= aPath.getBufSizeInSymbols())
+ return false;
+
+ copy_ustr_n( aPath, strHome->buffer, strHome->length+1);
+ nPathLen = strHome->length;
+
+ if (nLen > RTL_CONSTASCII_LENGTH(STR_INI_METAHOME))
+ {
+ pPath += RTL_CONSTASCII_LENGTH(STR_INI_METAHOME);
+ nLen -= RTL_CONSTASCII_LENGTH(STR_INI_METAHOME);
+
+ if (nLen + nPathLen >= aPath.getBufSizeInSymbols())
+ return false;
+
+ copy_ustr_n(aPath + nPathLen, pPath, nLen+1);
+ nPathLen += nLen;
+ }
+
+ rtl_uString_release(strHome);
+ }
+
+ else if ((rtl_ustr_ascii_compare_WithLength(pPath, RTL_CONSTASCII_LENGTH(STR_INI_METACFG), STR_INI_METACFG) == 0) &&
+ ((nLen == RTL_CONSTASCII_LENGTH(STR_INI_METACFG)) || (pPath[RTL_CONSTASCII_LENGTH(STR_INI_METACFG)] == '/')))
+ {
+ rtl_uString * strConfig = nullptr;
+ oslSecurity security = osl_getCurrentSecurity();
+
+ bFailed = ! osl_getConfigDir(security, &strConfig);
+ osl_freeSecurityHandle(security);
+
+ if (bFailed) return false;
+
+ if ( ::sal::static_int_cast< sal_uInt32 >( strConfig->length ) >= aPath.getBufSizeInSymbols())
+ return false;
+
+ copy_ustr_n( aPath, strConfig->buffer, strConfig->length+1 );
+ nPathLen = strConfig->length;
+
+ if (nLen > RTL_CONSTASCII_LENGTH(STR_INI_METACFG))
+ {
+ pPath += RTL_CONSTASCII_LENGTH(STR_INI_METACFG);
+ nLen -= RTL_CONSTASCII_LENGTH(STR_INI_METACFG);
+
+ if (nLen + nPathLen >= aPath.getBufSizeInSymbols())
+ return false;
+
+ copy_ustr_n(aPath + nPathLen, pPath, nLen+1);
+ nPathLen += nLen;
+ }
+
+ rtl_uString_release(strConfig);
+ }
+
+ else if ((rtl_ustr_ascii_compare_WithLength(pPath, RTL_CONSTASCII_LENGTH(STR_INI_METASYS), STR_INI_METASYS) == 0) &&
+ ((nLen == RTL_CONSTASCII_LENGTH(STR_INI_METASYS)) || (pPath[RTL_CONSTASCII_LENGTH(STR_INI_METASYS)] == '/')))
+ {
+ if (((nPathLen = GetWindowsDirectoryW(o3tl::toW(aPath), aPath.getBufSizeInSymbols())) == 0) || (nPathLen >= aPath.getBufSizeInSymbols()))
+ return false;
+
+ if (nLen > RTL_CONSTASCII_LENGTH(STR_INI_METASYS))
+ {
+ pPath += RTL_CONSTASCII_LENGTH(STR_INI_METASYS);
+ nLen -= RTL_CONSTASCII_LENGTH(STR_INI_METASYS);
+
+ if (nLen + nPathLen >= aPath.getBufSizeInSymbols())
+ return false;
+
+ copy_ustr_n(aPath + nPathLen, pPath, nLen+1);
+ nPathLen += nLen;
+ }
+ }
+
+ else if ((rtl_ustr_ascii_compare_WithLength(pPath, RTL_CONSTASCII_LENGTH(STR_INI_METAINS), STR_INI_METAINS) == 0) &&
+ ((nLen == RTL_CONSTASCII_LENGTH(STR_INI_METAINS)) || (pPath[RTL_CONSTASCII_LENGTH(STR_INI_METAINS)] == '/') ||
+ (pPath[RTL_CONSTASCII_LENGTH(STR_INI_METAINS)] == '"') ) )
+ {
+ if (! lookupProfile(pPath + RTL_CONSTASCII_LENGTH(STR_INI_METAINS), aFile, aPath))
+ return false;
+
+ nPathLen = rtl_ustr_getLength(aPath);
+ }
+
+ else if( ::sal::static_int_cast< sal_uInt32 >( nLen ) < aPath.getBufSizeInSymbols())
+ {
+ copy_ustr_n(aPath, pPath, nLen+1);
+ nPathLen = rtl_ustr_getLength(aPath);
+ }
+ else
+ return false;
+ }
+ else
+ {
+ rtl_uString * strConfigDir = nullptr;
+ oslSecurity security = osl_getCurrentSecurity();
+
+ bFailed = ! osl_getConfigDir(security, &strConfigDir);
+ osl_freeSecurityHandle(security);
+
+ if (bFailed) return false;
+ if ( ::sal::static_int_cast< sal_uInt32 >( strConfigDir->length ) >= aPath.getBufSizeInSymbols() )
+ return false;
+
+ copy_ustr_n(aPath, strConfigDir->buffer, strConfigDir->length+1);
+ nPathLen = strConfigDir->length;
+ }
+
+ if (nPathLen && (aPath[nPathLen - 1] != L'/') && (aPath[nPathLen - 1] != L'\\'))
+ {
+ aPath[nPathLen++] = L'\\';
+ aPath[nPathLen] = 0;
+ }
+
+ if (nPathLen + nFileLen >= aPath.getBufSizeInSymbols())
+ return false;
+
+ /* append file name */
+ copy_ustr_n(aPath + nPathLen, aFile, nFileLen+1);
+ nPathLen += nFileLen;
+
+ /* copy filename */
+ rtl_uString_newFromStr_WithLength(&strTmp, aPath, nPathLen);
+ nError = osl_getFileURLFromSystemPath(strTmp, strProfileName);
+ rtl_uString_release(strTmp);
+
+ return nError == osl_File_E_None;
+}
+
+sal_uInt32 SAL_CALL osl_getProfileSections(oslProfile Profile, char* pszBuffer, sal_uInt32 MaxLen)
+{
+ sal_uInt32 i, n = 0;
+ osl_TProfileImpl* pProfile = acquireProfile(Profile, false);
+
+ if (pProfile == nullptr)
+ return 0;
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ if (MaxLen != 0)
+ {
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ osl_TProfileSection* pSec = &pProfile->m_Sections[i];
+
+ if ((n + pSec->m_Len + 1) < MaxLen)
+ {
+ strncpy(&pszBuffer[n], &pProfile->m_Lines[pSec->m_Line][pSec->m_Offset],
+ pSec->m_Len);
+ n += pSec->m_Len;
+ pszBuffer[n++] = '\0';
+ }
+ else
+ break;
+ }
+
+ pszBuffer[n++] = '\0';
+ }
+ else
+ {
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ n += pProfile->m_Sections[i].m_Len + 1;
+
+ n += 1;
+ }
+ }
+ else
+ {
+ std::vector<wchar_t> aBuf(MaxLen + 1);
+ GetPrivateProfileSectionNamesW(aBuf.data(), MaxLen, o3tl::toW(rtl_uString_getStr(pProfile->m_strFileName)));
+
+ n = WideCharToMultiByte(CP_ACP, 0, aBuf.data(), -1, pszBuffer, MaxLen, nullptr, nullptr);
+ }
+
+ releaseProfile(pProfile);
+
+ return n;
+}
+
+static osl_TStamp getFileStamp(osl_TFile* pFile)
+{
+ FILETIME FileTime;
+
+ if ((pFile->m_Handle == INVALID_HANDLE_VALUE) ||
+ (! GetFileTime(pFile->m_Handle, nullptr, nullptr, &FileTime)))
+ memset(&FileTime, 0, sizeof(FileTime));
+
+ return FileTime;
+}
+
+static bool lockFile(const osl_TFile* pFile, osl_TLockMode eMode)
+{
+ bool status = false;
+ OVERLAPPED Overlapped = {};
+
+ if (pFile->m_Handle == INVALID_HANDLE_VALUE)
+ return false;
+
+ switch (eMode)
+ {
+ case un_lock:
+ status = UnlockFileEx(
+ pFile->m_Handle, 0, 0xFFFFFFFF, 0, &Overlapped);
+ break;
+
+ case read_lock:
+ status = LockFileEx(
+ pFile->m_Handle, 0, 0, 0xFFFFFFFF, 0, &Overlapped);
+ break;
+
+ case write_lock:
+ status = LockFileEx(
+ pFile->m_Handle, LOCKFILE_EXCLUSIVE_LOCK, 0, 0xFFFFFFFF, 0,
+ &Overlapped);
+ break;
+ }
+
+ return status;
+}
+
+static osl_TFile* openFileImpl(rtl_uString * strFileName, oslProfileOption ProfileFlags )
+{
+ osl_TFile* pFile = static_cast< osl_TFile*>( calloc( 1, sizeof(osl_TFile) ) );
+ if (!pFile)
+ return nullptr;
+ bool bWriteable = false;
+
+ if ( ProfileFlags & ( osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE ) )
+ {
+#ifdef DEBUG_OSL_PROFILE
+ SAL_INFO("sal.osl", "setting bWriteable to TRUE");
+#endif
+ bWriteable=true;
+ }
+
+ if (! bWriteable)
+ {
+ pFile->m_Handle = CreateFileW( o3tl::toW(rtl_uString_getStr( strFileName )), GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+
+ /* mfe: argghh!!! do not check if the file could be opened */
+ /* default mode expects it that way!!! */
+ }
+ else
+ {
+#ifdef DEBUG_OSL_PROFILE
+ SAL_INFO("sal.osl", "opening read/write " << pszFilename);
+#endif
+
+ if ((pFile->m_Handle = CreateFileW( o3tl::toW(rtl_uString_getStr( strFileName )), GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr))
+ == INVALID_HANDLE_VALUE)
+ {
+ free(pFile);
+ return nullptr;
+ }
+ }
+
+ pFile->m_pWriteBuf=nullptr;
+ pFile->m_nWriteBufFree=0;
+ pFile->m_nWriteBufLen=0;
+
+ if ( ProfileFlags & (osl_Profile_WRITELOCK | osl_Profile_READLOCK ) )
+ {
+#ifdef DEBUG_OSL_PROFILE
+ SAL_INFO("sal.osl", "locking file " << pszFilename);
+#endif
+
+ lockFile(pFile, bWriteable ? write_lock : read_lock);
+ }
+
+ return pFile;
+}
+
+static osl_TStamp closeFileImpl(osl_TFile* pFile)
+{
+ osl_TStamp stamp = {0, 0};
+
+ if ( pFile == nullptr )
+ {
+ return stamp;
+ }
+
+ if (pFile->m_Handle != INVALID_HANDLE_VALUE)
+ {
+ stamp = getFileStamp(pFile);
+
+ lockFile(pFile, un_lock);
+
+ CloseHandle(pFile->m_Handle);
+ pFile->m_Handle = INVALID_HANDLE_VALUE;
+ }
+
+ if ( pFile->m_pWriteBuf != nullptr )
+ {
+ free(pFile->m_pWriteBuf);
+ }
+
+ free(pFile);
+
+ return stamp;
+}
+
+static bool rewindFile(osl_TFile* pFile, bool bTruncate)
+{
+ if (pFile->m_Handle != INVALID_HANDLE_VALUE)
+ {
+ pFile->m_pReadPtr = pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf);
+
+ SetFilePointer(pFile->m_Handle, 0, nullptr, FILE_BEGIN);
+
+ if (bTruncate)
+ SetEndOfFile(pFile->m_Handle);
+ }
+
+ return true;
+}
+
+static bool getLine(osl_TFile* pFile, char *pszLine, int MaxLen)
+{
+ DWORD Max;
+ size_t Free;
+ char* pChr;
+ char* pLine = pszLine;
+
+ if (pFile->m_Handle == INVALID_HANDLE_VALUE)
+ return false;
+
+ MaxLen -= 1;
+
+ do
+ {
+ size_t Bytes = sizeof(pFile->m_ReadBuf) - (pFile->m_pReadPtr - pFile->m_ReadBuf);
+
+ if (Bytes <= 1)
+ {
+ /* refill buffer */
+ memcpy(pFile->m_ReadBuf, pFile->m_pReadPtr, Bytes);
+ pFile->m_pReadPtr = pFile->m_ReadBuf;
+
+ Free = sizeof(pFile->m_ReadBuf) - Bytes;
+
+ if (! ReadFile(pFile->m_Handle, &pFile->m_ReadBuf[Bytes], Free, &Max, nullptr))
+ {
+ *pLine = '\0';
+ return false;
+ }
+
+ if (Max < Free)
+ {
+ if ((Max == 0) && (pLine == pszLine))
+ {
+ *pLine = '\0';
+ return false;
+ }
+
+ pFile->m_ReadBuf[Bytes + Max] = '\0';
+ }
+ }
+
+ for (pChr = pFile->m_pReadPtr;
+ (*pChr != '\n') && (*pChr != '\r') && (*pChr != '\0') &&
+ (pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf) - 1));
+ pChr++);
+
+ Max = min(static_cast<int>(pChr - pFile->m_pReadPtr), MaxLen);
+ memcpy(pLine, pFile->m_pReadPtr, Max);
+ MaxLen -= Max;
+ pLine += Max;
+
+ if (pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf) - 1))
+ {
+ if (*pChr != '\0')
+ {
+ if ((pChr[0] == '\r') && (pChr[1] == '\n'))
+ pChr += 2;
+ else
+ pChr += 1;
+ }
+
+ if ((pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf))) &&
+ (*pChr == '\0'))
+ pChr = pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf);
+
+ *pLine = '\0';
+
+ /* setting MaxLen to -1 indicates terminating read loop */
+ MaxLen = -1;
+ }
+
+ pFile->m_pReadPtr = pChr;
+ }
+ while (MaxLen > 0);
+
+ return true;
+}
+
+static bool putLine(osl_TFile* pFile, const char *pszLine)
+{
+ unsigned int Len = strlen(pszLine);
+
+ if ( pFile == nullptr || pFile->m_Handle == INVALID_HANDLE_VALUE )
+ {
+ return false;
+ }
+
+ if ( pFile->m_pWriteBuf == nullptr )
+ {
+ pFile->m_pWriteBuf = static_cast<char*>(malloc(Len+3));
+ pFile->m_nWriteBufLen = Len+3;
+ pFile->m_nWriteBufFree = Len+3;
+ }
+ else
+ {
+ if ( pFile->m_nWriteBufFree <= Len + 3 )
+ {
+ char* pTmp;
+
+ pTmp=static_cast<char*>(realloc(pFile->m_pWriteBuf,( ( pFile->m_nWriteBufLen + Len ) * 2) ));
+ if ( pTmp == nullptr )
+ {
+ return false;
+ }
+ pFile->m_pWriteBuf = pTmp;
+ pFile->m_nWriteBufFree = pFile->m_nWriteBufFree + pFile->m_nWriteBufLen + ( 2 * Len );
+ pFile->m_nWriteBufLen = ( pFile->m_nWriteBufLen + Len ) * 2;
+ memset( (pFile->m_pWriteBuf) + ( pFile->m_nWriteBufLen - pFile->m_nWriteBufFree ), 0, pFile->m_nWriteBufFree);
+ }
+ }
+
+ memcpy(pFile->m_pWriteBuf + ( pFile->m_nWriteBufLen - pFile->m_nWriteBufFree ),pszLine,Len+1);
+
+ pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len]='\r';
+ pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len + 1]='\n';
+ pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len + 2]='\0';
+
+ pFile->m_nWriteBufFree-=Len+2;
+
+ return true;
+}
+
+/* platform specific end */
+
+static const char* stripBlanks(const char* String, sal_uInt32* pLen)
+{
+ if ( (pLen != nullptr) && ( *pLen != 0 ) )
+ {
+ while ((String[*pLen - 1] == ' ') || (String[*pLen - 1] == '\t'))
+ (*pLen)--;
+
+ while ((*String == ' ') || (*String == '\t'))
+ {
+ String++;
+ (*pLen)--;
+ }
+ }
+ else
+ while ((*String == ' ') || (*String == '\t'))
+ String++;
+
+ return String;
+}
+
+static const char* addLine(osl_TProfileImpl* pProfile, const char* Line)
+{
+ if (pProfile->m_NoLines >= pProfile->m_MaxLines)
+ {
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_MaxLines = LINES_INI;
+ pProfile->m_Lines = static_cast<char **>(calloc(pProfile->m_MaxLines, sizeof(char *)));
+ }
+ else
+ {
+ unsigned int index=0;
+ unsigned int oldmax=pProfile->m_MaxLines;
+
+ pProfile->m_MaxLines += LINES_ADD;
+ if (auto p = static_cast<char **>(realloc(pProfile->m_Lines, pProfile->m_MaxLines * sizeof(char *))))
+ {
+ pProfile->m_Lines = p;
+
+ for ( index = oldmax ; index < pProfile->m_MaxLines ; ++index )
+ {
+ pProfile->m_Lines[index]=nullptr;
+ }
+ }
+ else
+ {
+ free(pProfile->m_Lines);
+ pProfile->m_Lines = nullptr;
+ }
+ }
+
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_NoLines = 0;
+ pProfile->m_MaxLines = 0;
+ return nullptr;
+ }
+
+ }
+
+ if ( pProfile->m_Lines != nullptr && pProfile->m_Lines[pProfile->m_NoLines] != nullptr )
+ {
+ free(pProfile->m_Lines[pProfile->m_NoLines]);
+ }
+ pProfile->m_Lines[pProfile->m_NoLines++] = strdup(Line);
+
+ return pProfile->m_Lines[pProfile->m_NoLines - 1];
+}
+
+static const char* insertLine(osl_TProfileImpl* pProfile, const char* Line, sal_uInt32 LineNo)
+{
+ if (pProfile->m_NoLines >= pProfile->m_MaxLines)
+ {
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_MaxLines = LINES_INI;
+ pProfile->m_Lines = static_cast<char **>(calloc(pProfile->m_MaxLines, sizeof(char *)));
+ }
+ else
+ {
+ pProfile->m_MaxLines += LINES_ADD;
+ if (auto p = static_cast<char**>(
+ realloc(pProfile->m_Lines, pProfile->m_MaxLines * sizeof(char*))))
+ {
+ pProfile->m_Lines = p;
+
+ memset(&pProfile->m_Lines[pProfile->m_NoLines], 0,
+ (pProfile->m_MaxLines - pProfile->m_NoLines - 1) * sizeof(char*));
+ }
+ else
+ {
+ free(pProfile->m_Lines);
+ pProfile->m_Lines = nullptr;
+ }
+ }
+
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_NoLines = 0;
+ pProfile->m_MaxLines = 0;
+ return nullptr;
+ }
+ }
+
+ LineNo = std::min(LineNo, pProfile->m_NoLines);
+
+ if (LineNo < pProfile->m_NoLines)
+ {
+ sal_uInt32 i, n;
+
+ memmove(&pProfile->m_Lines[LineNo + 1], &pProfile->m_Lines[LineNo],
+ (pProfile->m_NoLines - LineNo) * sizeof(char *));
+
+ /* adjust line references */
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ osl_TProfileSection* pSec = &pProfile->m_Sections[i];
+
+ if (pSec->m_Line >= LineNo)
+ pSec->m_Line++;
+
+ for (n = 0; n < pSec->m_NoEntries; n++)
+ if (pSec->m_Entries[n].m_Line >= LineNo)
+ pSec->m_Entries[n].m_Line++;
+ }
+ }
+
+ pProfile->m_NoLines++;
+
+ pProfile->m_Lines[LineNo] = strdup(Line);
+
+ return pProfile->m_Lines[LineNo];
+}
+
+static void removeLine(osl_TProfileImpl* pProfile, sal_uInt32 LineNo)
+{
+ if (LineNo < pProfile->m_NoLines)
+ {
+ free(pProfile->m_Lines[LineNo]);
+ pProfile->m_Lines[LineNo]=nullptr;
+ if (pProfile->m_NoLines - LineNo > 1)
+ {
+ sal_uInt32 i, n;
+
+ memmove(&pProfile->m_Lines[LineNo], &pProfile->m_Lines[LineNo + 1],
+ (pProfile->m_NoLines - LineNo - 1) * sizeof(char *));
+
+ memset(&pProfile->m_Lines[pProfile->m_NoLines - 1],
+ 0,
+ (pProfile->m_MaxLines - pProfile->m_NoLines) * sizeof(char*));
+
+ /* adjust line references */
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ osl_TProfileSection* pSec = &pProfile->m_Sections[i];
+
+ if (pSec->m_Line > LineNo)
+ pSec->m_Line--;
+
+ for (n = 0; n < pSec->m_NoEntries; n++)
+ if (pSec->m_Entries[n].m_Line > LineNo)
+ pSec->m_Entries[n].m_Line--;
+ }
+ }
+ else
+ {
+ pProfile->m_Lines[LineNo] = nullptr;
+ }
+
+ pProfile->m_NoLines--;
+ }
+
+ return;
+}
+
+static void setEntry(osl_TProfileImpl* pProfile, osl_TProfileSection* pSection,
+ sal_uInt32 NoEntry, sal_uInt32 Line,
+ const char* Entry, sal_uInt32 Len)
+{
+ Entry = stripBlanks(Entry, &Len);
+ pSection->m_Entries[NoEntry].m_Line = Line;
+ pSection->m_Entries[NoEntry].m_Offset = Entry - pProfile->m_Lines[Line];
+ pSection->m_Entries[NoEntry].m_Len = Len;
+
+ return;
+}
+
+static bool addEntry(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection,
+ int Line, const char* Entry, sal_uInt32 Len)
+{
+ if (pSection != nullptr)
+ {
+ if (pSection->m_NoEntries >= pSection->m_MaxEntries)
+ {
+ if (pSection->m_Entries == nullptr)
+ {
+ pSection->m_MaxEntries = ENTRIES_INI;
+ pSection->m_Entries = static_cast<osl_TProfileEntry *>(malloc(
+ pSection->m_MaxEntries * sizeof(osl_TProfileEntry)));
+ }
+ else
+ {
+ pSection->m_MaxEntries += ENTRIES_ADD;
+ if (auto p = static_cast<osl_TProfileEntry*>(realloc(
+ pSection->m_Entries, pSection->m_MaxEntries * sizeof(osl_TProfileEntry))))
+ pSection->m_Entries = p;
+ else
+ {
+ free(pSection->m_Entries);
+ pSection->m_Entries = nullptr;
+ }
+ }
+
+ if (pSection->m_Entries == nullptr)
+ {
+ pSection->m_NoEntries = 0;
+ pSection->m_MaxEntries = 0;
+ return false;
+ }
+ }
+
+ pSection->m_NoEntries++;
+
+ Entry = stripBlanks(Entry, &Len);
+ setEntry(pProfile, pSection, pSection->m_NoEntries - 1, Line,
+ Entry, Len);
+
+ return true;
+ }
+
+ return false;
+}
+
+static void removeEntry(osl_TProfileSection *pSection, sal_uInt32 NoEntry)
+{
+ if (NoEntry < pSection->m_NoEntries)
+ {
+ if (pSection->m_NoEntries - NoEntry > 1)
+ {
+ memmove(&pSection->m_Entries[NoEntry],
+ &pSection->m_Entries[NoEntry + 1],
+ (pSection->m_NoEntries - NoEntry - 1) * sizeof(osl_TProfileEntry));
+ pSection->m_Entries[pSection->m_NoEntries - 1].m_Line=0;
+ pSection->m_Entries[pSection->m_NoEntries - 1].m_Offset=0;
+ pSection->m_Entries[pSection->m_NoEntries - 1].m_Len=0;
+ }
+
+ pSection->m_NoEntries--;
+ }
+
+ return;
+}
+
+static bool addSection(osl_TProfileImpl* pProfile, int Line, const char* Section, sal_uInt32 Len)
+{
+ if (pProfile->m_NoSections >= pProfile->m_MaxSections)
+ {
+ if (pProfile->m_Sections == nullptr)
+ {
+ pProfile->m_MaxSections = SECTIONS_INI;
+ pProfile->m_Sections = static_cast<osl_TProfileSection*>(calloc(pProfile->m_MaxSections, sizeof(osl_TProfileSection)));
+ }
+ else
+ {
+ unsigned int index=0;
+ unsigned int oldmax=pProfile->m_MaxSections;
+
+ pProfile->m_MaxSections += SECTIONS_ADD;
+ if (auto p = static_cast<osl_TProfileSection*>(realloc(
+ pProfile->m_Sections, pProfile->m_MaxSections * sizeof(osl_TProfileSection))))
+ {
+ pProfile->m_Sections = p;
+ for ( index = oldmax ; index < pProfile->m_MaxSections ; ++index )
+ {
+ pProfile->m_Sections[index].m_Entries=nullptr;
+ }
+ }
+ else
+ {
+ free(pProfile->m_Sections);
+ pProfile->m_Sections = nullptr;
+ }
+ }
+
+ if (pProfile->m_Sections == nullptr)
+ {
+ pProfile->m_NoSections = 0;
+ pProfile->m_MaxSections = 0;
+ return false;
+ }
+ }
+
+ pProfile->m_NoSections++;
+
+ if ( pProfile->m_Sections[(pProfile->m_NoSections) - 1].m_Entries != nullptr )
+ {
+ free(pProfile->m_Sections[(pProfile->m_NoSections) - 1].m_Entries);
+ }
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Entries = nullptr;
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_NoEntries = 0;
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_MaxEntries = 0;
+
+ Section = stripBlanks(Section, &Len);
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Line = Line;
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Offset = Section - pProfile->m_Lines[Line];
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Len = Len;
+
+ return true;
+}
+
+static void removeSection(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection)
+{
+ sal_uInt32 Section;
+
+ if ((Section = pSection - pProfile->m_Sections) < pProfile->m_NoSections)
+ {
+ free (pSection->m_Entries);
+ pSection->m_Entries=nullptr;
+ if (pProfile->m_NoSections - Section > 1)
+ {
+ memmove(&pProfile->m_Sections[Section], &pProfile->m_Sections[Section + 1],
+ (pProfile->m_NoSections - Section - 1) * sizeof(osl_TProfileSection));
+
+ memset(&pProfile->m_Sections[pProfile->m_NoSections - 1],
+ 0,
+ (pProfile->m_MaxSections - pProfile->m_NoSections) * sizeof(osl_TProfileSection));
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Entries = nullptr;
+ }
+ else
+ {
+ pSection->m_Entries = nullptr;
+ }
+
+ pProfile->m_NoSections--;
+ }
+
+ return;
+}
+
+static osl_TProfileSection* findEntry(osl_TProfileImpl* pProfile, const char* Section,
+ const char* Entry, sal_uInt32 *pNoEntry)
+{
+ static sal_uInt32 Sect = 0;
+ sal_uInt32 i, n;
+ sal_uInt32 Len;
+ osl_TProfileSection* pSec = nullptr;
+
+ Len = strlen(Section);
+ Section = stripBlanks(Section, &Len);
+
+ n = Sect;
+
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ n %= pProfile->m_NoSections;
+ pSec = &pProfile->m_Sections[n];
+ if ((Len == pSec->m_Len) &&
+ (strnicmp(Section, &pProfile->m_Lines[pSec->m_Line][pSec->m_Offset], pSec->m_Len)
+ == 0))
+ break;
+ n++;
+ }
+
+ Sect = n;
+
+ if (i < pProfile->m_NoSections)
+ {
+ Len = strlen(Entry);
+ Entry = stripBlanks(Entry, &Len);
+
+ *pNoEntry = pSec->m_NoEntries;
+
+ for (i = 0; i < pSec->m_NoEntries; i++)
+ {
+ const char* pStr = &pProfile->m_Lines[pSec->m_Entries[i].m_Line]
+ [pSec->m_Entries[i].m_Offset];
+ if ((Len == pSec->m_Entries[i].m_Len) &&
+ (strnicmp(Entry, pStr, pSec->m_Entries[i].m_Len)
+ == 0))
+ {
+ *pNoEntry = i;
+ break;
+ }
+ }
+ }
+ else
+ pSec = nullptr;
+
+ return pSec;
+}
+
+static bool loadProfile(osl_TFile* pFile, osl_TProfileImpl* pProfile)
+{
+ sal_uInt32 i;
+ char const * pStr;
+ char const * pChar;
+ char Line[4096];
+
+ pProfile->m_NoLines = 0;
+ pProfile->m_NoSections = 0;
+
+ OSL_VERIFY(rewindFile(pFile, false));
+
+ while (getLine(pFile, Line, sizeof(Line)))
+ {
+ if (! addLine(pProfile, Line))
+ return false;
+ }
+
+ for (i = 0; i < pProfile->m_NoLines; i++)
+ {
+ pStr = stripBlanks(pProfile->m_Lines[i], nullptr);
+
+ if ((*pStr == '\0') || (*pStr == ';'))
+ continue;
+
+ if ((*pStr != '[') || ((pChar = strrchr(pStr, ']')) == nullptr) ||
+ ((pChar - pStr) <= 2))
+ {
+ /* insert entry */
+
+ if (pProfile->m_NoSections < 1)
+ continue;
+
+ if ((pChar = strchr(pStr, '=')) == nullptr)
+ pChar = pStr + strlen(pStr);
+
+ if (! addEntry(pProfile, &pProfile->m_Sections[pProfile->m_NoSections - 1],
+ i, pStr, pChar - pStr))
+ return false;
+ }
+ else
+ {
+ /* new section */
+ if (! addSection(pProfile, i, pStr + 1, pChar - pStr - 1))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool storeProfile(osl_TProfileImpl* pProfile, bool bCleanup)
+{
+ if (pProfile->m_Lines != nullptr)
+ {
+ if (pProfile->m_Flags & FLG_MODIFIED)
+ {
+ sal_uInt32 i;
+
+ osl_TFile* pTmpFile = osl_openTmpProfileImpl(pProfile);
+
+ if ( pTmpFile == nullptr )
+ {
+ return false;
+ }
+
+ OSL_VERIFY(rewindFile(pTmpFile, true));
+
+ for (i = 0; i < pProfile->m_NoLines; i++)
+ {
+ OSL_VERIFY(putLine(pTmpFile, pProfile->m_Lines[i]));
+ }
+
+ if ( ! writeProfileImpl(pTmpFile) )
+ {
+ if ( pTmpFile->m_pWriteBuf != nullptr )
+ {
+ free(pTmpFile->m_pWriteBuf);
+ }
+
+ pTmpFile->m_pWriteBuf=nullptr;
+ pTmpFile->m_nWriteBufLen=0;
+ pTmpFile->m_nWriteBufFree=0;
+
+ closeFileImpl(pTmpFile);
+
+ return false;
+ }
+
+ pProfile->m_Flags &= ~FLG_MODIFIED;
+
+ closeFileImpl(pProfile->m_pFile);
+ closeFileImpl(pTmpFile);
+
+ osl_ProfileSwapProfileNames(pProfile);
+
+ pProfile->m_pFile = openFileImpl(pProfile->m_strFileName,pProfile->m_Flags);
+
+ }
+
+ if (bCleanup)
+ {
+ while (pProfile->m_NoLines > 0)
+ removeLine(pProfile, pProfile->m_NoLines - 1);
+
+ free(pProfile->m_Lines);
+ pProfile->m_Lines = nullptr;
+ pProfile->m_MaxLines = 0;
+
+ while (pProfile->m_NoSections > 0)
+ removeSection(pProfile, &pProfile->m_Sections[pProfile->m_NoSections - 1]);
+
+ free(pProfile->m_Sections);
+ pProfile->m_Sections = nullptr;
+ pProfile->m_MaxSections = 0;
+ }
+ }
+
+ return true;
+}
+
+static osl_TFile* osl_openTmpProfileImpl(osl_TProfileImpl* pProfile)
+{
+ osl_TFile* pFile=nullptr;
+ rtl_uString* ustrExtension=nullptr;
+ rtl_uString* ustrTmpName=nullptr;
+ oslProfileOption PFlags=0;
+
+ rtl_uString_newFromAscii(&ustrExtension,"tmp");
+
+ /* generate tmp profilename */
+ ustrTmpName=osl_ProfileGenerateExtension(pProfile->m_strFileName,ustrExtension);
+ rtl_uString_release(ustrExtension);
+
+ if (ustrTmpName == nullptr)
+ return nullptr;
+
+ if (!(pProfile->m_Flags & osl_Profile_READLOCK))
+ PFlags |= osl_Profile_WRITELOCK;
+
+ /* open this file */
+ pFile = openFileImpl(ustrTmpName,pProfile->m_Flags | PFlags);
+
+ /* return new pFile */
+ return pFile;
+}
+
+static bool osl_ProfileSwapProfileNames(osl_TProfileImpl* pProfile)
+{
+ rtl_uString* ustrBakFile=nullptr;
+ rtl_uString* ustrTmpFile=nullptr;
+ rtl_uString* ustrIniFile=nullptr;
+ rtl_uString* ustrExtension=nullptr;
+
+ rtl_uString_newFromAscii(&ustrExtension,"bak");
+
+ ustrBakFile=osl_ProfileGenerateExtension(pProfile->m_strFileName,ustrExtension);
+ rtl_uString_release(ustrExtension);
+ ustrExtension=nullptr;
+
+ rtl_uString_newFromAscii(&ustrExtension,"ini");
+
+ ustrIniFile=osl_ProfileGenerateExtension(pProfile->m_strFileName,ustrExtension);
+ rtl_uString_release(ustrExtension);
+ ustrExtension=nullptr;
+
+ rtl_uString_newFromAscii(&ustrExtension,"tmp");
+
+ ustrTmpFile=osl_ProfileGenerateExtension(pProfile->m_strFileName,ustrExtension);
+ rtl_uString_release(ustrExtension);
+ ustrExtension=nullptr;
+
+ /* unlink bak */
+ DeleteFileW( o3tl::toW(rtl_uString_getStr( ustrBakFile )) );
+
+ /* rename ini bak */
+ MoveFileExW( o3tl::toW(rtl_uString_getStr( ustrIniFile )), o3tl::toW(rtl_uString_getStr( ustrBakFile )), MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH );
+
+ /* rename tmp ini */
+ MoveFileExW( o3tl::toW(rtl_uString_getStr( ustrTmpFile )), o3tl::toW(rtl_uString_getStr( ustrIniFile )), MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH );
+
+ return false;
+}
+
+static rtl_uString* osl_ProfileGenerateExtension(rtl_uString* ustrFileName, rtl_uString* ustrExtension)
+{
+ rtl_uString* ustrNewFileName = nullptr;
+ rtl_uString* ustrOldExtension = nullptr;
+
+ sal_Unicode* pFileNameBuf = rtl_uString_getStr(ustrFileName);
+
+ rtl_uString_newFromAscii(&ustrOldExtension, ".");
+
+ sal_Unicode* pExtensionBuf = rtl_uString_getStr(ustrOldExtension);
+
+ sal_Int32 nIndex = rtl_ustr_lastIndexOfChar(pFileNameBuf, *pExtensionBuf);
+
+ rtl_uString_newReplaceStrAt(&ustrNewFileName,
+ ustrFileName,
+ nIndex+1,
+ 3,
+ ustrExtension);
+
+ return ustrNewFileName;
+}
+
+static osl_TProfileImpl* acquireProfile(oslProfile Profile, bool bWriteable)
+{
+ osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
+ oslProfileOption PFlags=0;
+
+ if ( bWriteable )
+ {
+ PFlags = osl_Profile_DEFAULT | osl_Profile_WRITELOCK;
+ }
+ else
+ {
+ PFlags = osl_Profile_DEFAULT;
+ }
+
+ if (pProfile == nullptr)
+ {
+#ifdef DEBUG_OSL_PROFILE
+ SAL_INFO("sal.osl", "AUTOOPEN MODE");
+#endif
+
+ if ( ( pProfile = static_cast<osl_TProfileImpl*>(osl_openProfile( nullptr, PFlags )) ) != nullptr )
+ {
+ pProfile->m_Flags |= FLG_AUTOOPEN;
+ }
+ }
+ else
+ {
+#ifdef DEBUG_OSL_PROFILE
+ SAL_INFO("sal.osl", "try to acquire");
+#endif
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ if (! (pProfile->m_Flags & (osl_Profile_READLOCK |
+ osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE)))
+ {
+ osl_TStamp Stamp;
+#ifdef DEBUG_OSL_PROFILE
+ SAL_INFO("sal.osl", "DEFAULT MODE");
+#endif
+ pProfile->m_pFile = openFileImpl(
+ pProfile->m_strFileName, pProfile->m_Flags | PFlags);
+ if (!pProfile->m_pFile)
+ return nullptr;
+
+ Stamp = getFileStamp(pProfile->m_pFile);
+
+ if (memcmp(&Stamp, &(pProfile->m_Stamp), sizeof(osl_TStamp)))
+ {
+ pProfile->m_Stamp = Stamp;
+
+ loadProfile(pProfile->m_pFile, pProfile);
+ }
+ }
+ else
+ {
+#ifdef DEBUG_OSL_PROFILE
+ SAL_INFO("sal.osl", "READ/WRITELOCK MODE");
+#endif
+
+ /* A readlock file could not be written */
+ if ((pProfile->m_Flags & osl_Profile_READLOCK) && bWriteable)
+ {
+ return nullptr;
+ }
+ }
+ }
+ }
+
+ return pProfile;
+}
+
+static bool releaseProfile(osl_TProfileImpl* pProfile)
+{
+ if ( pProfile == nullptr )
+ {
+ return false;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ if (pProfile->m_Flags & FLG_AUTOOPEN)
+ {
+ return osl_closeProfile(static_cast<oslProfile>(pProfile));
+ }
+ else
+ {
+#ifdef DEBUG_OSL_PROFILE
+ SAL_INFO("sal.osl", "DEFAULT MODE");
+#endif
+ if (! (pProfile->m_Flags & (osl_Profile_READLOCK |
+ osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE)))
+ {
+ if (pProfile->m_Flags & FLG_MODIFIED)
+ storeProfile(pProfile, false);
+
+ closeFileImpl(pProfile->m_pFile);
+ pProfile->m_pFile = nullptr;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool lookupProfile(const sal_Unicode *strPath, const sal_Unicode *strFile, sal_Unicode *strProfile)
+{
+ char *pChr;
+ char Buffer[4096] = "";
+ char Product[132] = "";
+
+ ::osl::LongPathBuffer< sal_Unicode > aPath( MAX_LONG_PATH );
+ aPath[0] = 0;
+
+ if (*strPath == L'"')
+ {
+ int i = 0;
+
+ strPath++;
+
+ while ((strPath[i] != L'"') && (strPath[i] != L'\0'))
+ i++;
+
+ WideCharToMultiByte(CP_ACP,0, o3tl::toW(strPath), i, Product, sizeof(Product), nullptr, nullptr);
+ Product[i] = '\0';
+ strPath += i;
+
+ if (*strPath == L'"')
+ strPath++;
+
+ if ( (*strPath == L'/') || (*strPath == L'\\') )
+ {
+ strPath++;
+ }
+ }
+
+ else
+ {
+ /* if we have not product identification, do a special handling for soffice.ini */
+ if (rtl_ustr_ascii_compare(strFile, SVERSION_PROFILE) == 0)
+ {
+ rtl_uString * strSVProfile = nullptr;
+ rtl_uString * strSVFallback = nullptr;
+ rtl_uString * strSVLocation = nullptr;
+ rtl_uString * strSVName = nullptr;
+ ::osl::LongPathBuffer< char > aDir( MAX_LONG_PATH );
+ oslProfile hProfile;
+
+ rtl_uString_newFromAscii(&strSVFallback, SVERSION_FALLBACK);
+ rtl_uString_newFromAscii(&strSVLocation, SVERSION_LOCATION);
+ rtl_uString_newFromAscii(&strSVName, SVERSION_NAME);
+
+ /* open sversion.ini in the system directory, and try to locate the entry
+ with the highest version for StarOffice */
+ if (osl_getProfileName( strSVFallback, strSVName, &strSVProfile))
+ {
+ hProfile = osl_openProfile(strSVProfile, osl_Profile_READLOCK);
+ if (hProfile)
+ {
+ osl_getProfileSectionEntries(
+ hProfile, SVERSION_SECTION, Buffer, sizeof(Buffer));
+
+ for (pChr = Buffer; *pChr != '\0'; pChr += strlen(pChr) + 1)
+ {
+ if ((strnicmp(
+ pChr, SVERSION_SOFFICE,
+ sizeof(SVERSION_SOFFICE) - 1)
+ == 0)
+ && (stricmp(Product, pChr) < 0))
+ {
+ osl_readProfileString(
+ hProfile, SVERSION_SECTION, pChr, aDir,
+ aDir.getBufSizeInSymbols(), "");
+
+ /* check for existence of path */
+ if (access(aDir, 0) >= 0)
+ strcpy(Product, pChr);
+ }
+ }
+
+ osl_closeProfile(hProfile);
+ }
+ rtl_uString_release(strSVProfile);
+ strSVProfile = nullptr;
+ }
+
+ /* open sversion.ini in the users directory, and try to locate the entry
+ with the highest version for StarOffice */
+ if ( osl_getProfileName(strSVLocation, strSVName, &strSVProfile) )
+ {
+ hProfile = osl_openProfile(strSVProfile, osl_Profile_READLOCK);
+ if (hProfile)
+ {
+ osl_getProfileSectionEntries(
+ hProfile, SVERSION_SECTION, Buffer, sizeof(Buffer));
+
+ for (pChr = Buffer; *pChr != '\0'; pChr += strlen(pChr) + 1)
+ {
+ if ((strnicmp(
+ pChr, SVERSION_SOFFICE,
+ sizeof(SVERSION_SOFFICE) - 1)
+ == 0)
+ && (stricmp(Product, pChr) < 0))
+ {
+ osl_readProfileString(
+ hProfile, SVERSION_SECTION, pChr, aDir,
+ aDir.getBufSizeInSymbols(), "");
+
+ /* check for existence of path */
+ if (access(aDir, 0) >= 0)
+ strcpy(Product, pChr);
+ }
+ }
+
+ osl_closeProfile(hProfile);
+ }
+ rtl_uString_release(strSVProfile);
+ }
+
+ rtl_uString_release(strSVFallback);
+ rtl_uString_release(strSVLocation);
+ rtl_uString_release(strSVName);
+
+ /* remove any trailing build number */
+ if ((pChr = strrchr(Product, '/')) != nullptr)
+ *pChr = '\0';
+ }
+ }
+
+ rtl_uString * strExecutable = nullptr;
+ rtl_uString * strTmp = nullptr;
+ sal_Int32 nPos;
+
+ /* try to find the file in the directory of the executable */
+ if (osl_getExecutableFile(&strTmp) != osl_Process_E_None)
+ return false;
+
+ /* convert to native path */
+ if (osl_getSystemPathFromFileURL(strTmp, &strExecutable) != osl_File_E_None)
+ {
+ rtl_uString_release(strTmp);
+ return false;
+ }
+
+ rtl_uString_release(strTmp);
+
+ DWORD dwPathLen = 0;
+
+ /* separate path from filename */
+ if ((nPos = rtl_ustr_lastIndexOfChar(strExecutable->buffer, L'\\')) == -1)
+ {
+ if ((nPos = rtl_ustr_lastIndexOfChar(strExecutable->buffer, L':')) == -1)
+ {
+ rtl_uString_release(strExecutable);
+ return false;
+ }
+ else
+ {
+ copy_ustr_n(aPath, strExecutable->buffer, nPos);
+ aPath[nPos] = 0;
+ dwPathLen = nPos;
+ }
+ }
+ else
+ {
+ copy_ustr_n(aPath, strExecutable->buffer, nPos);
+ dwPathLen = nPos;
+ aPath[dwPathLen] = 0;
+ }
+
+ /* if we have no product identification use the executable file name */
+ if (*Product == 0)
+ {
+ WideCharToMultiByte(CP_ACP,0, o3tl::toW(strExecutable->buffer + nPos + 1), -1, Product, sizeof(Product), nullptr, nullptr);
+
+ /* remove extension */
+ if ((pChr = strrchr(Product, '.')) != nullptr)
+ *pChr = '\0';
+ }
+
+ rtl_uString_release(strExecutable);
+
+ /* remember last subdir */
+ nPos = rtl_ustr_lastIndexOfChar(aPath, L'\\');
+
+ copy_ustr_n(aPath + dwPathLen++, L"\\", 2);
+
+ if (*strPath)
+ {
+ copy_ustr_n(aPath + dwPathLen, strPath, rtl_ustr_getLength(strPath)+1);
+ dwPathLen += rtl_ustr_getLength(strPath);
+ }
+
+ {
+ ::osl::LongPathBuffer< char > aTmpPath( MAX_LONG_PATH );
+
+ WideCharToMultiByte(CP_ACP,0, o3tl::toW(aPath), -1, aTmpPath, aTmpPath.getBufSizeInSymbols(), nullptr, nullptr);
+
+ /* if file not exists, remove any specified subdirectories
+ like "bin" or "program" */
+
+ if (((access(aTmpPath, 0) < 0) && (nPos != -1)) || (*strPath == 0))
+ {
+ static const char *SubDirs[] = SVERSION_DIRS;
+
+ unsigned i = 0;
+ char *pStr = aTmpPath + nPos;
+
+ for (i = 0; i < SAL_N_ELEMENTS(SubDirs); i++)
+ if (strnicmp(pStr + 1, SubDirs[i], strlen(SubDirs[i])) == 0)
+ {
+ if ( *strPath == 0)
+ {
+ strcpy(pStr + 1,SVERSION_USER);
+ if ( access(aTmpPath, 0) < 0 )
+ {
+ *(pStr+1)='\0';
+ }
+ else
+ {
+ dwPathLen = nPos + MultiByteToWideChar( CP_ACP, 0, SVERSION_USER, -1, o3tl::toW(aPath + nPos + 1), aPath.getBufSizeInSymbols() - (nPos + 1) );
+ }
+ }
+ else
+ {
+ copy_ustr_n(aPath + nPos + 1, strPath, rtl_ustr_getLength(strPath)+1);
+ dwPathLen = nPos + 1 + rtl_ustr_getLength(strPath);
+ }
+
+ break;
+ }
+ }
+ }
+
+ if ((aPath[dwPathLen - 1] != L'/') && (aPath[dwPathLen - 1] != L'\\'))
+ {
+ aPath[dwPathLen++] = L'\\';
+ aPath[dwPathLen] = 0;
+ }
+
+ copy_ustr_n(aPath + dwPathLen, strFile, rtl_ustr_getLength(strFile)+1);
+
+ {
+ ::osl::LongPathBuffer< char > aTmpPath( MAX_LONG_PATH );
+
+ WideCharToMultiByte(CP_ACP,0, o3tl::toW(aPath), -1, aTmpPath, aTmpPath.getBufSizeInSymbols(), nullptr, nullptr);
+
+ if ((access(aTmpPath, 0) < 0) && (Product[0] != '\0'))
+ {
+ rtl_uString * strSVFallback = nullptr;
+ rtl_uString * strSVProfile = nullptr;
+ rtl_uString * strSVLocation = nullptr;
+ rtl_uString * strSVName = nullptr;
+ oslProfile hProfile;
+
+ rtl_uString_newFromAscii(&strSVFallback, SVERSION_FALLBACK);
+ rtl_uString_newFromAscii(&strSVLocation, SVERSION_LOCATION);
+ rtl_uString_newFromAscii(&strSVName, SVERSION_NAME);
+
+ /* open sversion.ini in the system directory, and try to locate the entry
+ with the highest version for StarOffice */
+ if (osl_getProfileName(strSVLocation, strSVName, &strSVProfile))
+ {
+ hProfile = osl_openProfile(
+ strSVProfile, osl_Profile_READLOCK);
+ if (hProfile)
+ {
+ osl_readProfileString(
+ hProfile, SVERSION_SECTION, Product, Buffer,
+ sizeof(Buffer), "");
+ osl_closeProfile(hProfile);
+
+ /* if not found, try the fallback */
+ if (Buffer[0] == '\0')
+ {
+ if (osl_getProfileName(
+ strSVFallback, strSVName, &strSVProfile))
+ {
+ hProfile = osl_openProfile(
+ strSVProfile, osl_Profile_READLOCK);
+ if (hProfile)
+ {
+ osl_readProfileString(
+ hProfile, SVERSION_SECTION, Product,
+ Buffer, sizeof(Buffer), "");
+ }
+ }
+
+ osl_closeProfile(hProfile);
+ }
+
+ if (Buffer[0] != '\0')
+ {
+ dwPathLen = MultiByteToWideChar(
+ CP_ACP, 0, Buffer, -1, o3tl::toW(aPath), aPath.getBufSizeInSymbols() );
+ dwPathLen -=1;
+
+ /* build full path */
+ if ((aPath[dwPathLen - 1] != L'/')
+ && (aPath[dwPathLen - 1] != L'\\'))
+ {
+ copy_ustr_n(aPath + dwPathLen++, L"\\", 2);
+ }
+
+ if (*strPath)
+ {
+ copy_ustr_n(aPath + dwPathLen, strPath, rtl_ustr_getLength(strPath)+1);
+ dwPathLen += rtl_ustr_getLength(strPath);
+ }
+ else
+ {
+ ::osl::LongPathBuffer< char > aTmpPath2( MAX_LONG_PATH );
+ int n;
+
+ if ((n = WideCharToMultiByte(
+ CP_ACP,0, o3tl::toW(aPath), -1, aTmpPath2,
+ aTmpPath2.getBufSizeInSymbols(), nullptr, nullptr))
+ > 0)
+ {
+ strcpy(aTmpPath2 + n, SVERSION_USER);
+ if (access(aTmpPath2, 0) >= 0)
+ {
+ dwPathLen += MultiByteToWideChar(
+ CP_ACP, 0, SVERSION_USER, -1,
+ o3tl::toW(aPath + dwPathLen),
+ aPath.getBufSizeInSymbols() - dwPathLen );
+ }
+ }
+ }
+ }
+ }
+
+ rtl_uString_release(strSVProfile);
+ }
+
+ rtl_uString_release(strSVFallback);
+ rtl_uString_release(strSVLocation);
+ rtl_uString_release(strSVName);
+ }
+ }
+
+ aPath[dwPathLen] = 0;
+
+ /* copy filename */
+ copy_ustr_n(strProfile, aPath, dwPathLen+1);
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/random.cxx b/sal/osl/w32/random.cxx
new file mode 100644
index 000000000..a2c364da2
--- /dev/null
+++ b/sal/osl/w32/random.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#define _CRT_RAND_S
+
+#include <stdlib.h>
+#include <memory.h>
+
+#include <oslrandom.h>
+
+int osl_get_system_random_data(char* buffer, size_t desired_len)
+{
+ unsigned int val;
+
+ /* if unaligned fill to alignment */
+ if (reinterpret_cast<uintptr_t>(buffer) & 3)
+ {
+ size_t len = 4 - (reinterpret_cast<size_t>(buffer) & 3);
+
+ if (len > desired_len)
+ {
+ len = desired_len;
+ }
+ if (rand_s(&val))
+ {
+ return 0;
+ }
+ memcpy(buffer, &val, len);
+ buffer += len;
+ desired_len -= len;
+ }
+ /* fill directly into the buffer as long as we can */
+ while (desired_len >= 4)
+ {
+ if (rand_s(reinterpret_cast<unsigned int*>(buffer)))
+ {
+ return 0;
+ }
+ else
+ {
+ buffer += 4;
+ desired_len -= 4;
+ }
+ }
+ /* deal with the partial int reminder to fill */
+ if (desired_len)
+ {
+ if (rand_s(&val))
+ {
+ return 0;
+ }
+ memcpy(buffer, &val, desired_len);
+ }
+ return 1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/salinit.cxx b/sal/osl/w32/salinit.cxx
new file mode 100644
index 000000000..c0b917127
--- /dev/null
+++ b/sal/osl/w32/salinit.cxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "system.h"
+#include "time.hxx"
+
+#include <osl/process.h>
+#include <sal/main.h>
+#include <sal/types.h>
+
+extern "C" {
+
+// Prototypes for initialization and deinitialization of SAL library
+
+void sal_detail_initialize(int argc, char ** argv)
+{
+ if (argc == sal::detail::InitializeSoffice)
+ {
+ return;
+ }
+ sal_initGlobalTimer();
+#ifndef _WIN64
+ SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
+#endif
+ SetDllDirectoryW(L""); // remove the current directory from the default DLL search order
+ SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT);
+
+ WSADATA wsaData;
+ int error;
+ WORD wVersionRequested;
+
+ wVersionRequested = MAKEWORD(1, 1);
+
+ error = WSAStartup(wVersionRequested, &wsaData);
+ if ( 0 == error )
+ {
+ WORD const wMajorVersionRequired = 1;
+ WORD const wMinorVersionRequired = 1;
+
+ if ((LOBYTE(wsaData.wVersion) < wMajorVersionRequired) ||
+ ((LOBYTE(wsaData.wVersion) == wMajorVersionRequired) &&
+ (HIBYTE(wsaData.wVersion) < wMinorVersionRequired)))
+ {
+ // How to handle a very unlikely error ???
+ }
+ }
+ else
+ {
+ // How to handle a very unlikely error ???
+ }
+
+ osl_setCommandArgs(argc, argv);
+}
+
+void sal_detail_deinitialize()
+{
+ if ( SOCKET_ERROR == WSACleanup() )
+ {
+ // We should never reach this point or we did wrong elsewhere
+ }
+}
+
+} // extern "C"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/secimpl.hxx b/sal/osl/w32/secimpl.hxx
new file mode 100644
index 000000000..7c952bb64
--- /dev/null
+++ b/sal/osl/w32/secimpl.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_W32_SECIMPL_HXX
+#define INCLUDED_SAL_OSL_W32_SECIMPL_HXX
+
+#include <winnetwk.h>
+
+#include <osl/security.h>
+
+#define USER_BUFFER_SIZE 256
+
+typedef struct
+{
+ HANDLE m_hProfile;
+ HANDLE m_hToken;
+ sal_Unicode m_User[USER_BUFFER_SIZE];
+ /* extension by fileserver login */
+ NETRESOURCEW* m_pNetResource;
+} oslSecurityImpl;
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/security.cxx b/sal/osl/w32/security.cxx
new file mode 100644
index 000000000..21ed64a78
--- /dev/null
+++ b/sal/osl/w32/security.cxx
@@ -0,0 +1,662 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.h"
+#include <userenv.h>
+
+#include <cassert>
+#include <osl/security.h>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/file.h>
+#include <systools/win32/uwinapi.h>
+#include <sddl.h>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include "secimpl.hxx"
+
+/* To get an impersonation token we need to create an impersonation
+ duplicate so every access token has to be created with duplicate
+ access rights */
+
+#define TOKEN_DUP_QUERY (TOKEN_QUERY|TOKEN_DUPLICATE)
+
+static bool GetSpecialFolder(rtl_uString **strPath, REFKNOWNFOLDERID rFolder);
+// We use LPCTSTR here, because we use it with SE_foo_NAME constants
+// which are defined in winnt.h as UNICODE-dependent TEXT("PrivilegeName")
+static bool Privilege(LPCTSTR pszPrivilege, bool bEnable);
+static bool getUserNameImpl(oslSecurity Security, rtl_uString **strName, bool bIncludeDomain);
+
+oslSecurity SAL_CALL osl_getCurrentSecurity(void)
+{
+ oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl)));
+ if (pSecImpl)
+ {
+ pSecImpl->m_pNetResource = nullptr;
+ pSecImpl->m_User[0] = '\0';
+ pSecImpl->m_hToken = nullptr;
+ pSecImpl->m_hProfile = nullptr;
+ }
+ return pSecImpl;
+}
+
+oslSecurityError SAL_CALL osl_loginUser( rtl_uString *strUserName, rtl_uString *strPasswd, oslSecurity *pSecurity )
+{
+ oslSecurityError ret;
+
+ sal_Unicode* strUser;
+ sal_Unicode* strDomain = o3tl::toU(_wcsdup(o3tl::toW(rtl_uString_getStr(strUserName))));
+ HANDLE hUserToken;
+ LUID luid;
+
+ if (nullptr != (strUser = o3tl::toU(wcschr(o3tl::toW(strDomain), L'/'))))
+ *strUser++ = L'\0';
+ else
+ {
+ strUser = strDomain;
+ strDomain = nullptr;
+ }
+
+ // this process must have the right: 'act as a part of operatingsystem'
+ OSL_ASSERT(LookupPrivilegeValue(nullptr, SE_TCB_NAME, &luid));
+ (void) luid;
+
+ if (LogonUserW(o3tl::toW(strUser), strDomain ? o3tl::toW(strDomain) : L"", o3tl::toW(rtl_uString_getStr(strPasswd)),
+ LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
+ &hUserToken))
+ {
+ oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl)));
+ if (pSecImpl)
+ {
+ pSecImpl->m_pNetResource = nullptr;
+ pSecImpl->m_hToken = hUserToken;
+ pSecImpl->m_hProfile = nullptr;
+ wcscpy(o3tl::toW(pSecImpl->m_User), o3tl::toW(strUser));
+ }
+ *pSecurity = pSecImpl;
+ ret = pSecImpl ? osl_Security_E_None : osl_Security_E_Unknown;
+ }
+ else
+ {
+ ret = osl_Security_E_UserUnknown;
+ }
+
+ if (strDomain)
+ free(strDomain);
+ else
+ free(strUser);
+
+ return ret;
+}
+
+oslSecurityError SAL_CALL osl_loginUserOnFileServer(rtl_uString *strUserName,
+ rtl_uString *strPasswd,
+ rtl_uString *strFileServer,
+ oslSecurity *pSecurity)
+{
+ oslSecurityError ret;
+ DWORD err;
+ NETRESOURCEW netResource;
+ sal_Unicode* remoteName;
+ sal_Unicode* userName;
+
+ remoteName = static_cast<sal_Unicode *>(malloc((rtl_uString_getLength(strFileServer) + rtl_uString_getLength(strUserName) + 4) * sizeof(sal_Unicode)));
+ userName = static_cast<sal_Unicode *>(malloc((rtl_uString_getLength(strFileServer) + rtl_uString_getLength(strUserName) + 2) * sizeof(sal_Unicode)));
+
+ wcscpy(o3tl::toW(remoteName), L"\\\\");
+ wcscat(o3tl::toW(remoteName), o3tl::toW(rtl_uString_getStr(strFileServer)));
+ wcscat(o3tl::toW(remoteName), L"\\");
+ wcscat(o3tl::toW(remoteName), o3tl::toW(rtl_uString_getStr(strUserName)));
+
+ wcscpy(o3tl::toW(userName), o3tl::toW(rtl_uString_getStr(strFileServer)));
+ wcscat(o3tl::toW(userName), L"\\");
+ wcscat(o3tl::toW(userName), o3tl::toW(rtl_uString_getStr(strUserName)));
+
+ netResource.dwScope = RESOURCE_GLOBALNET;
+ netResource.dwType = RESOURCETYPE_DISK;
+ netResource.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+ netResource.dwUsage = RESOURCEUSAGE_CONNECTABLE;
+ netResource.lpLocalName = nullptr;
+ netResource.lpRemoteName = o3tl::toW(remoteName);
+ netResource.lpComment = nullptr;
+ netResource.lpProvider = nullptr;
+
+ err = WNetAddConnection2W(&netResource, o3tl::toW(rtl_uString_getStr(strPasswd)), o3tl::toW(userName), 0);
+
+ if ((err == NO_ERROR) || (err == ERROR_ALREADY_ASSIGNED))
+ {
+ oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl)));
+ if (pSecImpl)
+ {
+ pSecImpl->m_pNetResource = static_cast<NETRESOURCEW *>(malloc(sizeof(NETRESOURCE)));
+ if (pSecImpl->m_pNetResource)
+ {
+ *pSecImpl->m_pNetResource = netResource;
+ pSecImpl->m_hToken = nullptr;
+ pSecImpl->m_hProfile = nullptr;
+ wcscpy(o3tl::toW(pSecImpl->m_User), o3tl::toW(rtl_uString_getStr(strUserName)));
+ }
+ else
+ {
+ free(pSecImpl);
+ pSecImpl = nullptr;
+ }
+ }
+ *pSecurity = pSecImpl;
+
+ ret = pSecImpl ? osl_Security_E_None : osl_Security_E_Unknown;
+ }
+ else
+ {
+ ret = osl_Security_E_UserUnknown;
+ }
+
+ free(remoteName);
+ free(userName);
+
+ return ret;
+}
+
+sal_Bool SAL_CALL osl_isAdministrator(oslSecurity Security)
+{
+ if (!Security)
+ return false;
+
+ HANDLE hImpersonationToken = nullptr;
+ PSID psidAdministrators;
+ SID_IDENTIFIER_AUTHORITY siaNtAuthority = { SECURITY_NT_AUTHORITY };
+ bool bSuccess = false;
+
+ /* If Security contains an access token we need to duplicate it to an impersonation
+ access token. NULL works with CheckTokenMembership() as the current effective
+ impersonation token
+ */
+
+ if ( static_cast<oslSecurityImpl*>(Security)->m_hToken )
+ {
+ if ( !DuplicateToken (static_cast<oslSecurityImpl*>(Security)->m_hToken, SecurityImpersonation, &hImpersonationToken) )
+ return false;
+ }
+
+ /* CheckTokenMembership() can be used on W2K and higher (NT4 no longer supported by OOo)
+ and also works on Vista to retrieve the effective user rights. Just checking for
+ membership of Administrators group is not enough on Vista this would require additional
+ complicated checks as described in KB article Q118626: http://support.microsoft.com/kb/118626/en-us
+ */
+
+ if (AllocateAndInitializeSid(&siaNtAuthority,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &psidAdministrators))
+ {
+ BOOL fSuccess = FALSE;
+
+ if (CheckTokenMembership(hImpersonationToken, psidAdministrators, &fSuccess) && fSuccess)
+ bSuccess = true;
+
+ FreeSid(psidAdministrators);
+ }
+
+ if (hImpersonationToken)
+ CloseHandle(hImpersonationToken);
+
+ return bSuccess;
+}
+
+void SAL_CALL osl_freeSecurityHandle(oslSecurity Security)
+{
+ if (!Security)
+ return;
+
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
+
+ if (pSecImpl->m_pNetResource != nullptr)
+ {
+ WNetCancelConnection2W(pSecImpl->m_pNetResource->lpRemoteName, 0, true);
+
+ free(pSecImpl->m_pNetResource->lpRemoteName);
+ free(pSecImpl->m_pNetResource);
+ }
+
+ if (pSecImpl->m_hToken)
+ CloseHandle(pSecImpl->m_hToken);
+
+ if ( pSecImpl->m_hProfile )
+ CloseHandle(pSecImpl->m_hProfile);
+
+ free (pSecImpl);
+}
+
+sal_Bool SAL_CALL osl_getUserIdent(oslSecurity Security, rtl_uString **strIdent)
+{
+ if (!Security)
+ return false;
+
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
+
+ HANDLE hAccessToken = pSecImpl->m_hToken;
+
+ if (hAccessToken == nullptr)
+ OpenProcessToken(GetCurrentProcess(), TOKEN_DUP_QUERY, &hAccessToken);
+
+ if (hAccessToken)
+ {
+ DWORD nInfoBuffer = 512;
+ UCHAR* pInfoBuffer = static_cast<UCHAR *>(malloc(nInfoBuffer));
+
+ while (!GetTokenInformation(hAccessToken, TokenUser,
+ pInfoBuffer, nInfoBuffer, &nInfoBuffer))
+ {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ if (auto p = static_cast<UCHAR *>(realloc(pInfoBuffer, nInfoBuffer)))
+ pInfoBuffer = p;
+ else
+ {
+ free(pInfoBuffer);
+ pInfoBuffer = nullptr;
+ break;
+ }
+ }
+ else
+ {
+ free(pInfoBuffer);
+ pInfoBuffer = nullptr;
+ break;
+ }
+ }
+
+ if (pSecImpl->m_hToken == nullptr)
+ CloseHandle(hAccessToken);
+
+ if (pInfoBuffer)
+ {
+ PSID pSid = reinterpret_cast<PTOKEN_USER>(pInfoBuffer)->User.Sid;
+
+ LPWSTR pSidStr = nullptr;
+ bool bResult = ConvertSidToStringSidW(pSid, &pSidStr);
+ if (bResult)
+ {
+ rtl_uString_newFromStr(strIdent, o3tl::toU(pSidStr));
+ LocalFree(pSidStr);
+ }
+ else
+ {
+ const DWORD dwError = GetLastError();
+ SAL_WARN(
+ "sal.osl",
+ "ConvertSidToStringSidW failed. GetLastError returned: " << dwError);
+ }
+
+ free(pInfoBuffer);
+
+ return bResult;
+ }
+ }
+ else
+ {
+ DWORD needed = 0;
+
+ WNetGetUserW(nullptr, nullptr, &needed);
+ if (needed < 16)
+ needed = 16;
+
+ if (auto Ident = static_cast<sal_Unicode *>(malloc(needed*sizeof(sal_Unicode))))
+ {
+ if (WNetGetUserW(nullptr, o3tl::toW(Ident), &needed) != NO_ERROR)
+ {
+ wcscpy(o3tl::toW(Ident), L"unknown");
+ Ident[7] = L'\0';
+ }
+
+ rtl_uString_newFromStr( strIdent, Ident);
+ free(Ident);
+ return true;
+ }
+ }
+ return false;
+}
+
+sal_Bool SAL_CALL osl_getUserName(oslSecurity Security, rtl_uString **strName)
+{
+ return getUserNameImpl(Security, strName, true);
+}
+
+sal_Bool SAL_CALL osl_getShortUserName(oslSecurity Security, rtl_uString **strName)
+{
+ return getUserNameImpl(Security, strName, false);
+}
+
+sal_Bool SAL_CALL osl_getHomeDir(oslSecurity Security, rtl_uString **pustrDirectory)
+{
+ if (!Security)
+ return false;
+
+ rtl_uString *ustrSysDir = nullptr;
+ bool bSuccess = false;
+
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
+
+ if (pSecImpl->m_pNetResource != nullptr)
+ {
+ rtl_uString_newFromStr( &ustrSysDir, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName));
+
+ bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath( ustrSysDir, pustrDirectory );
+ }
+ else
+ {
+ bSuccess = GetSpecialFolder(&ustrSysDir, FOLDERID_Documents) &&
+ (osl_File_E_None == osl_getFileURLFromSystemPath(ustrSysDir, pustrDirectory));
+ }
+
+ if ( ustrSysDir )
+ rtl_uString_release( ustrSysDir );
+
+ return bSuccess;
+}
+
+sal_Bool SAL_CALL osl_getConfigDir(oslSecurity Security, rtl_uString **pustrDirectory)
+{
+ if (!Security)
+ return false;
+
+ bool bSuccess = false;
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
+
+ if (pSecImpl->m_pNetResource != nullptr)
+ {
+ rtl_uString *ustrSysDir = nullptr;
+
+ rtl_uString_newFromStr( &ustrSysDir, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName));
+ bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath( ustrSysDir, pustrDirectory);
+
+ if ( ustrSysDir )
+ rtl_uString_release( ustrSysDir );
+ }
+ else
+ {
+ if (pSecImpl->m_hToken)
+ {
+ /* not implemented */
+ OSL_ASSERT(false);
+ }
+ else
+ {
+ rtl_uString *ustrFile = nullptr;
+ sal_Unicode sFile[_MAX_PATH];
+
+ if ( !GetSpecialFolder( &ustrFile, FOLDERID_RoamingAppData) )
+ {
+ OSL_VERIFY(GetWindowsDirectoryW(o3tl::toW(sFile), _MAX_DIR) > 0);
+
+ rtl_uString_newFromStr( &ustrFile, sFile);
+ }
+
+ bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath(ustrFile, pustrDirectory);
+
+ if ( ustrFile )
+ rtl_uString_release( ustrFile );
+ }
+ }
+
+ return bSuccess;
+}
+
+sal_Bool SAL_CALL osl_loadUserProfile(oslSecurity Security)
+{
+ /* CreateProcessAsUser does not load the specified user's profile
+ into the HKEY_USERS registry key. This means that access to information
+ in the HKEY_CURRENT_USER registry key may not produce results consistent
+ with a normal interactive logon.
+ It is your responsibility to load the user's registry hive into HKEY_USERS
+ with the LoadUserProfile function before calling CreateProcessAsUser.
+ */
+
+ RegCloseKey(HKEY_CURRENT_USER);
+
+ if (!Privilege(SE_RESTORE_NAME, true))
+ return false;
+
+ bool bOk = false;
+ HANDLE hAccessToken = static_cast<oslSecurityImpl*>(Security)->m_hToken;
+
+ /* try to create user profile */
+ if ( !hAccessToken )
+ {
+ /* retrieve security handle if not done before e.g. osl_getCurrentSecurity()
+ */
+ HANDLE hProcess = GetCurrentProcess();
+
+ if (hProcess != nullptr)
+ {
+ OpenProcessToken(hProcess, TOKEN_IMPERSONATE, &hAccessToken);
+ CloseHandle(hProcess);
+ }
+ }
+
+ rtl_uString *buffer = nullptr;
+ PROFILEINFOW pi;
+
+ getUserNameImpl(Security, &buffer, false);
+
+ ZeroMemory(&pi, sizeof(pi));
+ pi.dwSize = sizeof(pi);
+ pi.lpUserName = o3tl::toW(rtl_uString_getStr(buffer));
+ pi.dwFlags = PI_NOUI;
+
+ if (LoadUserProfileW(hAccessToken, &pi))
+ {
+ UnloadUserProfile(hAccessToken, pi.hProfile);
+
+ bOk = true;
+ }
+
+ rtl_uString_release(buffer);
+
+ if (hAccessToken && (hAccessToken != static_cast<oslSecurityImpl*>(Security)->m_hToken))
+ CloseHandle(hAccessToken);
+
+ return bOk;
+}
+
+void SAL_CALL osl_unloadUserProfile(oslSecurity Security)
+{
+ if ( static_cast<oslSecurityImpl*>(Security)->m_hProfile == nullptr )
+ return;
+
+ HANDLE hAccessToken = static_cast<oslSecurityImpl*>(Security)->m_hToken;
+
+ if ( !hAccessToken )
+ {
+ /* retrieve security handle if not done before e.g. osl_getCurrentSecurity()
+ */
+ HANDLE hProcess = GetCurrentProcess();
+
+ if (hProcess != nullptr)
+ {
+ OpenProcessToken(hProcess, TOKEN_IMPERSONATE, &hAccessToken);
+ CloseHandle(hProcess);
+ }
+ }
+
+ /* unloading the user profile */
+ UnloadUserProfile(hAccessToken, static_cast<oslSecurityImpl*>(Security)->m_hProfile);
+
+ static_cast<oslSecurityImpl*>(Security)->m_hProfile = nullptr;
+
+ if (hAccessToken && (hAccessToken != static_cast<oslSecurityImpl*>(Security)->m_hToken))
+ CloseHandle(hAccessToken);
+}
+
+static bool GetSpecialFolder(rtl_uString **strPath, REFKNOWNFOLDERID rFolder)
+{
+ bool bRet = false;
+ PWSTR PathW;
+ if (SUCCEEDED(SHGetKnownFolderPath(rFolder, KF_FLAG_CREATE, nullptr, &PathW)))
+ {
+ rtl_uString_newFromStr(strPath, o3tl::toU(PathW));
+ CoTaskMemFree(PathW);
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+// We use LPCTSTR here, because we use it with SE_foo_NAME constants
+// which are defined in winnt.h as UNICODE-dependent TEXT("PrivilegeName")
+static bool Privilege(LPCTSTR strPrivilege, bool bEnable)
+{
+ HANDLE hToken;
+ TOKEN_PRIVILEGES tp;
+
+ // obtain the processes token
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_DUP_QUERY, &hToken))
+ return false;
+
+ // get the luid
+ if (!LookupPrivilegeValue(nullptr, strPrivilege, &tp.Privileges[0].Luid))
+ return false;
+
+ tp.PrivilegeCount = 1;
+
+ if (bEnable)
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ else
+ tp.Privileges[0].Attributes = 0;
+
+ // enable or disable the privilege
+ if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, nullptr, nullptr))
+ return false;
+
+ if (!CloseHandle(hToken))
+ return false;
+
+ return true;
+}
+
+static bool getUserNameImpl(oslSecurity Security, rtl_uString **strName, bool bIncludeDomain)
+{
+ if (!Security)
+ return false;
+
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
+
+ HANDLE hAccessToken = pSecImpl->m_hToken;
+
+ if (hAccessToken == nullptr)
+ OpenProcessToken(GetCurrentProcess(), TOKEN_DUP_QUERY, &hAccessToken);
+
+ if (hAccessToken)
+ {
+ DWORD nInfoBuffer = 512;
+ UCHAR* pInfoBuffer = static_cast<UCHAR *>(malloc(nInfoBuffer));
+
+ while (!GetTokenInformation(hAccessToken, TokenUser,
+ pInfoBuffer, nInfoBuffer, &nInfoBuffer))
+ {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ if (auto p = static_cast<UCHAR *>(realloc(pInfoBuffer, nInfoBuffer)))
+ pInfoBuffer = p;
+ else
+ {
+ free(pInfoBuffer);
+ pInfoBuffer = nullptr;
+ break;
+ }
+ }
+ else
+ {
+ free(pInfoBuffer);
+ pInfoBuffer = nullptr;
+ break;
+ }
+ }
+
+ if (pSecImpl->m_hToken == nullptr)
+ CloseHandle(hAccessToken);
+
+ if (pInfoBuffer)
+ {
+ sal_Unicode UserName[128];
+ sal_Unicode DomainName[128];
+ sal_Unicode Name[257];
+ DWORD nUserName = SAL_N_ELEMENTS(UserName);
+ DWORD nDomainName = SAL_N_ELEMENTS(DomainName);
+ SID_NAME_USE sUse;
+
+ if (LookupAccountSidW(nullptr, reinterpret_cast<PTOKEN_USER>(pInfoBuffer)->User.Sid,
+ o3tl::toW(UserName), &nUserName,
+ o3tl::toW(DomainName), &nDomainName, &sUse))
+ {
+ if (bIncludeDomain)
+ {
+ wcscpy(o3tl::toW(Name), o3tl::toW(DomainName));
+ wcscat(o3tl::toW(Name), L"/");
+ wcscat(o3tl::toW(Name), o3tl::toW(UserName));
+ }
+ else
+ {
+ wcscpy(o3tl::toW(Name), o3tl::toW(UserName));
+ }
+
+ rtl_uString_newFromStr(strName, Name);
+ free(pInfoBuffer);
+ return true;
+ }
+ }
+ }
+ else
+ {
+ DWORD needed=0;
+ sal_Unicode *pNameW=nullptr;
+
+ WNetGetUserW(nullptr, nullptr, &needed);
+ pNameW = static_cast<sal_Unicode *>(malloc (needed*sizeof(sal_Unicode)));
+ assert(pNameW); // Don't handle OOM conditions
+
+ if (WNetGetUserW(nullptr, o3tl::toW(pNameW), &needed) == NO_ERROR)
+ {
+ rtl_uString_newFromStr( strName, pNameW);
+
+ if (pNameW)
+ free(pNameW);
+ return true;
+ }
+ else if (pSecImpl->m_User[0] != '\0')
+ {
+ rtl_uString_newFromStr(strName, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName));
+
+ if (pNameW)
+ free(pNameW);
+
+ return true;
+ }
+
+ if (pNameW)
+ free(pNameW);
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/signal.cxx b/sal/osl/w32/signal.cxx
new file mode 100644
index 000000000..dcb05beae
--- /dev/null
+++ b/sal/osl/w32/signal.cxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <stdlib.h>
+
+#include <config_features.h>
+
+#include <signalshared.hxx>
+
+#include <systools/win32/uwinapi.h>
+#include <errorrep.h>
+#include <werapi.h>
+
+namespace
+{
+long WINAPI signalHandlerFunction(LPEXCEPTION_POINTERS lpEP);
+
+LPTOP_LEVEL_EXCEPTION_FILTER pPreviousHandler = nullptr;
+}
+
+bool onInitSignal()
+{
+ pPreviousHandler = SetUnhandledExceptionFilter(signalHandlerFunction);
+
+ WerAddExcludedApplication(L"SOFFICE.EXE", FALSE);
+
+ return true;
+}
+
+bool onDeInitSignal()
+{
+ SetUnhandledExceptionFilter(pPreviousHandler);
+
+ return false;
+}
+
+namespace
+{
+/* magic Microsoft C++ compiler exception constant */
+#define EXCEPTION_MSC_CPP_EXCEPTION 0xe06d7363
+
+long WINAPI signalHandlerFunction(LPEXCEPTION_POINTERS lpEP)
+{
+#if HAVE_FEATURE_BREAKPAD
+ // we should make sure to call the breakpad handler as
+ // first step when we hit a problem
+ if (pPreviousHandler)
+ pPreviousHandler(lpEP);
+#endif
+
+ static bool bNested = false;
+
+ oslSignalInfo info;
+
+ info.UserSignal = lpEP->ExceptionRecord->ExceptionCode;
+ info.UserData = nullptr;
+
+ switch (lpEP->ExceptionRecord->ExceptionCode)
+ {
+ /* Transform unhandled exceptions into access violations.
+ Microsoft C++ compiler (add more for other compilers if necessary).
+ */
+ case EXCEPTION_MSC_CPP_EXCEPTION:
+ case EXCEPTION_ACCESS_VIOLATION:
+ info.Signal = osl_Signal_AccessViolation;
+ break;
+
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ info.Signal = osl_Signal_IntegerDivideByZero;
+ break;
+
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ info.Signal = osl_Signal_FloatDivideByZero;
+ break;
+
+ case EXCEPTION_BREAKPOINT:
+ info.Signal = osl_Signal_DebugBreak;
+ break;
+
+ default:
+ info.Signal = osl_Signal_System;
+ break;
+ }
+
+ oslSignalAction action;
+
+ if (!bNested)
+ {
+ bNested = true;
+ action = callSignalHandler(&info);
+ }
+ else
+ action = osl_Signal_ActKillApp;
+
+ switch (action)
+ {
+ case osl_Signal_ActCallNextHdl:
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ case osl_Signal_ActAbortApp:
+ return EXCEPTION_EXECUTE_HANDLER;
+
+ case osl_Signal_ActKillApp:
+ SetErrorMode(SEM_NOGPFAULTERRORBOX);
+ exit(255);
+ break;
+ default:
+ break;
+ }
+
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/socket.cxx b/sal/osl/w32/socket.cxx
new file mode 100644
index 000000000..2548be0d5
--- /dev/null
+++ b/sal/osl/w32/socket.cxx
@@ -0,0 +1,1611 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <utility>
+
+#include "system.h"
+
+#include <osl/socket.h>
+#include <osl/thread.h>
+#include <osl/diagnose.h>
+#include <rtl/alloc.h>
+#include <rtl/byteseq.h>
+#include <sal/log.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/safeint.hxx>
+#include <comphelper/windowserrorstring.hxx>
+
+#include "sockimpl.hxx"
+
+/*
+ oslSocketAddr is a pointer to a Berkeley struct sockaddr.
+ I refrained from using sockaddr_in because of possible further
+ extensions of this socket-interface (IP-NG?).
+ The intention was to hide all Berkeley data-structures from
+ direct access past the osl-interface.
+
+ The current implementation is internet (IP) centered. All
+ the constructor-functions (osl_create...) take parameters
+ that will probably make sense only in the IP-environment
+ (e.g. because of using the dotted-Addr-format).
+
+ If the interface will be extended to host other protocol-
+ families, I expect no externally visible changes in the
+ existing functions. You'll probably need only new
+ constructor-functions who take the different Addr
+ formats into consideration (maybe a long dotted Addr
+ or whatever).
+*/
+
+/*
+ _Note_ that I rely on the fact that oslSocketAddr and struct sockaddr
+ are the same! I don't like it very much but see no other easy way to
+ conceal the struct sockaddr from the eyes of the user.
+*/
+
+#define OSL_INVALID_SOCKET INVALID_SOCKET /* WIN32 */
+#define OSL_SOCKET_ERROR SOCKET_ERROR /* WIN32 */
+
+static DWORD FamilyMap[]= {
+ AF_INET, /* osl_Socket_FamilyInet */
+ AF_IPX, /* osl_Socket_FamilyIpx */
+ 0 /* osl_Socket_FamilyInvalid */
+};
+
+static oslAddrFamily osl_AddrFamilyFromNative(DWORD nativeType)
+{
+ oslAddrFamily i= oslAddrFamily(0);
+ while(i != osl_Socket_FamilyInvalid)
+ {
+ if(FamilyMap[i] == nativeType)
+ return i;
+ i = static_cast<oslAddrFamily>( static_cast<int>(i) + 1);
+ }
+ return i;
+}
+
+#define FAMILY_FROM_NATIVE(y) osl_AddrFamilyFromNative(y)
+#define FAMILY_TO_NATIVE(x) static_cast<short>(FamilyMap[x])
+
+static DWORD ProtocolMap[]= {
+ 0, /* osl_Socket_FamilyInet */
+ NSPROTO_IPX, /* osl_Socket_FamilyIpx */
+ NSPROTO_SPX, /* osl_Socket_ProtocolSpx */
+ NSPROTO_SPXII, /* osl_Socket_ProtocolSpx_ii */
+ 0 /* osl_Socket_ProtocolInvalid */
+};
+
+#define PROTOCOL_TO_NATIVE(x) ProtocolMap[x]
+
+static DWORD TypeMap[]= {
+ SOCK_STREAM, /* osl_Socket_TypeStream */
+ SOCK_DGRAM, /* osl_Socket_TypeDgram */
+ SOCK_RAW, /* osl_Socket_TypeRaw */
+ SOCK_RDM, /* osl_Socket_TypeRdm */
+ SOCK_SEQPACKET, /* osl_Socket_TypeSeqPacket */
+ 0 /* osl_Socket_TypeInvalid */
+};
+
+static oslSocketType osl_SocketTypeFromNative(DWORD nativeType)
+{
+ oslSocketType i= oslSocketType(0);
+ while(i != osl_Socket_TypeInvalid)
+ {
+ if(TypeMap[i] == nativeType)
+ return i;
+ i = static_cast<oslSocketType>(static_cast<int>(i)+1);
+ }
+ return i;
+}
+
+#define TYPE_TO_NATIVE(x) TypeMap[x]
+#define TYPE_FROM_NATIVE(y) osl_SocketTypeFromNative(y)
+
+static DWORD OptionMap[]= {
+ SO_DEBUG, /* osl_Socket_OptionDebug */
+ SO_ACCEPTCONN, /* osl_Socket_OptionAcceptConn */
+ SO_REUSEADDR, /* osl_Socket_OptionReuseAddr */
+ SO_KEEPALIVE, /* osl_Socket_OptionKeepAlive */
+ SO_DONTROUTE, /* osl_Socket_OptionDontRoute */
+ SO_BROADCAST, /* osl_Socket_OptionBroadcast */
+ SO_USELOOPBACK, /* osl_Socket_OptionUseLoopback */
+ SO_LINGER, /* osl_Socket_OptionLinger */
+ SO_OOBINLINE, /* osl_Socket_OptionOOBinLine */
+ SO_SNDBUF, /* osl_Socket_OptionSndBuf */
+ SO_RCVBUF, /* osl_Socket_OptionRcvBuf */
+ SO_SNDLOWAT, /* osl_Socket_OptionSndLowat */
+ SO_RCVLOWAT, /* osl_Socket_OptionRcvLowat */
+ SO_SNDTIMEO, /* osl_Socket_OptionSndTimeo */
+ SO_RCVTIMEO, /* osl_Socket_OptionRcvTimeo */
+ SO_ERROR, /* osl_Socket_OptionError */
+ SO_TYPE, /* osl_Socket_OptionType */
+ TCP_NODELAY, /* osl_Socket_OptionTcpNoDelay */
+ 0 /* osl_Socket_OptionInvalid */
+};
+
+#define OPTION_TO_NATIVE(x) OptionMap[x]
+
+static DWORD OptionLevelMap[]= {
+ SOL_SOCKET, /* osl_Socket_LevelSocket */
+ IPPROTO_TCP, /* osl_Socket_LevelTcp */
+ 0 /* osl_invalid_SocketLevel */
+};
+
+#define OPTION_LEVEL_TO_NATIVE(x) OptionLevelMap[x]
+
+static DWORD SocketMsgFlagMap[]= {
+ 0, /* osl_Socket_MsgNormal */
+ MSG_OOB, /* osl_Socket_MsgOOB */
+ MSG_PEEK, /* osl_Socket_MsgPeek */
+ MSG_DONTROUTE, /* osl_Socket_MsgDontRoute */
+ MSG_MAXIOVLEN /* osl_Socket_MsgMaxIOVLen */
+};
+
+#define MSG_FLAG_TO_NATIVE(x) SocketMsgFlagMap[x]
+
+static DWORD SocketDirection[]= {
+ SD_RECEIVE, /* osl_Socket_DirRead */
+ SD_SEND, /* osl_Socket_DirWrite */
+ SD_BOTH /* osl_Socket_DirReadwrite */
+};
+
+#define DIRECTION_TO_NATIVE(x) SocketDirection[x]
+
+static int SocketError[]= {
+ 0, /* no error */
+ WSAENOTSOCK, /* Socket operation on non-socket */
+ WSAEDESTADDRREQ, /* Destination address required */
+ WSAEMSGSIZE, /* Message too long */
+ WSAEPROTOTYPE, /* Protocol wrong type for socket */
+ WSAENOPROTOOPT, /* Protocol not available */
+ WSAEPROTONOSUPPORT, /* Protocol not supported */
+ WSAESOCKTNOSUPPORT, /* Socket type not supported */
+ WSAEOPNOTSUPP, /* Operation not supported on socket */
+ WSAEPFNOSUPPORT, /* Protocol family not supported */
+ WSAEAFNOSUPPORT, /* Address family not supported by protocol family */
+ WSAEADDRINUSE, /* Address already in use */
+ WSAEADDRNOTAVAIL, /* Can't assign requested address */
+ WSAENETDOWN, /* Network is down */
+ WSAENETUNREACH, /* Network is unreachable */
+ WSAENETRESET, /* Network dropped connection because of reset */
+ WSAECONNABORTED, /* Software caused connection abort */
+ WSAECONNRESET, /* Connection reset by peer */
+ WSAENOBUFS, /* No buffer space available */
+ WSAEISCONN, /* Socket is already connected */
+ WSAENOTCONN, /* Socket is not connected */
+ WSAESHUTDOWN, /* Can't send after socket shutdown */
+ WSAETOOMANYREFS, /* Too many references: can't splice */
+ WSAETIMEDOUT, /* Connection timed out */
+ WSAECONNREFUSED, /* Connection refused */
+ WSAEHOSTDOWN, /* Host is down */
+ WSAEHOSTUNREACH, /* No route to host */
+ WSAEWOULDBLOCK, /* call would block on non-blocking socket */
+ WSAEALREADY, /* operation already in progress */
+ WSAEINPROGRESS /* operation now in progress */
+};
+
+static oslSocketError osl_SocketErrorFromNative(int nativeType)
+{
+ oslSocketError i= oslSocketError(0);
+
+ while(i != osl_Socket_E_InvalidError)
+ {
+ if(SocketError[i] == nativeType)
+ return i;
+
+ i = static_cast<oslSocketError>( static_cast<int>(i) + 1);
+ }
+ return i;
+}
+
+#define ERROR_FROM_NATIVE(y) osl_SocketErrorFromNative(y)
+
+#if OSL_DEBUG_LEVEL > 0
+static sal_uInt32 g_nSocketAddr = 0;
+
+namespace {
+
+struct LeakWarning
+{
+ ~LeakWarning()
+ {
+ SAL_WARN_IF( g_nSocketAddr, "sal.osl", "sal_socket: " << g_nSocketAddr << " socket address instances leak" );
+ }
+};
+
+}
+
+static LeakWarning socketWarning;
+#endif
+
+static oslSocket createSocketImpl(SOCKET Socket)
+{
+ oslSocket pSockImpl = static_cast<oslSocket>(rtl_allocateZeroMemory( sizeof(struct oslSocketImpl)));
+ pSockImpl->m_Socket = Socket;
+ pSockImpl->m_nRefCount = 1;
+ return pSockImpl;
+}
+
+static void destroySocketImpl(oslSocketImpl *pImpl)
+{
+ if (pImpl)
+ {
+ free (pImpl);
+ }
+}
+
+static oslSocketAddr createSocketAddr( )
+{
+ oslSocketAddr pAddr = static_cast<oslSocketAddr>(rtl_allocateZeroMemory( sizeof( struct oslSocketAddrImpl )));
+ pAddr->m_nRefCount = 1;
+#if OSL_DEBUG_LEVEL > 0
+ g_nSocketAddr ++;
+#endif
+ return pAddr;
+}
+
+static oslSocketAddr createSocketAddrWithFamily(
+ oslAddrFamily family, sal_Int32 port, sal_uInt32 nAddr )
+{
+ OSL_ASSERT( family == osl_Socket_FamilyInet );
+
+ oslSocketAddr pAddr = createSocketAddr();
+ switch( family )
+ {
+ case osl_Socket_FamilyInet:
+ {
+ struct sockaddr_in* pInetAddr= reinterpret_cast<struct sockaddr_in*>(&pAddr->m_sockaddr);
+
+ pInetAddr->sin_family = FAMILY_TO_NATIVE(osl_Socket_FamilyInet);
+ pInetAddr->sin_addr.s_addr = nAddr;
+ pInetAddr->sin_port = static_cast<sal_uInt16>(port&0xffff);
+ break;
+ }
+ default:
+ pAddr->m_sockaddr.sa_family = FAMILY_TO_NATIVE(family);
+ }
+ return pAddr;
+}
+
+static oslSocketAddr createSocketAddFromSystem( struct sockaddr *pSystemSockAddr )
+{
+ oslSocketAddr pAddr = createSocketAddr();
+ memcpy( &(pAddr->m_sockaddr), pSystemSockAddr, sizeof( sockaddr ) );
+ return pAddr;
+}
+
+static void destroySocketAddr( oslSocketAddr addr )
+{
+#if OSL_DEBUG_LEVEL > 0
+ g_nSocketAddr --;
+#endif
+ free( addr );
+}
+
+oslSocketAddr SAL_CALL osl_createEmptySocketAddr(oslAddrFamily Family)
+{
+ oslSocketAddr pAddr = nullptr;
+
+ /* is it an internet-Addr? */
+ if (Family == osl_Socket_FamilyInet)
+ pAddr = createSocketAddrWithFamily(Family, 0 , htonl(INADDR_ANY) );
+ else
+ pAddr = createSocketAddrWithFamily( Family , 0 , 0 );
+
+ return pAddr;
+}
+
+/** @deprecated, to be removed */
+oslSocketAddr SAL_CALL osl_copySocketAddr(oslSocketAddr Addr)
+{
+ oslSocketAddr pCopy = nullptr;
+ if (Addr)
+ {
+ pCopy = createSocketAddr();
+
+ if (pCopy)
+ memcpy(&(pCopy->m_sockaddr),&(Addr->m_sockaddr), sizeof(struct sockaddr));
+ }
+ return pCopy;
+}
+
+sal_Bool SAL_CALL osl_isEqualSocketAddr(oslSocketAddr Addr1, oslSocketAddr Addr2)
+{
+ OSL_ASSERT(Addr1);
+ OSL_ASSERT(Addr2);
+ struct sockaddr* pAddr1= &(Addr1->m_sockaddr);
+ struct sockaddr* pAddr2= &(Addr2->m_sockaddr);
+
+ OSL_ASSERT(pAddr1);
+ OSL_ASSERT(pAddr2);
+
+ if (pAddr1->sa_family == pAddr2->sa_family)
+ {
+ switch (pAddr1->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in* pInetAddr1= reinterpret_cast<struct sockaddr_in*>(pAddr1);
+ struct sockaddr_in* pInetAddr2= reinterpret_cast<struct sockaddr_in*>(pAddr2);
+
+ if ((pInetAddr1->sin_family == pInetAddr2->sin_family) &&
+ (pInetAddr1->sin_addr.s_addr == pInetAddr2->sin_addr.s_addr) &&
+ (pInetAddr1->sin_port == pInetAddr2->sin_port))
+ return true;
+ [[fallthrough]];
+ }
+
+ default:
+ {
+ return (memcmp(pAddr1, pAddr2, sizeof(struct sockaddr)) == 0);
+ }
+ }
+ }
+
+ return false;
+}
+
+oslSocketAddr SAL_CALL osl_createInetBroadcastAddr (
+ rtl_uString *strDottedAddr,
+ sal_Int32 Port)
+{
+ sal_uInt32 nAddr = OSL_INADDR_NONE;
+
+ if (strDottedAddr && strDottedAddr->length)
+ {
+ IN_ADDR addr;
+ INT ret = InetPtonW(AF_INET, o3tl::toW(strDottedAddr->buffer), & addr);
+ if (1 == ret)
+ {
+ nAddr = addr.S_un.S_addr;
+ }
+ }
+
+ if (nAddr != OSL_INADDR_NONE)
+ {
+ /* Limited broadcast */
+ nAddr = ntohl(nAddr);
+ if (IN_CLASSA(nAddr))
+ {
+ nAddr &= IN_CLASSA_NET;
+ nAddr |= IN_CLASSA_HOST;
+ }
+ else if (IN_CLASSB(nAddr))
+ {
+ nAddr &= IN_CLASSB_NET;
+ nAddr |= IN_CLASSB_HOST;
+ }
+ else if (IN_CLASSC(nAddr))
+ {
+ nAddr &= IN_CLASSC_NET;
+ nAddr |= IN_CLASSC_HOST;
+ }
+ else
+ {
+ /* No broadcast in class D */
+ return nullptr;
+ }
+ nAddr = htonl(nAddr);
+ }
+
+ oslSocketAddr pAddr =
+ createSocketAddrWithFamily( osl_Socket_FamilyInet, htons( static_cast<sal_uInt16>(Port)), nAddr );
+ return pAddr;
+}
+
+oslSocketAddr SAL_CALL osl_createInetSocketAddr (
+ rtl_uString *strDottedAddr,
+ sal_Int32 Port)
+{
+ sal_uInt32 Addr;
+
+ IN_ADDR addr;
+ INT ret = InetPtonW(AF_INET, o3tl::toW(strDottedAddr->buffer), & addr);
+ Addr = ret == 1 ? addr.S_un.S_addr : OSL_INADDR_NONE;
+
+ oslSocketAddr pAddr = nullptr;
+ if(Addr != OSL_INADDR_NONE)
+ {
+ pAddr = createSocketAddrWithFamily( osl_Socket_FamilyInet, htons( static_cast<sal_uInt16>(Port)), Addr );
+ }
+ return pAddr;
+}
+
+oslSocketResult SAL_CALL osl_setAddrOfSocketAddr( oslSocketAddr pAddr, sal_Sequence *pByteSeq )
+{
+ OSL_ASSERT( pAddr );
+ OSL_ASSERT( pByteSeq );
+
+ oslSocketResult res = osl_Socket_Error;
+ if( pAddr && pByteSeq )
+ {
+ OSL_ASSERT( pAddr->m_sockaddr.sa_family == FAMILY_TO_NATIVE( osl_Socket_FamilyInet ) );
+ OSL_ASSERT( pByteSeq->nElements == 4 );
+ struct sockaddr_in * pSystemInetAddr = reinterpret_cast<struct sockaddr_in *>(&pAddr->m_sockaddr);
+ memcpy( &(pSystemInetAddr->sin_addr) , pByteSeq->elements , 4 );
+ res = osl_Socket_Ok;
+ }
+ return res;
+}
+
+/** Returns the addr field in the struct sockaddr. ppByteSeq is in network byteorder. *ppByteSeq may
+ either be 0 or contain a constructed sal_Sequence.
+ */
+oslSocketResult SAL_CALL osl_getAddrOfSocketAddr( oslSocketAddr pAddr, sal_Sequence **ppByteSeq )
+{
+ OSL_ASSERT( pAddr );
+ OSL_ASSERT( ppByteSeq );
+
+ oslSocketResult res = osl_Socket_Error;
+ if( pAddr && ppByteSeq )
+ {
+ struct sockaddr_in * pSystemInetAddr = reinterpret_cast<struct sockaddr_in *>(&pAddr->m_sockaddr);
+ rtl_byte_sequence_constructFromArray( ppByteSeq , reinterpret_cast<sal_Int8 *>(&pSystemInetAddr->sin_addr),4);
+ res = osl_Socket_Ok;
+ }
+ return res;
+}
+
+struct oslHostAddrImpl {
+ rtl_uString *pHostName;
+ oslSocketAddr pSockAddr;
+} ;
+
+oslHostAddr SAL_CALL osl_createHostAddr (
+ rtl_uString *strHostname,
+ const oslSocketAddr pSocketAddr)
+{
+ oslHostAddr pAddr;
+ rtl_uString *cn= nullptr;
+
+ if ((strHostname == nullptr) || (strHostname->length == 0) || (pSocketAddr == nullptr))
+ return nullptr;
+
+ rtl_uString_newFromString( &cn, strHostname);
+
+ pAddr= static_cast<oslHostAddr>(malloc (sizeof (struct oslHostAddrImpl)));
+
+ if (pAddr == nullptr)
+ {
+ rtl_uString_release(cn);
+ return nullptr;
+ }
+
+ pAddr->pHostName= cn;
+ pAddr->pSockAddr= osl_copySocketAddr( pSocketAddr );
+
+ return pAddr;
+}
+
+oslHostAddr SAL_CALL osl_createHostAddrByName(rtl_uString *strHostname)
+{
+ if ((strHostname == nullptr) || (strHostname->length == 0))
+ return nullptr;
+
+ PADDRINFOW pAddrInfo = nullptr;
+ int ret = GetAddrInfoW(
+ o3tl::toW(strHostname->buffer), nullptr, nullptr, & pAddrInfo);
+ if (0 != ret)
+ {
+ SAL_INFO("sal.osl", "GetAddrInfoW failed: " << WSAGetLastError());
+ return nullptr;
+ }
+
+ oslHostAddr pRet = nullptr;
+ for (PADDRINFOW pIter = pAddrInfo; pIter; pIter = pIter->ai_next)
+ {
+ if (AF_INET == pIter->ai_family)
+ {
+ pRet = static_cast<oslHostAddr>(
+ rtl_allocateZeroMemory(sizeof(struct oslHostAddrImpl)));
+ if (pIter->ai_canonname == nullptr) {
+ rtl_uString_new(&pRet->pHostName);
+ } else {
+ rtl_uString_newFromStr(&pRet->pHostName, o3tl::toU(pIter->ai_canonname));
+ }
+ pRet->pSockAddr = createSocketAddr();
+ memcpy(& pRet->pSockAddr->m_sockaddr,
+ pIter->ai_addr, pIter->ai_addrlen);
+ break; // ignore other results
+ }
+ }
+ FreeAddrInfoW(pAddrInfo);
+ return pRet;
+}
+
+oslHostAddr SAL_CALL osl_createHostAddrByAddr(const oslSocketAddr pAddr)
+{
+ if (pAddr == nullptr)
+ return nullptr;
+
+ if (pAddr->m_sockaddr.sa_family != FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ return nullptr;
+
+ const struct sockaddr_in *sin= reinterpret_cast<const struct sockaddr_in *>(&pAddr->m_sockaddr);
+
+ if (sin->sin_addr.s_addr == htonl(INADDR_ANY))
+ return nullptr;
+
+ WCHAR buf[NI_MAXHOST];
+ int ret = GetNameInfoW(
+ & pAddr->m_sockaddr, sizeof(struct sockaddr),
+ buf, NI_MAXHOST,
+ nullptr, 0, 0);
+ if (0 != ret)
+ {
+ SAL_INFO("sal.osl", "GetNameInfoW failed: " << WSAGetLastError());
+ return nullptr;
+ }
+
+ oslHostAddr pRet = static_cast<oslHostAddr>(
+ rtl_allocateZeroMemory(sizeof(struct oslHostAddrImpl)));
+ rtl_uString_newFromStr(&pRet->pHostName, o3tl::toU(buf));
+ pRet->pSockAddr = createSocketAddr();
+ memcpy(& pRet->pSockAddr->m_sockaddr,
+ & pAddr->m_sockaddr, sizeof(struct sockaddr));
+ return pRet;
+}
+
+oslHostAddr SAL_CALL osl_copyHostAddr(const oslHostAddr Addr)
+{
+ oslHostAddr pAddr = Addr;
+
+ if (pAddr)
+ return osl_createHostAddr (pAddr->pHostName, pAddr->pSockAddr);
+ else
+ return nullptr;
+}
+
+void SAL_CALL osl_getHostnameOfHostAddr(
+ const oslHostAddr pAddr, rtl_uString **strHostname)
+{
+ if (pAddr)
+ rtl_uString_assign (strHostname, pAddr->pHostName);
+ else
+ rtl_uString_new (strHostname);
+}
+
+oslSocketAddr SAL_CALL osl_getSocketAddrOfHostAddr(const oslHostAddr pAddr)
+{
+ if (pAddr)
+ return pAddr->pSockAddr;
+ else
+ return nullptr;
+}
+
+void SAL_CALL osl_destroyHostAddr(oslHostAddr pAddr)
+{
+ if (pAddr)
+ {
+ if (pAddr->pHostName)
+ rtl_uString_release (pAddr->pHostName);
+ if (pAddr->pSockAddr)
+ osl_destroySocketAddr( pAddr->pSockAddr );
+
+ free (pAddr);
+ }
+}
+
+oslSocketResult SAL_CALL osl_getLocalHostname (rtl_uString **strLocalHostname)
+{
+ static auto const init = []() -> std::pair<oslSocketResult, OUString> {
+ sal_Unicode LocalHostname[256] = {0};
+
+ char Host[256]= "";
+ if (gethostname(Host, sizeof(Host)) == 0)
+ {
+ OUString u;
+ if (rtl_convertStringToUString(
+ &u.pData, Host, strlen(Host), osl_getThreadTextEncoding(),
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR))
+ && o3tl::make_unsigned(u.getLength()) < SAL_N_ELEMENTS(LocalHostname))
+ {
+ memcpy(LocalHostname, u.getStr(), (u.getLength() + 1) * sizeof (sal_Unicode));
+ }
+ }
+
+ if (rtl_ustr_getLength(LocalHostname) > 0)
+ {
+ return {osl_Socket_Ok, OUString(LocalHostname)};
+ }
+
+ return {osl_Socket_Error, OUString()};
+ }();
+
+ rtl_uString_assign (strLocalHostname, init.second.pData);
+
+ return init.first;
+}
+
+oslSocketAddr SAL_CALL osl_resolveHostname(rtl_uString* strHostname)
+{
+ oslHostAddr pAddr = osl_createHostAddrByName (strHostname);
+ if (pAddr)
+ {
+ oslSocketAddr SockAddr = osl_copySocketAddr( pAddr->pSockAddr );
+ osl_destroyHostAddr(pAddr);
+ return SockAddr;
+ }
+ return nullptr;
+}
+
+sal_Int32 SAL_CALL osl_getServicePort (
+ rtl_uString* strServicename,
+ rtl_uString* strProtocol)
+{
+ struct servent* ps;
+
+ rtl_String *str_Servicename=nullptr;
+ rtl_String *str_Protocol=nullptr;
+
+ rtl_uString2String(
+ &str_Servicename,
+ rtl_uString_getStr(strServicename),
+ rtl_uString_getLength(strServicename),
+ RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);
+ rtl_uString2String(
+ &str_Protocol,
+ rtl_uString_getStr(strProtocol),
+ rtl_uString_getLength(strProtocol),
+ RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);
+
+ ps= getservbyname(
+ rtl_string_getStr(str_Servicename),
+ rtl_string_getStr(str_Protocol));
+
+ rtl_string_release( str_Servicename );
+ rtl_string_release( str_Protocol );
+
+ if (ps != nullptr)
+ return ntohs(ps->s_port);
+
+ return OSL_INVALID_PORT;
+}
+
+void SAL_CALL osl_destroySocketAddr(oslSocketAddr pAddr)
+{
+ destroySocketAddr( pAddr );
+}
+
+oslAddrFamily SAL_CALL osl_getFamilyOfSocketAddr(oslSocketAddr pAddr)
+{
+ if (pAddr)
+ return FAMILY_FROM_NATIVE(pAddr->m_sockaddr.sa_family);
+ else
+ return osl_Socket_FamilyInvalid;
+}
+
+sal_Int32 SAL_CALL osl_getInetPortOfSocketAddr(oslSocketAddr pAddr)
+{
+ if( pAddr )
+ {
+ struct sockaddr_in* pSystemInetAddr= reinterpret_cast<struct sockaddr_in*>(&pAddr->m_sockaddr);
+
+ if (pSystemInetAddr->sin_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ return ntohs(pSystemInetAddr->sin_port);
+ }
+ return OSL_INVALID_PORT;
+}
+
+sal_Bool SAL_CALL osl_setInetPortOfSocketAddr (
+ oslSocketAddr pAddr,
+ sal_Int32 Port)
+{
+ if (pAddr == nullptr)
+ return false;
+
+ struct sockaddr_in* pSystemInetAddr= reinterpret_cast<struct sockaddr_in*>(&pAddr->m_sockaddr);
+
+ if (pSystemInetAddr->sin_family != FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ return false;
+
+ pSystemInetAddr->sin_port= htons(static_cast<short>(Port));
+ return true;
+}
+
+oslSocketResult SAL_CALL osl_getHostnameOfSocketAddr (
+ oslSocketAddr Addr,
+ rtl_uString **strHostName)
+{
+ oslHostAddr pAddr= osl_createHostAddrByAddr (Addr);
+
+ if (pAddr)
+ {
+ rtl_uString_newFromString(strHostName, pAddr->pHostName);
+
+ osl_destroyHostAddr(pAddr);
+
+ return osl_Socket_Ok;
+ }
+
+ return osl_Socket_Error;
+}
+
+oslSocketResult SAL_CALL osl_getDottedInetAddrOfSocketAddr (
+ oslSocketAddr pAddr,
+ rtl_uString **strDottedInetAddr)
+{
+ if (pAddr == nullptr)
+ return osl_Socket_Error;
+
+ struct sockaddr_in *pSystemInetAddr = reinterpret_cast<struct sockaddr_in*>(&pAddr->m_sockaddr);
+ if (pSystemInetAddr->sin_family != FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ return osl_Socket_Error;
+
+ *strDottedInetAddr = nullptr;
+ WCHAR buf[16]; // 16 for IPV4, 46 for IPV6
+ PCWSTR ret = InetNtopW(
+ AF_INET, & pSystemInetAddr->sin_addr,
+ buf, SAL_N_ELEMENTS(buf));
+ if (nullptr == ret)
+ {
+ SAL_INFO("sal.osl", "InetNtopW failed: " << WSAGetLastError());
+ return osl_Socket_Error;
+ }
+ rtl_uString_newFromStr(strDottedInetAddr, o3tl::toU(ret));
+ OSL_ASSERT(*strDottedInetAddr != nullptr);
+
+ return osl_Socket_Ok;
+}
+
+oslSocket SAL_CALL osl_createSocket(
+ oslAddrFamily Family,
+ oslSocketType Type,
+ oslProtocol Protocol)
+{
+ /* alloc memory */
+ oslSocket pSocket = createSocketImpl(0);
+
+ if (pSocket == nullptr)
+ return nullptr;
+
+ /* create socket */
+ pSocket->m_Socket = socket(FAMILY_TO_NATIVE(Family),
+ TYPE_TO_NATIVE(Type),
+ PROTOCOL_TO_NATIVE(Protocol));
+
+ /* creation failed => free memory */
+ if(pSocket->m_Socket == OSL_INVALID_SOCKET)
+ {
+ int nErrno = WSAGetLastError();
+ SAL_WARN("sal.osl", "socket creation failed: (" << nErrno << "): " << WindowsErrorString(nErrno));
+
+ destroySocketImpl(pSocket);
+ pSocket = nullptr;
+ }
+ else
+ {
+ pSocket->m_Flags = 0;
+ }
+
+ return pSocket;
+}
+
+void SAL_CALL osl_acquireSocket(oslSocket pSocket)
+{
+ osl_atomic_increment(&(pSocket->m_nRefCount));
+}
+
+void SAL_CALL osl_releaseSocket(oslSocket pSocket)
+{
+ if (pSocket && osl_atomic_decrement(&(pSocket->m_nRefCount)) == 0)
+ {
+ osl_closeSocket(pSocket);
+ destroySocketImpl(pSocket);
+ }
+}
+
+void SAL_CALL osl_closeSocket(oslSocket pSocket)
+{
+ /* socket already invalid */
+ if (!pSocket)
+ return;
+
+ /* close */
+ closesocket(pSocket->m_Socket);
+
+ pSocket->m_Socket = OSL_INVALID_SOCKET;
+}
+
+/**
+ Note that I rely on the fact that oslSocketAddr and struct sockaddr
+ are the same! I don't like it very much but see no other easy way
+ to conceal the struct sockaddr from the eyes of the user.
+*/
+oslSocketAddr SAL_CALL osl_getLocalAddrOfSocket(oslSocket pSocket)
+{
+ struct sockaddr Addr;
+ int AddrLen;
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return nullptr;
+
+ AddrLen= sizeof(struct sockaddr);
+
+ if (getsockname(pSocket->m_Socket, &Addr, &AddrLen) == OSL_SOCKET_ERROR)
+ return nullptr;
+
+ oslSocketAddr pAddr = createSocketAddFromSystem( &Addr );
+ return pAddr;
+}
+
+oslSocketAddr SAL_CALL osl_getPeerAddrOfSocket(oslSocket pSocket)
+{
+ struct sockaddr Addr;
+ int AddrLen;
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return nullptr;
+
+ AddrLen= sizeof(struct sockaddr);
+
+ if (getpeername(pSocket->m_Socket, &Addr, &AddrLen) == OSL_SOCKET_ERROR)
+ return nullptr;
+
+ oslSocketAddr pAddr = createSocketAddFromSystem( &Addr );
+ return pAddr;
+}
+
+sal_Bool SAL_CALL osl_bindAddrToSocket ( oslSocket pSocket, oslSocketAddr pAddr)
+{
+ OSL_ASSERT( pAddr );
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return false;
+
+ return (bind(pSocket->m_Socket,
+ &(pAddr->m_sockaddr),
+ sizeof(struct sockaddr)) != OSL_SOCKET_ERROR);
+}
+
+oslSocketResult SAL_CALL osl_connectSocketTo (
+ oslSocket pSocket,
+ oslSocketAddr pAddr,
+ const TimeValue* pTimeout)
+{
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return osl_Socket_Error;
+
+ if (pAddr == nullptr) /* EDESTADDRREQ */
+ return osl_Socket_Error;
+
+ if (pTimeout == nullptr)
+ {
+ if(connect(pSocket->m_Socket,
+ &(pAddr->m_sockaddr),
+ sizeof(struct sockaddr)) == OSL_SOCKET_ERROR)
+ return osl_Socket_Error;
+ else
+ return osl_Socket_Ok;
+ }
+ else
+ {
+ fd_set fds;
+ int error;
+ struct timeval tv;
+ unsigned long Param;
+ oslSocketResult Result= osl_Socket_Ok;
+
+ if (pSocket->m_Flags & OSL_SOCKET_FLAGS_NONBLOCKING)
+ {
+ if (connect(pSocket->m_Socket,
+ &(pAddr->m_sockaddr),
+ sizeof(struct sockaddr)) == OSL_SOCKET_ERROR)
+ {
+ switch (WSAGetLastError())
+ {
+ case WSAEWOULDBLOCK:
+ case WSAEINPROGRESS:
+ return osl_Socket_InProgress;
+
+ default:
+ return osl_Socket_Error;
+ }
+ }
+ else
+ return osl_Socket_Ok;
+ }
+
+ /* set socket temporarily to non-blocking */
+ Param= 1;
+ OSL_VERIFY(ioctlsocket(
+ pSocket->m_Socket, FIONBIO, &Param) != OSL_SOCKET_ERROR);
+
+ /* initiate connect */
+ if (connect(pSocket->m_Socket,
+ &(pAddr->m_sockaddr),
+ sizeof(struct sockaddr)) != OSL_SOCKET_ERROR)
+ {
+ /* immediate connection */
+
+ Param= 0;
+ ioctlsocket(pSocket->m_Socket, FIONBIO, &Param);
+
+ return osl_Socket_Ok;
+ }
+ else
+ {
+ error = WSAGetLastError();
+
+ /* really an error or just delayed? */
+ if (error != WSAEWOULDBLOCK && error != WSAEINPROGRESS)
+ {
+ Param= 0;
+ ioctlsocket(pSocket->m_Socket, FIONBIO, &Param);
+
+ return osl_Socket_Error;
+ }
+ }
+
+ /* prepare select set for socket */
+ FD_ZERO(&fds);
+ FD_SET(pSocket->m_Socket, &fds);
+
+ /* divide milliseconds into seconds and microseconds */
+ tv.tv_sec= pTimeout->Seconds;
+ tv.tv_usec= pTimeout->Nanosec / 1000L;
+
+ /* select */
+ error= select(pSocket->m_Socket+1,
+ nullptr,
+ &fds,
+ nullptr,
+ &tv);
+
+ if (error > 0) /* connected */
+ {
+ SAL_WARN_IF(
+ !FD_ISSET(pSocket->m_Socket, &fds),
+ "sal.osl",
+ "osl_connectSocketTo(): select returned but socket not set");
+
+ Result= osl_Socket_Ok;
+
+ }
+ else if(error < 0) /* error */
+ {
+ /* errno == EBADF: most probably interrupted by close() */
+ if(WSAGetLastError() == WSAEBADF)
+ {
+ /* do not access pSockImpl because it is about to be or */
+ /* already destroyed */
+ return osl_Socket_Interrupted;
+ }
+ else
+ Result= osl_Socket_Error;
+
+ }
+ else /* timeout */
+ Result= osl_Socket_TimedOut;
+
+ /* clean up */
+ Param= 0;
+ ioctlsocket(pSocket->m_Socket, FIONBIO, &Param);
+
+ return Result;
+ }
+}
+
+sal_Bool SAL_CALL osl_listenOnSocket (
+ oslSocket pSocket,
+ sal_Int32 MaxPendingConnections)
+{
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return false;
+
+ return (listen(pSocket->m_Socket,
+ MaxPendingConnections == -1 ?
+ SOMAXCONN :
+ MaxPendingConnections) != OSL_SOCKET_ERROR);
+}
+
+oslSocket SAL_CALL osl_acceptConnectionOnSocket (
+ oslSocket pSocket,
+ oslSocketAddr* ppAddr)
+{
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return nullptr;
+
+ SOCKET Connection;
+ if(ppAddr)
+ {
+ if( *ppAddr )
+ {
+ osl_destroySocketAddr( *ppAddr );
+ *ppAddr = nullptr;
+ }
+ int AddrLen= sizeof(struct sockaddr);
+
+ /* user wants to know peer Addr */
+ struct sockaddr Addr;
+
+ Connection= accept(pSocket->m_Socket, &Addr, &AddrLen);
+ OSL_ASSERT(AddrLen == sizeof(struct sockaddr));
+
+ if(Connection != static_cast<SOCKET>(OSL_SOCKET_ERROR))
+ *ppAddr= createSocketAddFromSystem(&Addr);
+ else
+ *ppAddr = nullptr;
+ }
+ else
+ {
+ /* user is not interested in peer-addr */
+ Connection= accept(pSocket->m_Socket, nullptr, nullptr);
+ }
+
+ /* accept failed? */
+ if(Connection == static_cast<SOCKET>(OSL_SOCKET_ERROR))
+ return nullptr;
+
+ /* alloc memory */
+ oslSocket pConnectionSocket;
+ pConnectionSocket= createSocketImpl(Connection);
+
+ pConnectionSocket->m_Flags = 0;
+
+ return pConnectionSocket;
+}
+
+sal_Int32 SAL_CALL osl_receiveSocket (
+ oslSocket pSocket,
+ void* pBuffer,
+ sal_uInt32 BytesToRead,
+ oslSocketMsgFlag Flag)
+{
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return osl_Socket_Error;
+
+ return recv(pSocket->m_Socket,
+ static_cast<char*>(pBuffer),
+ BytesToRead,
+ MSG_FLAG_TO_NATIVE(Flag));
+}
+
+sal_Int32 SAL_CALL osl_receiveFromSocket (
+ oslSocket pSocket,
+ oslSocketAddr SenderAddr,
+ void* pBuffer,
+ sal_uInt32 BufferSize,
+ oslSocketMsgFlag Flag)
+{
+ struct sockaddr *pSystemSockAddr = nullptr;
+ int AddrLen = 0;
+ if( SenderAddr )
+ {
+ AddrLen = sizeof( struct sockaddr );
+ pSystemSockAddr = &(SenderAddr->m_sockaddr);
+ }
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return osl_Socket_Error;
+
+ return recvfrom(pSocket->m_Socket,
+ static_cast<char*>(pBuffer),
+ BufferSize,
+ MSG_FLAG_TO_NATIVE(Flag),
+ pSystemSockAddr,
+ &AddrLen);
+}
+
+sal_Int32 SAL_CALL osl_sendSocket (
+ oslSocket pSocket,
+ const void* pBuffer,
+ sal_uInt32 BytesToSend,
+ oslSocketMsgFlag Flag)
+{
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return osl_Socket_Error;
+
+ return send(pSocket->m_Socket,
+ static_cast<char const *>(pBuffer),
+ BytesToSend,
+ MSG_FLAG_TO_NATIVE(Flag));
+}
+
+sal_Int32 SAL_CALL osl_sendToSocket (
+ oslSocket pSocket,
+ oslSocketAddr ReceiverAddr,
+ const void* pBuffer,
+ sal_uInt32 BytesToSend,
+ oslSocketMsgFlag Flag)
+{
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return osl_Socket_Error;
+
+ /* ReceiverAddr might be 0 when used on a connected socket. */
+ /* Then sendto should behave like send. */
+
+ struct sockaddr *pSystemSockAddr = nullptr;
+ if( ReceiverAddr )
+ pSystemSockAddr = &(ReceiverAddr->m_sockaddr);
+
+ return sendto(pSocket->m_Socket,
+ static_cast<char const *>(pBuffer),
+ BytesToSend,
+ MSG_FLAG_TO_NATIVE(Flag),
+ pSystemSockAddr,
+ pSystemSockAddr == nullptr ? 0 : sizeof(struct sockaddr));
+}
+
+sal_Int32 SAL_CALL osl_readSocket( oslSocket pSocket, void *pBuffer, sal_Int32 n )
+{
+ sal_uInt8 * Ptr = static_cast<sal_uInt8 *>(pBuffer);
+
+ OSL_ASSERT( pSocket);
+
+ /* loop until all desired bytes were read or an error occurred */
+ sal_uInt32 BytesRead= 0;
+ sal_uInt32 BytesToRead= n;
+ while (BytesToRead > 0)
+ {
+ sal_Int32 RetVal;
+ RetVal= osl_receiveSocket(pSocket,
+ Ptr,
+ BytesToRead,
+ osl_Socket_MsgNormal);
+
+ /* error occurred? */
+ if(RetVal <= 0)
+ {
+ break;
+ }
+
+ BytesToRead -= RetVal;
+ BytesRead += RetVal;
+ Ptr += RetVal;
+ }
+
+ return BytesRead;
+}
+
+sal_Int32 SAL_CALL osl_writeSocket( oslSocket pSocket, const void *pBuffer, sal_Int32 n )
+{
+ OSL_ASSERT( pSocket );
+
+ /* loop until all desired bytes were send or an error occurred */
+ sal_uInt32 BytesSend= 0;
+ sal_uInt32 BytesToSend= n;
+ sal_uInt8 const *Ptr = static_cast<sal_uInt8 const *>(pBuffer);
+ while (BytesToSend > 0)
+ {
+ sal_Int32 RetVal;
+
+ RetVal= osl_sendSocket( pSocket,Ptr,BytesToSend,osl_Socket_MsgNormal);
+
+ /* error occurred? */
+ if(RetVal <= 0)
+ {
+ break;
+ }
+
+ BytesToSend -= RetVal;
+ BytesSend += RetVal;
+ Ptr += RetVal;
+
+ }
+ return BytesSend;
+}
+
+sal_Bool SAL_CALL osl_isReceiveReady (
+ oslSocket pSocket,
+ const TimeValue* pTimeout)
+{
+ fd_set fds;
+ struct timeval tv;
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return false;
+
+ FD_ZERO(&fds);
+ FD_SET(pSocket->m_Socket, &fds);
+
+ if (pTimeout)
+ {
+ tv.tv_sec = pTimeout->Seconds;
+ tv.tv_usec = pTimeout->Nanosec / 1000L;
+ }
+
+ return (select(pSocket->m_Socket + 1, /* no of sockets to monitor */
+ &fds, /* check read operations */
+ nullptr, /* check write ops */
+ nullptr, /* ckeck for OOB */
+ pTimeout ? &tv : nullptr)==1); /* use timeout? */
+}
+
+/*****************************************************************************/
+/* osl_isSendReady */
+/*****************************************************************************/
+sal_Bool SAL_CALL osl_isSendReady (
+ oslSocket pSocket,
+ const TimeValue* pTimeout)
+{
+ fd_set fds;
+ struct timeval tv;
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return false;
+
+ FD_ZERO(&fds);
+ FD_SET(pSocket->m_Socket, &fds);
+
+ if (pTimeout)
+ {
+ tv.tv_sec = pTimeout->Seconds;
+ tv.tv_usec = pTimeout->Nanosec / 1000L;
+ }
+
+ return (select(pSocket->m_Socket + 1, /* no of sockets to monitor */
+ nullptr, /* check read operations */
+ &fds, /* check write ops */
+ nullptr, /* ckeck for OOB */
+ pTimeout ? &tv : nullptr)==1); /* use timeout? */
+}
+
+sal_Bool SAL_CALL osl_isExceptionPending (
+ oslSocket pSocket,
+ const TimeValue* pTimeout)
+{
+ fd_set fds;
+ struct timeval tv;
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return false;
+
+ FD_ZERO(&fds);
+ FD_SET(pSocket->m_Socket, &fds);
+
+ if (pTimeout)
+ {
+ tv.tv_sec = pTimeout->Seconds;
+ tv.tv_usec = pTimeout->Nanosec / 1000L;
+ }
+
+ return (select(pSocket->m_Socket + 1, /* no of sockets to monitor */
+ nullptr, /* check read operations */
+ nullptr, /* check write ops */
+ &fds, /* ckeck for OOB */
+ pTimeout ? &tv : nullptr)==1); /* use timeout? */
+}
+
+sal_Bool SAL_CALL osl_shutdownSocket (
+ oslSocket pSocket,
+ oslSocketDirection Direction)
+{
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return false;
+
+ return (shutdown(pSocket->m_Socket, DIRECTION_TO_NATIVE(Direction))==0);
+}
+
+sal_Int32 SAL_CALL osl_getSocketOption (
+ oslSocket pSocket,
+ oslSocketOptionLevel Level,
+ oslSocketOption Option,
+ void* pBuffer,
+ sal_uInt32 BufferLen)
+{
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return osl_Socket_Error;
+
+ int len = BufferLen;
+ if (getsockopt(pSocket->m_Socket,
+ OPTION_LEVEL_TO_NATIVE(Level),
+ OPTION_TO_NATIVE(Option),
+ static_cast<char *>(pBuffer),
+ &len) == -1)
+ {
+ return -1;
+ }
+
+ return len;
+}
+
+sal_Bool SAL_CALL osl_setSocketOption (
+ oslSocket pSocket,
+ oslSocketOptionLevel Level,
+ oslSocketOption Option,
+ void* pBuffer,
+ sal_uInt32 BufferLen)
+{
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return false;
+
+ return(setsockopt(pSocket->m_Socket,
+ OPTION_LEVEL_TO_NATIVE(Level),
+ OPTION_TO_NATIVE(Option),
+ static_cast<char*>(pBuffer),
+ BufferLen) == 0);
+}
+
+sal_Bool SAL_CALL osl_enableNonBlockingMode ( oslSocket pSocket, sal_Bool On)
+{
+ unsigned long Param= On ? 1 : 0;
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return false;
+
+ pSocket->m_Flags = Param ?
+ (pSocket->m_Flags | OSL_SOCKET_FLAGS_NONBLOCKING) :
+ (pSocket->m_Flags & ~OSL_SOCKET_FLAGS_NONBLOCKING) ;
+
+ return (
+ ioctlsocket(pSocket->m_Socket, FIONBIO, &Param) != OSL_SOCKET_ERROR);
+}
+
+sal_Bool SAL_CALL osl_isNonBlockingMode(oslSocket pSocket)
+{
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return false;
+
+ return (pSocket->m_Flags & OSL_SOCKET_FLAGS_NONBLOCKING) != 0;
+}
+
+oslSocketType SAL_CALL osl_getSocketType(oslSocket pSocket)
+{
+ int Type=0;
+ int TypeSize= sizeof(Type);
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return osl_Socket_TypeInvalid;
+
+ if(getsockopt(pSocket->m_Socket,
+ OPTION_LEVEL_TO_NATIVE(osl_Socket_LevelSocket),
+ OPTION_TO_NATIVE(osl_Socket_OptionType),
+ reinterpret_cast<char *>(&Type),
+ &TypeSize) == -1)
+ {
+ /* error */
+ return osl_Socket_TypeInvalid;
+ }
+
+ return TYPE_FROM_NATIVE(Type);
+}
+
+void SAL_CALL osl_getLastSocketErrorDescription (
+ oslSocket /*Socket*/,
+ rtl_uString **strError)
+{
+ int error;
+
+ switch(error = WSAGetLastError())
+ {
+ case WSAENOTSOCK:
+ rtl_uString_newFromAscii (strError, "WSAENOTSOCK, Socket operation on non-socket. A socket created in one process is used by another process.");
+ break;
+
+ case WSAEDESTADDRREQ:
+ rtl_uString_newFromAscii (strError, "WSAEDESTADDRREQ, Destination Addr required");
+ break;
+
+ case WSAEMSGSIZE:
+ rtl_uString_newFromAscii (strError, "WSAEMSGSIZE, Message too long");
+ break;
+
+ case WSAEPROTOTYPE:
+ rtl_uString_newFromAscii (strError, "WSAEPROTOTYPE, Protocol wrong type for socket");
+ break;
+
+ case WSAENOPROTOOPT:
+ rtl_uString_newFromAscii (strError, "WSAENOPROTOOPT, Protocol not available");
+ break;
+
+ case WSAEPROTONOSUPPORT:
+ rtl_uString_newFromAscii (strError, "WSAEPROTONOSUPPORT, Protocol not supported");
+ break;
+
+ case WSAESOCKTNOSUPPORT:
+ rtl_uString_newFromAscii (strError, "WSAESOCKTNOSUPPORT, Socket type not supported");
+ break;
+
+ case WSAEOPNOTSUPP:
+ rtl_uString_newFromAscii (strError, "WSAEOPNOTSUPP, Operation not supported on socket");
+ break;
+
+ case WSAEPFNOSUPPORT:
+ rtl_uString_newFromAscii (strError, "WSAEPFNOSUPPORT, Protocol family not supported");
+ break;
+
+ case WSAEAFNOSUPPORT:
+ rtl_uString_newFromAscii (strError, "WSEAFNOSUPPORT, Addr family not supported by protocol family");
+ break;
+
+ case WSAEADDRINUSE:
+ rtl_uString_newFromAscii (strError, "WSAEADDRINUSE, Triggered by bind() because a process went down without closing a socket.");
+ break;
+
+ case WSAEADDRNOTAVAIL:
+ rtl_uString_newFromAscii (strError, "WSAEADDRNOTAVAIL, Can't assign requested Addr");
+ break;
+
+ case WSAENETDOWN:
+ rtl_uString_newFromAscii (strError, "WSAENETDOWN, Network is down");
+ break;
+
+ case WSAENETUNREACH:
+ rtl_uString_newFromAscii (strError, "WSAENETUNREACH, Network is unreachable");
+ break;
+
+ case WSAENETRESET:
+ rtl_uString_newFromAscii (strError, "WSAENETRESET, Network dropped connection or reset");
+ break;
+
+ case WSAECONNABORTED:
+ rtl_uString_newFromAscii (strError, "WSAECONNABORTED, Software caused connection abort");
+ break;
+
+ case WSAECONNRESET:
+ rtl_uString_newFromAscii (strError, "WSAECONNRESET, Connection reset by peer");
+ break;
+
+ case WSAENOBUFS:
+ rtl_uString_newFromAscii (strError, "WSAENOBUFS, No buffer space available.");
+ break;
+
+ case WSAEISCONN:
+ rtl_uString_newFromAscii (strError, "WSAEISCONN, Socket is already connected");
+ break;
+
+ case WSAENOTCONN:
+ rtl_uString_newFromAscii (strError, "WSAENOTCONN, Socket is not connected");
+ break;
+
+ case WSAESHUTDOWN:
+ rtl_uString_newFromAscii (strError, "WSAESHUTDOWN, Can't send after socket shutdown");
+ break;
+
+ case WSAETIMEDOUT:
+ rtl_uString_newFromAscii (strError, "WSAETIMEDOUT, Connection timed out");
+ break;
+
+ case WSAECONNREFUSED:
+ rtl_uString_newFromAscii (strError, "WSAECONNREFUSED, Connection refused");
+ break;
+
+ case WSAEHOSTDOWN:
+ rtl_uString_newFromAscii (strError, "WSAEHOSTDOWN, Networking subsystem not started");
+ break;
+
+ case WSAEHOSTUNREACH:
+ rtl_uString_newFromAscii (strError, "WSAEHOSTUNREACH, No route to host");
+ break;
+
+ case WSAEWOULDBLOCK:
+ rtl_uString_newFromAscii (strError, "WSAEWOULDBLOCK, Operation would block");
+ break;
+
+ case WSAEINPROGRESS:
+ rtl_uString_newFromAscii (strError, "WSAEINPROGRESS, Operation now in progress");
+ break;
+
+ case WSAEALREADY:
+ rtl_uString_newFromAscii (strError, "WSAEALREADY, Operation already in progress");
+ break;
+
+ case WSAEINTR:
+ rtl_uString_newFromAscii (strError, "WSAEALREADY, Operation was interrupted");
+ break;
+
+ case WSAEBADF:
+ rtl_uString_newFromAscii (strError, "WSAEBADF, Bad file number");
+ break;
+
+ case WSAEACCES:
+ rtl_uString_newFromAscii (strError, "WSAEACCES, Access is denied");
+ break;
+
+ case WSAEFAULT:
+ rtl_uString_newFromAscii (strError, "WSAEFAULT, Bad memory Addr");
+ break;
+
+ case WSAEINVAL:
+ rtl_uString_newFromAscii (strError, "WSAEINVAL, The socket has not been bound with bind() or is already connected");
+ break;
+
+ case WSAEMFILE:
+ rtl_uString_newFromAscii (strError, "WSAEMFILE, No more file descriptors are available");
+ break;
+
+ case WSAETOOMANYREFS:
+ rtl_uString_newFromAscii (strError, "WSAETOOMANYREFS, Undocumented WinSock error");
+ break;
+
+ case WSAENAMETOOLONG:
+ rtl_uString_newFromAscii (strError, "WSAENAMETOOLONG, Undocumented WinSock error");
+ break;
+
+ case WSAENOTEMPTY:
+ rtl_uString_newFromAscii (strError, "WSAENOTEMPTY, Undocumented WinSock error");
+ break;
+
+ case WSAEPROCLIM:
+ rtl_uString_newFromAscii (strError, "WSAEPROCLIM, Undocumented WinSock error");
+ break;
+
+ case WSAEUSERS:
+ rtl_uString_newFromAscii (strError, "WSAEUSERS, Undocumented WinSock error");
+ break;
+
+ case WSAEDQUOT:
+ rtl_uString_newFromAscii (strError, "WSAEDQUOT, Undocumented WinSock error");
+ break;
+
+ case WSAESTALE:
+ rtl_uString_newFromAscii (strError, "WSAESTALE, Undocumented WinSock error");
+ break;
+
+ case WSAEREMOTE:
+ rtl_uString_newFromAscii (strError, "WSAEREMOTE, Undocumented WinSock error");
+ break;
+
+ case WSAEDISCON:
+ rtl_uString_newFromAscii (strError, "WSAEDISCON, Circuit was gracefully terminated");
+ break;
+
+ case WSASYSNOTREADY:
+ rtl_uString_newFromAscii (strError, "WSASYSNOTREADY, The underlying network subsystem is not ready for network communication");
+ break;
+
+ case WSAVERNOTSUPPORTED:
+ rtl_uString_newFromAscii (strError, "WSAVERNOTSUPPORTED, The version of Windows Sockets API support requested is not provided by this particular Windows Sockets implementation");
+ break;
+
+ case WSANOTINITIALISED:
+ rtl_uString_newFromAscii (strError, "WSANOTINITIALISED, WSAStartup() has not been called");
+ break;
+
+ case WSAHOST_NOT_FOUND:
+ rtl_uString_newFromAscii (strError, "WSAHOST_NOT_FOUND, Authoritative answer host not found");
+ break;
+
+ case WSATRY_AGAIN:
+ rtl_uString_newFromAscii (strError, "WSATRY_AGAIN, Non-authoritative answer host not found or SERVERFAIL");
+ break;
+
+ case WSANO_RECOVERY:
+ rtl_uString_newFromAscii (strError, "WSANO_RECOVERY, Non recoverable errors, FORMERR, REFUSED, NOTIMP");
+ break;
+
+ case WSANO_DATA:
+ rtl_uString_newFromAscii (strError, "WSANO_DATA or WSANO_ADDRESS, Valid name, no data record of requested type");
+ break;
+
+ default:
+ {
+ sal_Unicode message[128];
+
+ wsprintfW(o3tl::toW(message), L"Unknown WinSock Error Number %d", error);
+ rtl_uString_newFromStr (strError, message);
+ }
+
+ return;
+
+ }
+}
+
+oslSocketError SAL_CALL osl_getLastSocketError(oslSocket /*Socket*/)
+{
+ return ERROR_FROM_NATIVE(WSAGetLastError());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/sockimpl.hxx b/sal/osl/w32/sockimpl.hxx
new file mode 100644
index 000000000..72b204a2e
--- /dev/null
+++ b/sal/osl/w32/sockimpl.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <osl/socket.h>
+#include <osl/interlck.h>
+
+#define OSL_SOCKET_FLAGS_NONBLOCKING 0x0001
+
+struct oslSocketImpl {
+ oslInterlockedCount m_nRefCount;
+ SOCKET m_Socket;
+ int m_Flags;
+};
+
+struct oslSocketAddrImpl
+{
+ struct sockaddr m_sockaddr;
+ oslInterlockedCount m_nRefCount;
+};
+
+oslSocket osl_createSocketImpl_(SOCKET Socket);
+void osl_destroySocketImpl_(oslSocket pImpl);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/system.h b/sal/osl/w32/system.h
new file mode 100644
index 000000000..921d746fd
--- /dev/null
+++ b/sal/osl/w32/system.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#if OSL_DEBUG_LEVEL <= 3
+#define NO_DEBUG_CRT
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <malloc.h>
+#include <limits.h>
+#include <process.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <share.h>
+#include <direct.h>
+
+/* Must define this else build breaks because Winsock2.h
+ includes Windows.h and without WIN32_LEAN_AND_MEAN
+ also includes mswsock.h which needs a forward typedef
+ of SOCKET ...
+*/
+#define WIN32_LEAN_AND_MEAN
+
+// winsock2.h includes windows.h
+#include <winsock2.h>
+#include <wsipx.h>
+#include <ws2tcpip.h>
+#include <shlobj.h>
+#ifndef NO_DEBUG_CRT
+#include <crtdbg.h>
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/tempfile.cxx b/sal/osl/w32/tempfile.cxx
new file mode 100644
index 000000000..f0065bf2d
--- /dev/null
+++ b/sal/osl/w32/tempfile.cxx
@@ -0,0 +1,238 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <systools/win32/uwinapi.h>
+
+#include <osl/file.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <rtl/ustring.hxx>
+
+#include "file-impl.hxx"
+#include "file_error.hxx"
+#include "file_url.hxx"
+#include "path_helper.hxx"
+
+#include <malloc.h>
+#include <cassert>
+
+// Allocate n number of t's on the stack return a pointer to it in p
+#define STACK_ALLOC(p, t, n) __try {(p) = static_cast<t*>(_alloca((n)*sizeof(t)));} \
+ __except(EXCEPTION_EXECUTE_HANDLER) {(p) = nullptr;}
+
+// Temp file functions
+
+static oslFileError osl_setup_base_directory_impl_(
+ rtl_uString* pustrDirectoryURL,
+ rtl_uString** ppustr_base_dir)
+{
+ OUString dir_url;
+ OUString dir;
+ oslFileError error = osl_File_E_None;
+
+ if (pustrDirectoryURL)
+ dir_url = pustrDirectoryURL;
+ else
+ error = osl_getTempDirURL(&dir_url.pData);
+
+ if (error == osl_File_E_None)
+ error = osl_getSystemPathFromFileURL_(dir_url, &dir.pData, false);
+
+ if (error == osl_File_E_None)
+ rtl_uString_assign(ppustr_base_dir, dir.pData);
+
+ return error;
+}
+
+static oslFileError osl_setup_createTempFile_impl_(
+ rtl_uString* pustrDirectoryURL,
+ oslFileHandle* pHandle,
+ rtl_uString** ppustrTempFileURL,
+ rtl_uString** ppustr_base_dir,
+ sal_Bool* b_delete_on_close)
+{
+ oslFileError osl_error;
+
+ OSL_PRECOND(((pHandle != nullptr) || (ppustrTempFileURL != nullptr)), "Invalid parameter!");
+
+ if ((pHandle == nullptr) && (ppustrTempFileURL == nullptr))
+ {
+ osl_error = osl_File_E_INVAL;
+ }
+ else
+ {
+ osl_error = osl_setup_base_directory_impl_(
+ pustrDirectoryURL, ppustr_base_dir);
+
+ *b_delete_on_close = (ppustrTempFileURL == nullptr);
+ }
+
+ return osl_error;
+}
+
+static oslFileError osl_win32_GetTempFileName_impl_(
+ rtl_uString* base_directory, LPWSTR temp_file_name)
+{
+ oslFileError osl_error = osl_File_E_None;
+
+ if (GetTempFileNameW(
+ o3tl::toW(rtl_uString_getStr(base_directory)),
+ L"",
+ 0,
+ temp_file_name) == 0)
+ {
+ osl_error = oslTranslateFileError(GetLastError());
+ }
+
+ return osl_error;
+}
+
+static bool osl_win32_CreateFile_impl_(
+ LPCWSTR file_name, bool b_delete_on_close, oslFileHandle* p_handle)
+{
+ DWORD flags = FILE_ATTRIBUTE_NORMAL;
+ HANDLE hFile;
+
+ assert(p_handle);
+
+ if (b_delete_on_close)
+ flags |= FILE_FLAG_DELETE_ON_CLOSE;
+
+ hFile = CreateFileW(
+ file_name,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ nullptr,
+ TRUNCATE_EXISTING,
+ flags,
+ nullptr);
+
+ // @@@ ERROR HANDLING @@@
+ if (IsValidHandle(hFile))
+ *p_handle = osl_createFileHandleFromOSHandle(hFile, osl_File_OpenFlag_Read | osl_File_OpenFlag_Write);
+
+ return IsValidHandle(hFile);
+}
+
+static oslFileError osl_createTempFile_impl_(
+ rtl_uString* base_directory,
+ LPWSTR tmp_name,
+ bool b_delete_on_close,
+ oslFileHandle* pHandle,
+ rtl_uString** ppustrTempFileURL)
+{
+ oslFileError osl_error;
+
+ do
+ {
+ osl_error = osl_win32_GetTempFileName_impl_(base_directory, tmp_name);
+
+ /* if file could not be opened try again */
+
+ if ((osl_File_E_None != osl_error) || (nullptr == pHandle) ||
+ osl_win32_CreateFile_impl_(tmp_name, b_delete_on_close, pHandle))
+ break;
+
+ } while(true); // try until success
+
+ if ((osl_error == osl_File_E_None) && !b_delete_on_close)
+ {
+ rtl_uString* pustr = nullptr;
+ rtl_uString_newFromStr(&pustr, o3tl::toU(tmp_name));
+ osl_getFileURLFromSystemPath(pustr, ppustrTempFileURL);
+ rtl_uString_release(pustr);
+ }
+
+ return osl_error;
+}
+
+oslFileError SAL_CALL osl_createTempFile(
+ rtl_uString* pustrDirectoryURL,
+ oslFileHandle* pHandle,
+ rtl_uString** ppustrTempFileURL)
+{
+ rtl_uString* base_directory = nullptr;
+ LPWSTR tmp_name;
+ sal_Bool b_delete_on_close;
+ oslFileError osl_error;
+
+ osl_error = osl_setup_createTempFile_impl_(
+ pustrDirectoryURL,
+ pHandle,
+ ppustrTempFileURL,
+ &base_directory,
+ &b_delete_on_close);
+
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+
+ /* allocate enough space on the stack, the file name can not be longer than MAX_PATH */
+ STACK_ALLOC(tmp_name, WCHAR, (rtl_uString_getLength(base_directory) + MAX_PATH));
+
+ if (tmp_name)
+ {
+ osl_error = osl_createTempFile_impl_(
+ base_directory,
+ tmp_name,
+ b_delete_on_close,
+ pHandle,
+ ppustrTempFileURL);
+ }
+ else // stack alloc failed
+ {
+ osl_error = osl_File_E_NOMEM;
+ }
+
+ if (base_directory)
+ rtl_uString_release(base_directory);
+
+ return osl_error;
+}
+
+oslFileError SAL_CALL osl_getTempDirURL(rtl_uString** pustrTempDir)
+{
+ ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
+ LPWSTR lpBuffer = o3tl::toW(aBuffer);
+ DWORD nBufferLength = aBuffer.getBufSizeInSymbols() - 1;
+
+ DWORD nLength;
+ oslFileError error;
+
+ nLength = GetTempPathW( aBuffer.getBufSizeInSymbols(), lpBuffer );
+
+ if ( nLength > nBufferLength )
+ {
+ // the provided path has invalid length
+ error = osl_File_E_NOENT;
+ }
+ else if ( nLength )
+ {
+ if ( '\\' == lpBuffer[nLength-1] )
+ --nLength;
+
+ const OUString ustrTempPath(o3tl::toU(lpBuffer), static_cast<sal_Int32>(nLength));
+
+ error = osl_getFileURLFromSystemPath(ustrTempPath.pData, pustrTempDir);
+ }
+ else
+ error = oslTranslateFileError( GetLastError() );
+
+ return error;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/thread.cxx b/sal/osl/w32/thread.cxx
new file mode 100644
index 000000000..fe5ac30b0
--- /dev/null
+++ b/sal/osl/w32/thread.cxx
@@ -0,0 +1,540 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.h"
+#include "thread.hxx"
+
+#include <comphelper/windowserrorstring.hxx>
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+#include <osl/thread.h>
+#include <rtl/alloc.h>
+#include <osl/time.h>
+#include <osl/interlck.h>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <systools/win32/comtools.hxx>
+
+#include <errno.h>
+
+namespace {
+
+/**
+ Thread-data structure hidden behind oslThread:
+ */
+typedef struct
+{
+ HANDLE m_hThread; /* OS-handle used for all thread-functions */
+ DWORD m_ThreadId; /* identifier for this thread */
+ sal_Int32 m_nTerminationRequested;
+ oslWorkerFunction m_WorkerFunction;
+ void* m_pData;
+
+} osl_TThreadImpl;
+
+}
+
+static oslThread oslCreateThread(oslWorkerFunction pWorker, void* pThreadData, sal_uInt32 nFlags);
+
+static DWORD WINAPI oslWorkerWrapperFunction(_In_ LPVOID pData)
+{
+ osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(pData);
+
+ /* Initialize COM - Multi Threaded Apartment (MTA) for all threads */
+ sal::systools::CoInitializeGuard aGuard(COINIT_MULTITHREADED, false,
+ sal::systools::CoInitializeGuard::WhenFailed::NoThrow);
+
+ /* call worker-function with data */
+
+ pThreadImpl->m_WorkerFunction(pThreadImpl->m_pData);
+
+ return 0;
+}
+
+static oslThread oslCreateThread(oslWorkerFunction pWorker,
+ void* pThreadData,
+ sal_uInt32 nFlags)
+{
+ osl_TThreadImpl* pThreadImpl;
+
+ /* alloc mem. for our internal data structure */
+ pThreadImpl= static_cast<osl_TThreadImpl *>(malloc(sizeof(osl_TThreadImpl)));
+
+ OSL_ASSERT(pThreadImpl);
+
+ if ( pThreadImpl == nullptr )
+ {
+ return nullptr;
+ }
+
+ pThreadImpl->m_WorkerFunction= pWorker;
+ pThreadImpl->m_pData= pThreadData;
+ pThreadImpl->m_nTerminationRequested= 0;
+
+ pThreadImpl->m_hThread= CreateThread(
+ nullptr, /* no security */
+ 0, /* default stack-size */
+ oslWorkerWrapperFunction, /* worker-function */
+ pThreadImpl, /* provide worker-function with data */
+ nFlags, /* start thread immediately or suspended */
+ &pThreadImpl->m_ThreadId);
+
+ if(pThreadImpl->m_hThread == nullptr)
+ {
+ SAL_WARN("sal.osl", "CreateThread failed:" << WindowsErrorString(GetLastError()));
+
+ /* create failed */
+ free(pThreadImpl);
+ return nullptr;
+ }
+
+ return pThreadImpl;
+}
+
+oslThread SAL_CALL osl_createThread(oslWorkerFunction pWorker,
+ void* pThreadData)
+{
+ return oslCreateThread(pWorker, pThreadData, 0);
+}
+
+oslThread SAL_CALL osl_createSuspendedThread(oslWorkerFunction pWorker,
+ void* pThreadData)
+{
+ return oslCreateThread(pWorker, pThreadData, CREATE_SUSPENDED);
+}
+
+oslThreadIdentifier SAL_CALL osl_getThreadIdentifier(oslThread Thread)
+{
+ osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread);
+
+ if (pThreadImpl != nullptr)
+ return static_cast<oslThreadIdentifier>(pThreadImpl->m_ThreadId);
+ else
+ return static_cast<oslThreadIdentifier>(GetCurrentThreadId());
+}
+
+void SAL_CALL osl_destroyThread(oslThread Thread)
+{
+ osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread);
+
+ if (Thread == nullptr) /* valid ptr? */
+ {
+ /* thread already destroyed or not created */
+ return;
+ }
+
+ /* !!!! _exitthreadex does _not_ call CloseHandle !!! */
+ CloseHandle( pThreadImpl->m_hThread );
+
+ /* free memory */
+ free(Thread);
+}
+
+void SAL_CALL osl_resumeThread(oslThread Thread)
+{
+ osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread);
+
+ OSL_ASSERT(pThreadImpl); /* valid ptr? */
+
+ ResumeThread(pThreadImpl->m_hThread);
+}
+
+void SAL_CALL osl_suspendThread(oslThread Thread)
+{
+ osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread);
+
+ OSL_ASSERT(pThreadImpl); /* valid ptr? */
+
+ SuspendThread(pThreadImpl->m_hThread);
+}
+
+void SAL_CALL osl_setThreadPriority(oslThread Thread,
+ oslThreadPriority Priority)
+{
+ int winPriority;
+ osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread);
+
+ OSL_ASSERT(pThreadImpl); /* valid ptr? */
+
+ /* map enum to WIN32 levels
+ it would be faster and more elegant to preset
+ the enums, but that would require an #ifdef in
+ the exported header, which is not desired.
+ */
+ switch(Priority) {
+
+ case osl_Thread_PriorityHighest:
+ winPriority= THREAD_PRIORITY_HIGHEST;
+ break;
+
+ case osl_Thread_PriorityAboveNormal:
+ winPriority= THREAD_PRIORITY_ABOVE_NORMAL;
+ break;
+
+ case osl_Thread_PriorityNormal:
+ winPriority= THREAD_PRIORITY_NORMAL;
+ break;
+
+ case osl_Thread_PriorityBelowNormal:
+ winPriority= THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+
+ case osl_Thread_PriorityLowest:
+ winPriority= THREAD_PRIORITY_LOWEST;
+ break;
+
+ case osl_Thread_PriorityUnknown:
+ OSL_ASSERT(FALSE); /* only fools try this...*/
+
+ /* let release-version behave friendly */
+ return;
+
+ default:
+ OSL_ASSERT(FALSE); /* enum expanded, but forgotten here...*/
+
+ /* let release-version behave friendly */
+ return;
+ }
+
+ SetThreadPriority(pThreadImpl->m_hThread, winPriority);
+}
+
+oslThreadPriority SAL_CALL osl_getThreadPriority(const oslThread Thread)
+{
+ int winPriority;
+ oslThreadPriority Priority;
+
+ osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread);
+
+ /* invalid arguments ?*/
+ if(pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr)
+ {
+ return osl_Thread_PriorityUnknown;
+ }
+
+ winPriority=
+ GetThreadPriority(pThreadImpl->m_hThread);
+
+ if(winPriority == THREAD_PRIORITY_ERROR_RETURN)
+ {
+ return osl_Thread_PriorityUnknown;
+ }
+
+ /* map WIN32 priority to enum */
+ switch(winPriority)
+ {
+ case THREAD_PRIORITY_TIME_CRITICAL:
+ case THREAD_PRIORITY_HIGHEST:
+ Priority= osl_Thread_PriorityHighest;
+ break;
+
+ case THREAD_PRIORITY_ABOVE_NORMAL:
+ Priority= osl_Thread_PriorityAboveNormal;
+ break;
+
+ case THREAD_PRIORITY_NORMAL:
+ Priority= osl_Thread_PriorityNormal;
+ break;
+
+ case THREAD_PRIORITY_BELOW_NORMAL:
+ Priority= osl_Thread_PriorityBelowNormal;
+ break;
+
+ case THREAD_PRIORITY_IDLE:
+ case THREAD_PRIORITY_LOWEST:
+ Priority= osl_Thread_PriorityLowest;
+ break;
+
+ default:
+ OSL_ASSERT(FALSE); /* WIN32 API changed, incorporate new prio-level! */
+
+ /* release-version behaves friendly */
+ Priority= osl_Thread_PriorityUnknown;
+ }
+
+ return Priority;
+}
+
+sal_Bool SAL_CALL osl_isThreadRunning(const oslThread Thread)
+{
+ osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread);
+
+ /* invalid arguments ?*/
+ if(pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr)
+ {
+ return false;
+ }
+
+ return WaitForSingleObject(pThreadImpl->m_hThread, 0) != WAIT_OBJECT_0;
+}
+
+void SAL_CALL osl_joinWithThread(oslThread Thread)
+{
+ osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread);
+
+ /* invalid arguments?*/
+ if(pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr)
+ {
+ /* assume thread is not running */
+ return;
+ }
+
+ WaitForSingleObject(pThreadImpl->m_hThread, INFINITE);
+}
+
+void SAL_CALL osl_waitThread(const TimeValue* pDelay)
+{
+ if (pDelay)
+ {
+ DWORD millisecs = pDelay->Seconds * 1000L + pDelay->Nanosec / 1000000L;
+
+ Sleep(millisecs);
+ }
+}
+
+void SAL_CALL osl_terminateThread(oslThread Thread)
+{
+ osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread);
+
+ /* invalid arguments?*/
+ if (pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr)
+ {
+ /* assume thread is not running */
+ return;
+ }
+
+ osl_atomic_increment(&(pThreadImpl->m_nTerminationRequested));
+}
+
+sal_Bool SAL_CALL osl_scheduleThread(oslThread Thread)
+{
+ osl_TThreadImpl* pThreadImpl= static_cast<osl_TThreadImpl*>(Thread);
+
+ osl_yieldThread();
+
+ /* invalid arguments?*/
+ if (pThreadImpl==nullptr || pThreadImpl->m_hThread==nullptr)
+ {
+ /* assume thread is not running */
+ return false;
+ }
+
+ return 0 == pThreadImpl->m_nTerminationRequested;
+}
+
+void SAL_CALL osl_yieldThread(void)
+{
+ Sleep(0);
+}
+
+static void impSetThreadDescription(char const * name) {
+ // SetThreadDescription is only available since Windows 10 version 1607
+ typedef HRESULT(WINAPI * TSetThreadDescription)(HANDLE, PCWSTR);
+ static const auto pSetThreadDescription = reinterpret_cast<TSetThreadDescription>(
+ GetProcAddress(GetModuleHandleA("KernelBase.dll"), "SetThreadDescription"));
+ if (pSetThreadDescription)
+ {
+ if (const int nReqCCh = MultiByteToWideChar(CP_ACP, 0, name, -1, nullptr, 0))
+ {
+ if (PWSTR wStr = static_cast<PWSTR>(malloc(nReqCCh * sizeof(WCHAR))))
+ {
+ if (MultiByteToWideChar(CP_ACP, 0, name, -1, wStr, nReqCCh))
+ pSetThreadDescription(GetCurrentThread(), wStr);
+ free(wStr);
+ }
+ }
+ }
+}
+
+void SAL_CALL osl_setThreadName(char const * name) {
+ /* See < https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code >: */
+#pragma pack(push, 8)
+ struct {
+ DWORD dwType = 0x1000;
+ LPCSTR szName;
+ DWORD dwThreadID = DWORD(-1);
+ DWORD dwFlags = 0;
+ } info;
+#pragma pack(pop)
+ info.szName = name;
+ __try {
+ RaiseException(
+ 0x406D1388, 0, sizeof info / sizeof (ULONG_PTR),
+ reinterpret_cast<ULONG_PTR *>(&info));
+ } __except (EXCEPTION_EXECUTE_HANDLER) {}
+
+ impSetThreadDescription(name);
+}
+
+namespace {
+
+typedef struct TLS_
+{
+ DWORD dwIndex;
+ oslThreadKeyCallbackFunction pfnCallback;
+ struct TLS_ *pNext, *pPrev;
+} TLS, *PTLS;
+
+PTLS g_pThreadKeyList = nullptr;
+osl::Mutex& getThreadKeyListMutex()
+{
+ static osl::Mutex g_ThreadKeyListMutex;
+ return g_ThreadKeyListMutex;
+}
+
+}
+
+static void AddKeyToList( PTLS pTls )
+{
+ if ( pTls )
+ {
+ osl::MutexGuard aGuard(getThreadKeyListMutex());
+
+ pTls->pNext = g_pThreadKeyList;
+ pTls->pPrev = nullptr;
+
+ if ( g_pThreadKeyList )
+ g_pThreadKeyList->pPrev = pTls;
+
+ g_pThreadKeyList = pTls;
+ }
+}
+
+static void RemoveKeyFromList( PTLS pTls )
+{
+ if ( pTls )
+ {
+ osl::MutexGuard aGuard(getThreadKeyListMutex());
+ if ( pTls->pPrev )
+ pTls->pPrev->pNext = pTls->pNext;
+ else
+ {
+ OSL_ASSERT( pTls == g_pThreadKeyList );
+ g_pThreadKeyList = pTls->pNext;
+ }
+
+ if ( pTls->pNext )
+ pTls->pNext->pPrev = pTls->pPrev;
+ }
+}
+
+void osl_callThreadKeyCallbackOnThreadDetach(void)
+{
+ PTLS pTls;
+
+ osl::MutexGuard aGuard(getThreadKeyListMutex());
+ pTls = g_pThreadKeyList;
+ while ( pTls )
+ {
+ if ( pTls->pfnCallback )
+ {
+ void *pValue = TlsGetValue( pTls->dwIndex );
+
+ if ( pValue )
+ pTls->pfnCallback( pValue );
+ }
+
+ pTls = pTls->pNext;
+ }
+}
+
+oslThreadKey SAL_CALL osl_createThreadKey(oslThreadKeyCallbackFunction pCallback)
+{
+ PTLS pTls = static_cast<PTLS>(malloc( sizeof(TLS) ));
+
+ if ( pTls )
+ {
+ pTls->pfnCallback = pCallback;
+ if ( DWORD(-1) == (pTls->dwIndex = TlsAlloc()) )
+ {
+ free( pTls );
+ pTls = nullptr;
+ }
+ else
+ AddKeyToList( pTls );
+ }
+
+ return pTls;
+}
+
+void SAL_CALL osl_destroyThreadKey(oslThreadKey Key)
+{
+ if (Key != nullptr)
+ {
+ PTLS pTls = static_cast<PTLS>(Key);
+
+ RemoveKeyFromList( pTls );
+ TlsFree( pTls->dwIndex );
+ free( pTls );
+ }
+}
+
+void* SAL_CALL osl_getThreadKeyData(oslThreadKey Key)
+{
+ if (Key != nullptr)
+ {
+ PTLS pTls = static_cast<PTLS>(Key);
+
+ return TlsGetValue( pTls->dwIndex );
+ }
+
+ return nullptr;
+}
+
+sal_Bool SAL_CALL osl_setThreadKeyData(oslThreadKey Key, void *pData)
+{
+ if (Key != nullptr)
+ {
+ PTLS pTls = static_cast<PTLS>(Key);
+ void* pOldData = nullptr;
+ bool fSuccess;
+
+ if ( pTls->pfnCallback )
+ pOldData = TlsGetValue( pTls->dwIndex );
+
+ fSuccess = TlsSetValue( pTls->dwIndex, pData );
+
+ if ( fSuccess && pTls->pfnCallback && pOldData )
+ pTls->pfnCallback( pOldData );
+
+ return fSuccess;
+ }
+
+ return false;
+}
+
+namespace
+{
+rtl_TextEncoding& getThreadTextEncodingImpl()
+{
+ static thread_local rtl_TextEncoding s_enc = rtl_getTextEncodingFromWindowsCodePage(GetACP());
+ return s_enc;
+}
+}
+
+rtl_TextEncoding SAL_CALL osl_getThreadTextEncoding(void) { return getThreadTextEncodingImpl(); }
+
+rtl_TextEncoding SAL_CALL osl_setThreadTextEncoding( rtl_TextEncoding Encoding )
+{
+ rtl_TextEncoding oldEncoding = getThreadTextEncodingImpl();
+ getThreadTextEncodingImpl() = Encoding;
+ return oldEncoding;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/thread.hxx b/sal/osl/w32/thread.hxx
new file mode 100644
index 000000000..8002d8b7f
--- /dev/null
+++ b/sal/osl/w32/thread.hxx
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <sal/types.h>
+
+void osl_callThreadKeyCallbackOnThreadDetach(void);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sal/osl/w32/time.cxx b/sal/osl/w32/time.cxx
new file mode 100644
index 000000000..3f11746fe
--- /dev/null
+++ b/sal/osl/w32/time.cxx
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include "system.h"
+
+#include "filetime.hxx"
+#include "time.hxx"
+
+#include <osl/diagnose.h>
+#include <osl/time.h>
+#include <sys/timeb.h>
+
+sal_Bool SAL_CALL osl_getSystemTime(TimeValue* pTimeVal)
+{
+ unsigned __int64 CurTime;
+
+ typedef VOID (WINAPI *GetSystemTimePreciseAsFileTime_PROC)(LPFILETIME);
+
+ OSL_ASSERT(pTimeVal != nullptr);
+
+ static GetSystemTimePreciseAsFileTime_PROC pGetSystemTimePreciseAsFileTime = []()
+ {
+ HMODULE hModule = GetModuleHandleW( L"Kernel32.dll" );
+ return reinterpret_cast<GetSystemTimePreciseAsFileTime_PROC>(
+ GetProcAddress(hModule, "GetSystemTimePreciseAsFileTime"));
+ }();
+
+ // use ~1 microsecond resolution if available
+ if (pGetSystemTimePreciseAsFileTime)
+ pGetSystemTimePreciseAsFileTime(reinterpret_cast<LPFILETIME>(&CurTime));
+ else
+ {
+ SYSTEMTIME SystemTime;
+ GetSystemTime(&SystemTime);
+ SystemTimeToFileTime(&SystemTime, reinterpret_cast<LPFILETIME>(&CurTime));
+ }
+
+ static const unsigned __int64 OffTime = [] {
+ SYSTEMTIME SystemTime;
+ SystemTime.wYear = 1970;
+ SystemTime.wMonth = 1;
+ SystemTime.wDayOfWeek = 0;
+ SystemTime.wDay = 1;
+ SystemTime.wHour = 0;
+ SystemTime.wMinute = 0;
+ SystemTime.wSecond = 0;
+ SystemTime.wMilliseconds = 0;
+
+ unsigned __int64 ft;
+ SystemTimeToFileTime(&SystemTime, reinterpret_cast<LPFILETIME>(&ft));
+ return ft;
+ }();
+
+ const unsigned __int64 Value = CurTime - OffTime;
+
+ pTimeVal->Seconds = static_cast<unsigned long>(Value / 10000000L);
+ pTimeVal->Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100);
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_getDateTimeFromTimeValue( const TimeValue* pTimeVal, oslDateTime* pDateTime )
+{
+ FILETIME aFileTime;
+ SYSTEMTIME aSystemTime;
+
+ if ( TimeValueToFileTime(pTimeVal, &aFileTime) )
+ {
+ if ( FileTimeToSystemTime( &aFileTime, &aSystemTime ) )
+ {
+ pDateTime->NanoSeconds = pTimeVal->Nanosec;
+
+ pDateTime->Seconds = aSystemTime.wSecond;
+ pDateTime->Minutes = aSystemTime.wMinute;
+ pDateTime->Hours = aSystemTime.wHour;
+ pDateTime->Day = aSystemTime.wDay;
+ pDateTime->DayOfWeek = aSystemTime.wDayOfWeek;
+ pDateTime->Month = aSystemTime.wMonth;
+ pDateTime->Year = aSystemTime.wYear;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL osl_getTimeValueFromDateTime( const oslDateTime* pDateTime, TimeValue* pTimeVal )
+{
+ FILETIME aFileTime;
+ SYSTEMTIME aSystemTime;
+
+ aSystemTime.wMilliseconds = 0;
+ aSystemTime.wSecond = pDateTime->Seconds;
+ aSystemTime.wMinute = pDateTime->Minutes;
+ aSystemTime.wHour = pDateTime->Hours;
+ aSystemTime.wDay = pDateTime->Day;
+ aSystemTime.wDayOfWeek = pDateTime->DayOfWeek;
+ aSystemTime.wMonth = pDateTime->Month;
+ aSystemTime.wYear = pDateTime->Year;
+
+ if ( SystemTimeToFileTime( &aSystemTime, &aFileTime ) )
+ {
+ if (FileTimeToTimeValue( &aFileTime, pTimeVal ) )
+ {
+ pTimeVal->Nanosec = pDateTime->NanoSeconds;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL osl_getLocalTimeFromSystemTime( const TimeValue* pSystemTimeVal, TimeValue* pLocalTimeVal )
+{
+ TIME_ZONE_INFORMATION aTimeZoneInformation;
+
+ // get timezone information
+ DWORD Success = GetTimeZoneInformation( &aTimeZoneInformation );
+ if (Success == TIME_ZONE_ID_INVALID)
+ return false;
+
+ sal_Int64 bias = aTimeZoneInformation.Bias;
+
+ // add bias for daylight saving time
+ if ( Success == TIME_ZONE_ID_DAYLIGHT )
+ bias+=aTimeZoneInformation.DaylightBias;
+
+ if ( static_cast<sal_Int64>(pSystemTimeVal->Seconds) > ( bias * 60 ) )
+ {
+ pLocalTimeVal->Seconds = static_cast<sal_uInt32>(pSystemTimeVal->Seconds - ( bias * 60) );
+ pLocalTimeVal->Nanosec = pSystemTimeVal->Nanosec;
+
+ return true;
+ }
+ return false;
+}
+
+sal_Bool SAL_CALL osl_getSystemTimeFromLocalTime( const TimeValue* pLocalTimeVal, TimeValue* pSystemTimeVal )
+{
+ TIME_ZONE_INFORMATION aTimeZoneInformation;
+
+ // get timezone information
+ DWORD Success = GetTimeZoneInformation( &aTimeZoneInformation );
+ if ( Success == TIME_ZONE_ID_INVALID )
+ return false;
+
+ sal_Int64 bias = aTimeZoneInformation.Bias;
+
+ // add bias for daylight saving time
+ if ( Success == TIME_ZONE_ID_DAYLIGHT )
+ bias+=aTimeZoneInformation.DaylightBias;
+
+ if ( static_cast<sal_Int64>(pLocalTimeVal->Seconds) + ( bias * 60 ) > 0 )
+ {
+ pSystemTimeVal->Seconds = static_cast<sal_uInt32>( pLocalTimeVal->Seconds + ( bias * 60) );
+ pSystemTimeVal->Nanosec = pLocalTimeVal->Nanosec;
+
+ return true;
+ }
+
+ return false;
+}
+
+static struct _timeb startTime;
+void sal_initGlobalTimer()
+{
+ _ftime( &startTime );
+}
+
+sal_uInt32 SAL_CALL osl_getGlobalTimer(void)
+{
+ struct _timeb currentTime;
+ sal_uInt32 nSeconds;
+
+ _ftime( &currentTime );
+
+ nSeconds = static_cast<sal_uInt32>( currentTime.time - startTime.time );
+
+ return ( nSeconds * 1000 ) + static_cast<long>( currentTime.millitm - startTime.millitm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/w32/time.hxx b/sal/osl/w32/time.hxx
new file mode 100644
index 000000000..c0ca9d777
--- /dev/null
+++ b/sal/osl/w32/time.hxx
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SAL_OSL_W32_TIME_HXX
+#define INCLUDED_SAL_OSL_W32_TIME_HXX
+
+#include <sal/config.h>
+
+void sal_initGlobalTimer(void);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */