summaryrefslogtreecommitdiffstats
path: root/sal/osl/w32
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sal/osl/w32
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-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
37 files changed, 12997 insertions, 0 deletions
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: */