summaryrefslogtreecommitdiffstats
path: root/sal/osl/unx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /sal/osl/unx
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--sal/osl/unx/asm/interlck_sparc.s79
-rw-r--r--sal/osl/unx/backtrace.c303
-rw-r--r--sal/osl/unx/backtrace.h58
-rw-r--r--sal/osl/unx/backtraceapi.cxx72
-rw-r--r--sal/osl/unx/conditn.cxx274
-rw-r--r--sal/osl/unx/createfilehandlefromfd.hxx25
-rw-r--r--sal/osl/unx/file.cxx1621
-rw-r--r--sal/osl/unx/file_error_transl.cxx180
-rw-r--r--sal/osl/unx/file_error_transl.hxx37
-rw-r--r--sal/osl/unx/file_impl.hxx58
-rw-r--r--sal/osl/unx/file_misc.cxx1062
-rw-r--r--sal/osl/unx/file_path_helper.cxx261
-rw-r--r--sal/osl/unx/file_path_helper.hxx251
-rw-r--r--sal/osl/unx/file_stat.cxx453
-rw-r--r--sal/osl/unx/file_url.cxx986
-rw-r--r--sal/osl/unx/file_url.hxx50
-rw-r--r--sal/osl/unx/file_volume.cxx336
-rw-r--r--sal/osl/unx/interlck.cxx95
-rw-r--r--sal/osl/unx/memory.cxx38
-rw-r--r--sal/osl/unx/module.cxx347
-rw-r--r--sal/osl/unx/mutex.cxx189
-rw-r--r--sal/osl/unx/nlsupport.cxx872
-rw-r--r--sal/osl/unx/nlsupport.hxx37
-rw-r--r--sal/osl/unx/osxlocale.cxx104
-rw-r--r--sal/osl/unx/pipe.cxx523
-rw-r--r--sal/osl/unx/process.cxx1194
-rw-r--r--sal/osl/unx/process_impl.cxx473
-rw-r--r--sal/osl/unx/profile.cxx1866
-rw-r--r--sal/osl/unx/random.cxx46
-rw-r--r--sal/osl/unx/readwrite_helper.cxx79
-rw-r--r--sal/osl/unx/readwrite_helper.hxx26
-rw-r--r--sal/osl/unx/salinit.cxx100
-rw-r--r--sal/osl/unx/saltime.hxx29
-rw-r--r--sal/osl/unx/secimpl.hxx34
-rw-r--r--sal/osl/unx/security.cxx535
-rw-r--r--sal/osl/unx/signal.cxx478
-rw-r--r--sal/osl/unx/socket.cxx2240
-rw-r--r--sal/osl/unx/sockimpl.hxx61
-rw-r--r--sal/osl/unx/soffice.cxx25
-rw-r--r--sal/osl/unx/soffice.hxx26
-rw-r--r--sal/osl/unx/system.cxx167
-rw-r--r--sal/osl/unx/system.hxx384
-rw-r--r--sal/osl/unx/system.mm1
-rw-r--r--sal/osl/unx/tempfile.cxx337
-rw-r--r--sal/osl/unx/thread.cxx1100
-rw-r--r--sal/osl/unx/time.cxx310
-rw-r--r--sal/osl/unx/unixerrnostring.hxx27
-rw-r--r--sal/osl/unx/uunxapi.cxx896
-rw-r--r--sal/osl/unx/uunxapi.hxx79
-rw-r--r--sal/osl/unx/uunxapi.mm1
50 files changed, 18825 insertions, 0 deletions
diff --git a/sal/osl/unx/asm/interlck_sparc.s b/sal/osl/unx/asm/interlck_sparc.s
new file mode 100644
index 000000000..7b971a50a
--- /dev/null
+++ b/sal/osl/unx/asm/interlck_sparc.s
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+/*
+ * Implements osl_[increment|decrement]InterlockedCount:
+ * sparcv9/sparcv8plus architecture: use the "cas" instruction
+ *
+ * 32 bit mode with v8plus support or 64 bit mode:
+ * sparcv9 mode is implied. Assemble with -xarch=v8plus (32 bit) or
+ * -xarch=v9 (64 bit).
+ *
+ */
+
+#if !defined(__sparcv8plus) && !defined(__sparcv9) && !defined(__sparc_v9__)
+
+#error LibreOffice requires SPARCv8plus or SPARCv9 CPU with "cas" instruction
+
+#endif
+
+.section ".text"
+ .global osl_incrementInterlockedCount
+ .align 8
+
+! Implements osl_[increment|decrement]InterlockedCount with sparcv9(sparcv8plus) "cas"
+! instruction.
+
+osl_incrementInterlockedCount:
+
+1: ld [%o0], %o1
+ add %o1, 1, %o2
+! allow linux to build for v8
+ .word 0xD5E21009
+! cas [%o0], %o1, %o2
+ cmp %o1, %o2
+ bne 1b
+ nop ! delay slot
+ retl
+ add %o2, 1, %o0 ! delay slot
+
+ .type osl_incrementInterlockedCount,#function
+ .size osl_incrementInterlockedCount,.-osl_incrementInterlockedCount
+
+
+.section ".text"
+ .global osl_decrementInterlockedCount
+ .align 8
+
+osl_decrementInterlockedCount:
+
+1: ld [%o0], %o1
+ sub %o1, 1, %o2
+! allow linux to build for v8
+ .word 0xD5E21009
+! cas [%o0], %o1, %o2
+ cmp %o1, %o2
+ bne 1b
+ nop ! delay slot
+ retl
+ sub %o2, 1, %o0 ! delay slot
+
+ .type osl_decrementInterlockedCount,#function
+ .size osl_decrementInterlockedCount,.-osl_decrementInterlockedCount
+
diff --git a/sal/osl/unx/backtrace.c b/sal/osl/unx/backtrace.c
new file mode 100644
index 000000000..a3aeb3dae
--- /dev/null
+++ b/sal/osl/unx/backtrace.c
@@ -0,0 +1,303 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/types.h>
+
+#ifdef __sun
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <sys/frame.h>
+#include "backtrace.h"
+
+#if defined(SPARC)
+
+#if defined IS_LP64
+
+#define FRAME_PTR_OFFSET 1
+#define FRAME_OFFSET 0
+#define STACK_BIAS 0x7ff
+
+#else
+
+#define FRAME_PTR_OFFSET 1
+#define FRAME_OFFSET 0
+#define STACK_BIAS 0
+
+#endif
+
+#elif defined( INTEL )
+
+#define FRAME_PTR_OFFSET 3
+#define FRAME_OFFSET 0
+#define STACK_BIAS 0
+
+#else
+
+#error Unknown Solaris target platform.
+
+#endif /* defined SPARC or INTEL */
+
+int backtrace( void **buffer, int max_frames )
+{
+ jmp_buf ctx;
+ long fpval;
+ struct frame *fp;
+ int i;
+
+ /* flush register windows */
+#ifdef SPARC
+ asm("ta 3");
+#endif
+
+ /* get stack- and framepointer */
+ setjmp(ctx);
+
+ fpval = ((long*)(ctx))[FRAME_PTR_OFFSET];
+ fp = (struct frame*)((char*)(fpval) + STACK_BIAS);
+
+ for (i = 0; (i < FRAME_OFFSET) && (fp != 0); i++)
+ fp = (struct frame*)((char*)(fp->fr_savfp) + STACK_BIAS);
+
+ /* iterate through backtrace */
+ for (i = 0; (fp != 0) && (fp->fr_savpc != 0) && (i < max_frames); i++)
+ {
+ /* saved (prev) frame */
+ struct frame * prev = (struct frame*)((char*)(fp->fr_savfp) + STACK_BIAS);
+
+ /* store frame */
+ *(buffer++) = (void*)(fp->fr_savpc);
+
+ /* prev frame (w/ stack growing top down) */
+ fp = (prev > fp) ? prev : 0;
+ }
+
+ /* return number of frames stored */
+ return i;
+}
+
+char ** backtrace_symbols(void * const * buffer, int size)
+{
+ (void)buffer; (void)size;
+ return NULL; /*TODO*/
+}
+
+void backtrace_symbols_fd( void **buffer, int size, int fd )
+{
+ FILE *fp = fdopen( fd, "w" );
+
+ if ( fp )
+ {
+ void **pFramePtr;
+
+ for ( pFramePtr = buffer; size > 0 && pFramePtr && *pFramePtr; pFramePtr++, size-- )
+ {
+ Dl_info dli;
+ ptrdiff_t offset;
+
+ if ( 0 != dladdr( *pFramePtr, &dli ) )
+ {
+ if ( dli.dli_fname && dli.dli_fbase )
+ {
+ offset = (ptrdiff_t)*pFramePtr - (ptrdiff_t)dli.dli_fbase;
+ fprintf( fp, "%s+0x%" SAL_PRI_PTRDIFFT "x", dli.dli_fname, offset );
+ }
+ if ( dli.dli_sname && dli.dli_saddr )
+ {
+ offset = (ptrdiff_t)*pFramePtr - (ptrdiff_t)dli.dli_saddr;
+ fprintf( fp, "(%s+0x%" SAL_PRI_PTRDIFFT "x)", dli.dli_sname, offset );
+ }
+ }
+ fprintf( fp, "[%p]\n", *pFramePtr );
+ }
+
+ fclose( fp );
+ }
+}
+
+#elif defined FREEBSD || defined NETBSD || defined OPENBSD || defined(DRAGONFLY)
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdio.h>
+#include "backtrace.h"
+
+#if defined(POWERPC) || defined(POWERPC64)
+
+#define FRAME_PTR_OFFSET 1
+#define FRAME_OFFSET 0
+
+#else
+
+#define FRAME_PTR_OFFSET 3
+#define FRAME_OFFSET 0
+
+#endif
+
+int backtrace( void **buffer, int max_frames )
+{
+ struct frame *fp;
+ jmp_buf ctx;
+ int i;
+ /* get stack- and framepointer */
+ setjmp(ctx);
+ fp = (struct frame*)(((size_t*)(ctx))[FRAME_PTR_OFFSET]);
+ for ( i=0; (i<FRAME_OFFSET) && (fp!=0); i++)
+ fp = fp->fr_savfp;
+ /* iterate through backtrace */
+ for (i=0; fp && fp->fr_savpc && i<max_frames; i++)
+ {
+ /* store frame */
+ *(buffer++) = (void *)fp->fr_savpc;
+ /* next frame */
+ fp=fp->fr_savfp;
+ }
+ return i;
+}
+
+char ** backtrace_symbols(void * const * buffer, int size)
+{
+ (void)buffer; (void)size;
+ return NULL; /*TODO*/
+}
+
+void backtrace_symbols_fd( void **buffer, int size, int fd )
+{
+ FILE *fp = fdopen( fd, "w" );
+
+ if ( fp )
+ {
+ void **pFramePtr;
+ for ( pFramePtr = buffer; size > 0 && pFramePtr && *pFramePtr; pFramePtr++, size-- )
+ {
+ Dl_info dli;
+ ptrdiff_t offset;
+
+ if ( 0 != dladdr( *pFramePtr, &dli ) )
+ {
+ if ( dli.dli_fname && dli.dli_fbase )
+ {
+ offset = (ptrdiff_t)*pFramePtr - (ptrdiff_t)dli.dli_fbase;
+ fprintf( fp, "%s+0x%" SAL_PRI_PTRDIFFT "x", dli.dli_fname, offset );
+ }
+ if ( dli.dli_sname && dli.dli_saddr )
+ {
+ offset = (ptrdiff_t)*pFramePtr - (ptrdiff_t)dli.dli_saddr;
+ fprintf( fp, "(%s+0x%" SAL_PRI_PTRDIFFT "x)", dli.dli_sname, offset );
+ }
+ }
+ fprintf( fp, "[%p]\n", *pFramePtr );
+ }
+ fclose( fp );
+ }
+}
+
+#elif defined( MACOSX )
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include "backtrace.h"
+
+/* glib backtrace is only available on MacOsX 10.5 or higher
+ so we do it on our own */
+
+int backtrace( void **buffer, int max_frames )
+{
+ void **frame = (void **)__builtin_frame_address(0);
+ void **bp = ( void **)(*frame);
+ void *ip = frame[1];
+ int i;
+
+ for ( i = 0; bp && ip && i < max_frames; i++ )
+ {
+ *(buffer++) = ip;
+
+ ip = bp[1];
+ bp = (void**)(bp[0]);
+ }
+
+ return i;
+}
+
+char ** backtrace_symbols(void * const * buffer, int size)
+{
+ (void)buffer; (void)size;
+ return NULL; /*TODO*/
+}
+
+void backtrace_symbols_fd( void **buffer, int size, int fd )
+{
+ FILE *fp = fdopen( fd, "w" );
+
+ if ( fp )
+ {
+ void **pFramePtr;
+
+ for ( pFramePtr = buffer; size > 0 && pFramePtr && *pFramePtr; pFramePtr++, size-- )
+ {
+ Dl_info dli;
+
+ if ( 0 != dladdr( *pFramePtr, &dli ) )
+ {
+ ptrdiff_t offset;
+
+ if ( dli.dli_fname && dli.dli_fbase )
+ {
+ offset = (ptrdiff_t)*pFramePtr - (ptrdiff_t)dli.dli_fbase;
+ fprintf( fp, "%s+0x%tx", dli.dli_fname, offset );
+ }
+ if ( dli.dli_sname && dli.dli_saddr )
+ {
+ offset = (ptrdiff_t)*pFramePtr - (ptrdiff_t)dli.dli_saddr;
+ fprintf( fp, "(%s+0x%tx)", dli.dli_sname, offset );
+ }
+ }
+ fprintf( fp, "[%p]\n", *pFramePtr );
+ }
+
+ fclose( fp );
+ }
+}
+
+#elif !defined LINUX
+
+int backtrace( void **buffer, int max_frames )
+{
+ (void)buffer; (void)max_frames;
+ return 0;
+}
+
+char ** backtrace_symbols(void * const * buffer, int size)
+{
+ (void)buffer; (void)size;
+ return NULL; /*TODO*/
+}
+
+void backtrace_symbols_fd( void **buffer, int size, int fd )
+{
+ (void)buffer; (void)size; (void)fd;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/backtrace.h b/sal/osl/unx/backtrace.h
new file mode 100644
index 000000000..5fafc1c90
--- /dev/null
+++ b/sal/osl/unx/backtrace.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_BACKTRACE_H
+#define INCLUDED_SAL_OSL_UNX_BACKTRACE_H
+
+#if defined (LINUX)
+
+#include <execinfo.h>
+
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* backtrace function with same behaviour as defined in GNU libc */
+
+int backtrace( void **buffer, int max_frames );
+
+char ** backtrace_symbols(void * const * buffer, int size);
+
+void backtrace_symbols_fd( void **buffer, int size, int fd );
+
+/* no frame.h on FreeBSD */
+#if defined (FREEBSD) || defined (NETBSD) || defined (OPENBSD) || \
+ defined (DRAGONFLY)
+struct frame {
+ struct frame *fr_savfp;
+ long fr_savpc;
+};
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/backtraceapi.cxx b/sal/osl/unx/backtraceapi.cxx
new file mode 100644
index 000000000..d11128353
--- /dev/null
+++ b/sal/osl/unx/backtraceapi.cxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstdlib>
+#include <limits>
+#include <memory>
+
+#include <o3tl/runtimetooustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <sal/backtrace.hxx>
+
+#include "backtrace.h"
+#include <backtraceasstring.hxx>
+
+namespace {
+
+struct FreeGuard {
+ FreeGuard(char ** theBuffer): buffer(theBuffer) {}
+
+ ~FreeGuard() { std::free(buffer); }
+
+ char ** buffer;
+};
+
+}
+
+OUString osl::detail::backtraceAsString(sal_uInt32 maxDepth) {
+ std::unique_ptr<sal::BacktraceState> backtrace = sal::backtrace_get( maxDepth );
+ return sal::backtrace_to_string( backtrace.get());
+}
+
+std::unique_ptr<sal::BacktraceState> sal::backtrace_get(sal_uInt32 maxDepth)
+{
+ assert(maxDepth != 0);
+ auto const maxInt = static_cast<unsigned int>(
+ std::numeric_limits<int>::max());
+ if (maxDepth > maxInt) {
+ maxDepth = static_cast<sal_uInt32>(maxInt);
+ }
+ auto b1 = new void *[maxDepth];
+ int n = backtrace(b1, static_cast<int>(maxDepth));
+ return std::unique_ptr<BacktraceState>(new BacktraceState{ b1, n });
+}
+
+OUString sal::backtrace_to_string(BacktraceState* backtraceState)
+{
+ FreeGuard b2(backtrace_symbols(backtraceState->buffer, backtraceState->nDepth));
+ if (b2.buffer == nullptr) {
+ return OUString();
+ }
+ OUStringBuffer b3;
+ for (int i = 0; i != backtraceState->nDepth; ++i) {
+ if (i != 0) {
+ b3.append("\n");
+ }
+ b3.append(o3tl::runtimeToOUString(b2.buffer[i]));
+ }
+ return b3.makeStringAndClear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/conditn.cxx b/sal/osl/unx/conditn.cxx
new file mode 100644
index 000000000..d73d67e91
--- /dev/null
+++ b/sal/osl/unx/conditn.cxx
@@ -0,0 +1,274 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <assert.h>
+
+#include "system.hxx"
+#include "unixerrnostring.hxx"
+#include <sal/log.hxx>
+#include <sal/types.h>
+
+#include <osl/conditn.h>
+#include <osl/time.h>
+
+namespace {
+
+struct oslConditionImpl
+{
+ pthread_cond_t m_Condition;
+ pthread_mutex_t m_Lock;
+ bool m_State;
+};
+
+}
+
+oslCondition SAL_CALL osl_createCondition()
+{
+ oslConditionImpl* pCond;
+ int nRet=0;
+
+ pCond = static_cast<oslConditionImpl*>(malloc(sizeof(oslConditionImpl)));
+
+ if ( pCond == nullptr )
+ {
+ return nullptr;
+ }
+
+ pCond->m_State = false;
+
+ /* init condition variable with default attr. (PTHREAD_PROCESS_PRIVAT) */
+ nRet = pthread_cond_init(&pCond->m_Condition, PTHREAD_CONDATTR_DEFAULT);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "pthread_cond_init failed: " << UnixErrnoString(nRet) );
+
+ free(pCond);
+ return nullptr;
+ }
+
+ nRet = pthread_mutex_init(&pCond->m_Lock, PTHREAD_MUTEXATTR_DEFAULT);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "pthread_mutex_init failed: " << UnixErrnoString(nRet) );
+
+ nRet = pthread_cond_destroy(&pCond->m_Condition);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "pthread_cond_destroy failed: " << UnixErrnoString(nRet) );
+
+ free(pCond);
+ pCond = nullptr;
+ }
+
+ SAL_INFO( "sal.osl.condition", "osl_createCondition(): " << pCond );
+
+ return static_cast<oslCondition>(pCond);
+}
+
+void SAL_CALL osl_destroyCondition(oslCondition Condition)
+{
+ oslConditionImpl* pCond;
+
+ pCond = static_cast<oslConditionImpl*>(Condition);
+
+ SAL_INFO( "sal.osl.condition", "osl_destroyCondition(" << pCond << ")" );
+
+ if ( pCond )
+ {
+ int nRet = pthread_cond_destroy(&pCond->m_Condition);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "pthread_cond_destroy failed: " << UnixErrnoString(nRet) );
+ nRet = pthread_mutex_destroy(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "pthread_mutex_destroy failed: " << UnixErrnoString(nRet) );
+
+ free(Condition);
+ }
+}
+
+sal_Bool SAL_CALL osl_setCondition(oslCondition Condition)
+{
+ oslConditionImpl* pCond;
+ int nRet=0;
+
+ assert(Condition);
+ pCond = static_cast<oslConditionImpl*>(Condition);
+
+ nRet = pthread_mutex_lock(&pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_setCondition(" << pCond << "): pthread_mutex_lock failed: " << UnixErrnoString(nRet) );
+ return false;
+ }
+
+ pCond->m_State = true;
+ nRet = pthread_cond_broadcast(&pCond->m_Condition);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_setCondition(" << pCond << "): pthread_cond_broadcast failed: " << UnixErrnoString(nRet) );
+ // try to unlock the mutex
+ pthread_mutex_unlock(&pCond->m_Lock);
+ return false;
+ }
+
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_setCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+ return false;
+ }
+
+ SAL_INFO( "sal.osl.condition", "osl_setCondition(" << pCond << ")" );
+
+ return true;
+
+}
+
+sal_Bool SAL_CALL osl_resetCondition(oslCondition Condition)
+{
+ oslConditionImpl* pCond;
+ int nRet=0;
+
+ assert(Condition);
+
+ pCond = static_cast<oslConditionImpl*>(Condition);
+
+ nRet = pthread_mutex_lock(&pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_resetCondition(" << pCond << "): pthread_mutex_lock failed: " << UnixErrnoString(nRet) );
+ return false;
+ }
+
+ pCond->m_State = false;
+
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_resetCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+ return false;
+ }
+
+ SAL_INFO( "sal.osl.condition", "osl_resetCondition(" << pCond << ")" );
+
+ return true;
+}
+
+oslConditionResult SAL_CALL osl_waitCondition(oslCondition Condition, const TimeValue* pTimeout)
+{
+ oslConditionImpl* pCond;
+ int nRet=0;
+ oslConditionResult Result = osl_cond_result_ok;
+
+ assert(Condition);
+ pCond = static_cast<oslConditionImpl*>(Condition);
+
+ SAL_INFO( "sal.osl.condition", "osl_waitCondition(" << pCond << ")" );
+
+ nRet = pthread_mutex_lock(&pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_mutex_lock failed: " << UnixErrnoString(nRet) );
+ return osl_cond_result_error;
+ }
+
+ if ( pTimeout )
+ {
+ if ( ! pCond->m_State )
+ {
+ struct timeval tp;
+ struct timespec to;
+
+ gettimeofday(&tp, nullptr);
+
+ SET_TIMESPEC( to, tp.tv_sec + pTimeout->Seconds,
+ tp.tv_usec * 1000 + pTimeout->Nanosec );
+
+ /* spurious wake up prevention */
+ do
+ {
+ const int ret = pthread_cond_timedwait(&pCond->m_Condition, &pCond->m_Lock, &to);
+ if ( ret != 0 )
+ {
+ if ( ret == ETIME || ret == ETIMEDOUT )
+ {
+ Result = osl_cond_result_timeout;
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+
+ return Result;
+ }
+ if ( ret != EINTR )
+ {
+ Result = osl_cond_result_error;
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+ return Result;
+ }
+ }
+ }
+ while ( !pCond->m_State );
+ }
+ }
+ else
+ {
+ while ( !pCond->m_State )
+ {
+ nRet = pthread_cond_wait(&pCond->m_Condition, &pCond->m_Lock);
+ if ( nRet != 0 )
+ {
+ SAL_WARN( "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_cond_wait failed: " << UnixErrnoString(nRet) );
+ Result = osl_cond_result_error;
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+
+ return Result;
+ }
+ }
+ }
+
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_waitCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+
+ SAL_INFO( "sal.osl.condition", "osl_waitCondition(" << pCond << "): " << (Result == osl_cond_result_ok ? "OK" : "ERROR") );
+
+ return Result;
+}
+
+sal_Bool SAL_CALL osl_checkCondition(oslCondition Condition)
+{
+ bool State;
+ oslConditionImpl* pCond;
+ int nRet=0;
+
+ assert(Condition);
+ pCond = static_cast<oslConditionImpl*>(Condition);
+
+ nRet = pthread_mutex_lock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_checkCondition(" << pCond << "): pthread_mutex_lock failed: " << UnixErrnoString(nRet) );
+
+ State = pCond->m_State;
+
+ nRet = pthread_mutex_unlock(&pCond->m_Lock);
+ SAL_WARN_IF( nRet != 0, "sal.osl.condition", "osl_checkCondition(" << pCond << "): pthread_mutex_unlock failed: " << UnixErrnoString(nRet) );
+
+ SAL_INFO( "sal.osl.condition", "osl_checkCondition(" << pCond << "): " << (State ? "YES" : "NO") );
+
+ return State;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/createfilehandlefromfd.hxx b/sal/osl/unx/createfilehandlefromfd.hxx
new file mode 100644
index 000000000..daf20e847
--- /dev/null
+++ b/sal/osl/unx/createfilehandlefromfd.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_UNX_CREATEFILEHANDLEFROMFD_HXX
+#define INCLUDED_SAL_OSL_UNX_CREATEFILEHANDLEFROMFD_HXX
+
+#include <sal/config.h>
+
+#include <osl/file.h>
+
+namespace osl::detail {
+
+oslFileHandle createFileHandleFromFD(int fd); // defined in file.cxx
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file.cxx b/sal/osl/unx/file.cxx
new file mode 100644
index 000000000..d8396f627
--- /dev/null
+++ b/sal/osl/unx/file.cxx
@@ -0,0 +1,1621 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <o3tl/safeint.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <osl/detail/file.h>
+#include <rtl/alloc.h>
+#include <rtl/byteseq.hxx>
+#include <rtl/string.hxx>
+
+#include "system.hxx"
+#include "createfilehandlefromfd.hxx"
+#include "file_error_transl.hxx"
+#include "file_impl.hxx"
+#include "file_url.hxx"
+#include "uunxapi.hxx"
+#include "unixerrnostring.hxx"
+
+#include <algorithm>
+#include <atomic>
+#include <vector>
+#include <cassert>
+#include <limits>
+
+#include <string.h>
+#include <pthread.h>
+#include <sys/mman.h>
+
+#if defined(MACOSX)
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#define HAVE_O_EXLOCK
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#endif /* MACOSX */
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#include <android/log.h>
+#include <android/asset_manager.h>
+#endif
+
+namespace {
+
+enum class State
+{
+ Seekable = 1, /*< default */
+ Readable = 2, /*< default */
+ Writeable = 4, /*< open() sets, write() requires, else osl_File_E_BADF */
+ Modified = 8 /*< write() sets, flush() resets */
+};
+
+}
+
+template<> struct o3tl::typed_flags<State>: o3tl::is_typed_flags<State, 15> {};
+
+namespace {
+
+struct FileHandle_Impl
+{
+ pthread_mutex_t m_mutex;
+ rtl_String * m_strFilePath; /*< holds native file path */
+ int m_fd;
+
+ enum Kind
+ {
+ KIND_FD = 1,
+ KIND_MEM = 2
+ };
+ int m_kind;
+ /** State
+ */
+ State m_state;
+
+ sal_uInt64 m_size; /*< file size */
+ off_t m_offset; /*< physical offset from begin of file */
+ // m_fileptr is hit hard in some situations, where the overhead of a mutex starts to show up, so use an atomic
+ std::atomic<off_t> m_fileptr; /*< logical offset from begin of file */
+
+ off_t m_bufptr; /*< buffer offset from begin of file */
+ size_t m_buflen; /*< buffer filled [0, m_bufsiz - 1] */
+
+ size_t m_bufsiz;
+ sal_uInt8 * m_buffer;
+#ifdef ANDROID
+ rtl_String* m_memstreambuf; /*< used for in-memory streams */
+#endif
+
+ explicit FileHandle_Impl(int fd, Kind kind = KIND_FD, char const * path = "<anon>");
+ ~FileHandle_Impl();
+
+ static void* operator new (size_t n);
+ static void operator delete (void * p);
+
+ static size_t getpagesize();
+
+ sal_uInt64 getPos() const;
+ void setPos(sal_uInt64 uPos);
+
+ sal_uInt64 getSize() const;
+ oslFileError setSize(sal_uInt64 uSize);
+
+ oslFileError readAt(
+ off_t nOffset,
+ void* pBuffer,
+ size_t nBytesRequested,
+ sal_uInt64* pBytesRead);
+
+ oslFileError writeAt(
+ off_t nOffset,
+ void const* pBuffer,
+ size_t nBytesToWrite,
+ sal_uInt64* pBytesWritten);
+
+ oslFileError readFileAt(
+ off_t nOffset,
+ void* pBuffer,
+ size_t nBytesRequested,
+ sal_uInt64* pBytesRead);
+
+ oslFileError writeFileAt(
+ off_t nOffset,
+ void const* pBuffer,
+ size_t nBytesToWrite,
+ sal_uInt64* pBytesWritten);
+
+ oslFileError readLineAt(
+ off_t nOffset,
+ sal_Sequence** ppSequence,
+ sal_uInt64* pBytesRead);
+
+ static oslFileError writeSequence_Impl(
+ sal_Sequence** ppSequence,
+ size_t* pnOffset,
+ const void* pBuffer,
+ size_t nBytes);
+
+ oslFileError syncFile();
+
+ class Guard
+ {
+ pthread_mutex_t *m_mutex;
+
+ public:
+ explicit Guard(pthread_mutex_t *pMutex);
+ ~Guard();
+ };
+};
+
+}
+
+FileHandle_Impl::Guard::Guard(pthread_mutex_t * pMutex)
+ : m_mutex(pMutex)
+{
+ assert(m_mutex);
+ (void) pthread_mutex_lock(m_mutex); // ignoring EINVAL if a null mutex is passed ...
+}
+
+FileHandle_Impl::Guard::~Guard()
+{
+ assert(m_mutex);
+ (void) pthread_mutex_unlock(m_mutex);
+}
+
+FileHandle_Impl::FileHandle_Impl(int fd, enum Kind kind, char const * path)
+ : m_strFilePath(nullptr),
+ m_fd (fd),
+ m_kind (kind),
+ m_state (State::Seekable | State::Readable),
+ m_size (0),
+ m_offset (0),
+ m_fileptr (0),
+ m_bufptr (-1),
+ m_buflen (0),
+ m_bufsiz (0),
+ m_buffer (nullptr)
+{
+ (void) pthread_mutex_init(&m_mutex, nullptr);
+ rtl_string_newFromStr(&m_strFilePath, path);
+ if (m_kind == KIND_FD)
+ {
+ size_t const pagesize = getpagesize();
+ if (pagesize != size_t(-1))
+ {
+ m_bufsiz = pagesize;
+ m_buffer = static_cast<sal_uInt8 *>(malloc(m_bufsiz));
+ if (m_buffer)
+ memset(m_buffer, 0, m_bufsiz);
+ }
+ }
+}
+
+FileHandle_Impl::~FileHandle_Impl()
+{
+ if (m_kind == KIND_FD)
+ {
+ free(m_buffer);
+ m_buffer = nullptr;
+ }
+
+ rtl_string_release(m_strFilePath);
+ m_strFilePath = nullptr;
+ (void) pthread_mutex_destroy(&m_mutex); // ignoring EBUSY ...
+}
+
+void* FileHandle_Impl::operator new (size_t n)
+{
+ return malloc(n);
+}
+
+void FileHandle_Impl::operator delete (void * p)
+{
+ free(p);
+}
+
+size_t FileHandle_Impl::getpagesize()
+{
+ return sal::static_int_cast< size_t >(::sysconf(_SC_PAGESIZE));
+}
+
+sal_uInt64 FileHandle_Impl::getPos() const
+{
+ return sal::static_int_cast< sal_uInt64 >(m_fileptr.load());
+}
+
+void FileHandle_Impl::setPos(sal_uInt64 uPos)
+{
+ m_fileptr = sal::static_int_cast< off_t >(uPos);
+}
+
+sal_uInt64 FileHandle_Impl::getSize() const
+{
+ off_t const bufend = std::max(off_t(0), m_bufptr) + m_buflen;
+ return std::max(m_size, sal::static_int_cast< sal_uInt64 >(bufend));
+}
+
+oslFileError FileHandle_Impl::setSize(sal_uInt64 uSize)
+{
+ off_t const nSize = sal::static_int_cast< off_t >(uSize);
+ if (ftruncate_with_name(m_fd, nSize, m_strFilePath) == -1)
+ {
+ /* Failure. Save original result. Try fallback algorithm */
+ oslFileError result = oslTranslateFileError(errno);
+
+ /* Check against current size. Fail upon 'shrink' */
+ if (uSize <= getSize())
+ {
+ /* Failure upon 'shrink'. Return original result */
+ return result;
+ }
+
+ /* Save current position */
+ off_t const nCurPos = lseek(m_fd, off_t(0), SEEK_CUR);
+ if (nCurPos == off_t(-1))
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "lseek(" << m_fd << ",0,SEEK_CUR): " << UnixErrnoString(e));
+ return result;
+ }
+ else
+ SAL_INFO("sal.file", "lseek(" << m_fd << ",0,SEEK_CUR): OK");
+
+ /* Try 'expand' via 'lseek()' and 'write()' */
+ if (lseek(m_fd, static_cast<off_t>(nSize - 1), SEEK_SET) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "lseek(" << m_fd << "," << nSize - 1 << ",SEEK_SET): " << UnixErrnoString(e));
+ return result;
+ }
+ else
+ SAL_INFO("sal.file", "lseek(" << m_fd << "," << nSize - 1 << ",SEEK_SET): OK");
+
+ if (write(m_fd, "", size_t(1)) == -1)
+ {
+ /* Failure. Restore saved position */
+ int e = errno;
+ SAL_INFO("sal.file", "write(" << m_fd << ",\"\",1): " << UnixErrnoString(e));
+ (void) lseek(m_fd, nCurPos, SEEK_SET);
+ return result;
+ }
+ else
+ SAL_INFO("sal.file", "write(" << m_fd << ",\"\",1): OK");
+
+ /* Success. Restore saved position */
+ if (lseek(m_fd, nCurPos, SEEK_SET) == -1)
+ return result;
+ }
+
+ m_size = sal::static_int_cast< sal_uInt64 >(nSize);
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::readAt(
+ off_t nOffset,
+ void * pBuffer,
+ size_t nBytesRequested,
+ sal_uInt64 * pBytesRead)
+{
+ SAL_WARN_IF(!(m_state & State::Seekable), "sal.osl", "FileHandle_Impl::readAt(): not seekable");
+ if (!(m_state & State::Seekable))
+ return osl_File_E_SPIPE;
+
+ SAL_WARN_IF(!(m_state & State::Readable), "sal.osl", "FileHandle_Impl::readAt(): not readable");
+ if (!(m_state & State::Readable))
+ return osl_File_E_BADF;
+
+ if (m_kind == KIND_MEM)
+ {
+ ssize_t nBytes;
+
+ m_offset = nOffset;
+
+ if (o3tl::make_unsigned(m_offset) >= m_size)
+ {
+ nBytes = 0;
+ }
+ else
+ {
+ nBytes = std::min(nBytesRequested, static_cast<size_t>(m_size - m_offset));
+ memmove(pBuffer, m_buffer + m_offset, nBytes);
+ m_offset += nBytes;
+ }
+ *pBytesRead = nBytes;
+ return osl_File_E_None;
+ }
+
+ ssize_t nBytes = ::pread(m_fd, pBuffer, nBytesRequested, nOffset);
+ if ((nBytes == -1) && (errno == EOVERFLOW))
+ {
+ /* Some 'pread()'s fail with EOVERFLOW when reading at (or past)
+ * end-of-file, different from 'lseek() + read()' behaviour.
+ * Returning '0 bytes read' and 'osl_File_E_None' instead.
+ */
+ nBytes = 0;
+ }
+
+ if (nBytes == -1)
+ return oslTranslateFileError(errno);
+
+ *pBytesRead = nBytes;
+
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::writeAt(
+ off_t nOffset,
+ void const * pBuffer,
+ size_t nBytesToWrite,
+ sal_uInt64 * pBytesWritten)
+{
+ SAL_WARN_IF(!(m_state & State::Seekable), "sal.osl", "FileHandle_Impl::writeAt(): not seekable");
+ if (!(m_state & State::Seekable))
+ return osl_File_E_SPIPE;
+
+ SAL_WARN_IF(!(m_state & State::Writeable), "sal.osl", "FileHandle_Impl::writeAt(): not writeable");
+ if (!(m_state & State::Writeable))
+ return osl_File_E_BADF;
+
+ ssize_t nBytes = ::pwrite(m_fd, pBuffer, nBytesToWrite, nOffset);
+ if (nBytes == -1)
+ return oslTranslateFileError(errno);
+
+ m_size = std::max(m_size, sal::static_int_cast< sal_uInt64 >(nOffset + nBytes));
+
+ *pBytesWritten = nBytes;
+
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::readFileAt(
+ off_t nOffset,
+ void* pBuffer,
+ size_t nBytesRequested,
+ sal_uInt64* pBytesRead)
+{
+ if (!(m_state & State::Seekable))
+ {
+ // not seekable (pipe)
+ ssize_t nBytes = ::read(m_fd, pBuffer, nBytesRequested);
+ if (nBytes == -1)
+ return oslTranslateFileError(errno);
+
+ *pBytesRead = nBytes;
+
+ return osl_File_E_None;
+ }
+
+ if (m_kind == KIND_MEM || !m_buffer)
+ {
+ // not buffered
+ return readAt(nOffset, pBuffer, nBytesRequested, pBytesRead);
+ }
+
+ sal_uInt8 *buffer = static_cast<sal_uInt8*>(pBuffer);
+ for (*pBytesRead = 0; nBytesRequested > 0; )
+ {
+ off_t const bufptr = (nOffset / m_bufsiz) * m_bufsiz;
+ size_t const bufpos = nOffset % m_bufsiz;
+
+ if (bufptr != m_bufptr)
+ {
+ // flush current buffer
+ oslFileError result = syncFile();
+ if (result != osl_File_E_None)
+ return result;
+
+ m_bufptr = -1;
+ m_buflen = 0;
+
+ if (nBytesRequested >= m_bufsiz)
+ {
+ // buffer too small, read through from file
+ sal_uInt64 uDone = 0;
+ result = readAt(nOffset, &(buffer[*pBytesRead]), nBytesRequested, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ *pBytesRead += uDone;
+
+ return osl_File_E_None;
+ }
+
+ // update buffer (pointer)
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ m_bufptr = bufptr;
+ m_buflen = uDone;
+ }
+
+ if (bufpos >= m_buflen)
+ {
+ // end of file
+ return osl_File_E_None;
+ }
+
+ size_t const bytes = std::min(m_buflen - bufpos, nBytesRequested);
+ SAL_INFO("sal.fileio", "FileHandle_Impl::readFileAt(" << m_fd << ", " << nOffset << ", " << bytes << ")");
+
+ memcpy(&(buffer[*pBytesRead]), &(m_buffer[bufpos]), bytes);
+ nBytesRequested -= bytes;
+ *pBytesRead += bytes;
+ nOffset += bytes;
+ }
+
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::writeFileAt(
+ off_t nOffset,
+ void const * pBuffer,
+ size_t nBytesToWrite,
+ sal_uInt64 * pBytesWritten)
+{
+ if (!(m_state & State::Seekable))
+ {
+ // not seekable (pipe)
+ ssize_t nBytes = ::write(m_fd, pBuffer, nBytesToWrite);
+ if (nBytes == -1)
+ return oslTranslateFileError(errno);
+
+ *pBytesWritten = nBytes;
+
+ return osl_File_E_None;
+ }
+ if (!m_buffer)
+ {
+ // not buffered
+ return writeAt(nOffset, pBuffer, nBytesToWrite, pBytesWritten);
+ }
+
+ sal_uInt8 const * buffer = static_cast<sal_uInt8 const *>(pBuffer);
+ for (*pBytesWritten = 0; nBytesToWrite > 0;)
+ {
+ off_t const bufptr = (nOffset / m_bufsiz) * m_bufsiz;
+ size_t const bufpos = nOffset % m_bufsiz;
+ if (bufptr != m_bufptr)
+ {
+ // flush current buffer
+ oslFileError result = syncFile();
+ if (result != osl_File_E_None)
+ return result;
+ m_bufptr = -1;
+ m_buflen = 0;
+
+ if (nBytesToWrite >= m_bufsiz)
+ {
+ // buffer too small, write through to file
+ sal_uInt64 uDone = 0;
+ result = writeAt(nOffset, &(buffer[*pBytesWritten]), nBytesToWrite, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ if (uDone != nBytesToWrite)
+ return osl_File_E_IO;
+
+ *pBytesWritten += uDone;
+
+ return osl_File_E_None;
+ }
+
+ // update buffer (pointer)
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ m_bufptr = bufptr;
+ m_buflen = uDone;
+ }
+
+ size_t const bytes = std::min(m_bufsiz - bufpos, nBytesToWrite);
+ SAL_INFO("sal.fileio", "FileHandle_Impl::writeFileAt(" << m_fd << ", " << nOffset << ", " << bytes << ")");
+
+ memcpy(&(m_buffer[bufpos]), &(buffer[*pBytesWritten]), bytes);
+ nBytesToWrite -= bytes;
+ *pBytesWritten += bytes;
+ nOffset += bytes;
+
+ m_buflen = std::max(m_buflen, bufpos + bytes);
+ m_state |= State::Modified;
+ }
+
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::readLineAt(
+ off_t nOffset,
+ sal_Sequence ** ppSequence,
+ sal_uInt64 * pBytesRead)
+{
+ oslFileError result = osl_File_E_None;
+
+ off_t bufptr = nOffset / m_bufsiz * m_bufsiz;
+ if (bufptr != m_bufptr)
+ {
+ /* flush current buffer */
+ result = syncFile();
+ if (result != osl_File_E_None)
+ return result;
+
+ /* update buffer (pointer) */
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ m_bufptr = bufptr;
+ m_buflen = uDone;
+ }
+
+ static int const LINE_STATE_BEGIN = 0;
+ static int const LINE_STATE_CR = 1;
+ static int const LINE_STATE_LF = 2;
+
+ size_t bufpos = nOffset - m_bufptr, curpos = bufpos, dstpos = 0;
+ int state = (bufpos >= m_buflen) ? LINE_STATE_LF : LINE_STATE_BEGIN;
+
+ while (state != LINE_STATE_LF)
+ {
+ if (curpos >= m_buflen)
+ {
+ /* buffer examined */
+ if ((curpos - bufpos) > 0)
+ {
+ /* flush buffer to sequence */
+ result = writeSequence_Impl(
+ ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos);
+ if (result != osl_File_E_None)
+ return result;
+
+ *pBytesRead += curpos - bufpos;
+ nOffset += curpos - bufpos;
+ }
+
+ bufptr = nOffset / m_bufsiz * m_bufsiz;
+ if (bufptr != m_bufptr)
+ {
+ /* update buffer (pointer) */
+ sal_uInt64 uDone = 0;
+ result = readAt(bufptr, m_buffer, m_bufsiz, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ m_bufptr = bufptr;
+ m_buflen = uDone;
+ }
+
+ bufpos = nOffset - m_bufptr;
+ curpos = bufpos;
+ if (bufpos >= m_buflen)
+ break;
+ }
+
+ switch (state)
+ {
+ case LINE_STATE_CR:
+ state = LINE_STATE_LF;
+ switch (m_buffer[curpos])
+ {
+ case 0x0A: /* CRLF */
+ /* eat current char */
+ curpos++;
+ break;
+ default: /* single CR */
+ /* keep current char */
+ break;
+ }
+ break;
+ default:
+ /* determine next state */
+ switch (m_buffer[curpos])
+ {
+ case 0x0A: /* single LF */
+ state = LINE_STATE_LF;
+ break;
+ case 0x0D: /* CR */
+ state = LINE_STATE_CR;
+ break;
+ default: /* advance to next char */
+ curpos++;
+ break;
+ }
+ if (state != LINE_STATE_BEGIN)
+ {
+ /* skip the newline char */
+ curpos++;
+
+ /* flush buffer to sequence */
+ result = writeSequence_Impl(
+ ppSequence, &dstpos, &(m_buffer[bufpos]), curpos - bufpos - 1);
+ if (result != osl_File_E_None)
+ return result;
+
+ *pBytesRead += curpos - bufpos;
+ nOffset += curpos - bufpos;
+ }
+ break;
+ }
+ }
+
+ result = writeSequence_Impl(ppSequence, &dstpos, nullptr, 0);
+ if (result != osl_File_E_None)
+ return result;
+
+ if (dstpos > 0)
+ return osl_File_E_None;
+
+ if (bufpos >= m_buflen)
+ return osl_File_E_AGAIN;
+
+ return osl_File_E_None;
+}
+
+oslFileError FileHandle_Impl::writeSequence_Impl(
+ sal_Sequence ** ppSequence,
+ size_t * pnOffset,
+ const void * pBuffer,
+ size_t nBytes)
+{
+ sal_Int32 nElements = *pnOffset + nBytes;
+ if (!*ppSequence)
+ {
+ /* construct sequence */
+ rtl_byte_sequence_constructNoDefault(ppSequence, nElements);
+ }
+ else if (nElements != (*ppSequence)->nElements)
+ {
+ /* resize sequence */
+ rtl_byte_sequence_realloc(ppSequence, nElements);
+ }
+
+ if (*ppSequence && nBytes != 0)
+ {
+ /* fill sequence */
+ memcpy(&((*ppSequence)->elements[*pnOffset]), pBuffer, nBytes);
+ *pnOffset += nBytes;
+ }
+
+ return (*ppSequence) ? osl_File_E_None : osl_File_E_NOMEM;
+}
+
+oslFileError FileHandle_Impl::syncFile()
+{
+ oslFileError result = osl_File_E_None;
+ if (m_state & State::Modified)
+ {
+ sal_uInt64 uDone = 0;
+ result = writeAt(m_bufptr, m_buffer, m_buflen, &uDone);
+ if (result != osl_File_E_None)
+ return result;
+
+ if (uDone != m_buflen)
+ return osl_File_E_IO;
+
+ m_state &= ~State::Modified;
+ }
+
+ return result;
+}
+
+oslFileHandle osl::detail::createFileHandleFromFD(int fd)
+{
+ if (fd == -1)
+ return nullptr; // EINVAL
+
+ struct stat aFileStat;
+ if (fstat(fd, &aFileStat) == -1)
+ return nullptr; // EBADF
+
+ FileHandle_Impl *pImpl = new FileHandle_Impl(fd);
+
+ // assume writeable
+ pImpl->m_state |= State::Writeable;
+ if (!S_ISREG(aFileStat.st_mode))
+ {
+ /* not a regular file, mark not seekable */
+ pImpl->m_state &= ~State::Seekable;
+ }
+ else
+ {
+ /* regular file, init current size */
+ pImpl->m_size = sal::static_int_cast< sal_uInt64 >(aFileStat.st_size);
+ }
+
+ SAL_INFO("sal.file", "osl::detail::createFileHandleFromFD(" << pImpl->m_fd << ", writeable) => " << pImpl->m_strFilePath);
+
+ return static_cast<oslFileHandle>(pImpl);
+}
+
+static int osl_file_adjustLockFlags(const char *path, int flags)
+{
+#ifdef MACOSX
+ /*
+ * The AFP implementation of MacOS X 10.4 treats O_EXLOCK in a way
+ * that makes it impossible for OOo to create a backup copy of the
+ * file it keeps opened. OTOH O_SHLOCK for AFP behaves as desired by
+ * the OOo file handling, so we need to check the path of the file
+ * for the filesystem name.
+ */
+ struct statfs s;
+ if(statfs(path, &s) >= 0)
+ {
+ if(strncmp("afpfs", s.f_fstypename, 5) == 0)
+ {
+ flags &= ~O_EXLOCK;
+ flags |= O_SHLOCK;
+ }
+ else
+ {
+ /* Needed flags to allow opening a webdav file */
+ flags &= ~(O_EXLOCK | O_SHLOCK | O_NONBLOCK);
+ }
+ }
+#else
+ (void) path;
+#endif
+
+ return flags;
+}
+
+static bool osl_file_queryLocking(sal_uInt32 uFlags)
+{
+#if !defined HAVE_O_EXLOCK
+ if (!(uFlags & osl_File_OpenFlag_NoLock)
+ && ((uFlags & osl_File_OpenFlag_Write)
+ || (uFlags & osl_File_OpenFlag_Create)))
+ {
+ static bool enabled = getenv("SAL_ENABLE_FILE_LOCKING") != nullptr;
+ // getenv is not thread safe, so minimize use of result
+ return enabled;
+ }
+#else
+ (void) uFlags;
+#endif
+ return false;
+}
+
+#ifdef HAVE_O_EXLOCK
+#define OPEN_WRITE_FLAGS ( O_RDWR | O_EXLOCK | O_NONBLOCK )
+#define OPEN_CREATE_FLAGS ( O_CREAT | O_RDWR | O_EXLOCK | O_NONBLOCK )
+#else
+#define OPEN_WRITE_FLAGS ( O_RDWR )
+#define OPEN_CREATE_FLAGS ( O_CREAT | O_RDWR )
+#endif
+
+#if defined ANDROID
+
+namespace {
+
+static oslFileError openMemoryAsFile(const OString &rData,
+ oslFileHandle *pHandle,
+ const char *path)
+{
+ const char *address = rData.getStr();
+ size_t size = rData.getLength();
+
+ FileHandle_Impl *pImpl = new FileHandle_Impl(-1, FileHandle_Impl::KIND_MEM, path);
+ pImpl->m_size = sal::static_int_cast< sal_uInt64 >(size);
+
+ *pHandle = (oslFileHandle)(pImpl);
+
+ pImpl->m_bufptr = 0;
+ pImpl->m_buflen = size;
+ pImpl->m_memstreambuf = rData.pData;
+ rtl_string_acquire(pImpl->m_memstreambuf);
+
+ pImpl->m_bufsiz = size;
+ pImpl->m_buffer = reinterpret_cast<sal_uInt8*>(const_cast<char *>(address));
+
+ return osl_File_E_None;
+}
+
+/*
+ * Reading files from /assets/ on Android via a transition into the VM
+ * shows on profiles and is rather slow; so we cache small files as
+ * used by UNO, UI-builder etc.
+ */
+class AndroidFileCache {
+public:
+ struct Entry {
+ OString maFilePath;
+ OString maData;
+ };
+ AndroidFileCache(size_t nElements)
+ : mnCur(0)
+ {
+ maEntries.resize(nElements);
+ assert (maEntries.size() == nElements);
+ }
+ Entry *find(const char *cpFilePath)
+ {
+ for (auto &it : maEntries)
+ {
+ if (!strcmp(it.maFilePath.getStr(), cpFilePath))
+ return &it;
+ }
+ return nullptr;
+ }
+ // no clever LRU - but - good enough for now.
+ void insert(const char *cpFilePath, OString &rData)
+ {
+ assert (maEntries.size() > 0);
+ if (++mnCur >= maEntries.size())
+ mnCur = 0;
+ maEntries[mnCur].maFilePath = OString(cpFilePath, strlen(cpFilePath));
+ maEntries[mnCur].maData = rData;
+ }
+ static AndroidFileCache &getHitCache()
+ {
+ static AndroidFileCache *pCache = new AndroidFileCache(16);
+ return *pCache;
+ }
+ static AndroidFileCache &getMissCache()
+ {
+ static AndroidFileCache *pCache = new AndroidFileCache(32);
+ return *pCache;
+ }
+private:
+ size_t mnCur;
+ std::vector<Entry> maEntries;
+};
+
+} // namespace
+
+#endif
+
+oslFileError openFilePath(const char *cpFilePath, oslFileHandle* pHandle,
+ sal_uInt32 uFlags, mode_t mode)
+{
+ oslFileError eRet;
+
+#ifdef ANDROID
+ /* Opening a file from /assets read-only means
+ * we should mmap it from the .apk file
+ */
+ if (strncmp(cpFilePath, "/assets/", sizeof ("/assets/") - 1) == 0)
+ {
+ OString aData;
+ bool bCache = true;
+
+ const char *cpAssetsPath = cpFilePath + sizeof("/assets/") - 1;
+ // some requests are /assets//foo...
+ if (cpAssetsPath[0] == '/')
+ {
+ __android_log_print(ANDROID_LOG_DEBUG,"libo:sal/osl/unx/file", "double-slash in path: %s", cpFilePath);
+ cpAssetsPath++;
+ }
+
+ AndroidFileCache::Entry *pHit = AndroidFileCache::getHitCache().find(cpAssetsPath);
+ if (pHit)
+ aData = pHit->maData;
+
+ else
+ {
+ bCache = false;
+ AndroidFileCache::Entry *pMiss = AndroidFileCache::getMissCache().find(cpAssetsPath);
+ if (pMiss)
+ {
+ errno = ENOENT;
+ __android_log_print(ANDROID_LOG_ERROR,"libo:sal/osl/unx/file", "miss cache: failed to open %s", cpFilePath);
+ return osl_File_E_NOENT;
+ }
+ AAssetManager* mgr = lo_get_native_assetmgr();
+ AAsset* asset = AAssetManager_open(mgr, cpAssetsPath, AASSET_MODE_BUFFER);
+ if (!asset)
+ {
+ AndroidFileCache::getMissCache().insert(cpAssetsPath, aData);
+ errno = ENOENT;
+ __android_log_print(ANDROID_LOG_ERROR,"libo:sal/osl/unx/file", "failed to open %s", cpFilePath);
+ return osl_File_E_NOENT;
+ }
+ else
+ {
+ rtl_String *pData = nullptr;
+ size_t size = AAsset_getLength(asset);
+ rtl_string_new_WithLength(&pData, size);
+ pData->length = size;
+ AAsset_read(asset, pData->buffer, size);
+ AAsset_close(asset);
+
+ aData = OString(pData, SAL_NO_ACQUIRE);
+
+ if (pData->length < 50 * 1024)
+ AndroidFileCache::getHitCache().insert(cpAssetsPath, aData);
+ }
+ }
+
+ if (uFlags & osl_File_OpenFlag_Write)
+ {
+ // It seems to work better to silently "open" it read-only
+ // and let write attempts, if any, fail later. Otherwise
+ // loading a document from /assets fails with that idiotic
+ // "General Error" dialog...
+ }
+ SAL_INFO("sal.file", "osl_openFile(" << cpFilePath << ") => '" << cpAssetsPath << "'"
+ << aData.getLength() << " bytes from file " << (bCache ? "cache" : "system"));
+ return openMemoryAsFile(aData, pHandle, cpFilePath);
+ }
+#endif
+
+ /* set mode and flags */
+ int defmode = (uFlags & osl_File_OpenFlag_Private) ? S_IRUSR : S_IRUSR | S_IRGRP | S_IROTH;
+ int flags = O_RDONLY;
+
+ if (uFlags & osl_File_OpenFlag_Write)
+ {
+ defmode |= (uFlags & osl_File_OpenFlag_Private) ? S_IWUSR : S_IWUSR | S_IWGRP | S_IWOTH;
+ flags = OPEN_WRITE_FLAGS;
+ }
+
+ if (uFlags & osl_File_OpenFlag_Create)
+ {
+ defmode |= (uFlags & osl_File_OpenFlag_Private) ? S_IWUSR : S_IWUSR | S_IWGRP | S_IWOTH;
+ flags = OPEN_CREATE_FLAGS;
+ }
+
+ if (mode == mode_t(-1))
+ mode = defmode;
+
+ /* Check for flags passed in from SvFileStream::Open() */
+ if (uFlags & osl_File_OpenFlag_Trunc)
+ flags |= O_TRUNC;
+
+ if (!(uFlags & osl_File_OpenFlag_NoExcl))
+ flags |= O_EXCL;
+
+ if (uFlags & osl_File_OpenFlag_NoLock)
+ {
+#ifdef HAVE_O_EXLOCK
+ flags &= ~(O_EXLOCK | O_SHLOCK | O_NONBLOCK);
+#endif /* HAVE_O_EXLOCK */
+ }
+ else
+ {
+ flags = osl_file_adjustLockFlags (cpFilePath, flags);
+ }
+
+ // O_EXCL can be set only when O_CREAT is set
+ if (flags & O_EXCL && !(flags & O_CREAT))
+ flags &= ~O_EXCL;
+
+ /* open the file */
+ int fd = open_c( cpFilePath, flags, mode );
+ if (fd == -1)
+ {
+ return oslTranslateFileError(errno);
+ }
+
+#if !HAVE_FEATURE_MACOSX_SANDBOX
+ /* reset O_NONBLOCK flag */
+ if (flags & O_NONBLOCK)
+ {
+ int f = fcntl(fd, F_GETFL, 0);
+ if (f == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "fcntl(" << fd << ",F_GETFL,0): " << UnixErrnoString(e));
+ eRet = oslTranslateFileError(e);
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return eRet;
+ }
+ else
+ SAL_INFO("sal.file", "fcntl(" << fd << ",F_GETFL,0): OK");
+
+ if (fcntl(fd, F_SETFL, (f & ~O_NONBLOCK)) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "fcntl(" << fd << ",F_SETFL,(f & ~O_NONBLOCK)): " << UnixErrnoString(e));
+ eRet = oslTranslateFileError(e);
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return eRet;
+ }
+ else
+ SAL_INFO("sal.file", "fcntl(" << fd << ",F_SETFL,(f & ~O_NONBLOCK)): OK");
+ }
+#endif
+
+ /* get file status (mode, size) */
+ struct stat aFileStat;
+ if (fstat(fd, &aFileStat) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "fstat(" << fd << "): " << UnixErrnoString(e));
+ eRet = oslTranslateFileError(e);
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return eRet;
+ }
+ else
+ SAL_INFO("sal.file", "fstat(" << fd << "): OK");
+
+ if (!S_ISREG(aFileStat.st_mode))
+ {
+ /* we only open regular files here */
+ SAL_INFO("sal.file", "osl_openFile(" << cpFilePath << "): not a regular file");
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return osl_File_E_INVAL;
+ }
+
+ if (osl_file_queryLocking(uFlags))
+ {
+#ifdef MACOSX
+ if (flock(fd, LOCK_EX | LOCK_NB) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "flock(" << fd << ",LOCK_EX|LOCK_NB): " << UnixErrnoString(e));
+ /* Mac OSX returns ENOTSUP for webdav drives. We should try read lock */
+
+ // Restore errno after possibly having been overwritten by the SAL_INFO above...
+ errno = e;
+ if ((errno != ENOTSUP) || ((flock(fd, LOCK_SH | LOCK_NB) == 1) && (errno != ENOTSUP)))
+ {
+ eRet = oslTranslateFileError(errno);
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return eRet;
+ }
+ }
+ else
+ SAL_INFO("sal.file", "flock(" << fd << ",LOCK_EX|LOCK_NB): OK");
+#else /* F_SETLK */
+ struct flock aflock;
+
+ aflock.l_type = F_WRLCK;
+ aflock.l_whence = SEEK_SET;
+ aflock.l_start = 0;
+ aflock.l_len = 0;
+
+ if (fcntl(fd, F_SETLK, &aflock) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "fcntl(" << fd << ",F_SETLK): " << UnixErrnoString(e));
+ eRet = oslTranslateFileError(e);
+ (void) close(fd);
+ SAL_INFO("sal.file", "close(" << fd << ")");
+ return eRet;
+ }
+#endif /* F_SETLK */
+ }
+
+ /* allocate memory for impl structure */
+ FileHandle_Impl *pImpl = new FileHandle_Impl(fd, FileHandle_Impl::KIND_FD, cpFilePath);
+ if (flags & O_RDWR)
+ pImpl->m_state |= State::Writeable;
+
+ pImpl->m_size = sal::static_int_cast< sal_uInt64 >(aFileStat.st_size);
+
+ *pHandle = static_cast<oslFileHandle>(pImpl);
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_openFile(rtl_uString* ustrFileURL, oslFileHandle* pHandle, sal_uInt32 uFlags)
+{
+ return openFile(ustrFileURL, pHandle, uFlags, mode_t(-1));
+}
+
+oslFileError openFile(rtl_uString* ustrFileURL, oslFileHandle* pHandle, sal_uInt32 uFlags, mode_t mode)
+{
+ oslFileError eRet;
+
+ if ((!ustrFileURL) || (ustrFileURL->length == 0) || (!pHandle))
+ return osl_File_E_INVAL;
+
+ /* convert file URL to system path */
+ char buffer[PATH_MAX];
+ eRet = FileURLToPath(buffer, sizeof(buffer), ustrFileURL);
+ if (eRet != osl_File_E_None)
+ return eRet;
+
+#ifdef MACOSX
+ if (macxp_resolveAlias(buffer, sizeof(buffer)) != 0)
+ return oslTranslateFileError(errno);
+#endif /* MACOSX */
+
+ return openFilePath(buffer, pHandle, uFlags, mode);
+}
+
+oslFileError SAL_CALL osl_closeFile(oslFileHandle Handle)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if (!pImpl)
+ return osl_File_E_INVAL;
+
+ if (pImpl->m_kind == FileHandle_Impl::KIND_MEM)
+ {
+#ifdef ANDROID
+ rtl_string_release(pImpl->m_memstreambuf);
+ pImpl->m_memstreambuf = nullptr;
+
+ pImpl->m_buffer = NULL;
+#endif
+ delete pImpl;
+ return osl_File_E_None;
+ }
+
+ if (pImpl->m_fd < 0)
+ return osl_File_E_INVAL;
+
+ (void) pthread_mutex_lock(&(pImpl->m_mutex));
+
+ /* close(2) implicitly (and unconditionally) unlocks */
+ oslFileError result = pImpl->syncFile();
+ if (result != osl_File_E_None)
+ {
+ /* close, ignoring double failure */
+ (void) close(pImpl->m_fd);
+ SAL_INFO("sal.file", "close(" << pImpl->m_fd << ")");
+ }
+ else if (close(pImpl->m_fd) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "close(" << pImpl->m_fd << "): " << UnixErrnoString(e));
+ /* translate error code */
+ result = oslTranslateFileError(e);
+ }
+ else
+ SAL_INFO("sal.file", "close(" << pImpl->m_fd << "): OK");
+
+ (void) pthread_mutex_unlock(&(pImpl->m_mutex));
+ delete pImpl;
+ return result;
+}
+
+oslFileError SAL_CALL osl_syncFile(oslFileHandle Handle)
+{
+ FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)))
+ return osl_File_E_INVAL;
+
+ if (pImpl->m_kind == FileHandle_Impl::KIND_MEM)
+ return osl_File_E_None;
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+
+ oslFileError result = pImpl->syncFile();
+
+ if (result != osl_File_E_None)
+ return result;
+
+ if (fsync(pImpl->m_fd) == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "fsync(" << pImpl->m_fd << "): " << UnixErrnoString(e));
+ return oslTranslateFileError(e);
+ }
+ else
+ SAL_INFO("sal.file", "fsync(" << pImpl->m_fd << "): OK");
+
+ return osl_File_E_None;
+}
+
+const off_t MAX_OFF_T = std::numeric_limits< off_t >::max();
+
+namespace {
+
+// coverity[result_independent_of_operands] - crossplatform requirement
+template<typename T> bool exceedsMaxOffT(T n) { return n > MAX_OFF_T; }
+
+// coverity[result_independent_of_operands] - crossplatform requirement
+template<typename T> bool exceedsMinOffT(T n)
+{ return n < std::numeric_limits<off_t>::min(); }
+
+}
+
+oslFileError SAL_CALL osl_mapFile(
+ oslFileHandle Handle,
+ void** ppAddr,
+ sal_uInt64 uLength,
+ sal_uInt64 uOffset,
+ sal_uInt32 uFlags
+)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!ppAddr))
+ return osl_File_E_INVAL;
+
+ *ppAddr = nullptr;
+
+ if (uLength > SAL_MAX_SIZE)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nLength = sal::static_int_cast< size_t >(uLength);
+
+ if (exceedsMaxOffT(uOffset))
+ return osl_File_E_OVERFLOW;
+
+ if (pImpl->m_kind == FileHandle_Impl::KIND_MEM)
+ {
+ *ppAddr = pImpl->m_buffer + uOffset;
+ return osl_File_E_None;
+ }
+
+ off_t const nOffset = sal::static_int_cast< off_t >(uOffset);
+
+ void* p = mmap(nullptr, nLength, PROT_READ, MAP_SHARED, pImpl->m_fd, nOffset);
+
+ if (p == MAP_FAILED)
+ return oslTranslateFileError(errno);
+
+ *ppAddr = p;
+
+ if (uFlags & osl_File_MapFlag_RandomAccess)
+ {
+ // Determine memory pagesize.
+ size_t const nPageSize = FileHandle_Impl::getpagesize();
+ if (nPageSize != size_t(-1))
+ {
+ /*
+ * Pagein, touching first byte of every memory page.
+ * Note: volatile disables optimizing the loop away.
+ */
+ sal_uInt8 volatile *pData(static_cast<sal_uInt8*>(*ppAddr));
+ size_t nSize(nLength);
+
+ while (nSize > nPageSize)
+ {
+ pData[0];
+ pData += nPageSize;
+ nSize -= nPageSize;
+ }
+
+ if (nSize > 0)
+ pData[0];
+ }
+ }
+
+ if (uFlags & osl_File_MapFlag_WillNeed)
+ {
+ // On Linux, madvise(..., MADV_WILLNEED) appears to have the undesirable
+ // effect of not returning until the data has actually been paged in, so
+ // that its net effect would typically be to slow down the process
+ // (which could start processing at the beginning of the data while the
+ // OS simultaneously pages in the rest); on other platforms, it remains
+ // to be evaluated whether madvise or equivalent is available and
+ // actually useful:
+#if defined MACOSX || (defined(__sun) && (!defined(__XOPEN_OR_POSIX) || defined(_XPG6) || defined(__EXTENSIONS__)))
+ int e = posix_madvise(p, nLength, POSIX_MADV_WILLNEED);
+ if (e != 0)
+ SAL_INFO("sal.file", "posix_madvise(..., POSIX_MADV_WILLNEED) failed with " << e);
+
+#elif defined __sun
+ if (madvise(static_cast< caddr_t >(p), nLength, MADV_WILLNEED) != 0)
+ SAL_INFO("sal.file", "madvise(..., MADV_WILLNEED) failed with " << UnixErrnoString(errno));
+#endif
+ }
+
+ return osl_File_E_None;
+}
+
+static oslFileError unmapFile(void* pAddr, sal_uInt64 uLength)
+{
+ if (!pAddr)
+ return osl_File_E_INVAL;
+
+ if (uLength > SAL_MAX_SIZE)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nLength = sal::static_int_cast< size_t >(uLength);
+
+ if (munmap(pAddr, nLength) == -1)
+ return oslTranslateFileError(errno);
+
+ return osl_File_E_None;
+}
+
+#ifndef ANDROID
+
+// Note that osl_unmapFile() just won't work on Android in general
+// where for (uncompressed) files inside the .apk, in the /assets
+// folder osl_mapFile just returns a pointer to the file inside the
+// already mmapped .apk archive.
+
+oslFileError SAL_CALL osl_unmapFile(void* pAddr, sal_uInt64 uLength)
+{
+ return unmapFile(pAddr, uLength);
+}
+
+#endif
+
+oslFileError SAL_CALL osl_unmapMappedFile(oslFileHandle Handle, void* pAddr, sal_uInt64 uLength)
+{
+ FileHandle_Impl *pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if (!pImpl)
+ return osl_File_E_INVAL;
+
+ if (pImpl->m_kind == FileHandle_Impl::KIND_FD)
+ return unmapFile(pAddr, uLength);
+
+ // For parts of already mmapped "parent" files, whose mapping we
+ // can't change, not much we can or should do...
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_readLine(
+ oslFileHandle Handle,
+ sal_Sequence ** ppSequence)
+{
+ FileHandle_Impl *pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!ppSequence))
+ return osl_File_E_INVAL;
+
+ sal_uInt64 uBytesRead = 0;
+
+ // read at current fileptr; fileptr += uBytesRead;
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ oslFileError result = pImpl->readLineAt(pImpl->m_fileptr, ppSequence, &uBytesRead);
+
+ if (result == osl_File_E_None)
+ pImpl->m_fileptr += uBytesRead;
+
+ return result;
+}
+
+oslFileError SAL_CALL osl_readFile(
+ oslFileHandle Handle,
+ void * pBuffer,
+ sal_uInt64 uBytesRequested,
+ sal_uInt64 * pBytesRead)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!pBuffer) || (!pBytesRead))
+ return osl_File_E_INVAL;
+
+ static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max();
+ if (g_limit_ssize_t < uBytesRequested)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nBytesRequested = sal::static_int_cast< size_t >(uBytesRequested);
+
+ // read at current fileptr; fileptr += *pBytesRead;
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ oslFileError result = pImpl->readFileAt(pImpl->m_fileptr, pBuffer, nBytesRequested, pBytesRead);
+
+ if (result == osl_File_E_None)
+ pImpl->m_fileptr += *pBytesRead;
+
+ return result;
+}
+
+oslFileError SAL_CALL osl_writeFile(
+ oslFileHandle Handle,
+ const void * pBuffer,
+ sal_uInt64 uBytesToWrite,
+ sal_uInt64 * pBytesWritten)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || (pImpl->m_fd == -1) || (!pBuffer) || (!pBytesWritten))
+ return osl_File_E_INVAL;
+
+ if (!(pImpl->m_state & State::Writeable))
+ return osl_File_E_BADF;
+
+ static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max();
+ if (g_limit_ssize_t < uBytesToWrite)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nBytesToWrite = sal::static_int_cast< size_t >(uBytesToWrite);
+
+ // write at current fileptr; fileptr += *pBytesWritten;
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ oslFileError result = pImpl->writeFileAt(pImpl->m_fileptr, pBuffer, nBytesToWrite, pBytesWritten);
+ if (result == osl_File_E_None)
+ pImpl->m_fileptr += *pBytesWritten;
+
+ return result;
+}
+
+oslFileError SAL_CALL osl_readFileAt(
+ oslFileHandle Handle,
+ sal_uInt64 uOffset,
+ void* pBuffer,
+ sal_uInt64 uBytesRequested,
+ sal_uInt64* pBytesRead)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!pBuffer) || (!pBytesRead))
+ return osl_File_E_INVAL;
+
+ if (!(pImpl->m_state & State::Seekable))
+ return osl_File_E_SPIPE;
+
+ if (exceedsMaxOffT(uOffset))
+ return osl_File_E_OVERFLOW;
+
+ off_t const nOffset = sal::static_int_cast< off_t >(uOffset);
+
+ static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max();
+ if (g_limit_ssize_t < uBytesRequested)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nBytesRequested = sal::static_int_cast< size_t >(uBytesRequested);
+
+ // read at specified fileptr
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+
+ return pImpl->readFileAt(nOffset, pBuffer, nBytesRequested, pBytesRead);
+}
+
+oslFileError SAL_CALL osl_writeFileAt(
+ oslFileHandle Handle,
+ sal_uInt64 uOffset,
+ const void* pBuffer,
+ sal_uInt64 uBytesToWrite,
+ sal_uInt64* pBytesWritten)
+{
+ FileHandle_Impl* pImpl = static_cast<FileHandle_Impl*>(Handle);
+
+ if ((!pImpl) || (pImpl->m_fd == -1) || (!pBuffer) || (!pBytesWritten))
+ return osl_File_E_INVAL;
+
+ if (!(pImpl->m_state & State::Seekable))
+ return osl_File_E_SPIPE;
+
+ if (!(pImpl->m_state & State::Writeable))
+ return osl_File_E_BADF;
+
+ if (exceedsMaxOffT(uOffset))
+ return osl_File_E_OVERFLOW;
+
+ off_t const nOffset = sal::static_int_cast< off_t >(uOffset);
+
+ static sal_uInt64 const g_limit_ssize_t = std::numeric_limits< ssize_t >::max();
+ if (g_limit_ssize_t < uBytesToWrite)
+ return osl_File_E_OVERFLOW;
+
+ size_t const nBytesToWrite = sal::static_int_cast< size_t >(uBytesToWrite);
+
+ // write at specified fileptr
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+
+ return pImpl->writeFileAt(nOffset, pBuffer, nBytesToWrite, pBytesWritten);
+}
+
+oslFileError SAL_CALL osl_isEndOfFile(oslFileHandle Handle, sal_Bool *pIsEOF)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!pIsEOF))
+ return osl_File_E_INVAL;
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ *pIsEOF = (pImpl->getPos() == pImpl->getSize());
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_getFilePos(oslFileHandle Handle, sal_uInt64* pPos)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!pPos))
+ return osl_File_E_INVAL;
+
+ // no need to lock because pos is atomic
+ *pPos = pImpl->getPos();
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_setFilePos(oslFileHandle Handle, sal_uInt32 uHow, sal_Int64 uOffset)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)))
+ return osl_File_E_INVAL;
+
+ if (exceedsMaxOffT(uOffset) || exceedsMinOffT(uOffset))
+ return osl_File_E_OVERFLOW;
+
+ off_t nPos = 0, nOffset = sal::static_int_cast< off_t >(uOffset);
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ switch (uHow)
+ {
+ case osl_Pos_Absolut:
+ if (nOffset < 0)
+ return osl_File_E_INVAL;
+ break;
+
+ case osl_Pos_Current:
+ nPos = sal::static_int_cast< off_t >(pImpl->getPos());
+ if ((nOffset < 0) && (nPos < -1*nOffset))
+ return osl_File_E_INVAL;
+
+ assert(nPos >= 0);
+ if (nOffset > MAX_OFF_T - nPos)
+ return osl_File_E_OVERFLOW;
+ break;
+
+ case osl_Pos_End:
+ nPos = sal::static_int_cast< off_t >(pImpl->getSize());
+ if ((nOffset < 0) && (nPos < -1*nOffset))
+ return osl_File_E_INVAL;
+
+ assert(nPos >= 0);
+ if (nOffset > MAX_OFF_T - nPos)
+ return osl_File_E_OVERFLOW;
+ break;
+
+ default:
+ return osl_File_E_INVAL;
+ }
+
+ pImpl->setPos(nPos + nOffset);
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_getFileSize(oslFileHandle Handle, sal_uInt64* pSize)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || ((pImpl->m_kind == FileHandle_Impl::KIND_FD) && (pImpl->m_fd == -1)) || (!pSize))
+ return osl_File_E_INVAL;
+
+ FileHandle_Impl::Guard lock(&(pImpl->m_mutex));
+ *pSize = pImpl->getSize();
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_setFileSize(oslFileHandle Handle, sal_uInt64 uSize)
+{
+ FileHandle_Impl* pImpl = static_cast< FileHandle_Impl* >(Handle);
+
+ if ((!pImpl) || (pImpl->m_fd == -1))
+ return osl_File_E_INVAL;
+
+ if (!(pImpl->m_state & State::Writeable))
+ return osl_File_E_BADF;
+
+ if (exceedsMaxOffT(uSize))
+ return osl_File_E_OVERFLOW;
+
+ oslFileError result = pImpl->syncFile();
+ if (result != osl_File_E_None)
+ return result;
+
+ pImpl->m_bufptr = -1;
+ pImpl->m_buflen = 0;
+
+ return pImpl->setSize(uSize);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_error_transl.cxx b/sal/osl/unx/file_error_transl.cxx
new file mode 100644
index 000000000..60824f71e
--- /dev/null
+++ b/sal/osl/unx/file_error_transl.cxx
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cerrno>
+
+#include "file_error_transl.hxx"
+
+oslFileError oslTranslateFileError(int Errno)
+{
+ switch(Errno)
+ {
+ case EPERM:
+ return osl_File_E_PERM;
+
+ case ENOENT:
+ return osl_File_E_NOENT;
+
+ case ESRCH:
+ return osl_File_E_SRCH;
+
+ case EINTR:
+ return osl_File_E_INTR;
+
+ case EIO:
+ return osl_File_E_IO;
+
+ case ENXIO:
+ return osl_File_E_IO;
+
+ case E2BIG:
+ return osl_File_E_2BIG;
+
+ case ENOEXEC:
+ return osl_File_E_NOEXEC;
+
+ case EBADF:
+ return osl_File_E_BADF;
+
+ case ECHILD:
+ return osl_File_E_CHILD;
+
+ case EAGAIN:
+ return osl_File_E_AGAIN;
+
+ case ENOMEM:
+ return osl_File_E_NOMEM;
+
+ case EACCES:
+ return osl_File_E_ACCES;
+
+ case EFAULT:
+ return osl_File_E_FAULT;
+
+ case EBUSY:
+ return osl_File_E_BUSY;
+
+ case EEXIST:
+ return osl_File_E_EXIST;
+
+ case EXDEV:
+ return osl_File_E_XDEV;
+
+ case ENODEV:
+ return osl_File_E_NODEV;
+
+ case ENOTDIR:
+ return osl_File_E_NOTDIR;
+
+ case EISDIR:
+ return osl_File_E_ISDIR;
+
+ case EINVAL:
+ return osl_File_E_INVAL;
+
+ case ENFILE:
+ return osl_File_E_NFILE;
+
+ case EMFILE:
+ return osl_File_E_MFILE;
+
+ case ENOTTY:
+ return osl_File_E_NOTTY;
+
+ case EFBIG:
+ return osl_File_E_FBIG;
+
+ case ENOSPC:
+ return osl_File_E_NOSPC;
+
+ case ESPIPE:
+ return osl_File_E_SPIPE;
+
+ case EROFS:
+ return osl_File_E_ROFS;
+
+ case EMLINK:
+ return osl_File_E_MLINK;
+
+ case EPIPE:
+ return osl_File_E_PIPE;
+
+ case EDOM:
+ return osl_File_E_DOM;
+
+ case ERANGE:
+ return osl_File_E_RANGE;
+
+ case EDEADLK:
+ return osl_File_E_DEADLK;
+
+ case ENAMETOOLONG:
+ return osl_File_E_NAMETOOLONG;
+
+ case ENOLCK:
+ return osl_File_E_NOLCK;
+
+ case ENOSYS:
+ case ENOTSUP:
+#if EOPNOTSUPP != ENOTSUP
+ case EOPNOTSUPP:
+#endif
+ return osl_File_E_NOSYS;
+
+#if !defined(AIX) || !(defined(_ALL_SOURCE) && !defined(_LINUX_SOURCE_COMPAT))
+ case ENOTEMPTY:
+ return osl_File_E_NOTEMPTY;
+#endif
+
+ case ELOOP:
+ return osl_File_E_LOOP;
+
+#if !(defined(MACOSX) || defined(NETBSD) || defined(FREEBSD) || defined(OPENBSD) || defined(DRAGONFLY))
+ case EILSEQ:
+ return osl_File_E_ILSEQ;
+
+ case ENOLINK:
+ return osl_File_E_NOLINK;
+
+ case EMULTIHOP:
+ return osl_File_E_MULTIHOP;
+#endif /* MACOSX */
+
+#if !defined(HAIKU)
+ case EUSERS:
+ return osl_File_E_USERS;
+#endif
+
+ case EOVERFLOW:
+ return osl_File_E_OVERFLOW;
+
+ case ETIMEDOUT:
+ return osl_File_E_TIMEDOUT;
+
+ default:
+ assert(Errno != 0);
+ /* FIXME translateFileError: is this alright? Or add a new one: osl_File_E_Unknown? */
+ return osl_File_E_invalidError;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_error_transl.hxx b/sal/osl/unx/file_error_transl.hxx
new file mode 100644
index 000000000..81d972032
--- /dev/null
+++ b/sal/osl/unx/file_error_transl.hxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_FILE_ERROR_TRANSL_HXX
+#define INCLUDED_SAL_OSL_UNX_FILE_ERROR_TRANSL_HXX
+
+#include <osl/file.h>
+#include <sal/types.h>
+
+/** Translate errno's to osl file errors
+
+ @param [in] nErrno the errno; must not be 0
+
+ @returns the osl error code appropriate to the errno
+
+*/
+oslFileError oslTranslateFileError(int Errno);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_impl.hxx b/sal/osl/unx/file_impl.hxx
new file mode 100644
index 000000000..ed8a93ea5
--- /dev/null
+++ b/sal/osl/unx/file_impl.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_FILE_IMPL_HXX
+#define INCLUDED_SAL_OSL_UNX_FILE_IMPL_HXX
+
+#include <osl/file.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+struct DirectoryItem_Impl
+{
+ sal_Int32 m_RefCount;
+
+ rtl_String * m_strFilePath; /* holds native file name */
+ unsigned char m_DType;
+
+ explicit DirectoryItem_Impl(
+ rtl_String * strFilePath, unsigned char DType = 0);
+ ~DirectoryItem_Impl();
+
+ static void * operator new(size_t n);
+ static void operator delete (void * p);
+
+ void acquire(); /* @see osl_acquireDirectoryItem() */
+ void release(); /* @see osl_releaseDirectoryItem() */
+
+ oslFileType getFileType() const;
+};
+
+oslFileError openFile(
+ rtl_uString * pustrFileURL, oslFileHandle * pHandle, sal_uInt32 uFlags,
+ mode_t mode);
+
+oslFileError openFilePath(
+ const char *cpFilePath,
+ oslFileHandle* pHandle,
+ sal_uInt32 uFlags, mode_t mode );
+
+#endif // INCLUDED_SAL_OSL_UNX_FILE_IMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_misc.cxx b/sal/osl/unx/file_misc.cxx
new file mode 100644
index 000000000..f9f316d8b
--- /dev/null
+++ b/sal/osl/unx/file_misc.cxx
@@ -0,0 +1,1062 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/file.hxx>
+#include <osl/detail/file.h>
+
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/signal.h>
+#include <rtl/alloc.h>
+#include <rtl/string.hxx>
+#include <sal/log.hxx>
+
+#include "system.hxx"
+#include "file_impl.hxx"
+#include "file_error_transl.hxx"
+#include "file_path_helper.hxx"
+#include "file_url.hxx"
+#include "uunxapi.hxx"
+#include "readwrite_helper.hxx"
+#include "unixerrnostring.hxx"
+
+#include <sys/types.h>
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <new>
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+/************************************************************************
+ * TODO
+ *
+ * - Fix: check for corresponding struct sizes in exported functions
+ * - check size/use of oslDirectory
+ * - check size/use of oslDirectoryItem
+ ***********************************************************************/
+
+namespace {
+
+struct DirectoryImpl
+{
+ OString strPath; /* holds native directory path */
+ DIR* pDirStruct;
+#ifdef ANDROID
+ enum Kind
+ {
+ KIND_DIRENT = 1,
+ KIND_ASSETS = 2
+ };
+ int eKind;
+ lo_apk_dir* pApkDirStruct;
+#endif
+};
+
+}
+
+DirectoryItem_Impl::DirectoryItem_Impl(
+ rtl_String * strFilePath, unsigned char DType)
+ : m_RefCount (1),
+ m_strFilePath (strFilePath),
+ m_DType (DType)
+{
+ if (m_strFilePath != nullptr)
+ rtl_string_acquire(m_strFilePath);
+}
+DirectoryItem_Impl::~DirectoryItem_Impl()
+{
+ if (m_strFilePath != nullptr)
+ rtl_string_release(m_strFilePath);
+}
+
+void * DirectoryItem_Impl::operator new(size_t n)
+{
+ return malloc(n);
+}
+void DirectoryItem_Impl::operator delete(void * p)
+{
+ free(p);
+}
+
+void DirectoryItem_Impl::acquire()
+{
+ ++m_RefCount;
+}
+void DirectoryItem_Impl::release()
+{
+ if (--m_RefCount == 0)
+ delete this;
+}
+
+oslFileType DirectoryItem_Impl::getFileType() const
+{
+ switch (m_DType)
+ {
+#ifdef _DIRENT_HAVE_D_TYPE
+ case DT_LNK:
+ return osl_File_Type_Link;
+ case DT_DIR:
+ return osl_File_Type_Directory;
+ case DT_REG:
+ return osl_File_Type_Regular;
+ case DT_FIFO:
+ return osl_File_Type_Fifo;
+ case DT_SOCK:
+ return osl_File_Type_Socket;
+ case DT_CHR:
+ case DT_BLK:
+ return osl_File_Type_Special;
+#endif /* _DIRENT_HAVE_D_TYPE */
+ default:
+ break;
+ }
+ return osl_File_Type_Unknown;
+}
+
+static oslFileError osl_psz_createDirectory(
+ char const * pszPath, sal_uInt32 flags);
+static oslFileError osl_psz_removeDirectory(const char* pszPath);
+
+oslFileError SAL_CALL osl_openDirectory(rtl_uString* ustrDirectoryURL, oslDirectory* pDirectory)
+{
+ oslFileError eRet;
+
+ OString path;
+
+ if ((ustrDirectoryURL == nullptr) || (ustrDirectoryURL->length == 0) || (pDirectory == nullptr))
+ return osl_File_E_INVAL;
+
+ /* convert file URL to system path */
+ eRet = osl::detail::convertUrlToPathname(OUString::unacquired(&ustrDirectoryURL), &path);
+
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+ osl_systemPathRemoveSeparator(path.pData);
+
+#ifdef MACOSX
+ {
+ auto const n = std::max(int(path.getLength() + 1), int(PATH_MAX));
+ auto const tmp = std::make_unique<char[]>(n);
+ std::strcpy(tmp.get(), path.getStr());
+ if (macxp_resolveAlias(tmp.get(), n) != 0) {
+ return oslTranslateFileError(errno);
+ }
+ path = OString(tmp.get(), std::strlen(tmp.get()));
+ }
+#endif /* MACOSX */
+
+#ifdef ANDROID
+ if( strncmp( path.getStr(), "/assets/", sizeof( "/assets/" ) - 1) == 0 )
+ {
+ lo_apk_dir *pdir = lo_apk_opendir( path.getStr() );
+
+ if( pdir )
+ {
+ DirectoryImpl* pDirImpl = new(std::nothrow) DirectoryImpl;
+
+ if( pDirImpl )
+ {
+ pDirImpl->eKind = DirectoryImpl::KIND_ASSETS;
+ pDirImpl->pApkDirStruct = pdir;
+ pDirImpl->strPath = path;
+
+ *pDirectory = (oslDirectory) pDirImpl;
+ return osl_File_E_None;
+ }
+ else
+ {
+ errno = ENOMEM;
+ lo_apk_closedir( pdir );
+ }
+ }
+ }
+ else
+#endif
+ {
+ /* open directory */
+ DIR *pdir = opendir( path.getStr() );
+
+ if( pdir )
+ {
+ SAL_INFO("sal.file", "opendir(" << path << ") => " << pdir);
+
+ /* create and initialize impl structure */
+ DirectoryImpl* pDirImpl = new(std::nothrow) DirectoryImpl;
+
+ if( pDirImpl )
+ {
+ pDirImpl->pDirStruct = pdir;
+ pDirImpl->strPath = path;
+#ifdef ANDROID
+ pDirImpl->eKind = DirectoryImpl::KIND_DIRENT;
+#endif
+ *pDirectory = static_cast<oslDirectory>(pDirImpl);
+ return osl_File_E_None;
+ }
+ errno = ENOMEM;
+ closedir( pdir );
+ }
+ else
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "opendir(" << path << "): " << UnixErrnoString(e));
+ // Restore errno after possible modification by SAL_INFO above
+ errno = e;
+ }
+ }
+
+ return oslTranslateFileError(errno);
+}
+
+oslFileError SAL_CALL osl_closeDirectory(oslDirectory pDirectory)
+{
+ SAL_WARN_IF(!pDirectory, "sal.file", "pDirectory is nullptr");
+ DirectoryImpl* pDirImpl = static_cast<DirectoryImpl*>(pDirectory);
+ oslFileError err = osl_File_E_None;
+
+ if (!pDirImpl)
+ return osl_File_E_INVAL;
+
+#ifdef ANDROID
+ if (pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
+ {
+ if (lo_apk_closedir(pDirImpl->pApkDirStruct))
+ err = osl_File_E_IO;
+ }
+ else
+#endif
+ {
+ if (closedir( pDirImpl->pDirStruct) != 0)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "closedir(" << pDirImpl->pDirStruct << "): " << UnixErrnoString(e));
+ err = oslTranslateFileError(e);
+ }
+ else
+ SAL_INFO("sal.file", "closedir(" << pDirImpl->pDirStruct << "): OK");
+ }
+
+ delete pDirImpl;
+
+ return err;
+}
+
+/**********************************************
+ * osl_readdir_impl_
+ *
+ * readdir wrapper, filters out "." and ".."
+ * on request
+ *********************************************/
+
+static struct dirent* osl_readdir_impl_(DIR* pdir)
+{
+ struct dirent* pdirent;
+
+ while ((pdirent = readdir(pdir)) != nullptr)
+ {
+ if ((strcmp(pdirent->d_name, ".") == 0) || (strcmp(pdirent->d_name, "..") == 0))
+ continue;
+ break;
+ }
+
+ return pdirent;
+}
+
+oslFileError SAL_CALL osl_getNextDirectoryItem(oslDirectory pDirectory,
+ oslDirectoryItem* pItem, SAL_UNUSED_PARAMETER sal_uInt32 /*uHint*/)
+{
+ SAL_WARN_IF(!pDirectory, "sal.file", "pDirectory is nullptr");
+ SAL_WARN_IF(!pItem, "sal.file", "pItem is nullptr");
+
+ DirectoryImpl* pDirImpl = static_cast<DirectoryImpl*>(pDirectory);
+ OString strFileName;
+ struct dirent* pEntry;
+
+ if ((pDirectory == nullptr) || (pItem == nullptr))
+ return osl_File_E_INVAL;
+
+#ifdef ANDROID
+ if(pDirImpl->eKind == DirectoryImpl::KIND_ASSETS)
+ {
+ pEntry = lo_apk_readdir(pDirImpl->pApkDirStruct);
+ }
+ else
+#endif
+ {
+ pEntry = osl_readdir_impl_(pDirImpl->pDirStruct);
+ }
+
+ if (!pEntry)
+ return osl_File_E_NOENT;
+
+ char const * filename = pEntry->d_name;
+
+#if defined(MACOSX)
+ // convert decomposed filename to precomposed UTF-8
+ char composed_name[BUFSIZ];
+ CFMutableStringRef strRef = CFStringCreateMutable(nullptr, 0 );
+ CFStringAppendCString(strRef, filename, kCFStringEncodingUTF8); // UTF8 is default on Mac OSX
+ CFStringNormalize(strRef, kCFStringNormalizationFormC);
+ CFStringGetCString(strRef, composed_name, BUFSIZ, kCFStringEncodingUTF8);
+ CFRelease(strRef);
+ filename = composed_name;
+#endif
+
+ strFileName = OString(filename, strlen(filename));
+
+ auto const strFilePath = osl::systemPathMakeAbsolutePath(pDirImpl->strPath, strFileName);
+
+ DirectoryItem_Impl* pImpl = static_cast< DirectoryItem_Impl* >(*pItem);
+ if (pImpl)
+ {
+ pImpl->release();
+ pImpl = nullptr;
+ }
+#ifdef _DIRENT_HAVE_D_TYPE
+ pImpl = new DirectoryItem_Impl(strFilePath.pData, pEntry->d_type);
+#else
+ pImpl = new DirectoryItem_Impl(strFilePath.pData);
+#endif /* _DIRENT_HAVE_D_TYPE */
+ *pItem = pImpl;
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_getDirectoryItem(rtl_uString* ustrFileURL, oslDirectoryItem* pItem)
+{
+ OString strSystemPath;
+ oslFileError osl_error = osl_File_E_INVAL;
+
+ if ((!ustrFileURL) || (ustrFileURL->length == 0) || (!pItem))
+ return osl_File_E_INVAL;
+
+ osl_error = osl::detail::convertUrlToPathname(OUString::unacquired(&ustrFileURL), &strSystemPath);
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+
+ osl_systemPathRemoveSeparator(strSystemPath.pData);
+
+ if (osl::access(strSystemPath, F_OK) == -1)
+ {
+ osl_error = oslTranslateFileError(errno);
+ }
+ else
+ {
+ *pItem = new DirectoryItem_Impl(strSystemPath.pData);
+ }
+
+ return osl_error;
+}
+
+oslFileError SAL_CALL osl_acquireDirectoryItem( oslDirectoryItem Item )
+{
+ DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
+ if (pImpl == nullptr)
+ return osl_File_E_INVAL;
+
+ pImpl->acquire();
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_releaseDirectoryItem( oslDirectoryItem Item )
+{
+ DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
+ if (pImpl == nullptr)
+ return osl_File_E_INVAL;
+
+ pImpl->release();
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_createDirectory( rtl_uString* ustrDirectoryURL )
+{
+ return osl_createDirectoryWithFlags(
+ ustrDirectoryURL, osl_File_OpenFlag_Read | osl_File_OpenFlag_Write);
+}
+
+oslFileError osl_createDirectoryWithFlags(
+ rtl_uString * ustrDirectoryURL, sal_uInt32 flags)
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ SAL_WARN_IF((!ustrDirectoryURL) || (ustrDirectoryURL->length == 0),
+ "sal.file", "Invalid directory URL");
+
+ /* convert directory url to system path */
+ eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_createDirectory( path, flags );
+}
+
+oslFileError SAL_CALL osl_removeDirectory( rtl_uString* ustrDirectoryURL )
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ SAL_WARN_IF((!ustrDirectoryURL) || (ustrDirectoryURL->length == 0),
+ "sal.file", "Invalid directory URL");
+
+ /* convert directory url to system path */
+ eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_removeDirectory( path );
+}
+
+oslFileError osl_psz_createDirectory(char const * pszPath, sal_uInt32 flags)
+{
+ int nRet=0;
+ int mode
+ = (((flags & osl_File_OpenFlag_Read) == 0
+ ? 0
+ : ((flags & osl_File_OpenFlag_Private) == 0
+ ? S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
+ : S_IRUSR | S_IXUSR))
+ | ((flags & osl_File_OpenFlag_Write) == 0
+ ? 0
+ : ((flags & osl_File_OpenFlag_Private) == 0
+ ? S_IWUSR | S_IWGRP | S_IWOTH
+ : S_IWUSR)));
+
+ nRet = mkdir(pszPath,mode);
+
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ SAL_INFO("sal.file", "mkdir(" << pszPath << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
+ return oslTranslateFileError(nRet);
+ }
+ else
+ SAL_INFO("sal.file", "mkdir(" << pszPath << ",0" << std::oct << mode << std::dec << "): OK");
+
+ return osl_File_E_None;
+}
+
+static oslFileError osl_psz_removeDirectory( const char* pszPath )
+{
+ int nRet = rmdir(pszPath);
+
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ SAL_INFO("sal.file", "rmdir(" << pszPath << "): " << UnixErrnoString(nRet));
+ return oslTranslateFileError(nRet);
+ }
+ else
+ SAL_INFO("sal.file", "rmdir(" << pszPath << "): OK");
+
+ return osl_File_E_None;
+}
+
+static int path_make_parent(char* path)
+{
+ int i = rtl_str_lastIndexOfChar(path, '/');
+
+ if (i > 0)
+ {
+ *(path + i) = 0;
+ return i;
+ }
+ return 0;
+}
+
+static int create_dir_with_callback(
+ char* directory_path,
+ oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
+ void* pData)
+{
+ if (osl::mkdir(directory_path, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
+ {
+ if (aDirectoryCreationCallbackFunc)
+ {
+ OUString url;
+ osl::detail::convertPathnameToUrl(directory_path, &url);
+ aDirectoryCreationCallbackFunc(pData, url.pData);
+ }
+ return 0;
+ }
+ return errno;
+}
+
+static oslFileError create_dir_recursively_(
+ char* dir_path,
+ oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
+ void* pData)
+{
+ OSL_PRECOND((rtl_str_getLength(dir_path) > 0) && ((dir_path + (rtl_str_getLength(dir_path) - 1)) != (dir_path + rtl_str_lastIndexOfChar(dir_path, '/'))),
+ "Path must not end with a slash");
+
+ int native_err = create_dir_with_callback(
+ dir_path, aDirectoryCreationCallbackFunc, pData);
+
+ if (native_err == 0)
+ return osl_File_E_None;
+
+ if (native_err != ENOENT)
+ return oslTranslateFileError(native_err);
+
+ // we step back until '/a_dir' at maximum because
+ // we should get an error unequal ENOENT when
+ // we try to create 'a_dir' at '/' and would so
+ // return before
+ int pos = path_make_parent(dir_path);
+
+ oslFileError osl_error = create_dir_recursively_(
+ dir_path, aDirectoryCreationCallbackFunc, pData);
+
+ if (osl_error != osl_File_E_None && osl_error != osl_File_E_EXIST)
+ return osl_error;
+
+ dir_path[pos] = '/';
+
+ return create_dir_recursively_(dir_path, aDirectoryCreationCallbackFunc, pData);
+}
+
+oslFileError SAL_CALL osl_createDirectoryPath(
+ rtl_uString* aDirectoryUrl,
+ oslDirectoryCreationCallbackFunc aDirectoryCreationCallbackFunc,
+ void* pData)
+{
+ if (aDirectoryUrl == nullptr)
+ return osl_File_E_INVAL;
+
+ OString sys_path;
+ oslFileError osl_error = osl::detail::convertUrlToPathname(
+ OUString::unacquired(&aDirectoryUrl), &sys_path);
+
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+
+ osl::systemPathRemoveSeparator(sys_path);
+
+ // const_cast because sys_path is a local copy which we want to modify inplace instead of
+ // copy it into another buffer on the heap again
+ return create_dir_recursively_(sys_path.pData->buffer, aDirectoryCreationCallbackFunc, pData);
+}
+
+static oslFileError osl_unlinkFile(const char* pszPath);
+static oslFileError osl_psz_copyFile(const char* pszPath, const char* pszDestPath, bool preserveMetadata);
+static oslFileError osl_psz_moveFile(const char* pszPath, const char* pszDestPath);
+
+static oslFileError oslDoCopy(const char* pszSourceFileName, const char* pszDestFileName, mode_t nMode, size_t nSourceSize, bool DestFileExists);
+static void attemptChangeMetadata(const char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID);
+static int oslDoCopyLink(const char* pszSourceFileName, const char* pszDestFileName);
+static int oslDoCopyFile(const char* pszSourceFileName, const char* pszDestFileName, size_t nSourceSize, mode_t mode);
+static oslFileError oslDoMoveFile(const char* pszPath, const char* pszDestPath);
+
+oslFileError SAL_CALL osl_moveFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
+{
+ char srcPath[PATH_MAX];
+ char destPath[PATH_MAX];
+ oslFileError eRet;
+
+ SAL_WARN_IF((!ustrFileURL) || (ustrFileURL->length == 0), "sal.file", "Invalid source file URL");
+ SAL_WARN_IF((!ustrDestURL) || (ustrDestURL->length == 0), "sal.file", "Invalid destination file URL");
+
+ /* convert source url to system path */
+ eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+ /* convert destination url to system path */
+ eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return oslDoMoveFile( srcPath, destPath );
+}
+
+oslFileError SAL_CALL osl_replaceFile(rtl_uString* ustrFileURL, rtl_uString* ustrDestURL)
+{
+ int nGid = -1;
+ char destPath[PATH_MAX];
+ oslFileError eRet = FileURLToPath(destPath, PATH_MAX, ustrDestURL);
+ if (eRet == osl_File_E_None)
+ {
+ struct stat aFileStat;
+ // coverity[fs_check_call] - unavoidable TOCTOU
+ int nRet = stat(destPath, &aFileStat);
+ if (nRet == -1)
+ {
+ nRet = errno;
+ SAL_INFO("sal.file", "stat(" << destPath << "): " << UnixErrnoString(nRet));
+ }
+ else
+ {
+ nGid = aFileStat.st_gid;
+ }
+ }
+
+ eRet = osl_moveFile(ustrFileURL, ustrDestURL);
+
+ if (eRet == osl_File_E_None && nGid != -1)
+ {
+ int nRet = chown(destPath, -1, nGid);
+ if (nRet == -1)
+ {
+ nRet = errno;
+ SAL_INFO("sal.file",
+ "chown(" << destPath << "-1, " << nGid << "): " << UnixErrnoString(nRet));
+ }
+ }
+
+ return eRet;
+}
+
+oslFileError SAL_CALL osl_copyFile( rtl_uString* ustrFileURL, rtl_uString* ustrDestURL )
+{
+ char srcPath[PATH_MAX];
+ char destPath[PATH_MAX];
+ oslFileError eRet;
+
+ SAL_WARN_IF((!ustrFileURL) || (ustrFileURL->length == 0), "sal.file", "Invalid source file URL");
+ SAL_WARN_IF((!ustrDestURL) || (ustrDestURL->length == 0), "sal.file", "Invalid destination file URL");
+
+ /* convert source url to system path */
+ eRet = FileURLToPath( srcPath, PATH_MAX, ustrFileURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+ /* convert destination url to system path */
+ eRet = FileURLToPath( destPath, PATH_MAX, ustrDestURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( srcPath, PATH_MAX ) != 0 || macxp_resolveAlias( destPath, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_copyFile( srcPath, destPath, false );
+}
+
+oslFileError SAL_CALL osl_removeFile(rtl_uString* ustrFileURL)
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ SAL_WARN_IF(!ustrFileURL || ustrFileURL->length == 0, "sal.file", "Invalid file URL");
+
+ /* convert file url to system path */
+ eRet = FileURLToPath(path, PATH_MAX, ustrFileURL);
+ if (eRet != osl_File_E_None)
+ return eRet;
+
+#ifdef MACOSX
+ if (macxp_resolveAlias(path, PATH_MAX) != 0)
+ return oslTranslateFileError(errno);
+#endif/* MACOSX */
+
+ return osl_unlinkFile(path);
+}
+
+static oslFileError oslDoMoveFile(const char* pszPath, const char* pszDestPath)
+{
+ oslFileError tErr = osl_psz_moveFile(pszPath,pszDestPath);
+ if (tErr == osl_File_E_None)
+ return tErr;
+
+ if (tErr != osl_File_E_XDEV)
+ return tErr;
+
+ tErr = osl_psz_copyFile(pszPath,pszDestPath, true);
+
+ if (tErr != osl_File_E_None)
+ {
+ osl_unlinkFile(pszDestPath);
+ return tErr;
+ }
+
+ tErr = osl_unlinkFile(pszPath);
+
+ return tErr;
+}
+
+static oslFileError osl_unlinkFile(const char* pszPath)
+{
+ int nRet=0;
+ struct stat aStat;
+
+ nRet = lstat_c(pszPath,&aStat);
+ if (nRet < 0)
+ {
+ nRet=errno;
+ return oslTranslateFileError(nRet);
+ }
+
+ if (S_ISDIR(aStat.st_mode))
+ return osl_File_E_ISDIR;
+
+ nRet = unlink(pszPath);
+ if (nRet < 0)
+ {
+ nRet=errno;
+ SAL_INFO("sal.file", "unlink(" << pszPath << "): " << UnixErrnoString(nRet));
+ return oslTranslateFileError(nRet);
+ }
+ else
+ SAL_INFO("sal.file", "unlink(" << pszPath << "): OK");
+
+ return osl_File_E_None;
+}
+
+static oslFileError osl_psz_moveFile(const char* pszPath, const char* pszDestPath)
+{
+ int nRet = rename(pszPath,pszDestPath);
+
+ if (nRet < 0)
+ {
+ nRet=errno;
+ SAL_INFO("sal.file", "rename(" << pszPath << "," << pszDestPath << "): " << UnixErrnoString(nRet));
+ return oslTranslateFileError(nRet);
+ }
+ else
+ SAL_INFO("sal.file", "rename(" << pszPath << "," << pszDestPath << "): OK");
+
+ return osl_File_E_None;
+}
+
+static oslFileError osl_psz_copyFile( const char* pszPath, const char* pszDestPath, bool preserveMetadata )
+{
+ time_t nAcTime=0;
+ time_t nModTime=0;
+ uid_t nUID=0;
+ gid_t nGID=0;
+ int nRet=0;
+ mode_t nMode=0;
+ struct stat aFileStat;
+ oslFileError tErr=osl_File_E_invalidError;
+ size_t nSourceSize=0;
+ bool DestFileExists=true;
+
+ /* mfe: does the source file really exists? */
+ nRet = lstat_c(pszPath,&aFileStat);
+
+ if (nRet < 0)
+ {
+ nRet=errno;
+ return oslTranslateFileError(nRet);
+ }
+
+ /* we do only copy files here */
+ if (S_ISDIR(aFileStat.st_mode))
+ return osl_File_E_ISDIR;
+
+ nSourceSize = static_cast< size_t >(aFileStat.st_size);
+ nMode = aFileStat.st_mode;
+ nAcTime = aFileStat.st_atime;
+ nModTime = aFileStat.st_mtime;
+ nUID = aFileStat.st_uid;
+ nGID = aFileStat.st_gid;
+
+ nRet = stat_c(pszDestPath,&aFileStat);
+ if (nRet < 0)
+ {
+ nRet=errno;
+
+#ifdef IOS
+ // Checking for nonexistent files at least in the iCloud cache directory (like
+ // "/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/helloodt0.odt" fails
+ // with EPERM, not ENOENT.
+ if (nRet == EPERM)
+ DestFileExists=false;
+#endif
+
+ if (nRet == ENOENT)
+ DestFileExists=false;
+ }
+
+ /* mfe: the destination file must not be a directory! */
+ if (nRet == 0 && S_ISDIR(aFileStat.st_mode))
+ return osl_File_E_ISDIR;
+
+ /* mfe: file does not exists or is no dir */
+
+ tErr = oslDoCopy(pszPath, pszDestPath, nMode, nSourceSize, DestFileExists);
+
+ if (tErr != osl_File_E_None)
+ return tErr;
+
+ if (preserveMetadata)
+ attemptChangeMetadata(pszDestPath, nMode, nAcTime, nModTime, nUID, nGID);
+
+ return tErr;
+}
+
+static oslFileError oslDoCopy(const char* pszSourceFileName, const char* pszDestFileName, mode_t nMode, size_t nSourceSize, bool DestFileExists)
+{
+ int nRet=0;
+
+ OString tmpDestFile;
+ if ( DestFileExists )
+ {
+ //TODO: better pick a temp file name instead of adding .osl-tmp:
+ // use the destination file to avoid EXDEV /* Cross-device link */
+ tmpDestFile = pszDestFileName + OStringLiteral(".osl-tmp");
+ if (rename(pszDestFileName, tmpDestFile.getStr()) != 0)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "rename(" << pszDestFileName << ", " << tmpDestFile
+ << "): " << UnixErrnoString(e));
+ if (e == ENOENT)
+ {
+ DestFileExists = false;
+ }
+ else
+ {
+ return osl_File_E_EXIST; // for want of a better error code
+ }
+ }
+ else
+ {
+ SAL_INFO("sal.file", "rename(" << pszDestFileName << ", " << tmpDestFile
+ << "): OK");
+ }
+ }
+
+ /* mfe: should be S_ISREG */
+ if ( !S_ISLNK(nMode) )
+ {
+ /* copy SourceFile to DestFile */
+ nRet = oslDoCopyFile(pszSourceFileName,pszDestFileName,nSourceSize, nMode);
+ }
+ /* mfe: OK redundant at the moment */
+ else if ( S_ISLNK(nMode) )
+ {
+ nRet = oslDoCopyLink(pszSourceFileName,pszDestFileName);
+ }
+ else
+ {
+ /* mfe: what to do here? */
+ nRet=ENOSYS;
+ }
+
+ if ( nRet > 0 && DestFileExists )
+ {
+ if (unlink(pszDestFileName) != 0)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "unlink(" << pszDestFileName << "): " << UnixErrnoString(e));
+ }
+ else
+ SAL_INFO("sal.file", "unlink(" << pszDestFileName << "): OK");
+
+ if (rename(tmpDestFile.getStr(), pszDestFileName) != 0)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "rename(" << tmpDestFile << ", " << pszDestFileName
+ << "): " << UnixErrnoString(e));
+ }
+ else
+ SAL_INFO("sal.file", "rename(" << tmpDestFile << ", " << pszDestFileName << "): OK");
+ }
+
+ if ( nRet > 0 )
+ {
+ return oslTranslateFileError(nRet);
+ }
+
+ if ( DestFileExists )
+ {
+ unlink(tmpDestFile.getStr());
+ }
+
+ return osl_File_E_None;
+}
+
+void attemptChangeMetadata( const char* pszFileName, mode_t nMode, time_t nAcTime, time_t nModTime, uid_t nUID, gid_t nGID)
+{
+ struct utimbuf aTimeBuffer;
+
+#if !defined AT_FDCWD
+ if (!S_ISLNK(nMode) && chmod(pszFileName, nMode) < 0)
+#else
+ if ( fchmodat(AT_FDCWD, pszFileName, nMode, AT_SYMLINK_NOFOLLOW) < 0 )
+#endif
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): " << UnixErrnoString(e));
+ }
+ else
+ SAL_INFO("sal.file", "chmod(" << pszFileName << ",0" << std::oct << nMode << std::dec <<"): OK");
+
+ // No way to change utime of a symlink itself:
+ if (!S_ISLNK(nMode))
+ {
+ aTimeBuffer.actime=nAcTime;
+ aTimeBuffer.modtime=nModTime;
+ if ( utime(pszFileName,&aTimeBuffer) < 0 )
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "utime(" << pszFileName << "): errno " << e);
+ }
+ }
+
+ if ( nUID != getuid() )
+ {
+ nUID=getuid();
+ }
+ if ( lchown(pszFileName,nUID,nGID) < 0 )
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "lchown(" << pszFileName << "): errno " << e);
+ }
+ else
+ SAL_INFO("sal.file", "lchown(" << pszFileName << "): OK");
+}
+
+static int oslDoCopyLink(const char* pszSourceFileName, const char* pszDestFileName)
+{
+ int nRet=0;
+
+ /* mfe: if dest file is symbolic link remove the link and place the file instead (hro says so) */
+ /* mfe: if source is a link copy the link and not the file it points to (hro says so) */
+ char pszLinkContent[PATH_MAX+1];
+
+ pszLinkContent[0] = '\0';
+
+ nRet = readlink(pszSourceFileName,pszLinkContent,PATH_MAX);
+
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ return nRet;
+ }
+
+ pszLinkContent[ nRet ] = 0;
+
+ nRet = symlink(pszLinkContent,pszDestFileName);
+
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ return nRet;
+ }
+
+ return 0;
+}
+
+static int oslDoCopyFile(const char* pszSourceFileName, const char* pszDestFileName, size_t nSourceSize, mode_t mode)
+{
+ oslFileHandle SourceFileFH=nullptr;
+ int DestFileFD=0;
+ int nRet=0;
+
+ if (openFilePath(pszSourceFileName,
+ &SourceFileFH,
+ osl_File_OpenFlag_Read|osl_File_OpenFlag_NoLock|osl_File_OpenFlag_NoExcl, mode_t(-1)) != osl_File_E_None)
+ {
+ // Let's hope errno is still set relevantly after openFilePath...
+ nRet=errno;
+ return nRet;
+ }
+
+ DestFileFD=open(pszDestFileName, O_WRONLY | O_CREAT, mode);
+
+ if ( DestFileFD < 0 )
+ {
+ nRet=errno;
+ SAL_INFO("sal.file", "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): " << UnixErrnoString(nRet));
+ osl_closeFile(SourceFileFH);
+ return nRet;
+ }
+ else
+ SAL_INFO("sal.file", "open(" << pszDestFileName << ",O_WRONLY|O_CREAT,0" << std::oct << mode << std::dec << "): OK");
+
+ size_t nRemains = nSourceSize;
+
+ if ( nRemains )
+ {
+ /* mmap has problems, try the direct streaming */
+ char pBuffer[0x7FFF];
+
+ do
+ {
+ size_t nToRead = std::min( sizeof(pBuffer), nRemains );
+ sal_uInt64 nRead;
+ bool succeeded;
+ if ( osl_readFile( SourceFileFH, pBuffer, nToRead, &nRead ) != osl_File_E_None || nRead > nToRead || nRead == 0 )
+ break;
+
+ succeeded = safeWrite( DestFileFD, pBuffer, nRead );
+ if ( !succeeded )
+ break;
+
+ // We know nRead <= nToRead, so it must fit in a size_t
+ nRemains -= static_cast<size_t>(nRead);
+ }
+ while( nRemains );
+ }
+
+ if ( nRemains )
+ {
+ if ( errno )
+ nRet = errno;
+ else
+ nRet = ENOSPC;
+ }
+
+ osl_closeFile( SourceFileFH );
+ if ( close( DestFileFD ) == -1 )
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "close(" << DestFileFD << "): " << UnixErrnoString(e));
+ if ( nRet == 0 )
+ nRet = e;
+ }
+ else
+ SAL_INFO("sal.file", "close(" << DestFileFD << "): OK");
+
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_path_helper.cxx b/sal/osl/unx/file_path_helper.cxx
new file mode 100644
index 000000000..080a99c09
--- /dev/null
+++ b/sal/osl/unx/file_path_helper.cxx
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include "file_path_helper.hxx"
+#include "uunxapi.hxx"
+
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+const sal_Unicode FPH_CHAR_PATH_SEPARATOR = '/';
+const sal_Unicode FPH_CHAR_DOT = '.';
+const sal_Unicode FPH_CHAR_COLON = ':';
+
+void osl_systemPathRemoveSeparator(rtl_String* pstrPath)
+{
+ OSL_PRECOND(nullptr != pstrPath, "osl_systemPathRemoveSeparator: Invalid parameter");
+ if (pstrPath == nullptr)
+ return;
+
+ // maybe there are more than one separator at end
+ // so we run in a loop
+ while ((pstrPath->length > 1) && (pstrPath->buffer[pstrPath->length - 1] == FPH_CHAR_PATH_SEPARATOR))
+ {
+ pstrPath->length--;
+ pstrPath->buffer[pstrPath->length] = '\0';
+ }
+
+ SAL_WARN_IF( !((0 == pstrPath->length) || (1 == pstrPath->length) ||
+ (pstrPath->length > 1 && pstrPath->buffer[pstrPath->length - 1] != FPH_CHAR_PATH_SEPARATOR)),
+ "sal.osl",
+ "osl_systemPathRemoveSeparator: Post condition failed");
+}
+
+namespace {
+
+template<typename T> void systemPathEnsureSeparator(T* ppstrPath)
+{
+ assert(nullptr != ppstrPath);
+ sal_Int32 lp = ppstrPath->getLength();
+ sal_Int32 i = ppstrPath->lastIndexOf(FPH_CHAR_PATH_SEPARATOR);
+
+ if ((lp > 1 && i != (lp - 1)) || ((lp < 2) && i < 0))
+ {
+ *ppstrPath += "/";
+ }
+
+ SAL_WARN_IF( !ppstrPath->endsWith("/"),
+ "sal.osl",
+ "systemPathEnsureSeparator: Post condition failed");
+}
+
+}
+
+bool osl_systemPathIsRelativePath(const rtl_uString* pustrPath)
+{
+ OSL_PRECOND(nullptr != pustrPath, "osl_systemPathIsRelativePath: Invalid parameter");
+ return ((pustrPath == nullptr) || (pustrPath->length == 0) || (pustrPath->buffer[0] != FPH_CHAR_PATH_SEPARATOR));
+}
+
+namespace {
+
+template<typename T> T systemPathMakeAbsolutePath_(
+ const T& BasePath,
+ const T& RelPath)
+{
+ T base(BasePath);
+
+ if (!base.isEmpty())
+ systemPathEnsureSeparator(&base);
+
+ return base + RelPath;
+}
+
+}
+
+OString osl::systemPathMakeAbsolutePath(
+ const OString& BasePath,
+ const OString& RelPath)
+{
+ return systemPathMakeAbsolutePath_(BasePath, RelPath);
+}
+
+OUString osl::systemPathMakeAbsolutePath(
+ const OUString& BasePath,
+ const OUString& RelPath)
+{
+ return systemPathMakeAbsolutePath_(BasePath, RelPath);
+}
+
+void osl_systemPathGetFileNameOrLastDirectoryPart(
+ const rtl_String* pstrPath,
+ rtl_String** ppstrFileNameOrLastDirPart)
+{
+ OSL_PRECOND(pstrPath && ppstrFileNameOrLastDirPart,
+ "osl_systemPathGetFileNameOrLastDirectoryPart: Invalid parameter");
+
+ OString path(const_cast<rtl_String*>(pstrPath));
+
+ osl_systemPathRemoveSeparator(path.pData);
+
+ OString last_part;
+
+ if (path.getLength() > 1 || (path.getLength() == 1 && path[0] != FPH_CHAR_PATH_SEPARATOR))
+ {
+ sal_Int32 idx_ps = path.lastIndexOf(FPH_CHAR_PATH_SEPARATOR);
+ idx_ps++; // always right to increment by one even if idx_ps == -1!
+ last_part = path.copy(idx_ps);
+ }
+ rtl_string_assign(ppstrFileNameOrLastDirPart, last_part.pData);
+}
+
+bool osl_systemPathIsHiddenFileOrDirectoryEntry(
+ const rtl_String* pstrPath)
+{
+ OSL_PRECOND(nullptr != pstrPath, "osl_systemPathIsHiddenFileOrDirectoryEntry: Invalid parameter");
+ if ((pstrPath == nullptr) || (pstrPath->length == 0))
+ return false;
+
+ OString fdp;
+ osl_systemPathGetFileNameOrLastDirectoryPart(pstrPath, &fdp.pData);
+
+ return ((fdp.pData->length > 0) &&
+ (fdp.pData->buffer[0] == FPH_CHAR_DOT) &&
+ !osl_systemPathIsLocalOrParentDirectoryEntry(fdp.pData));
+}
+
+bool osl_systemPathIsLocalOrParentDirectoryEntry(
+ const rtl_String* pstrPath)
+{
+ OSL_PRECOND(pstrPath, "osl_systemPathIsLocalOrParentDirectoryEntry: Invalid parameter");
+
+ OString dirent;
+
+ osl_systemPathGetFileNameOrLastDirectoryPart(pstrPath, &dirent.pData);
+
+ return (dirent == "." ||
+ dirent == "..");
+}
+
+namespace {
+
+/** Simple iterator for a path list separated by the specified character
+*/
+class path_list_iterator
+{
+public:
+
+ /* after construction get_current_item
+ returns the first path in list, no need
+ to call reset first
+ */
+ path_list_iterator(const OUString& path_list, sal_Unicode list_separator = FPH_CHAR_COLON) :
+ m_path_list(path_list),
+ m_end(m_path_list.getStr() + m_path_list.getLength() + 1),
+ m_separator(list_separator)
+ {
+ reset();
+ }
+
+ path_list_iterator(const path_list_iterator&) = delete;
+ path_list_iterator& operator=(const path_list_iterator&) = delete;
+
+ void reset()
+ {
+ m_path_segment_begin = m_path_segment_end = m_path_list.getStr();
+ advance();
+ }
+
+ void next()
+ {
+ OSL_PRECOND(!done(), "path_list_iterator: Already done!");
+
+ m_path_segment_begin = ++m_path_segment_end;
+ advance();
+ }
+
+ bool done() const
+ {
+ return (m_path_segment_end >= m_end);
+ }
+
+ OUString get_current_item() const
+ {
+ return OUString(
+ m_path_segment_begin,
+ (m_path_segment_end - m_path_segment_begin));
+ }
+
+private:
+ /* move m_path_end to the next separator or
+ to the end of the string
+ */
+ void advance()
+ {
+ while (!done() && *m_path_segment_end && (*m_path_segment_end != m_separator))
+ ++m_path_segment_end;
+
+ OSL_ASSERT(m_path_segment_end <= m_end);
+ }
+
+private:
+ OUString m_path_list;
+ const sal_Unicode* m_end;
+ const sal_Unicode m_separator;
+ const sal_Unicode* m_path_segment_begin;
+ const sal_Unicode* m_path_segment_end;
+};
+
+}
+
+bool osl_searchPath(
+ const rtl_uString* pustrFilePath,
+ const rtl_uString* pustrSearchPathList,
+ rtl_uString** ppustrPathFound)
+{
+ OSL_PRECOND(pustrFilePath && pustrSearchPathList && ppustrPathFound, "osl_searchPath: Invalid parameter");
+
+ bool bfound = false;
+ OUString fp(const_cast<rtl_uString*>(pustrFilePath));
+ OUString pl(const_cast<rtl_uString*>(pustrSearchPathList));
+ path_list_iterator pli(pl);
+
+ while (!pli.done())
+ {
+ OUString p = pli.get_current_item();
+ systemPathEnsureSeparator(&p);
+ p += fp;
+
+ if (osl::access(osl::OUStringToOString(p), F_OK) > -1)
+ {
+ bfound = true;
+ rtl_uString_assign(ppustrPathFound, p.pData);
+ break;
+ }
+ pli.next();
+ }
+ return bfound;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_path_helper.hxx b/sal/osl/unx/file_path_helper.hxx
new file mode 100644
index 000000000..e210b27a3
--- /dev/null
+++ b/sal/osl/unx/file_path_helper.hxx
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_FILE_PATH_HELPER_HXX
+#define INCLUDED_SAL_OSL_UNX_FILE_PATH_HELPER_HXX
+
+#include <sal/types.h>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+
+/**
+ Removes the last separator from the given system path if any and if the path
+ is not the root path '/'
+
+ @param ppstrPath[inout] a system path if the path is not the root path
+ and the last character is a path separator it
+ will be cut off ppstrPath must not be NULL and
+ must point to a valid rtl_String
+
+ @returns nothing
+
+*/
+void osl_systemPathRemoveSeparator(rtl_String* pstrPath);
+
+/**
+ Returns true if the given path is a relative path and so starts not with '/'
+
+ @param pustrPath [in] a system path - must not be NULL
+
+ @retval sal_True the given path doesn't start with a separator
+ @retval sal_False the given path starts with a separator
+
+*/
+bool osl_systemPathIsRelativePath(
+ const rtl_uString* pustrPath);
+
+/**
+ Returns the file or the directory part of the given path
+
+ @param pstrPath [in] a system path, must not be NULL
+
+ @param ppstrFileOrDirPart [out] on return receives the last part of the
+ given directory or the file name if pstrPath is the
+ root path '/' an empty string will be returned if
+ pstrPath has a trailing '/' the last part before the
+ '/' will be returned else the part after the last '/'
+ will be returned
+
+ @returns nothing
+
+*/
+void osl_systemPathGetFileNameOrLastDirectoryPart(
+ const rtl_String* pstrPath,
+ rtl_String** ppstrFileNameOrLastDirPart);
+
+/**
+ @param pustrPath [in] a system path, must not be NULL
+
+ @retval sal_True the last part of the given system path starts with '.'
+ @retval sal_False the last part of the given system path is '.' or '..'
+ alone or doesn't start with a dot
+
+*/
+bool osl_systemPathIsHiddenFileOrDirectoryEntry(
+ const rtl_String* pustrPath);
+
+/************************************************
+ osl_systemPathIsLocalOrParentDirectoryEntry
+ Returns sal_True if the last part of the given
+ system path is the local directory entry '.'
+ or the parent directory entry '..'
+
+ @param pstrPath [in] a system path,
+ must not be NULL
+
+ @returns sal_True if the last part of the
+ given system path is '.' or '..'
+ else sal_False
+
+************************************************/
+
+bool osl_systemPathIsLocalOrParentDirectoryEntry(
+ const rtl_String* pstrPath);
+
+/************************************************
+ osl_searchPath
+ Searches for a file name or path name in all
+ directories specified by a given path list.
+ Symbolic links in the resulting path will not be
+ resolved, it's up to the caller to do this.
+
+ @param pustrFilePath [in] a file name or
+ directory name to search for, the name must
+ be provided as system path not as a file URL
+
+ @param pustrSearchPathList [in] a ':'
+ separated list of paths in which to search for
+ the file or directory name
+
+ @param ppustrPathFound [out] on success receives the
+ complete path of the file or directory found
+ as a system path
+
+ @returns sal_True if the specified file or
+ directory was found else sal_False
+ ***********************************************/
+
+bool osl_searchPath(
+ const rtl_uString* pustrFilePath,
+ const rtl_uString* pustrSearchPathList,
+ rtl_uString** ppustrPathFound);
+
+namespace osl
+{
+
+ /*******************************************
+ systemPathRemoveSeparator
+ Removes the last separator from the
+ given system path if any and if the path
+ is not the root path '/'
+
+ @param ppustrPath [inout] a system path
+ if the path is not the root path
+ and the last character is a
+ path separator it will be cut off
+ ppustrPath must not be NULL and
+ must point to a valid rtl_uString
+
+ @returns nothing
+
+ ******************************************/
+
+ inline void systemPathRemoveSeparator(/*inout*/ OString& Path)
+ {
+ osl_systemPathRemoveSeparator(Path.pData);
+ }
+
+ /*******************************************
+ systemPathIsRelativePath
+ Returns true if the given path is a
+ relative path and so starts not with '/'
+
+ @param pustrPath [in] a system path
+ pustrPath must not be NULL
+
+ @returns sal_True if the given path
+ doesn't start with a separator
+ else sal_False will be returned
+
+ ******************************************/
+
+ inline bool systemPathIsRelativePath(const OUString& Path)
+ {
+ return osl_systemPathIsRelativePath(Path.pData);
+ }
+
+ /******************************************
+ systemPathMakeAbsolutePath
+ Append a relative path to a base path
+
+ @param BasePath [in] a system
+ path that will be considered as
+ base path
+
+ @param RelPath [in] a system path
+ that will be considered as
+ relative path
+
+ @return the
+ resulting path which is a
+ concatenation of the base and
+ the relative path
+ if base path is empty the
+ resulting absolute path is the
+ relative path
+ if relative path is empty the
+ resulting absolute path is the
+ base path
+ if base and relative path are
+ empty the resulting absolute
+ path is also empty
+
+ *****************************************/
+
+ OString systemPathMakeAbsolutePath(
+ const OString& BasePath,
+ const OString& RelPath);
+
+ OUString systemPathMakeAbsolutePath(
+ const OUString& BasePath,
+ const OUString& RelPath);
+
+ /********************************************
+ systemPathIsHiddenFileOrDirectoryEntry
+ Returns sal_True if the last part of
+ given system path is not '.' or '..'
+ alone and starts with a '.'
+
+ @param pustrPath [in] a system path,
+ must not be NULL
+
+ @returns sal_True if the last part of
+ the given system path starts
+ with '.' or sal_False the last
+ part is '.' or '..' alone or
+ doesn't start with a dot
+
+ *********************************************/
+
+ inline bool systemPathIsHiddenFileOrDirectoryEntry(
+ const OString& Path)
+ {
+ return osl_systemPathIsHiddenFileOrDirectoryEntry(Path.pData);
+ }
+
+ /************************************************
+ searchPath
+ ***********************************************/
+
+ inline bool searchPath(
+ const OUString& ustrFilePath,
+ const OUString& ustrSearchPathList,
+ OUString& ustrPathFound)
+ {
+ return osl_searchPath(
+ ustrFilePath.pData,
+ ustrSearchPathList.pData,
+ &ustrPathFound.pData);
+ }
+
+ } // namespace osl
+
+ #endif /* #ifndef _OSL_PATH_HELPER_HXX_ */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_stat.cxx b/sal/osl/unx/file_stat.cxx
new file mode 100644
index 000000000..4d7a7cb43
--- /dev/null
+++ b/sal/osl/unx/file_stat.cxx
@@ -0,0 +1,453 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/file.h>
+
+#include "system.hxx"
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+
+#include "file_impl.hxx"
+#include "file_error_transl.hxx"
+#include "file_path_helper.hxx"
+#include "file_url.hxx"
+#include "uunxapi.hxx"
+
+namespace
+{
+ void set_file_type(const struct stat& file_stat, oslFileStatus* pStat)
+ {
+ /* links to directories state also to be a directory */
+ if (S_ISLNK(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Link;
+ else if (S_ISDIR(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Directory;
+ else if (S_ISREG(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Regular;
+ else if (S_ISFIFO(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Fifo;
+ else if (S_ISSOCK(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Socket;
+ else if (S_ISCHR(file_stat.st_mode) || S_ISBLK(file_stat.st_mode))
+ pStat->eType = osl_File_Type_Special;
+ else
+ pStat->eType = osl_File_Type_Unknown;
+
+ pStat->uValidFields |= osl_FileStatus_Mask_Type;
+ }
+
+ void set_file_access_mask(const struct stat& file_stat, oslFileStatus* pStat)
+ {
+ // user permissions
+ if (S_IRUSR & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OwnRead;
+
+ if (S_IWUSR & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OwnWrite;
+
+ if (S_IXUSR & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OwnExe;
+
+ // group permissions
+ if (S_IRGRP & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_GrpRead;
+
+ if (S_IWGRP & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_GrpWrite;
+
+ if (S_IXGRP & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_GrpExe;
+
+ // others permissions
+ if (S_IROTH & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OthRead;
+
+ if (S_IWOTH & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OthWrite;
+
+ if (S_IXOTH & file_stat.st_mode)
+ pStat->uAttributes |= osl_File_Attribute_OthExe;
+
+ pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
+ }
+
+ /* This code used not to use access(...) because access follows links which
+ may cause performance problems see #97133. (That apparently references a
+ no-longer accessible Hamburg-internal bug-tracking system.)
+ However, contrary to what is stated above the use of access calls is
+ required on network file systems not using unix semantics (AFS, see
+ fdo#43095).
+ */
+ void set_file_access_rights(const OString& file_path, oslFileStatus* pStat)
+ {
+ pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
+
+ if (osl::access(file_path, W_OK) < 0)
+ pStat->uAttributes |= osl_File_Attribute_ReadOnly;
+
+ if (osl::access(file_path, X_OK) == 0)
+ pStat->uAttributes |= osl_File_Attribute_Executable;
+ }
+
+ void set_file_hidden_status(const OString& file_path, oslFileStatus* pStat)
+ {
+ pStat->uAttributes = osl::systemPathIsHiddenFileOrDirectoryEntry(file_path) ? osl_File_Attribute_Hidden : 0;
+ pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
+ }
+
+ /* the set_file_access_rights must be called after set_file_hidden_status(...) and
+ set_file_access_mask(...) because of the hack in set_file_access_rights(...) */
+ void set_file_attributes(
+ const OString& file_path, const struct stat& file_stat, const sal_uInt32 uFieldMask, oslFileStatus* pStat)
+ {
+ set_file_hidden_status(file_path, pStat);
+ set_file_access_mask(file_stat, pStat);
+
+ // we set the file access rights only on demand
+ // because it's potentially expensive
+ if (uFieldMask & osl_FileStatus_Mask_Attributes)
+ set_file_access_rights(file_path, pStat);
+ }
+
+ void set_file_access_time(const struct stat& file_stat, oslFileStatus* pStat)
+ {
+ pStat->aAccessTime.Seconds = file_stat.st_atime;
+ pStat->aAccessTime.Nanosec = 0;
+ pStat->uValidFields |= osl_FileStatus_Mask_AccessTime;
+ }
+
+ void set_file_modify_time(const struct stat& file_stat, oslFileStatus* pStat)
+ {
+ pStat->aModifyTime.Seconds = file_stat.st_mtime;
+ pStat->aModifyTime.Nanosec = 0;
+ pStat->uValidFields |= osl_FileStatus_Mask_ModifyTime;
+ }
+
+ void set_file_size(const struct stat& file_stat, oslFileStatus* pStat)
+ {
+ if (S_ISREG(file_stat.st_mode))
+ {
+ pStat->uFileSize = file_stat.st_size;
+ pStat->uValidFields |= osl_FileStatus_Mask_FileSize;
+ }
+ }
+
+ /* we only need to call stat or lstat if one of the
+ following flags is set */
+ bool is_stat_call_necessary(sal_uInt32 field_mask, oslFileType file_type)
+ {
+ return (
+ ((field_mask & osl_FileStatus_Mask_Type) && (file_type == osl_File_Type_Unknown)) ||
+ (field_mask & osl_FileStatus_Mask_Attributes) ||
+ (field_mask & osl_FileStatus_Mask_CreationTime) ||
+ (field_mask & osl_FileStatus_Mask_AccessTime) ||
+ (field_mask & osl_FileStatus_Mask_ModifyTime) ||
+ (field_mask & osl_FileStatus_Mask_FileSize) ||
+ (field_mask & osl_FileStatus_Mask_LinkTargetURL) ||
+ (field_mask & osl_FileStatus_Mask_Validate));
+ }
+
+ oslFileError set_link_target_url(const OString& file_path, oslFileStatus* pStat)
+ {
+ OString link_target;
+ if (!osl::realpath(file_path, link_target))
+ return oslTranslateFileError(errno);
+
+ OUString url;
+ oslFileError osl_error = osl::detail::convertPathnameToUrl(link_target, &url);
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+ rtl_uString_assign(&pStat->ustrLinkTargetURL, url.pData);
+
+ pStat->uValidFields |= osl_FileStatus_Mask_LinkTargetURL;
+ return osl_File_E_None;
+ }
+
+ oslFileError setup_osl_getFileStatus(
+ DirectoryItem_Impl * pImpl, oslFileStatus* pStat, OString& file_path)
+ {
+ if ((pImpl == nullptr) || (pStat == nullptr))
+ return osl_File_E_INVAL;
+
+ file_path = OString(pImpl->m_strFilePath);
+ OSL_ASSERT(!file_path.isEmpty());
+ if (file_path.isEmpty())
+ return osl_File_E_INVAL;
+
+ pStat->uValidFields = 0;
+ return osl_File_E_None;
+ }
+
+}
+
+oslFileError SAL_CALL osl_getFileStatus(oslDirectoryItem Item, oslFileStatus* pStat, sal_uInt32 uFieldMask)
+{
+ DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
+
+ OString file_path;
+ oslFileError osl_error = setup_osl_getFileStatus(pImpl, pStat, file_path);
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+
+ struct stat file_stat;
+
+ bool bStatNeeded = is_stat_call_necessary(uFieldMask, pImpl->getFileType());
+ if (bStatNeeded && (osl::lstat(file_path, file_stat) != 0))
+ return oslTranslateFileError(errno);
+
+ if (bStatNeeded)
+ {
+ // we set all these attributes because it's cheap
+ set_file_type(file_stat, pStat);
+ set_file_access_time(file_stat, pStat);
+ set_file_modify_time(file_stat, pStat);
+ set_file_size(file_stat, pStat);
+ set_file_attributes(file_path, file_stat, uFieldMask, pStat);
+
+ // file exists semantic of osl_FileStatus_Mask_Validate
+ if ((uFieldMask & osl_FileStatus_Mask_LinkTargetURL) && S_ISLNK(file_stat.st_mode))
+ {
+ osl_error = set_link_target_url(file_path, pStat);
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+ }
+ }
+#ifdef _DIRENT_HAVE_D_TYPE
+ else if (uFieldMask & osl_FileStatus_Mask_Type)
+ {
+ pStat->eType = pImpl->getFileType();
+ pStat->uValidFields |= osl_FileStatus_Mask_Type;
+ }
+#endif /* _DIRENT_HAVE_D_TYPE */
+
+ if (uFieldMask & osl_FileStatus_Mask_FileURL)
+ {
+ OUString url;
+ if ((osl_error = osl::detail::convertPathnameToUrl(file_path, &url)) != osl_File_E_None)
+ return osl_error;
+ rtl_uString_assign(&pStat->ustrFileURL, url.pData);
+
+ pStat->uValidFields |= osl_FileStatus_Mask_FileURL;
+ }
+
+ if (uFieldMask & osl_FileStatus_Mask_FileName)
+ {
+ OString name;
+ osl_systemPathGetFileNameOrLastDirectoryPart(file_path.pData, &name.pData);
+ bool ok = rtl_convertStringToUString(
+ &pStat->ustrFileName, name.getStr(), name.getLength(), osl_getThreadTextEncoding(),
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT));
+ assert(ok); (void)ok;
+ pStat->uValidFields |= osl_FileStatus_Mask_FileName;
+ }
+ return osl_File_E_None;
+}
+
+static oslFileError osl_psz_setFileAttributes( const char* pszFilePath, sal_uInt64 uAttributes )
+{
+ oslFileError osl_error = osl_File_E_None;
+ mode_t nNewMode = 0;
+
+ OSL_ENSURE(!(osl_File_Attribute_Hidden & uAttributes), "osl_File_Attribute_Hidden doesn't work under Unix");
+
+ if (uAttributes & osl_File_Attribute_OwnRead)
+ nNewMode |= S_IRUSR;
+
+ if (uAttributes & osl_File_Attribute_OwnWrite)
+ nNewMode|=S_IWUSR;
+
+ if (uAttributes & osl_File_Attribute_OwnExe)
+ nNewMode|=S_IXUSR;
+
+ if (uAttributes & osl_File_Attribute_GrpRead)
+ nNewMode|=S_IRGRP;
+
+ if (uAttributes & osl_File_Attribute_GrpWrite)
+ nNewMode|=S_IWGRP;
+
+ if (uAttributes & osl_File_Attribute_GrpExe)
+ nNewMode|=S_IXGRP;
+
+ if (uAttributes & osl_File_Attribute_OthRead)
+ nNewMode|=S_IROTH;
+
+ if (uAttributes & osl_File_Attribute_OthWrite)
+ nNewMode|=S_IWOTH;
+
+ if (uAttributes & osl_File_Attribute_OthExe)
+ nNewMode|=S_IXOTH;
+
+ if (chmod(pszFilePath, nNewMode) < 0)
+ osl_error = oslTranslateFileError(errno);
+
+ return osl_error;
+}
+
+oslFileError SAL_CALL osl_setFileAttributes( rtl_uString* ustrFileURL, sal_uInt64 uAttributes )
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ OSL_ASSERT( ustrFileURL );
+
+ /* convert file url to system path */
+ eRet = FileURLToPath( path, PATH_MAX, ustrFileURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_setFileAttributes( path, uAttributes );
+}
+
+static oslFileError osl_psz_setFileTime (
+ const char* pszFilePath,
+ const TimeValue* pLastAccessTime,
+ const TimeValue* pLastWriteTime )
+{
+ int nRet=0;
+ struct utimbuf aTimeBuffer;
+ struct stat aFileStat;
+#ifdef DEBUG_OSL_FILE
+ struct tm* pTM=0;
+#endif
+
+ nRet = lstat_c(pszFilePath,&aFileStat);
+
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ return oslTranslateFileError(nRet);
+ }
+
+#ifdef DEBUG_OSL_FILE
+ fprintf(stderr,"File Times are (in localtime):\n");
+ pTM=localtime(&aFileStat.st_ctime);
+ fprintf(stderr,"CreationTime is '%s'\n",asctime(pTM));
+ pTM=localtime(&aFileStat.st_atime);
+ fprintf(stderr,"AccessTime is '%s'\n",asctime(pTM));
+ pTM=localtime(&aFileStat.st_mtime);
+ fprintf(stderr,"Modification is '%s'\n",asctime(pTM));
+
+ fprintf(stderr,"File Times are (in UTC):\n");
+ fprintf(stderr,"CreationTime is '%s'\n",ctime(&aFileStat.st_ctime));
+ fprintf(stderr,"AccessTime is '%s'\n",ctime(&aTimeBuffer.actime));
+ fprintf(stderr,"Modification is '%s'\n",ctime(&aTimeBuffer.modtime));
+#endif
+
+ if ( pLastAccessTime != nullptr )
+ {
+ aTimeBuffer.actime=pLastAccessTime->Seconds;
+ }
+ else
+ {
+ aTimeBuffer.actime=aFileStat.st_atime;
+ }
+
+ if ( pLastWriteTime != nullptr )
+ {
+ aTimeBuffer.modtime=pLastWriteTime->Seconds;
+ }
+ else
+ {
+ aTimeBuffer.modtime=aFileStat.st_mtime;
+ }
+
+ /* mfe: Creation time not used here! */
+
+#ifdef DEBUG_OSL_FILE
+ fprintf(stderr,"File Times are (in localtime):\n");
+ pTM=localtime(&aFileStat.st_ctime);
+ fprintf(stderr,"CreationTime now '%s'\n",asctime(pTM));
+ pTM=localtime(&aTimeBuffer.actime);
+ fprintf(stderr,"AccessTime now '%s'\n",asctime(pTM));
+ pTM=localtime(&aTimeBuffer.modtime);
+ fprintf(stderr,"Modification now '%s'\n",asctime(pTM));
+
+ fprintf(stderr,"File Times are (in UTC):\n");
+ fprintf(stderr,"CreationTime now '%s'\n",ctime(&aFileStat.st_ctime));
+ fprintf(stderr,"AccessTime now '%s'\n",ctime(&aTimeBuffer.actime));
+ fprintf(stderr,"Modification now '%s'\n",ctime(&aTimeBuffer.modtime));
+#endif
+
+ nRet = utime_c(pszFilePath,&aTimeBuffer);
+ if ( nRet < 0 )
+ {
+ nRet=errno;
+ return oslTranslateFileError(nRet);
+ }
+
+ return osl_File_E_None;
+}
+
+oslFileError SAL_CALL osl_setFileTime (
+ rtl_uString* ustrFileURL,
+ SAL_UNUSED_PARAMETER const TimeValue* /* pCreationTime */,
+ const TimeValue* pLastAccessTime,
+ const TimeValue* pLastWriteTime )
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ OSL_ASSERT( ustrFileURL );
+
+ /* convert file url to system path */
+ eRet = FileURLToPath( path, PATH_MAX, ustrFileURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_setFileTime( path, pLastAccessTime, pLastWriteTime );
+}
+
+sal_Bool
+SAL_CALL osl_identicalDirectoryItem( oslDirectoryItem a, oslDirectoryItem b)
+{
+ DirectoryItem_Impl *pA = static_cast<DirectoryItem_Impl *>(a);
+ DirectoryItem_Impl *pB = static_cast<DirectoryItem_Impl *>(b);
+ if (a == b)
+ return true;
+ /* same name => same item, unless renaming / moving madness has occurred */
+ if (pA->m_strFilePath == pB->m_strFilePath)
+ return true;
+
+ struct stat a_stat, b_stat;
+
+ if (osl::lstat(OString(pA->m_strFilePath), a_stat) != 0 ||
+ osl::lstat(OString(pB->m_strFilePath), b_stat) != 0)
+ return false;
+
+ return (a_stat.st_ino == b_stat.st_ino);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_url.cxx b/sal/osl/unx/file_url.cxx
new file mode 100644
index 000000000..5e99d5306
--- /dev/null
+++ b/sal/osl/unx/file_url.cxx
@@ -0,0 +1,986 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "file_url.hxx"
+
+#include "system.hxx"
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <stdexcept>
+#include <string_view>
+#include <type_traits>
+#include <limits.h>
+#include <errno.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <o3tl/safeint.hxx>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <osl/socket.h>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/process.h>
+
+#include <rtl/character.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/uri.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/textcvt.h>
+#include <sal/log.hxx>
+
+#include <uri_internal.hxx>
+
+#include "file_error_transl.hxx"
+#include "file_path_helper.hxx"
+
+#include "uunxapi.hxx"
+
+/** @file
+
+ General note
+
+ This file contains the part that handles File URLs.
+
+ File URLs as scheme specific notion of URIs
+ (RFC2396) may be handled platform independent, but
+ will not in osl which is considered wrong.
+ Future version of osl should handle File URLs this
+ way. In rtl/uri there is already a URI parser etc.
+ so this code should be consolidated.
+
+*/
+
+using namespace osl;
+
+namespace {
+
+// A slightly modified version of Pchar in rtl/source/uri.c, but without
+// encoding slashes:
+const sal_Bool uriCharClass[128] = {
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, true, false, false, true, false, true, true, // !"#$%&'
+ true, true, true, true, true, true, true, true, // ()*+,-./
+ true, true, true, true, true, true, true, true, // 01234567
+ true, true, true, false, false, true, false, false, // 89:;<=>?
+ true, true, true, true, true, true, true, true, // @ABCDEFG
+ true, true, true, true, true, true, true, true, // HIJKLMNO
+ true, true, true, true, true, true, true, true, // PQRSTUVW
+ true, true, true, false, false, false, false, true, // XYZ[\]^_
+ false, true, true, true, true, true, true, true, // `abcdefg
+ true, true, true, true, true, true, true, true, // hijklmno
+ true, true, true, true, true, true, true, true, // pqrstuvw
+ true, true, true, false, false, false, true, false}; // xyz{|}~
+
+}
+
+oslFileError SAL_CALL osl_getCanonicalName( rtl_uString* ustrFileURL, rtl_uString** pustrValidURL )
+{
+ OSL_FAIL("osl_getCanonicalName not implemented");
+
+ rtl_uString_newFromString(pustrValidURL, ustrFileURL);
+ return osl_File_E_None;
+}
+
+namespace {
+
+ class UnicodeToTextConverter_Impl
+ {
+ rtl_UnicodeToTextConverter m_converter;
+
+ UnicodeToTextConverter_Impl()
+ : m_converter (rtl_createUnicodeToTextConverter (osl_getThreadTextEncoding()))
+ {}
+
+ ~UnicodeToTextConverter_Impl()
+ {
+ rtl_destroyUnicodeToTextConverter (m_converter);
+ }
+ public:
+ static UnicodeToTextConverter_Impl & getInstance()
+ {
+ static UnicodeToTextConverter_Impl g_theConverter;
+ return g_theConverter;
+ }
+
+ sal_Size convert(
+ sal_Unicode const * pSrcBuf, sal_Size nSrcChars, char * pDstBuf, sal_Size nDstBytes,
+ sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtChars)
+ {
+ OSL_ASSERT(m_converter != nullptr);
+ return rtl_convertUnicodeToText (
+ m_converter, nullptr, pSrcBuf, nSrcChars, pDstBuf, nDstBytes, nFlags, pInfo, pSrcCvtChars);
+ }
+ };
+
+bool convert(OUStringBuffer const & in, OStringBuffer * append) {
+ assert(append != nullptr);
+ for (sal_Size nConvert = in.getLength();;) {
+ auto const oldLen = append->getLength();
+ auto n = std::min(
+ std::max(nConvert, sal_Size(PATH_MAX)),
+ sal_Size(std::numeric_limits<sal_Int32>::max() - oldLen));
+ // approximation of required converted size
+ auto s = append->appendUninitialized(n);
+ sal_uInt32 info;
+ sal_Size converted;
+ //TODO: context, for reliable treatment of DESTBUFFERTOSMALL:
+ n = UnicodeToTextConverter_Impl::getInstance().convert(
+ in.getStr() + in.getLength() - nConvert, nConvert, s, n,
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_FLUSH),
+ &info, &converted);
+ if ((info & RTL_UNICODETOTEXT_INFO_ERROR) != 0) {
+ return false;
+ }
+ append->setLength(oldLen + n);
+ assert(converted <= nConvert);
+ nConvert -= converted;
+ assert((nConvert == 0) == ((info & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0));
+ if ((info & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0) {
+ break;
+ }
+ }
+ return true;
+}
+
+bool decodeFromUtf8(std::u16string_view text, OString * result) {
+ assert(result != nullptr);
+ auto p = text.data();
+ auto const end = p + text.size();
+ OUStringBuffer ubuf(static_cast<int>(text.size()));
+ OStringBuffer bbuf(PATH_MAX);
+ while (p < end) {
+ rtl::uri::detail::EscapeType t;
+ sal_uInt32 c = rtl::uri::detail::readUcs4(&p, end, true, RTL_TEXTENCODING_UTF8, &t);
+ switch (t) {
+ case rtl::uri::detail::EscapeNo:
+ if (c == '%') {
+ return false;
+ }
+ [[fallthrough]];
+ case rtl::uri::detail::EscapeChar:
+ if (rtl::isSurrogate(c)) {
+ return false;
+ }
+ ubuf.appendUtf32(c);
+ break;
+ case rtl::uri::detail::EscapeOctet:
+ if (!convert(ubuf, &bbuf)) {
+ return false;
+ }
+ ubuf.setLength(0);
+ assert(c <= 0xFF);
+ bbuf.append(char(c));
+ break;
+ }
+ }
+ if (!convert(ubuf, &bbuf)) {
+ return false;
+ }
+ *result = bbuf.makeStringAndClear();
+ return true;
+}
+
+template<typename T> oslFileError getSystemPathFromFileUrl(
+ OUString const & url, T * path, bool resolveHome)
+{
+ assert(path != nullptr);
+ // For compatibility with assumptions in other parts of the code base,
+ // assume that anything starting with a slash is a system path instead of a
+ // (relative) file URL (except if it starts with two slashes, in which case
+ // it is a relative URL with an authority component):
+ if (url.isEmpty()
+ || (url[0] == '/' && (url.getLength() == 1 || url[1] != '/')))
+ {
+ return osl_File_E_INVAL;
+ }
+ // Check for non file scheme:
+ sal_Int32 i = 0;
+ if (rtl::isAsciiAlpha(url[0])) {
+ for (sal_Int32 j = 1; j != url.getLength(); ++j) {
+ auto c = url[j];
+ if (c == ':') {
+ if (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
+ url.pData->buffer, j,
+ RTL_CONSTASCII_STRINGPARAM("file"))
+ != 0)
+ {
+ return osl_File_E_INVAL;
+ }
+ i = j + 1;
+ break;
+ }
+ if (!rtl::isAsciiAlphanumeric(c) && c != '+' && c != '-'
+ && c != '.')
+ {
+ break;
+ }
+ }
+ }
+ // Handle query or fragment:
+ if (url.indexOf('?', i) != -1 || url.indexOf('#', i) != -1)
+ return osl_File_E_INVAL;
+ // Handle authority, supporting a host of "localhost", "127.0.0.1", or the exact value (e.g.,
+ // not supporting an additional final dot, for simplicity) reported by osl_getLocalHostname
+ // (and, in each case, ignoring case of ASCII letters):
+ if (url.getLength() - i >= 2 && url[i] == '/' && url[i + 1] == '/')
+ {
+ i += 2;
+ sal_Int32 j = url.indexOf('/', i);
+ if (j == -1)
+ j = url.getLength();
+ if (j != i
+ && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
+ url.pData->buffer + i, j - i,
+ RTL_CONSTASCII_STRINGPARAM("localhost"))
+ != 0)
+ && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
+ url.pData->buffer + i, j - i,
+ RTL_CONSTASCII_STRINGPARAM("127.0.0.1"))
+ != 0))
+ {
+ OUString hostname;
+ if (osl_getLocalHostname(&hostname.pData) != osl_Socket_Ok
+ || (rtl_ustr_compareIgnoreAsciiCase_WithLength(
+ url.pData->buffer + i, j - i, hostname.getStr(), hostname.getLength())
+ != 0))
+ {
+ return osl_File_E_INVAL;
+ }
+ }
+ i = j;
+ }
+ // Handle empty path:
+ if (i == url.getLength())
+ {
+ *path = "/";
+ return osl_File_E_None;
+ }
+ // Path must not contain %2F:
+ if (url.indexOf("%2F", i) != -1 || url.indexOf("%2f", i) != -1)
+ return osl_File_E_INVAL;
+
+ if constexpr (std::is_same_v<T, rtl::OString>) {
+ if (!decodeFromUtf8(std::u16string_view(url).substr(i), path)) {
+ return osl_File_E_INVAL;
+ }
+ } else if constexpr (std::is_same_v<T, rtl::OUString>) {
+ *path = rtl::Uri::decode(
+ url.copy(i), rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
+ } else {
+ static_assert(std::is_same_v<T, rtl::OString> || std::is_same_v<T, rtl::OUString>);
+ }
+ // Path must not contain %2F:
+ if (path->indexOf('\0') != -1)
+ return osl_File_E_INVAL;
+
+ // Handle ~ notation:
+ if (resolveHome && path->getLength() >= 2 && (*path)[1] == '~')
+ {
+ sal_Int32 j = path->indexOf('/', 2);
+ if (j == -1)
+ j = path->getLength();
+
+ if (j == 2)
+ {
+ OUString home;
+ if (!osl::Security().getHomeDir(home))
+ {
+ SAL_WARN("sal.file", "osl::Security::getHomeDir failed");
+ return osl_File_E_INVAL;
+ }
+
+ i = url.indexOf('/', i + 1);
+
+ if (i == -1)
+ i = url.getLength();
+ else
+ ++i;
+
+ //TODO: cheesy way of ensuring home's path ends in slash:
+ if (!home.isEmpty() && home[home.getLength() - 1] != '/')
+ home += "/";
+ try
+ {
+ home = rtl::Uri::convertRelToAbs(home, url.copy(i));
+ }
+ catch (rtl::MalformedUriException & e)
+ {
+ SAL_WARN("sal.file", "rtl::MalformedUriException " << e.getMessage());
+ return osl_File_E_INVAL;
+ }
+ return getSystemPathFromFileUrl(home, path, false);
+ }
+ // FIXME: replace ~user with user's home directory
+ return osl_File_E_INVAL;
+ }
+ return osl_File_E_None;
+}
+
+}
+
+oslFileError SAL_CALL osl_getSystemPathFromFileURL( rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath )
+{
+ OUString path;
+ oslFileError e;
+ try
+ {
+ e = getSystemPathFromFileUrl(
+ OUString::unacquired(&ustrFileURL), &path, true);
+ }
+ catch (std::length_error &)
+ {
+ e = osl_File_E_RANGE;
+ }
+
+ if (e == osl_File_E_None)
+ rtl_uString_assign(pustrSystemPath, path.pData);
+
+ return e;
+}
+
+oslFileError SAL_CALL osl_getFileURLFromSystemPath( rtl_uString *ustrSystemPath, rtl_uString **pustrFileURL )
+{
+ static const sal_Unicode pDoubleSlash[2] = { '/', '/' };
+
+ rtl_uString *pTmp = nullptr;
+ sal_Int32 nIndex;
+
+ if( ustrSystemPath->length == 0 )
+ return osl_File_E_INVAL;
+
+ /* temporary hack: if already file url, return ustrSystemPath */
+
+ if( rtl_ustr_ascii_shortenedCompare_WithLength( ustrSystemPath->buffer, ustrSystemPath->length,"file:", 5 ) == 0 )
+ return osl_File_E_INVAL;
+
+ /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */
+ if( ustrSystemPath->buffer[0] == '~' )
+ {
+ /* check if another user is specified */
+ if( ( ustrSystemPath->length == 1 ) ||
+ ( ustrSystemPath->buffer[1] == '/' ) )
+ {
+ /* osl_getHomeDir returns file URL */
+ oslSecurity pSecurity = osl_getCurrentSecurity();
+ osl_getHomeDir( pSecurity , &pTmp );
+ osl_freeSecurityHandle( pSecurity );
+
+ if (!pTmp)
+ return osl_File_E_INVAL;
+
+ /* remove "file://" prefix */
+ rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + 7, pTmp->length - 7 );
+
+ /* replace '~' in original string */
+ rtl_uString_newReplaceStrAt( &pTmp, ustrSystemPath, 0, 1, pTmp );
+ }
+ else
+ {
+ /* FIXME: replace ~user with users home directory */
+ return osl_File_E_INVAL;
+ }
+ }
+
+ /* check if initial string contains repeated '/' characters */
+ nIndex = rtl_ustr_indexOfStr_WithLength( ustrSystemPath->buffer, ustrSystemPath->length, pDoubleSlash, 2 );
+ if( nIndex != -1 )
+ {
+ sal_Int32 nSrcIndex;
+ sal_Int32 nDeleted = 0;
+
+ /* if pTmp is not already allocated, copy ustrSystemPath for modification */
+ if( pTmp == nullptr )
+ rtl_uString_newFromString( &pTmp, ustrSystemPath );
+
+ /* adapt index to pTmp */
+ nIndex += pTmp->length - ustrSystemPath->length;
+
+ /* replace repeated '/' characters with a single '/' */
+ for( nSrcIndex = nIndex + 1; nSrcIndex < pTmp->length; nSrcIndex++ )
+ {
+ if( (pTmp->buffer[nSrcIndex] == '/') && (pTmp->buffer[nIndex] == '/') )
+ nDeleted++;
+ else
+ pTmp->buffer[++nIndex] = pTmp->buffer[nSrcIndex];
+ }
+
+ /* adjust length member */
+ pTmp->length -= nDeleted;
+ }
+
+ if( pTmp == nullptr )
+ rtl_uString_assign( &pTmp, ustrSystemPath );
+
+ /* file URLs must be URI encoded */
+ rtl_uriEncode( pTmp, uriCharClass, rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8, pustrFileURL );
+
+ rtl_uString_release( pTmp );
+
+ /* absolute urls should start with 'file://' */
+ if( (*pustrFileURL)->buffer[0] == '/' )
+ {
+ rtl_uString *pProtocol = nullptr;
+
+ rtl_uString_newFromAscii( &pProtocol, "file://" );
+ rtl_uString_newConcat( pustrFileURL, pProtocol, *pustrFileURL );
+ rtl_uString_release( pProtocol );
+ }
+
+ return osl_File_E_None;
+}
+
+/*
+ * relative URLs are not accepted
+ */
+oslFileError osl_getSystemPathFromFileURL_Ex(
+ rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath)
+{
+ rtl_uString* temp = nullptr;
+ oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &temp);
+
+ if (osl_error == osl_File_E_None)
+ {
+ if (temp->buffer[0] == '/')
+ {
+ *pustrSystemPath = temp;
+ }
+ else
+ {
+ rtl_uString_release(temp);
+ osl_error = osl_File_E_INVAL;
+ }
+ }
+
+ return osl_error;
+}
+
+namespace
+{
+
+ /** Helper function, return a pointer to the final '\0'
+ of a string
+ */
+
+ sal_Unicode* ustrtoend(sal_Unicode* pStr)
+ {
+ return (pStr + rtl_ustr_getLength(pStr));
+ }
+
+ sal_Unicode* ustrchrcat(const sal_Unicode chr, sal_Unicode* d)
+ {
+ sal_Unicode* p = ustrtoend(d);
+ *p++ = chr;
+ *p = 0;
+ return d;
+ }
+
+ bool _islastchr(sal_Unicode* pStr, sal_Unicode Chr)
+ {
+ sal_Unicode* p = ustrtoend(pStr);
+ if (p > pStr)
+ p--;
+ return (*p == Chr);
+ }
+
+ /**
+ Remove the last part of a path, a path that has
+ only a '/' or no '/' at all will be returned
+ unmodified
+ */
+
+ sal_Unicode* _rmlastpathtoken(sal_Unicode* aPath)
+ {
+ /* we may always skip -2 because we
+ may at least stand on a '/' but
+ either there is no other character
+ before this '/' or it's another
+ character than the '/'
+ */
+ sal_Unicode* p = ustrtoend(aPath) - 2;
+
+ /* move back to the next path separator
+ or to the start of the string */
+ while ((p > aPath) && (*p != '/'))
+ p--;
+
+ if (p >= aPath)
+ {
+ if (*p == '/')
+ {
+ p++;
+ *p = '\0';
+ }
+ else
+ {
+ *p = '\0';
+ }
+ }
+
+ return aPath;
+ }
+
+ oslFileError _osl_resolvepath(
+ /*inout*/ sal_Unicode* path,
+ /*inout*/ bool* failed)
+ {
+ oslFileError ferr = osl_File_E_None;
+
+ if (!*failed)
+ {
+ char unresolved_path[PATH_MAX];
+ if (!UnicodeToText(unresolved_path, sizeof(unresolved_path), path, rtl_ustr_getLength(path)))
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ char resolved_path[PATH_MAX];
+ if (realpath(unresolved_path, resolved_path))
+ {
+ if (!TextToUnicode(resolved_path, strlen(resolved_path), path, PATH_MAX))
+ return oslTranslateFileError(ENAMETOOLONG);
+ }
+ else
+ {
+ if (EACCES == errno || ENOTDIR == errno || ENOENT == errno)
+ *failed = true;
+ else
+ ferr = oslTranslateFileError(errno);
+ }
+ }
+
+ return ferr;
+ }
+
+ /**
+ Works even with non existing paths. The resulting path must not exceed
+ PATH_MAX else osl_File_E_NAMETOOLONG is the result
+ */
+
+ oslFileError osl_getAbsoluteFileURL_impl_(const OUString& unresolved_path, OUString& resolved_path)
+ {
+ /* the given unresolved path must not exceed PATH_MAX */
+ if (unresolved_path.getLength() >= (PATH_MAX - 2))
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ sal_Unicode path_resolved_so_far[PATH_MAX];
+ const sal_Unicode* punresolved = unresolved_path.getStr();
+ sal_Unicode* presolvedsf = path_resolved_so_far;
+
+ /* reserve space for leading '/' and trailing '\0'
+ do not exceed this limit */
+ sal_Unicode* sentinel = path_resolved_so_far + PATH_MAX - 2;
+
+ /* if realpath fails with error ENOTDIR, EACCES or ENOENT
+ we will not call it again, because _osl_realpath should also
+ work with non existing directories etc. */
+ bool realpath_failed = false;
+ oslFileError ferr;
+
+ path_resolved_so_far[0] = '\0';
+
+ while (*punresolved != '\0')
+ {
+ /* ignore '/.' , skip one part back when '/..' */
+ if ((*punresolved == '.') && (*presolvedsf == '/'))
+ {
+ if (*(punresolved + 1) == '\0')
+ {
+ punresolved++;
+ continue;
+ }
+ if (*(punresolved + 1) == '/')
+ {
+ punresolved += 2;
+ continue;
+ }
+ if ((*(punresolved + 1) == '.') && (*(punresolved + 2) == '\0' || (*(punresolved + 2) == '/')))
+ {
+ _rmlastpathtoken(path_resolved_so_far);
+
+ presolvedsf = ustrtoend(path_resolved_so_far) - 1;
+
+ if (*(punresolved + 2) == '/')
+ punresolved += 3;
+ else
+ punresolved += 2;
+
+ continue;
+ }
+
+ /* a file or directory name may start with '.' */
+ if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ ustrchrcat(*punresolved++, path_resolved_so_far);
+
+ if (*punresolved == '\0' && !realpath_failed)
+ {
+ ferr = _osl_resolvepath(
+ path_resolved_so_far,
+ &realpath_failed);
+
+ if (ferr != osl_File_E_None)
+ return ferr;
+ }
+ }
+ else if (*punresolved == '/')
+ {
+ if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ ustrchrcat(*punresolved++, path_resolved_so_far);
+
+ if (!realpath_failed)
+ {
+ ferr = _osl_resolvepath(
+ path_resolved_so_far,
+ &realpath_failed);
+
+ if (ferr != osl_File_E_None)
+ return ferr;
+
+ if (!_islastchr(path_resolved_so_far, '/'))
+ {
+ if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ ustrchrcat('/', path_resolved_so_far);
+ }
+ }
+ }
+ else // any other character
+ {
+ if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel)
+ return oslTranslateFileError(ENAMETOOLONG);
+
+ ustrchrcat(*punresolved++, path_resolved_so_far);
+
+ if (*punresolved == '\0' && !realpath_failed)
+ {
+ ferr = _osl_resolvepath(
+ path_resolved_so_far,
+ &realpath_failed);
+
+ if (ferr != osl_File_E_None)
+ return ferr;
+ }
+ }
+ }
+
+ sal_Int32 len = rtl_ustr_getLength(path_resolved_so_far);
+
+ OSL_ASSERT(len < PATH_MAX);
+
+ resolved_path = OUString(path_resolved_so_far, len);
+
+ return osl_File_E_None;
+ }
+
+}
+
+oslFileError osl_getAbsoluteFileURL(
+ rtl_uString* ustrBaseDirURL,
+ rtl_uString* ustrRelativeURL,
+ rtl_uString** pustrAbsoluteURL)
+{
+ /* Work around the below call to getSystemPathFromFileURL rejecting input
+ that starts with "/" (for whatever reason it behaves that way; but
+ changing that would start to break lots of tests at least) */
+ OUString relUrl(ustrRelativeURL);
+ if (relUrl.startsWith("//"))
+ relUrl = "file:" + relUrl;
+ else if (relUrl.startsWith("/"))
+ relUrl = "file://" + relUrl;
+
+ OUString unresolved_path;
+
+ FileBase::RC frc = FileBase::getSystemPathFromFileURL(relUrl, unresolved_path);
+ if (frc != FileBase::E_None)
+ return oslFileError(frc);
+
+ if (systemPathIsRelativePath(unresolved_path))
+ {
+ OUString base_path;
+ oslFileError rc = osl_getSystemPathFromFileURL_Ex(ustrBaseDirURL, &base_path.pData);
+ if (rc != osl_File_E_None)
+ return rc;
+
+ unresolved_path = systemPathMakeAbsolutePath(base_path, unresolved_path);
+ }
+
+ OUString resolved_path;
+ oslFileError rc = osl_getAbsoluteFileURL_impl_(unresolved_path, resolved_path);
+ if (rc == osl_File_E_None)
+ {
+ rc = osl_getFileURLFromSystemPath(resolved_path.pData, pustrAbsoluteURL);
+ OSL_ASSERT(osl_File_E_None == rc);
+ }
+
+ return rc;
+}
+
+namespace osl::detail {
+
+ /**
+ No separate error code if unicode to text conversion or getenv fails because for the
+ caller there is no difference why a file could not be found in $PATH
+ */
+ bool find_in_PATH(const OUString& file_path, OUString& result)
+ {
+ bool bfound = false;
+ OUString path("PATH");
+ OUString env_path;
+
+ if (osl_getEnvironment(path.pData, &env_path.pData) == osl_Process_E_None)
+ bfound = osl::searchPath(file_path, env_path, result);
+
+ return bfound;
+ }
+}
+
+namespace
+{
+ /**
+ No separate error code if unicode to text conversion or getcwd fails because for the
+ caller there is no difference why a file could not be found in CDW
+ */
+ bool find_in_CWD(const OUString& file_path, OUString& result)
+ {
+ bool bfound = false;
+ OUString cwd_url;
+
+ if (osl_getProcessWorkingDir(&cwd_url.pData) == osl_Process_E_None)
+ {
+ OUString cwd;
+ FileBase::getSystemPathFromFileURL(cwd_url, cwd);
+ bfound = osl::searchPath(file_path, cwd, result);
+ }
+ return bfound;
+ }
+
+ bool find_in_searchPath(const OUString& file_path, rtl_uString* search_path, OUString& result)
+ {
+ return (search_path && osl::searchPath(file_path, OUString(search_path), result));
+ }
+
+}
+
+oslFileError osl_searchFileURL(rtl_uString* ustrFilePath, rtl_uString* ustrSearchPath, rtl_uString** pustrURL)
+{
+ OSL_PRECOND(ustrFilePath && pustrURL, "osl_searchFileURL: invalid parameter");
+
+ FileBase::RC rc;
+ OUString file_path;
+
+ // try to interpret search path as file url else assume it's a system path list
+ rc = FileBase::getSystemPathFromFileURL(ustrFilePath, file_path);
+ if (rc == FileBase::E_INVAL)
+ file_path = ustrFilePath;
+ else if (rc != FileBase::E_None)
+ return oslFileError(rc);
+
+ bool bfound = false;
+ OUString result;
+
+ if (find_in_searchPath(file_path, ustrSearchPath, result) ||
+ osl::detail::find_in_PATH(file_path, result) ||
+ find_in_CWD(file_path, result))
+ {
+ OUString resolved;
+
+ if (osl::realpath(result, resolved))
+ {
+ oslFileError osl_error = osl_getFileURLFromSystemPath(resolved.pData, pustrURL);
+ SAL_WARN_IF(osl_File_E_None != osl_error, "sal.file", "osl_getFileURLFromSystemPath failed");
+ bfound = true;
+ }
+ }
+ return bfound ? osl_File_E_None : osl_File_E_NOENT;
+}
+
+oslFileError FileURLToPath(char * buffer, size_t bufLen, rtl_uString* ustrFileURL)
+{
+ OString strSystemPath;
+ oslFileError osl_error = osl::detail::convertUrlToPathname(
+ OUString::unacquired(&ustrFileURL), &strSystemPath);
+
+ if(osl_error != osl_File_E_None)
+ return osl_error;
+
+ osl_systemPathRemoveSeparator(strSystemPath.pData);
+
+ if (o3tl::make_unsigned(strSystemPath.getLength()) >= bufLen) {
+ return osl_File_E_OVERFLOW;
+ }
+ std::strcpy(buffer, strSystemPath.getStr());
+
+ return osl_error;
+}
+
+int UnicodeToText( char * buffer, size_t bufLen, const sal_Unicode * uniText, sal_Int32 uniTextLen )
+{
+ sal_uInt32 nInfo = 0;
+ sal_Size nSrcChars = 0;
+
+ sal_Size nDestBytes = UnicodeToTextConverter_Impl::getInstance().convert (
+ uniText, uniTextLen, buffer, bufLen,
+ OUSTRING_TO_OSTRING_CVTFLAGS | RTL_UNICODETOTEXT_FLAGS_FLUSH, &nInfo, &nSrcChars);
+
+ if( nInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL )
+ {
+ errno = EOVERFLOW;
+ return 0;
+ }
+
+ /* ensure trailing '\0' */
+ buffer[nDestBytes] = '\0';
+ return nDestBytes;
+}
+
+namespace
+{
+ class TextToUnicodeConverter_Impl
+ {
+ rtl_TextToUnicodeConverter m_converter;
+
+ TextToUnicodeConverter_Impl()
+ : m_converter (rtl_createTextToUnicodeConverter (osl_getThreadTextEncoding()))
+ {}
+
+ ~TextToUnicodeConverter_Impl()
+ {
+ rtl_destroyTextToUnicodeConverter (m_converter);
+ }
+
+ public:
+ static TextToUnicodeConverter_Impl & getInstance()
+ {
+ static TextToUnicodeConverter_Impl g_theConverter;
+ return g_theConverter;
+ }
+
+ sal_Size convert(
+ char const * pSrcBuf, sal_Size nSrcBytes, sal_Unicode * pDstBuf, sal_Size nDstChars,
+ sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtBytes)
+ {
+ OSL_ASSERT(m_converter != nullptr);
+ return rtl_convertTextToUnicode (
+ m_converter, nullptr, pSrcBuf, nSrcBytes, pDstBuf, nDstChars, nFlags, pInfo, pSrcCvtBytes);
+ }
+ };
+}
+
+int TextToUnicode(
+ const char* text,
+ size_t text_buffer_size,
+ sal_Unicode* unic_text,
+ sal_Int32 unic_text_buffer_size)
+{
+ sal_uInt32 nInfo = 0;
+ sal_Size nSrcChars = 0;
+
+ sal_Size nDestBytes = TextToUnicodeConverter_Impl::getInstance().convert(
+ text, text_buffer_size, unic_text, unic_text_buffer_size,
+ OSTRING_TO_OUSTRING_CVTFLAGS | RTL_TEXTTOUNICODE_FLAGS_FLUSH, &nInfo, &nSrcChars);
+
+ if (nInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL)
+ {
+ errno = EOVERFLOW;
+ return 0;
+ }
+
+ /* ensure trailing '\0' */
+ unic_text[nDestBytes] = '\0';
+ return nDestBytes;
+}
+
+oslFileError osl::detail::convertUrlToPathname(OUString const & url, OString * pathname) {
+ assert(pathname != nullptr);
+ oslFileError e;
+ try {
+ e = getSystemPathFromFileUrl(url, pathname, true);
+ } catch (std::length_error &) {
+ e = osl_File_E_RANGE;
+ }
+ if (e == osl_File_E_None && !pathname->startsWith("/")) {
+ e = osl_File_E_INVAL;
+ }
+ return e;
+}
+
+oslFileError osl::detail::convertPathnameToUrl(OString const & pathname, OUString * url) {
+ assert(url != nullptr);
+ OUStringBuffer buf(10+pathname.getLength());
+ buf.append("file:");
+ if (pathname.startsWith("/")) {
+ buf.append("//");
+ // so if pathname should ever start with "//" that isn't mistaken for an authority
+ // component
+ }
+ for (sal_Size convert = pathname.getLength();;) {
+ auto n = std::max(convert, sal_Size(PATH_MAX)); // approximation of required converted size
+ OUStringBuffer ubuf(static_cast<int>(n));
+ auto s = ubuf.appendUninitialized(n);
+ sal_uInt32 info;
+ sal_Size converted;
+ //TODO: context, for reliable treatment of DESTBUFFERTOSMALL:
+ n = TextToUnicodeConverter_Impl::getInstance().convert(
+ pathname.getStr() + pathname.getLength() - convert, convert, s, n,
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR | RTL_TEXTTOUNICODE_FLAGS_FLUSH),
+ &info, &converted);
+ ubuf.setLength(n);
+ buf.append(
+ rtl::Uri::encode(
+ ubuf.makeStringAndClear(), uriCharClass, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8));
+ assert(converted <= convert);
+ convert -= converted;
+ if ((info & RTL_TEXTTOUNICODE_INFO_ERROR) != 0) {
+ assert(convert > 0);
+ //TODO: see writeEscapeOctet in sal/rtl/uri.cxx
+ buf.append("%");
+ unsigned char c = pathname[pathname.getLength() - convert];
+ assert(c >= 0x80);
+ static sal_Unicode const aHex[16]
+ = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; /* '0'--'9', 'A'--'F' */
+ buf.append(aHex[c >> 4]);
+ buf.append(aHex[c & 15]);
+ --convert;
+ continue;
+ }
+ assert((convert == 0) == ((info & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL) == 0));
+ if ((info & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL) == 0) {
+ break;
+ }
+ }
+ *url = buf.makeStringAndClear();
+ return osl_File_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_url.hxx b/sal/osl/unx/file_url.hxx
new file mode 100644
index 000000000..f9f31eb20
--- /dev/null
+++ b/sal/osl/unx/file_url.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_FILE_URL_HXX
+#define INCLUDED_SAL_OSL_UNX_FILE_URL_HXX
+
+#include <osl/file.h>
+
+namespace rtl {
+ class OString;
+ class OUString;
+}
+
+oslFileError osl_getSystemPathFromFileURL_Ex(rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath);
+
+oslFileError FileURLToPath(char * buffer, size_t bufLen, rtl_uString* ustrFileURL);
+
+int UnicodeToText(char * buffer, size_t bufLen, const sal_Unicode * uniText, sal_Int32 uniTextLen);
+
+int TextToUnicode(const char* text, size_t text_buffer_size, sal_Unicode* unic_text, sal_Int32 unic_text_buffer_size);
+
+namespace osl::detail {
+
+oslFileError convertUrlToPathname(rtl::OUString const & url, rtl::OString * pathname);
+
+oslFileError convertPathnameToUrl(rtl::OString const & pathname, rtl::OUString * url);
+
+bool find_in_PATH(const rtl::OUString& file_path, rtl::OUString& result);
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/file_volume.cxx b/sal/osl/unx/file_volume.cxx
new file mode 100644
index 000000000..5e661bd65
--- /dev/null
+++ b/sal/osl/unx/file_volume.cxx
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <osl/file.h>
+
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <rtl/alloc.h>
+
+#include "file_error_transl.hxx"
+#include "file_url.hxx"
+#include "system.hxx"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sal/macros.h>
+
+#ifdef HAVE_STATFS_H
+#undef HAVE_STATFS_H
+#endif
+
+#if defined(LINUX) && defined(__FreeBSD_kernel__)
+#undef LINUX
+#define FREEBSD 1
+#endif
+
+#if defined(__sun)
+
+#include <sys/mnttab.h>
+#include <sys/statvfs.h>
+#define HAVE_STATFS_H
+
+#elif defined(LINUX)
+
+#include <mntent.h>
+#include <sys/vfs.h>
+#define HAVE_STATFS_H
+
+#elif defined(NETBSD) || defined(FREEBSD) || defined(OPENBSD) || defined(DRAGONFLY)
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#define HAVE_STATFS_H
+
+#elif defined(MACOSX)
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#define HAVE_STATFS_H
+
+#endif /* HAVE_STATFS_H */
+
+/************************************************************************
+ * ToDo
+ *
+ * - Fix: check for corresponding struct sizes in exported functions
+ * - check size/use of oslVolumeInfo
+ ***********************************************************************/
+
+/******************************************************************************
+ *
+ * C-String Function Declarations
+ *
+ *****************************************************************************/
+
+static oslFileError osl_psz_getVolumeInformation(const char* , oslVolumeInfo* pInfo, sal_uInt32 uFieldMask);
+
+/****************************************************************************/
+/* osl_getVolumeInformation */
+/****************************************************************************/
+
+oslFileError osl_getVolumeInformation( rtl_uString* ustrDirectoryURL, oslVolumeInfo* pInfo, sal_uInt32 uFieldMask )
+{
+ char path[PATH_MAX];
+ oslFileError eRet;
+
+ OSL_ASSERT( ustrDirectoryURL );
+ OSL_ASSERT( pInfo );
+
+ /* convert directory url to system path */
+ eRet = FileURLToPath( path, PATH_MAX, ustrDirectoryURL );
+ if( eRet != osl_File_E_None )
+ return eRet;
+
+#ifdef MACOSX
+ if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
+ return oslTranslateFileError( errno );
+#endif/* MACOSX */
+
+ return osl_psz_getVolumeInformation( path, pInfo, uFieldMask);
+}
+
+/******************************************************************************
+ *
+ * C-String Versions of Exported Module Functions
+ *
+ *****************************************************************************/
+
+#ifdef HAVE_STATFS_H
+
+#if defined(FREEBSD) || defined(MACOSX) || defined(OPENBSD) || defined(DRAGONFLY)
+# define OSL_detail_STATFS_STRUCT struct statfs
+# define OSL_detail_STATFS(dir, sfs) statfs((dir), (sfs))
+# define OSL_detail_STATFS_BLKSIZ(a) (static_cast<sal_uInt64>((a).f_bsize))
+# define OSL_detail_STATFS_TYPENAME(a) ((a).f_fstypename)
+#if defined(OPENBSD)
+# define OSL_detail_STATFS_ISREMOTE(a) (rtl_str_compare((a).f_fstypename, "nfs") == 0)
+#else
+# define OSL_detail_STATFS_ISREMOTE(a) (((a).f_type & MNT_LOCAL) == 0)
+#endif
+
+/* always return true if queried for the properties of
+ the file system. If you think this is wrong under any
+ of the target platforms fix it!!!! */
+# define OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(a) (true)
+# define OSL_detail_STATFS_IS_CASE_PRESERVING_FS(a) (true)
+#endif /* FREEBSD || MACOSX || OPENBSD */
+
+#if defined(NETBSD)
+
+# define OSL_detail_STATFS_STRUCT struct statvfs
+# define OSL_detail_STATFS(dir, sfs) statvfs((dir), (sfs))
+# define OSL_detail_STATFS_ISREMOTE(a) (((a).f_flag & ST_LOCAL) == 0)
+
+# define OSL_detail_STATFS_BLKSIZ(a) ((sal_uInt64)((a).f_bsize))
+# define OSL_detail_STATFS_TYPENAME(a) ((a).f_fstypename)
+
+# define OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(a) (strcmp((a).f_fstypename, "msdos") != 0 && strcmp((a).f_fstypename, "ntfs") != 0 && strcmp((a).f_fstypename, "smbfs") != 0)
+# define OSL_detail_STATFS_IS_CASE_PRESERVING_FS(a) (strcmp((a).f_fstypename, "msdos") != 0)
+#endif /* NETBSD */
+
+#if defined(LINUX)
+# define OSL_detail_NFS_SUPER_MAGIC 0x6969
+# define OSL_detail_SMB_SUPER_MAGIC 0x517B
+# define OSL_detail_MSDOS_SUPER_MAGIC 0x4d44
+# define OSL_detail_NTFS_SUPER_MAGIC 0x5346544e
+# define OSL_detail_STATFS_STRUCT struct statfs
+# define OSL_detail_STATFS(dir, sfs) statfs((dir), (sfs))
+# define OSL_detail_STATFS_BLKSIZ(a) (static_cast<sal_uInt64>((a).f_bsize))
+# define OSL_detail_STATFS_IS_NFS(a) (OSL_detail_NFS_SUPER_MAGIC == (a).f_type)
+# define OSL_detail_STATFS_IS_SMB(a) (OSL_detail_SMB_SUPER_MAGIC == (a).f_type)
+# define OSL_detail_STATFS_ISREMOTE(a) (OSL_detail_STATFS_IS_NFS((a)) || OSL_detail_STATFS_IS_SMB((a)))
+# define OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(a) ((OSL_detail_MSDOS_SUPER_MAGIC != (a).f_type) && (OSL_detail_NTFS_SUPER_MAGIC != (a).f_type))
+# define OSL_detail_STATFS_IS_CASE_PRESERVING_FS(a) ((OSL_detail_MSDOS_SUPER_MAGIC != (a).f_type))
+#endif /* LINUX */
+
+#if defined(__sun)
+# define OSL_detail_STATFS_STRUCT struct statvfs
+# define OSL_detail_STATFS(dir, sfs) statvfs((dir), (sfs))
+# define OSL_detail_STATFS_BLKSIZ(a) ((sal_uInt64)((a).f_frsize))
+# define OSL_detail_STATFS_TYPENAME(a) ((a).f_basetype)
+# define OSL_detail_STATFS_ISREMOTE(a) (rtl_str_compare((a).f_basetype, "nfs") == 0)
+
+/* always return true if queried for the properties of
+ the file system. If you think this is wrong under any
+ of the target platforms fix it!!!! */
+# define OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(a) (true)
+# define OSL_detail_STATFS_IS_CASE_PRESERVING_FS(a) (true)
+#endif /* __sun */
+
+# define OSL_detail_STATFS_INIT(a) (memset(&(a), 0, sizeof(OSL_detail_STATFS_STRUCT)))
+
+#else /* no statfs available */
+
+# define OSL_detail_STATFS_STRUCT struct dummy {int i;}
+# define OSL_detail_STATFS_INIT(a) ((void)a)
+# define OSL_detail_STATFS(dir, sfs) (1)
+# define OSL_detail_STATFS_ISREMOTE(sfs) (false)
+# define OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(a) (true)
+# define OSL_detail_STATFS_IS_CASE_PRESERVING_FS(a) (true)
+#endif /* HAVE_STATFS_H */
+
+static oslFileError osl_psz_getVolumeInformation (
+ const char* pszDirectory, oslVolumeInfo* pInfo, sal_uInt32 uFieldMask)
+{
+ if (!pInfo)
+ return osl_File_E_INVAL;
+
+ pInfo->uValidFields = 0;
+ pInfo->uAttributes = 0;
+ pInfo->uTotalSpace = 0;
+ pInfo->uFreeSpace = 0;
+ pInfo->uUsedSpace = 0;
+
+ if ((uFieldMask
+ & (osl_VolumeInfo_Mask_Attributes | osl_VolumeInfo_Mask_TotalSpace
+ | osl_VolumeInfo_Mask_UsedSpace | osl_VolumeInfo_Mask_FreeSpace
+ | osl_VolumeInfo_Mask_FileSystemName
+ | osl_VolumeInfo_Mask_FileSystemCaseHandling))
+ != 0)
+ {
+ OSL_detail_STATFS_STRUCT sfs;
+ OSL_detail_STATFS_INIT(sfs);
+ // coverity[fs_check_call : FALSE]
+ if ((OSL_detail_STATFS(pszDirectory, &sfs)) < (0))
+ {
+ oslFileError result = oslTranslateFileError(errno);
+ return result;
+ }
+
+ /* FIXME: how to detect the kind of storage (fixed, cdrom, ...) */
+ if (uFieldMask & osl_VolumeInfo_Mask_Attributes)
+ {
+ bool const remote = OSL_detail_STATFS_ISREMOTE(sfs);
+ // extracted from the 'if' to avoid Clang -Wunreachable-code
+ if (remote)
+ pInfo->uAttributes |= osl_Volume_Attribute_Remote;
+
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes;
+ }
+
+ if (uFieldMask & osl_VolumeInfo_Mask_FileSystemCaseHandling)
+ {
+ if (OSL_detail_STATFS_IS_CASE_SENSITIVE_FS(sfs))
+ pInfo->uAttributes |= osl_Volume_Attribute_Case_Sensitive;
+
+ if (OSL_detail_STATFS_IS_CASE_PRESERVING_FS(sfs))
+ pInfo->uAttributes |= osl_Volume_Attribute_Case_Is_Preserved;
+
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_Attributes;
+ }
+
+#if defined(OSL_detail_STATFS_BLKSIZ)
+
+ if ((uFieldMask & osl_VolumeInfo_Mask_TotalSpace) ||
+ (uFieldMask & osl_VolumeInfo_Mask_UsedSpace))
+ {
+ pInfo->uTotalSpace = OSL_detail_STATFS_BLKSIZ(sfs);
+ pInfo->uTotalSpace *= static_cast<sal_uInt64>(sfs.f_blocks);
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_TotalSpace;
+ }
+
+ if ((uFieldMask & osl_VolumeInfo_Mask_FreeSpace) ||
+ (uFieldMask & osl_VolumeInfo_Mask_UsedSpace))
+ {
+ pInfo->uFreeSpace = OSL_detail_STATFS_BLKSIZ(sfs);
+
+ if (getuid() == 0)
+ pInfo->uFreeSpace *= static_cast<sal_uInt64>(sfs.f_bfree);
+ else
+ pInfo->uFreeSpace *= static_cast<sal_uInt64>(sfs.f_bavail);
+
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_FreeSpace;
+ }
+
+ if ((pInfo->uValidFields & osl_VolumeInfo_Mask_TotalSpace) &&
+ (pInfo->uValidFields & osl_VolumeInfo_Mask_FreeSpace ))
+ {
+ pInfo->uUsedSpace = pInfo->uTotalSpace - pInfo->uFreeSpace;
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_UsedSpace;
+ }
+
+#endif /* OSL_detail_STATFS_BLKSIZ */
+
+#if defined(OSL_detail_STATFS_TYPENAME)
+
+ if (uFieldMask & osl_VolumeInfo_Mask_FileSystemName)
+ {
+ rtl_string2UString(
+ &(pInfo->ustrFileSystemName),
+ OSL_detail_STATFS_TYPENAME(sfs),
+ rtl_str_getLength(OSL_detail_STATFS_TYPENAME(sfs)),
+ osl_getThreadTextEncoding(),
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+ OSL_ASSERT(pInfo->ustrFileSystemName != nullptr);
+
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_FileSystemName;
+ }
+
+#endif /* OSL_detail_STATFS_TYPENAME */
+ }
+
+ pInfo->uMaxNameLength = 0;
+ if (uFieldMask & osl_VolumeInfo_Mask_MaxNameLength)
+ {
+ long nLen = pathconf(pszDirectory, _PC_NAME_MAX);
+ if (nLen > 0)
+ {
+ pInfo->uMaxNameLength = static_cast<sal_uInt32>(nLen);
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_MaxNameLength;
+ }
+ }
+
+ pInfo->uMaxPathLength = 0;
+ if (uFieldMask & osl_VolumeInfo_Mask_MaxPathLength)
+ {
+ long nLen = pathconf (pszDirectory, _PC_PATH_MAX);
+ if (nLen > 0)
+ {
+ pInfo->uMaxPathLength = static_cast<sal_uInt32>(nLen);
+ pInfo->uValidFields |= osl_VolumeInfo_Mask_MaxPathLength;
+ }
+ }
+
+ return osl_File_E_None;
+}
+
+oslFileError osl_getVolumeDeviceMountPath( oslVolumeDeviceHandle, rtl_uString ** )
+{
+ return osl_File_E_INVAL;
+}
+
+oslFileError osl_acquireVolumeDeviceHandle( oslVolumeDeviceHandle )
+{
+ return osl_File_E_INVAL;
+}
+
+oslFileError osl_releaseVolumeDeviceHandle( oslVolumeDeviceHandle )
+{
+ return osl_File_E_INVAL;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/interlck.cxx b/sal/osl/unx/interlck.cxx
new file mode 100644
index 000000000..81404cb57
--- /dev/null
+++ b/sal/osl/unx/interlck.cxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.hxx"
+
+#include <config_global.h>
+#include <osl/interlck.h>
+
+#if ( defined (__sun) || defined ( NETBSD ) ) && defined ( SPARC )
+#error please use asm/interlck_sparc.s
+#elif defined (__sun) && defined ( X86 )
+#error please use asm/interlck_x86.s
+#elif HAVE_GCC_BUILTIN_ATOMIC
+oslInterlockedCount SAL_CALL osl_incrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ return __sync_add_and_fetch(pCount, 1);
+}
+oslInterlockedCount SAL_CALL osl_decrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ return __sync_sub_and_fetch(pCount, 1);
+}
+#elif defined ( __GNUC__ ) && ( defined ( X86 ) || defined ( X86_64 ) )
+/* That's possible on x86-64 too since oslInterlockedCount is a sal_Int32 */
+
+oslInterlockedCount SAL_CALL osl_incrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ register oslInterlockedCount nCount asm("%eax");
+ nCount = 1;
+ __asm__ __volatile__ (
+ "lock\n\t"
+ "xaddl %0, %1\n\t"
+ : "+r" (nCount), "+m" (*pCount)
+ : /* nothing */
+ : "memory");
+ return ++nCount;
+}
+
+oslInterlockedCount SAL_CALL osl_decrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ register oslInterlockedCount nCount asm("%eax");
+ nCount = -1;
+ __asm__ __volatile__ (
+ "lock\n\t"
+ "xaddl %0, %1\n\t"
+ : "+r" (nCount), "+m" (*pCount)
+ : /* nothing */
+ : "memory");
+ return --nCount;
+}
+#else
+/* use only if nothing else works, expensive due to single mutex for all reference counts */
+
+static pthread_mutex_t InterLock = PTHREAD_MUTEX_INITIALIZER;
+
+oslInterlockedCount SAL_CALL osl_incrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ oslInterlockedCount Count;
+
+ pthread_mutex_lock(&InterLock);
+ Count = ++(*pCount);
+ pthread_mutex_unlock(&InterLock);
+
+ return Count;
+}
+
+oslInterlockedCount SAL_CALL osl_decrementInterlockedCount(oslInterlockedCount* pCount)
+{
+ oslInterlockedCount Count;
+
+ pthread_mutex_lock(&InterLock);
+ Count = --(*pCount);
+ pthread_mutex_unlock(&InterLock);
+
+ return Count;
+}
+
+#endif /* default */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/memory.cxx b/sal/osl/unx/memory.cxx
new file mode 100644
index 000000000..745a7f14c
--- /dev/null
+++ b/sal/osl/unx/memory.cxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <oslmemory.h>
+
+#include <stdlib.h>
+#ifdef __ANDROID__
+#include <malloc.h>
+#endif
+
+void* osl_aligned_alloc( sal_Size align, sal_Size size )
+{
+ if (size == 0)
+ {
+ return nullptr;
+ }
+
+#if defined __ANDROID__
+ return memalign(align, size);
+#else
+ void* ptr;
+ int err = posix_memalign(&ptr, align, size);
+ return err ? nullptr : ptr;
+#endif
+}
+
+void osl_aligned_free( void* p )
+{
+ free(p);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/module.cxx b/sal/osl/unx/module.cxx
new file mode 100644
index 000000000..7befa6667
--- /dev/null
+++ b/sal/osl/unx/module.cxx
@@ -0,0 +1,347 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <osl/module.h>
+#include <osl/thread.h>
+#include <osl/process.h>
+#include <osl/file.h>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <assert.h>
+#include "system.hxx"
+#include "file_url.hxx"
+
+#ifdef AIX
+#include <sys/ldr.h>
+#endif
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+static bool getModulePathFromAddress(void * address, rtl_String ** path) {
+ bool result = false;
+ // We do want to have this functionality also in the
+ // DISABLE_DYNLOADING case, I think?
+#if defined(AIX)
+ int size = 4 * 1024;
+ char *buf, *filename=NULL;
+ struct ld_info *lp;
+
+ if ((buf = (char*)malloc(size)) == NULL)
+ return false;
+
+ //figure out how big a buffer we need
+ while (loadquery(L_GETINFO, buf, size) == -1 && errno == ENOMEM)
+ {
+ size += 4 * 1024;
+ free(buf);
+ if ((buf = (char*)malloc(size)) == NULL)
+ return false;
+ }
+
+ lp = (struct ld_info*) buf;
+ while (lp)
+ {
+ unsigned long start = (unsigned long)lp->ldinfo_dataorg;
+ unsigned long end = start + lp->ldinfo_datasize;
+ if (start <= (unsigned long)address && end > (unsigned long)address)
+ {
+ filename = lp->ldinfo_filename;
+ break;
+ }
+ if (!lp->ldinfo_next)
+ break;
+ lp = (struct ld_info*) ((char *) lp + lp->ldinfo_next);
+ }
+
+ if (filename)
+ {
+ rtl_string_newFromStr(path, filename);
+ result = sal_True;
+ }
+ else
+ {
+ result = sal_False;
+ }
+
+ free(buf);
+#else
+ Dl_info dl_info;
+
+#if defined(ANDROID) && !defined(DISABLE_DYNLOADING)
+ result = lo_dladdr(address, &dl_info) != 0;
+#else
+ result = dladdr(address, &dl_info) != 0;
+#endif
+
+ if (result)
+ {
+ rtl_string_newFromStr(path, dl_info.dli_fname);
+#if defined(ANDROID) && !defined(DISABLE_DYNLOADING)
+ free((void *) dl_info.dli_fname);
+#endif
+ }
+#endif
+ return result;
+}
+
+#ifndef DISABLE_DYNLOADING
+
+/*****************************************************************************/
+/* osl_loadModule */
+/*****************************************************************************/
+
+oslModule SAL_CALL osl_loadModule(rtl_uString *ustrModuleName, sal_Int32 nRtldMode)
+{
+ oslModule pModule=nullptr;
+ rtl_uString* ustrTmp = nullptr;
+
+ SAL_WARN_IF(ustrModuleName == nullptr, "sal.osl", "string is not valid");
+
+ /* ensure ustrTmp hold valid string */
+ if (osl_getSystemPathFromFileURL(ustrModuleName, &ustrTmp) != osl_File_E_None)
+ rtl_uString_assign(&ustrTmp, ustrModuleName);
+
+ if (ustrTmp)
+ {
+ char buffer[PATH_MAX];
+
+ if (UnicodeToText(buffer, PATH_MAX, ustrTmp->buffer, ustrTmp->length))
+ pModule = osl_loadModuleAscii(buffer, nRtldMode);
+ rtl_uString_release(ustrTmp);
+ }
+
+ return pModule;
+}
+
+/*****************************************************************************/
+/* osl_loadModuleAscii */
+/*****************************************************************************/
+
+oslModule SAL_CALL osl_loadModuleAscii(const char *pModuleName, sal_Int32 nRtldMode)
+{
+ SAL_WARN_IF(
+ ((nRtldMode & SAL_LOADMODULE_LAZY) != 0
+ && (nRtldMode & SAL_LOADMODULE_NOW) != 0),
+ "sal.osl", "only either LAZY or NOW");
+ if (pModuleName)
+ {
+#ifdef ANDROID
+ (void) nRtldMode;
+ void *pLib = lo_dlopen(pModuleName);
+#else
+ int rtld_mode =
+ ((nRtldMode & SAL_LOADMODULE_NOW) ? RTLD_NOW : RTLD_LAZY) |
+ ((nRtldMode & SAL_LOADMODULE_GLOBAL) ? RTLD_GLOBAL : RTLD_LOCAL);
+ void* pLib = dlopen(pModuleName, rtld_mode);
+
+ SAL_WARN_IF(
+ pLib == nullptr, "sal.osl",
+ "dlopen(" << pModuleName << ", " << rtld_mode << "): "
+ << dlerror());
+#endif
+ return pLib;
+ }
+ return nullptr;
+}
+
+oslModule osl_loadModuleRelativeAscii(
+ oslGenericFunction baseModule, char const * relativePath, sal_Int32 mode)
+{
+ assert(relativePath && "illegal argument");
+ if (relativePath[0] == '/') {
+ return osl_loadModuleAscii(relativePath, mode);
+ }
+ rtl_String * path = nullptr;
+ rtl_String * suffix = nullptr;
+ oslModule module;
+ if (!getModulePathFromAddress(
+ reinterpret_cast< void * >(baseModule), &path))
+ {
+ return nullptr;
+ }
+ rtl_string_newFromStr_WithLength(
+ &path, path->buffer,
+ (rtl_str_lastIndexOfChar_WithLength(path->buffer, path->length, '/')
+ + 1));
+ /* cut off everything after the last slash; should the original path
+ contain no slash, the resulting path is the empty string */
+ rtl_string_newFromStr(&suffix, relativePath);
+ rtl_string_newConcat(&path, path, suffix);
+ rtl_string_release(suffix);
+ module = osl_loadModuleAscii(path->buffer, mode);
+ rtl_string_release(path);
+ return module;
+}
+
+#endif // !DISABLE_DYNLOADING
+
+/*****************************************************************************/
+/* osl_getModuleHandle */
+/*****************************************************************************/
+
+sal_Bool SAL_CALL
+osl_getModuleHandle(rtl_uString *, oslModule *pResult)
+{
+#if !defined(DISABLE_DYNLOADING) || defined(IOS)
+ *pResult = static_cast<oslModule>(RTLD_DEFAULT);
+#else
+ *pResult = nullptr;
+#endif
+ return true;
+}
+
+#ifndef DISABLE_DYNLOADING
+
+/*****************************************************************************/
+/* osl_unloadModule */
+/*****************************************************************************/
+void SAL_CALL osl_unloadModule(oslModule hModule)
+{
+ if (hModule)
+ {
+#ifdef ANDROID
+ int nRet = lo_dlclose(hModule);
+#else
+ int nRet = dlclose(hModule);
+#endif
+ SAL_INFO_IF(
+ nRet != 0, "sal.osl", "dlclose(" << hModule << "): " << dlerror());
+ }
+}
+
+#endif // !DISABLE_DYNLOADING
+
+namespace {
+
+void * getSymbol(oslModule module, char const * symbol) {
+ assert(symbol != nullptr);
+ // We do want to use dlsym() also in the DISABLE_DYNLOADING case
+ // just to look up symbols in the static executable, I think:
+ void * p = dlsym(module, symbol);
+ SAL_INFO_IF(
+ p == nullptr, "sal.osl",
+ "dlsym(" << module << ", " << symbol << "): " << dlerror());
+ return p;
+}
+
+}
+
+/*****************************************************************************/
+/* osl_getSymbol */
+/*****************************************************************************/
+void* SAL_CALL
+osl_getSymbol(oslModule Module, rtl_uString* pSymbolName)
+{
+ // Arbitrarily using UTF-8:
+ OString s;
+ if (!OUString::unacquired(&pSymbolName).convertToString(
+ &s, RTL_TEXTENCODING_UTF8,
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
+ RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ SAL_INFO(
+ "sal.osl", "cannot convert \"" << OUString::unacquired(&pSymbolName)
+ << "\" to UTF-8");
+ return nullptr;
+ }
+ if (s.indexOf('\0') != -1) {
+ SAL_INFO("sal.osl", "\"" << s << "\" contains embedded NUL");
+ return nullptr;
+ }
+ return getSymbol(Module, s.getStr());
+}
+
+/*****************************************************************************/
+/* osl_getAsciiFunctionSymbol */
+/*****************************************************************************/
+oslGenericFunction SAL_CALL
+osl_getAsciiFunctionSymbol(oslModule Module, const char *pSymbol)
+{
+ return reinterpret_cast<oslGenericFunction>(getSymbol(Module, pSymbol));
+ // requires conditionally-supported conversion from void * to function
+ // pointer
+}
+
+/*****************************************************************************/
+/* osl_getFunctionSymbol */
+/*****************************************************************************/
+oslGenericFunction SAL_CALL
+osl_getFunctionSymbol(oslModule module, rtl_uString *puFunctionSymbolName)
+{
+ return reinterpret_cast<oslGenericFunction>(
+ osl_getSymbol(module, puFunctionSymbolName));
+ // requires conditionally-supported conversion from void * to function
+ // pointer
+}
+
+/*****************************************************************************/
+/* osl_getModuleURLFromAddress */
+/*****************************************************************************/
+sal_Bool SAL_CALL osl_getModuleURLFromAddress(void * addr, rtl_uString ** ppLibraryUrl)
+{
+ bool result = false;
+ rtl_String * path = nullptr;
+ if (getModulePathFromAddress(addr, &path))
+ {
+ rtl_uString * workDir = nullptr;
+ osl_getProcessWorkingDir(&workDir);
+ if (workDir)
+ {
+ rtl_string2UString(ppLibraryUrl,
+ path->buffer,
+ path->length,
+ osl_getThreadTextEncoding(),
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+
+ SAL_WARN_IF(
+ *ppLibraryUrl == nullptr, "sal.osl", "rtl_string2UString failed");
+ osl_getFileURLFromSystemPath(*ppLibraryUrl, ppLibraryUrl);
+ osl_getAbsoluteFileURL(workDir, *ppLibraryUrl, ppLibraryUrl);
+ SAL_INFO("sal.osl", "osl_getModuleURLFromAddress(" << addr << ") => " << OUString(*ppLibraryUrl));
+
+ rtl_uString_release(workDir);
+ result = true;
+ }
+ else
+ {
+ result = false;
+ }
+ rtl_string_release(path);
+ }
+ return result;
+}
+
+/*****************************************************************************/
+/* osl_getModuleURLFromFunctionAddress */
+/*****************************************************************************/
+sal_Bool SAL_CALL osl_getModuleURLFromFunctionAddress(oslGenericFunction addr, rtl_uString ** ppLibraryUrl)
+{
+ return osl_getModuleURLFromAddress(
+ reinterpret_cast<void*>(addr), ppLibraryUrl);
+ // requires conditionally-supported conversion from function pointer to
+ // void *
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/mutex.cxx b/sal/osl/unx/mutex.cxx
new file mode 100644
index 000000000..8cdc2a071
--- /dev/null
+++ b/sal/osl/unx/mutex.cxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#if defined LINUX
+// to define __USE_UNIX98, via _XOPEN_SOURCE, enabling pthread_mutexattr_settype
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+#endif
+#include "system.hxx"
+#include "unixerrnostring.hxx"
+
+#include <sal/log.hxx>
+#include <osl/mutex.h>
+
+#include <pthread.h>
+#include <stdlib.h>
+
+
+typedef struct _oslMutexImpl
+{
+ pthread_mutex_t mutex;
+} oslMutexImpl;
+
+oslMutex SAL_CALL osl_createMutex()
+{
+ oslMutexImpl* pMutex = static_cast<oslMutexImpl*>(malloc(sizeof(oslMutexImpl)));
+ pthread_mutexattr_t aMutexAttr;
+ int nRet=0;
+
+ SAL_WARN_IF(!pMutex, "sal.osl.mutex", "null pMutex");
+
+ if ( pMutex == nullptr )
+ {
+ return nullptr;
+ }
+
+ pthread_mutexattr_init(&aMutexAttr);
+
+ nRet = pthread_mutexattr_settype(&aMutexAttr, PTHREAD_MUTEX_RECURSIVE);
+ if( nRet == 0 )
+ nRet = pthread_mutex_init(&(pMutex->mutex), &aMutexAttr);
+ if ( nRet != 0 )
+ {
+ SAL_WARN("sal.osl.mutex", "pthread_muxex_init failed: " << UnixErrnoString(nRet));
+
+ free(pMutex);
+ pMutex = nullptr;
+ }
+
+ pthread_mutexattr_destroy(&aMutexAttr);
+
+ return pMutex;
+}
+
+void SAL_CALL osl_destroyMutex(oslMutex pMutex)
+{
+ SAL_WARN_IF(!pMutex, "sal.osl.mutex", "null pMutex");
+
+ if ( pMutex != nullptr )
+ {
+ int nRet = pthread_mutex_destroy(&(pMutex->mutex));
+ if ( nRet != 0 )
+ {
+ SAL_WARN("sal.osl.mutex", "pthread_mutex_destroy failed: " << UnixErrnoString(nRet));
+ }
+
+ free(pMutex);
+ }
+}
+
+#ifdef __COVERITY__
+ extern void __coverity_recursive_lock_acquire__(void*);
+ extern void __coverity_recursive_lock_release__(void*);
+ extern void __coverity_assert_locked__(void*);
+#endif
+
+sal_Bool SAL_CALL osl_acquireMutex(oslMutex pMutex)
+{
+ SAL_WARN_IF(!pMutex, "sal.osl.mutex", "null pMutex");
+
+ if ( pMutex != nullptr )
+ {
+ int nRet = pthread_mutex_lock(&(pMutex->mutex));
+ if ( nRet != 0 )
+ {
+ SAL_WARN("sal.osl.mutex", "pthread_mutex_lock failed: " << UnixErrnoString(nRet));
+ return false;
+ }
+#ifdef __COVERITY__
+ __coverity_recursive_lock_acquire__(pMutex);
+#endif
+ return true;
+ }
+
+ /* not initialized */
+ return false;
+}
+
+sal_Bool SAL_CALL osl_tryToAcquireMutex(oslMutex pMutex)
+{
+ bool result = false;
+
+ SAL_WARN_IF(!pMutex, "sal.osl.mutex", "null pMutex");
+
+ if ( pMutex )
+ {
+ int nRet = pthread_mutex_trylock(&(pMutex->mutex));
+ if ( nRet == 0 )
+ {
+#ifdef __COVERITY__
+ __coverity_recursive_lock_acquire__(pMutex);
+#endif
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+sal_Bool SAL_CALL osl_releaseMutex(oslMutex pMutex)
+{
+#ifdef __COVERITY__
+ __coverity_assert_locked__(pMutex);
+#endif
+ SAL_WARN_IF(!pMutex, "sal.osl.mutex", "null pMutex");
+
+ if ( pMutex )
+ {
+ int nRet = pthread_mutex_unlock(&(pMutex->mutex));
+ if ( nRet != 0 )
+ {
+ SAL_WARN("sal.osl.mutex", "pthread_mutex_unlock failed: " << UnixErrnoString(nRet));
+ return false;
+ }
+
+#ifdef __COVERITY__
+ __coverity_recursive_lock_release__(pMutex);
+#endif
+ return true;
+ }
+
+ /* not initialized */
+ return false;
+}
+
+static oslMutexImpl globalMutexImpl;
+
+static void globalMutexInitImpl() {
+ pthread_mutexattr_t attr;
+ if (pthread_mutexattr_init(&attr) != 0 ||
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) ||
+ pthread_mutex_init(&globalMutexImpl.mutex, &attr) != 0 ||
+ pthread_mutexattr_destroy(&attr) != 0)
+ {
+ abort();
+ }
+}
+
+oslMutex * SAL_CALL osl_getGlobalMutex()
+{
+ /* necessary to get a "oslMutex *" */
+ static oslMutex globalMutex = &globalMutexImpl;
+
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ if (pthread_once(&once, &globalMutexInitImpl) != 0) {
+ abort();
+ }
+
+ return &globalMutex;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/nlsupport.cxx b/sal/osl/unx/nlsupport.cxx
new file mode 100644
index 000000000..a4c7c807c
--- /dev/null
+++ b/sal/osl/unx/nlsupport.cxx
@@ -0,0 +1,872 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <cstring>
+
+#include <osl/nlsupport.h>
+#include <osl/diagnose.h>
+#include <osl/process.h>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include "nlsupport.hxx"
+
+#if defined(LINUX) || defined(__sun) || defined(NETBSD) || \
+ defined(FREEBSD) || defined(MACOSX) || defined(IOS) || defined(OPENBSD) || \
+ defined(DRAGONFLY)
+#if !defined(MACOSX) && !defined(IOS)
+#include <locale.h>
+#include <langinfo.h>
+#else
+#include <osl/module.h>
+#include <osl/thread.h>
+#endif /* !MACOSX && !IOS */
+#endif /* LINUX || __sun || NETBSD || MACOSX || IOS */
+
+#if defined(MACOSX) || defined(IOS)
+#include "system.hxx"
+#endif
+
+#include <string.h>
+
+namespace {
+
+struct Pair {
+ const char *key;
+ const rtl_TextEncoding value;
+};
+
+}
+
+/*****************************************************************************
+ compare function for binary search
+ *****************************************************************************/
+
+static int
+pair_compare (const char *key, const Pair *pair)
+{
+ int result = rtl_str_compareIgnoreAsciiCase( key, pair->key );
+ return result;
+}
+
+/*****************************************************************************
+ binary search on encoding tables
+ *****************************************************************************/
+
+static const Pair*
+pair_search (const char *key, const Pair *base, unsigned int member )
+{
+ unsigned int lower = 0;
+ unsigned int upper = member;
+
+ /* check for validity of input */
+ if ( (key == nullptr) || (base == nullptr) || (member == 0) )
+ return nullptr;
+
+ /* binary search */
+ while ( lower < upper )
+ {
+ const unsigned int current = (lower + upper) / 2;
+ const int comparison = pair_compare( key, base + current );
+ if (comparison < 0)
+ upper = current;
+ else if (comparison > 0)
+ lower = current + 1;
+ else
+ return base + current;
+ }
+
+ return nullptr;
+}
+
+/*****************************************************************************
+ convert rtl_Locale to locale string
+ *****************************************************************************/
+
+static char * compose_locale( rtl_Locale * pLocale, char * buffer, size_t n )
+{
+ /* check if a valid locale is specified */
+ if( pLocale && pLocale->Language &&
+ (pLocale->Language->length == 2 || pLocale->Language->length == 3) )
+ {
+ size_t offset = 0;
+
+ /* convert language code to ascii */
+ {
+ rtl_String *pLanguage = nullptr;
+
+ rtl_uString2String( &pLanguage,
+ pLocale->Language->buffer, pLocale->Language->length,
+ RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
+
+ if( sal::static_int_cast<sal_uInt32>(pLanguage->length) < n )
+ {
+ strcpy( buffer, pLanguage->buffer );
+ offset = pLanguage->length;
+ }
+
+ rtl_string_release( pLanguage );
+ }
+
+ /* convert country code to ascii */
+ if( pLocale->Country && (pLocale->Country->length == 2) )
+ {
+ rtl_String *pCountry = nullptr;
+
+ rtl_uString2String( &pCountry,
+ pLocale->Country->buffer, pLocale->Country->length,
+ RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
+
+ if( offset + pCountry->length + 1 < n )
+ {
+ strcpy( buffer + offset++, "_" );
+ strcpy( buffer + offset, pCountry->buffer );
+ offset += pCountry->length;
+ }
+
+ rtl_string_release( pCountry );
+ }
+
+ /* convert variant to ascii - check if there is enough space for the variant string */
+ if( pLocale->Variant && pLocale->Variant->length &&
+ ( sal::static_int_cast<sal_uInt32>(pLocale->Variant->length) < n - 6 ) )
+ {
+ rtl_String *pVariant = nullptr;
+
+ rtl_uString2String( &pVariant,
+ pLocale->Variant->buffer, pLocale->Variant->length,
+ RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
+
+ if( offset + pVariant->length + 1 < n )
+ {
+ strcpy( buffer + offset, pVariant->buffer );
+ }
+
+ rtl_string_release( pVariant );
+ }
+
+ return buffer;
+ }
+
+ return nullptr;
+}
+
+/*****************************************************************************
+ convert locale string to rtl_Locale
+ *****************************************************************************/
+
+static rtl_Locale * parse_locale( const char * locale )
+{
+ assert(locale != nullptr);
+
+ if (*locale == '\0' || std::strcmp(locale, "C") == 0
+ || std::strcmp(locale, "POSIX") == 0)
+ {
+ return rtl_locale_register(u"C", u"", u"");
+ }
+
+ size_t len = strlen( locale );
+
+ rtl_uString * pLanguage = nullptr;
+ rtl_uString * pCountry = nullptr;
+ rtl_uString * pVariant = nullptr;
+
+ size_t offset = std::min<size_t>(len, 2);
+
+ rtl_Locale * ret;
+
+ /* language is a two or three letter code */
+ if( (len > 3 && locale[3] == '_') || (len == 3 && locale[2] != '_') )
+ offset = 3;
+
+ /* convert language code to unicode */
+ rtl_string2UString( &pLanguage, locale, offset, RTL_TEXTENCODING_ASCII_US, OSTRING_TO_OUSTRING_CVTFLAGS );
+ OSL_ASSERT(pLanguage != nullptr);
+
+ /* convert country code to unicode */
+ if( len >= offset+3 && locale[offset] == '_' )
+ {
+ rtl_string2UString( &pCountry, locale + offset + 1, 2, RTL_TEXTENCODING_ASCII_US, OSTRING_TO_OUSTRING_CVTFLAGS );
+ OSL_ASSERT(pCountry != nullptr);
+ offset += 3;
+ }
+
+ /* convert variant code to unicode - do not rely on "." as delimiter */
+ if( len > offset ) {
+ rtl_string2UString( &pVariant, locale + offset, len - offset, RTL_TEXTENCODING_ASCII_US, OSTRING_TO_OUSTRING_CVTFLAGS );
+ OSL_ASSERT(pVariant != nullptr);
+ }
+
+ ret = rtl_locale_register( pLanguage->buffer, pCountry ? pCountry->buffer : u"", pVariant ? pVariant->buffer : u"" );
+
+ if (pVariant) rtl_uString_release(pVariant);
+ if (pCountry) rtl_uString_release(pCountry);
+ if (pLanguage) rtl_uString_release(pLanguage);
+
+ return ret;
+}
+
+#if defined(LINUX) || defined(__sun) || defined(NETBSD) || \
+ defined(FREEBSD) || defined(OPENBSD) || defined(DRAGONFLY)
+
+/*
+ * This implementation of osl_getTextEncodingFromLocale maps
+ * from nl_langinfo_l(CODESET) to rtl_textencoding defines.
+ * nl_langinfo() is supported only on Linux, Solaris,
+ * >= NetBSD 1.6 and >= FreeBSD 4.4
+ */
+
+#ifdef LINUX
+#if !defined(CODESET)
+#define CODESET _NL_CTYPE_CODESET_NAME
+#endif
+#endif
+
+/*
+ * _nl_language_list[] is an array list of supported encodings. Because
+ * we are using a binary search, the list has to be in ascending order.
+ * We are comparing the encodings case insensitive, so the list has
+ * to be completely upper or lowercase.
+ */
+
+#if defined(__sun)
+
+/* The values in the below list can be obtained with a script like
+ * #!/bin/sh
+ * for i in `locale -a`; do
+ * LC_ALL=$i locale -k code_set_name
+ * done
+ */
+static const Pair nl_language_list[] = {
+ { "5601", RTL_TEXTENCODING_EUC_KR }, /* ko_KR.EUC */
+ { "646", RTL_TEXTENCODING_ISO_8859_1 }, /* fake: ASCII_US */
+ { "ANSI-1251", RTL_TEXTENCODING_MS_1251 }, /* ru_RU.ANSI1251 */
+ { "BIG5", RTL_TEXTENCODING_BIG5 }, /* zh_CN.BIG5 */
+ { "BIG5-HKSCS", RTL_TEXTENCODING_BIG5_HKSCS }, /* zh_CN.BIG5HK */
+ { "CNS11643", RTL_TEXTENCODING_EUC_TW }, /* zh_TW.EUC */
+ { "EUCJP", RTL_TEXTENCODING_EUC_JP }, /* ja_JP.eucjp */
+ { "GB18030", RTL_TEXTENCODING_GB_18030 }, /* zh_CN.GB18030 */
+ { "GB2312", RTL_TEXTENCODING_GB_2312 }, /* zh_CN */
+ { "GBK", RTL_TEXTENCODING_GBK }, /* zh_CN.GBK */
+ { "ISO8859-1", RTL_TEXTENCODING_ISO_8859_1 },
+ { "ISO8859-10", RTL_TEXTENCODING_ISO_8859_10 },
+ { "ISO8859-13", RTL_TEXTENCODING_ISO_8859_13 }, /* lt_LT lv_LV */
+ { "ISO8859-14", RTL_TEXTENCODING_ISO_8859_14 },
+ { "ISO8859-15", RTL_TEXTENCODING_ISO_8859_15 },
+ { "ISO8859-2", RTL_TEXTENCODING_ISO_8859_2 },
+ { "ISO8859-3", RTL_TEXTENCODING_ISO_8859_3 },
+ { "ISO8859-4", RTL_TEXTENCODING_ISO_8859_4 },
+ { "ISO8859-5", RTL_TEXTENCODING_ISO_8859_5 },
+ { "ISO8859-6", RTL_TEXTENCODING_ISO_8859_6 },
+ { "ISO8859-7", RTL_TEXTENCODING_ISO_8859_7 },
+ { "ISO8859-8", RTL_TEXTENCODING_ISO_8859_8 },
+ { "ISO8859-9", RTL_TEXTENCODING_ISO_8859_9 },
+ { "KOI8-R", RTL_TEXTENCODING_KOI8_R },
+ { "KOI8-U", RTL_TEXTENCODING_KOI8_U },
+ { "PCK", RTL_TEXTENCODING_MS_932 },
+ { "SUN_EU_GREEK", RTL_TEXTENCODING_ISO_8859_7 }, /* 8859-7 + Euro */
+ { "TIS620.2533", RTL_TEXTENCODING_MS_874 }, /* th_TH.TIS620 */
+ { "UTF-8", RTL_TEXTENCODING_UTF8 }
+};
+
+/* XXX MS-874 is an extension to tis620, so this is not
+ * really equivalent */
+
+#elif defined(LINUX)
+
+static const Pair nl_language_list[] = {
+ { "ANSI_X3.110-1983", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-99 NAPLPS */
+ { "ANSI_X3.4-1968", RTL_TEXTENCODING_ISO_8859_1 }, /* fake: ASCII_US */
+ { "ASMO_449", RTL_TEXTENCODING_DONTKNOW }, /* ISO_9036 ARABIC7 */
+ { "BALTIC", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-179 */
+ { "BIG5", RTL_TEXTENCODING_BIG5 }, /* locale: zh_TW */
+ { "BIG5-HKSCS", RTL_TEXTENCODING_BIG5_HKSCS }, /* locale: zh_CN.BIG5HK */
+ { "BIG5HKSCS", RTL_TEXTENCODING_BIG5_HKSCS }, /* deprecated */
+ { "BS_4730", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-4 ISO646-GB */
+ { "BS_VIEWDATA", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-47 */
+ { "CP1250", RTL_TEXTENCODING_MS_1250 }, /* MS-EE */
+ { "CP1251", RTL_TEXTENCODING_MS_1251 }, /* MS-CYRL */
+ { "CP1252", RTL_TEXTENCODING_MS_1252 }, /* MS-ANSI */
+ { "CP1253", RTL_TEXTENCODING_MS_1253 }, /* MS-GREEK */
+ { "CP1254", RTL_TEXTENCODING_MS_1254 }, /* MS-TURK */
+ { "CP1255", RTL_TEXTENCODING_MS_1255 }, /* MS-HEBR */
+ { "CP1256", RTL_TEXTENCODING_MS_1256 }, /* MS-ARAB */
+ { "CP1257", RTL_TEXTENCODING_MS_1257 }, /* WINBALTRIM */
+ { "CSA_Z243.4-1985-1", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-121 */
+ { "CSA_Z243.4-1985-2", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-122 CSA7-2 */
+ { "CSA_Z243.4-1985-GR", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-123 */
+ { "CSN_369103", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-139 */
+ { "CWI", RTL_TEXTENCODING_DONTKNOW }, /* CWI-2 CP-HU */
+ { "DEC-MCS", RTL_TEXTENCODING_DONTKNOW }, /* DEC */
+ { "DIN_66003", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-21 */
+ { "DS_2089", RTL_TEXTENCODING_DONTKNOW }, /* DS2089 ISO646-DK */
+ { "EBCDIC-AT-DE", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-AT-DE-A", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-CA-FR", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-DK-NO", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-DK-NO-A", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-ES", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-ES-A", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-ES-S", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-FI-SE", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-FI-SE-A", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-FR", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-IS-FRISS", RTL_TEXTENCODING_DONTKNOW }, /* FRISS */
+ { "EBCDIC-IT", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-PT", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-UK", RTL_TEXTENCODING_DONTKNOW },
+ { "EBCDIC-US", RTL_TEXTENCODING_DONTKNOW },
+ { "ECMA-CYRILLIC", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-111 */
+ { "ES", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-17 */
+ { "ES2", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-85 */
+ { "EUC-JP", RTL_TEXTENCODING_EUC_JP }, /* locale: ja_JP.eucjp */
+ { "EUC-KR", RTL_TEXTENCODING_EUC_KR }, /* locale: ko_KR.euckr */
+ { "EUC-TW", RTL_TEXTENCODING_EUC_TW }, /* locale: zh_TW.euctw */
+ { "GB18030", RTL_TEXTENCODING_GB_18030 }, /* locale: zh_CN.gb18030 */
+ { "GB2312", RTL_TEXTENCODING_GB_2312 }, /* locale: zh_CN */
+ { "GB_1988-80", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-57 */
+ { "GBK", RTL_TEXTENCODING_GBK }, /* locale: zh_CN.GBK */
+ { "GOST_19768-74", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-153 */
+ { "GREEK-CCITT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-150 */
+ { "GREEK7", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-88 */
+ { "GREEK7-OLD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-18 */
+ { "HP-ROMAN8", RTL_TEXTENCODING_DONTKNOW }, /* ROMAN8 R8 */
+ { "IBM037", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-[US|CA|WT] */
+ { "IBM038", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-INT CP038 */
+ { "IBM1004", RTL_TEXTENCODING_DONTKNOW }, /* CP1004 OS2LATIN1 */
+ { "IBM1026", RTL_TEXTENCODING_DONTKNOW }, /* CP1026 1026 */
+ { "IBM1047", RTL_TEXTENCODING_DONTKNOW }, /* CP1047 1047 */
+ { "IBM256", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-INT1 */
+ { "IBM273", RTL_TEXTENCODING_DONTKNOW }, /* CP273 */
+ { "IBM274", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-BE CP274 */
+ { "IBM275", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-BR CP275 */
+ { "IBM277", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CP-[DK|NO] */
+ { "IBM278", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CP-[FISE]*/
+ { "IBM280", RTL_TEXTENCODING_DONTKNOW }, /* CP280 EBCDIC-CP-IT*/
+ { "IBM281", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-JP-E CP281 */
+ { "IBM284", RTL_TEXTENCODING_DONTKNOW }, /* CP284 EBCDIC-CP-ES */
+ { "IBM285", RTL_TEXTENCODING_DONTKNOW }, /* CP285 EBCDIC-CP-GB */
+ { "IBM290", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-JP-KANA */
+ { "IBM297", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CP-FR */
+ { "IBM420", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CP-AR1 */
+ { "IBM423", RTL_TEXTENCODING_DONTKNOW }, /* CP423 EBCDIC-CP-GR */
+ { "IBM424", RTL_TEXTENCODING_DONTKNOW }, /* CP424 EBCDIC-CP-HE */
+ { "IBM437", RTL_TEXTENCODING_IBM_437 }, /* CP437 437 */
+ { "IBM500", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CP-[BE|CH] */
+ { "IBM850", RTL_TEXTENCODING_IBM_850 }, /* CP850 850 */
+ { "IBM851", RTL_TEXTENCODING_DONTKNOW }, /* CP851 851 */
+ { "IBM852", RTL_TEXTENCODING_IBM_852 }, /* CP852 852 */
+ { "IBM855", RTL_TEXTENCODING_IBM_855 }, /* CP855 855 */
+ { "IBM857", RTL_TEXTENCODING_IBM_857 }, /* CP857 857 */
+ { "IBM860", RTL_TEXTENCODING_IBM_860 }, /* CP860 860 */
+ { "IBM861", RTL_TEXTENCODING_IBM_861 }, /* CP861 861 CP-IS */
+ { "IBM862", RTL_TEXTENCODING_IBM_862 }, /* CP862 862 */
+ { "IBM863", RTL_TEXTENCODING_IBM_863 }, /* CP863 863 */
+ { "IBM864", RTL_TEXTENCODING_IBM_864 }, /* CP864 */
+ { "IBM865", RTL_TEXTENCODING_IBM_865 }, /* CP865 865 */
+ { "IBM866", RTL_TEXTENCODING_IBM_866 }, /* CP866 866 */
+ { "IBM868", RTL_TEXTENCODING_DONTKNOW }, /* CP868 CP-AR */
+ { "IBM869", RTL_TEXTENCODING_IBM_869 }, /* CP869 869 CP-GR */
+ { "IBM870", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-[ROECE|YU] */
+ { "IBM871", RTL_TEXTENCODING_DONTKNOW }, /* CP871 EBCDIC-CP-IS */
+ { "IBM875", RTL_TEXTENCODING_DONTKNOW }, /* CP875 EBCDIC-GREEK */
+ { "IBM880", RTL_TEXTENCODING_DONTKNOW }, /* EBCDIC-CYRILLIC */
+ { "IBM891", RTL_TEXTENCODING_DONTKNOW }, /* CP891 */
+ { "IBM903", RTL_TEXTENCODING_DONTKNOW }, /* CP903 */
+ { "IBM904", RTL_TEXTENCODING_DONTKNOW }, /* CP904 904 */
+ { "IBM905", RTL_TEXTENCODING_DONTKNOW }, /* CP905 EBCDIC-CP-TR */
+ { "IBM918", RTL_TEXTENCODING_DONTKNOW }, /* CP918 EBCDIC-AR2 */
+ { "IEC_P27-1", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-143 */
+ { "INIS", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-49 */
+ { "INIS-8", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-50 */
+ { "INIS-CYRILLIC", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-51 */
+ { "INVARIANT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-170 */
+ { "ISO-8859-1", RTL_TEXTENCODING_ISO_8859_1 }, /* ISO-IR-100 CP819 */
+ { "ISO-8859-10", RTL_TEXTENCODING_ISO_8859_10 }, /* ISO-IR-157 LATIN6 */
+ { "ISO-8859-13", RTL_TEXTENCODING_ISO_8859_13 }, /* ISO-IR-179 LATIN7 */
+ { "ISO-8859-14", RTL_TEXTENCODING_ISO_8859_14 }, /* LATIN8 L8 */
+ { "ISO-8859-15", RTL_TEXTENCODING_ISO_8859_15 },
+ { "ISO-8859-2", RTL_TEXTENCODING_ISO_8859_2 }, /* LATIN2 L2 */
+ { "ISO-8859-3", RTL_TEXTENCODING_ISO_8859_3 }, /* LATIN3 L3 */
+ { "ISO-8859-4", RTL_TEXTENCODING_ISO_8859_4 }, /* LATIN4 L4 */
+ { "ISO-8859-5", RTL_TEXTENCODING_ISO_8859_5 }, /* CYRILLIC */
+ { "ISO-8859-6", RTL_TEXTENCODING_ISO_8859_6 }, /* ECMA-114 ARABIC */
+ { "ISO-8859-7", RTL_TEXTENCODING_ISO_8859_7 }, /* ECMA-118 GREEK8 */
+ { "ISO-8859-8", RTL_TEXTENCODING_ISO_8859_8 }, /* ISO_8859-8 HEBREW */
+ { "ISO-8859-9", RTL_TEXTENCODING_ISO_8859_9 }, /* ISO_8859-9 LATIN5 */
+ { "ISO-IR-90", RTL_TEXTENCODING_DONTKNOW }, /* ISO_6937-2:1983 */
+ { "ISO_10367-BOX", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-155 */
+ { "ISO_2033-1983", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-98 E13B */
+ { "ISO_5427", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-37 KOI-7 */
+ { "ISO_5427-EXT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-54 */
+ { "ISO_5428", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-55 */
+ { "ISO_646.BASIC", RTL_TEXTENCODING_ASCII_US }, /* REF */
+ { "ISO_646.IRV", RTL_TEXTENCODING_ASCII_US }, /* ISO-IR-2 IRV */
+ { "ISO_646.IRV:1983", RTL_TEXTENCODING_ISO_8859_1 }, /* fake: ASCII_US, used for "C" locale*/
+ { "ISO_6937", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-156 ISO6937*/
+ { "ISO_6937-2-25", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-152 */
+ { "ISO_6937-2-ADD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-142 */
+ { "ISO_8859-SUPP", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-154 */
+ { "IT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-15 */
+ { "JIS_C6220-1969-JP", RTL_TEXTENCODING_DONTKNOW }, /* KATAKANA X0201-7 */
+ { "JIS_C6220-1969-RO", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-14 */
+ { "JIS_C6229-1984-A", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-91 */
+ { "JIS_C6229-1984-B", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-92 */
+ { "JIS_C6229-1984-B-ADD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-93 */
+ { "JIS_C6229-1984-HAND", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-94 */
+ { "JIS_C6229-1984-HAND-ADD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-95 */
+ { "JIS_C6229-1984-KANA", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-96 */
+ { "JIS_X0201", RTL_TEXTENCODING_DONTKNOW }, /* X0201 */
+ { "JUS_I.B1.002", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-141 */
+ { "JUS_I.B1.003-MAC", RTL_TEXTENCODING_DONTKNOW }, /* MACEDONIAN */
+ { "JUS_I.B1.003-SERB", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-146 SERBIAN */
+ { "KOI-8", RTL_TEXTENCODING_DONTKNOW },
+ { "KOI8-R", RTL_TEXTENCODING_KOI8_R },
+ { "KOI8-U", RTL_TEXTENCODING_KOI8_U },
+ { "KSC5636", RTL_TEXTENCODING_DONTKNOW }, /* ISO646-KR */
+ { "LATIN-GREEK", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-19 */
+ { "LATIN-GREEK-1", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-27 */
+ { "MAC-IS", RTL_TEXTENCODING_APPLE_ROMAN },
+ { "MAC-UK", RTL_TEXTENCODING_APPLE_ROMAN },
+ { "MACINTOSH", RTL_TEXTENCODING_APPLE_ROMAN }, /* MAC */
+ { "MSZ_7795.3", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-86 */
+ { "NATS-DANO", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-9-1 */
+ { "NATS-DANO-ADD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-9-2 */
+ { "NATS-SEFI", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-8-1 */
+ { "NATS-SEFI-ADD", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-8-2 */
+ { "NC_NC00-10", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-151 */
+ { "NEXTSTEP", RTL_TEXTENCODING_DONTKNOW }, /* NEXT */
+ { "NF_Z_62-010", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-69 */
+ { "NF_Z_62-010_(1973)", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-25 */
+ { "NS_4551-1", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-60 */
+ { "NS_4551-2", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-61 */
+ { "PT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-16 */
+ { "PT2", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-84 */
+ { "SAMI", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-158 */
+ { "SEN_850200_B", RTL_TEXTENCODING_DONTKNOW }, /* ISO646-[FI|SE] */
+ { "SEN_850200_C", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-11 */
+ { "T.101-G2", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-128 */
+ { "T.61-7BIT", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-102 */
+ { "T.61-8BIT", RTL_TEXTENCODING_DONTKNOW }, /* T.61 ISO-IR-103 */
+ { "TIS-620", RTL_TEXTENCODING_MS_874 }, /* locale: th_TH */
+ { "UTF-8", RTL_TEXTENCODING_UTF8 }, /* ISO-10646/UTF-8 */
+ { "VIDEOTEX-SUPPL", RTL_TEXTENCODING_DONTKNOW }, /* ISO-IR-70 */
+ { "WIN-SAMI-2", RTL_TEXTENCODING_DONTKNOW } /* WS2 */
+};
+
+#elif defined(FREEBSD) || defined(DRAGONFLY)
+
+static const Pair nl_language_list[] = {
+ { "ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "BIG5", RTL_TEXTENCODING_BIG5 }, /* China - Traditional Chinese */
+ { "CP1251", RTL_TEXTENCODING_MS_1251 }, /* MS-CYRL */
+ { "CP866", RTL_TEXTENCODING_IBM_866 }, /* CP866 866 */
+ { "EUCCN", RTL_TEXTENCODING_EUC_CN }, /* China - Simplified Chinese */
+ { "EUCJP", RTL_TEXTENCODING_EUC_JP }, /* Japan */
+ { "EUCKR", RTL_TEXTENCODING_EUC_KR }, /* Korea */
+ { "ISO8859-1", RTL_TEXTENCODING_ISO_8859_1 }, /* Western */
+ { "ISO8859-15", RTL_TEXTENCODING_ISO_8859_15 }, /* Western Updated (w/Euro sign) */
+ { "ISO8859-2", RTL_TEXTENCODING_ISO_8859_2 }, /* Central European */
+ { "ISO8859-4", RTL_TEXTENCODING_ISO_8859_4 }, /* LATIN4 L4 */
+ { "ISO8859-5", RTL_TEXTENCODING_ISO_8859_5 }, /* Cyrillic */
+ { "ISO8859-7", RTL_TEXTENCODING_ISO_8859_7 }, /* Greek */
+ { "ISO8859-9", RTL_TEXTENCODING_ISO_8859_9 }, /* Turkish */
+ { "KOI8-R", RTL_TEXTENCODING_KOI8_R }, /* KOI8-R */
+ { "KOI8-U", RTL_TEXTENCODING_KOI8_U }, /* KOI8-U */
+ { "SJIS", RTL_TEXTENCODING_SHIFT_JIS }, /* Japan */
+ { "US-ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "UTF-8", RTL_TEXTENCODING_UTF8 } /* ISO-10646/UTF-8 */
+};
+
+#elif defined(NETBSD)
+
+static const Pair nl_language_list[] = {
+ { "ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "BIG5", RTL_TEXTENCODING_BIG5 }, /* China - Traditional Chinese */
+ { "Big5", RTL_TEXTENCODING_BIG5 }, /* China - Traditional Chinese */
+ { "Big5-HKSCS", RTL_TEXTENCODING_BIG5_HKSCS }, /* locale: zh_CN.BIG5HK */
+ { "Big5HKSCS", RTL_TEXTENCODING_BIG5_HKSCS }, /* deprecated */
+ { "CP1251", RTL_TEXTENCODING_MS_1251 }, /* MS-CYRL */
+ { "CP866", RTL_TEXTENCODING_IBM_866 }, /* CP866 866 */
+ { "CTEXT", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "eucCN", RTL_TEXTENCODING_EUC_CN }, /* China - Simplified Chinese */
+ { "eucJP", RTL_TEXTENCODING_EUC_JP }, /* Japan */
+ { "eucKR", RTL_TEXTENCODING_EUC_KR }, /* Korea */
+ { "eucTW", RTL_TEXTENCODING_EUC_TW }, /* China - Traditional Chinese */
+ { "GB18030", RTL_TEXTENCODING_GB_18030 }, /* locale: zh_CN.gb18030 */
+ { "GB2312", RTL_TEXTENCODING_GB_2312 }, /* locale: zh_CN */
+ { "ISO-2022-JP", RTL_TEXTENCODING_DONTKNOW }, /* */
+ { "ISO-2022-JP-2", RTL_TEXTENCODING_DONTKNOW }, /* */
+ { "ISO8859-1", RTL_TEXTENCODING_ISO_8859_1 }, /* Western */
+ { "ISO8859-13", RTL_TEXTENCODING_ISO_8859_13 }, /* ISO-IR-179 LATIN7 */
+ { "ISO8859-15", RTL_TEXTENCODING_ISO_8859_15 }, /* Western Updated (w/Euro sign) */
+ { "ISO8859-2", RTL_TEXTENCODING_ISO_8859_2 }, /* Central European */
+ { "ISO8859-4", RTL_TEXTENCODING_ISO_8859_4 }, /* LATIN4 L4 */
+ { "ISO8859-5", RTL_TEXTENCODING_ISO_8859_5 }, /* Cyrillic */
+ { "ISO8859-7", RTL_TEXTENCODING_ISO_8859_7 }, /* Greek */
+ { "ISO8859-9", RTL_TEXTENCODING_ISO_8859_9 }, /* Turkish */
+ { "KOI8-R", RTL_TEXTENCODING_KOI8_R }, /* KOI8-R */
+ { "KOI8-U", RTL_TEXTENCODING_KOI8_U }, /* KOI8-U */
+ { "PT154", RTL_TEXTENCODING_PT154 }, /* */
+ { "SJIS", RTL_TEXTENCODING_SHIFT_JIS }, /* Japan */
+ { "US-ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "UTF-8", RTL_TEXTENCODING_UTF8 } /* ISO-10646/UTF-8 */
+};
+
+#elif defined(OPENBSD)
+
+static const Pair nl_language_list[] = {
+ { "ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "BIG5", RTL_TEXTENCODING_BIG5 }, /* China - Traditional Chinese */
+ { "CP1251", RTL_TEXTENCODING_MS_1251 }, /* MS-CYRL */
+ { "CP866", RTL_TEXTENCODING_IBM_866 }, /* CP866 866 */
+ { "EUCCN", RTL_TEXTENCODING_EUC_CN }, /* China - Simplified Chinese */
+ { "EUCJP", RTL_TEXTENCODING_EUC_JP }, /* Japan */
+ { "EUCKR", RTL_TEXTENCODING_EUC_KR }, /* Korea */
+ { "ISO8859-1", RTL_TEXTENCODING_ISO_8859_1 }, /* Western */
+ { "ISO8859-15", RTL_TEXTENCODING_ISO_8859_15 }, /* Western Updated (w/Euro sign) */
+ { "ISO8859-2", RTL_TEXTENCODING_ISO_8859_2 }, /* Central European */
+ { "ISO8859-4", RTL_TEXTENCODING_ISO_8859_4 }, /* LATIN4 L4 */
+ { "ISO8859-5", RTL_TEXTENCODING_ISO_8859_5 }, /* Cyrillic */
+ { "ISO8859-7", RTL_TEXTENCODING_ISO_8859_7 }, /* Greek */
+ { "ISO8859-9", RTL_TEXTENCODING_ISO_8859_9 }, /* Turkish */
+ { "KOI8-R", RTL_TEXTENCODING_KOI8_R }, /* KOI8-R */
+ { "KOI8-U", RTL_TEXTENCODING_KOI8_U }, /* KOI8-U */
+ { "SJIS", RTL_TEXTENCODING_SHIFT_JIS }, /* Japan */
+ { "US-ASCII", RTL_TEXTENCODING_ASCII_US }, /* US-ASCII */
+ { "UTF-8", RTL_TEXTENCODING_UTF8 } /* ISO-10646/UTF-8 */
+};
+
+#endif /* ifdef __sun LINUX FREEBSD NETBSD OPENBSD */
+
+/*****************************************************************************
+ return the text encoding corresponding to the given locale
+ *****************************************************************************/
+
+rtl_TextEncoding osl_getTextEncodingFromLocale( rtl_Locale * pLocale )
+{
+ const Pair *language=nullptr;
+
+ char locale_buf[64] = "";
+ char codeset_buf[64];
+
+ char *codeset = nullptr;
+
+ /* default to process locale if pLocale == NULL */
+ if( pLocale == nullptr )
+ osl_getProcessLocale( &pLocale );
+
+ /* convert rtl_Locale to locale string */
+ compose_locale( pLocale, locale_buf, 64 );
+
+ locale_t ctype_locale = newlocale(
+ LC_CTYPE_MASK, locale_buf, static_cast<locale_t>(0));
+ if (ctype_locale == static_cast<locale_t>(0))
+ {
+ return RTL_TEXTENCODING_DONTKNOW;
+ }
+
+ /* get the charset as indicated by the LC_CTYPE locale */
+#if defined(NETBSD) && !defined(CODESET)
+ codeset = NULL;
+#else
+ codeset = nl_langinfo_l(CODESET, ctype_locale);
+ // per SUSv4, the return value of nl_langinfo_l can be invalidated by a
+ // subsequent call to nl_langinfo (not nl_langinfo_l) in any thread, but
+ // we cannot guard against that (at least, no code in LO itself should
+ // call nl_langinfo)
+#endif
+
+ if ( codeset != nullptr )
+ {
+ /* get codeset into mt save memory */
+ strncpy( codeset_buf, codeset, sizeof(codeset_buf) );
+ codeset_buf[sizeof(codeset_buf) - 1] = 0;
+ codeset = codeset_buf;
+ }
+
+ freelocale(ctype_locale);
+
+ /* search the codeset in our language list */
+ if ( codeset != nullptr )
+ {
+ language = pair_search (codeset, nl_language_list, SAL_N_ELEMENTS( nl_language_list ) );
+ }
+
+ OSL_ASSERT( language && ( RTL_TEXTENCODING_DONTKNOW != language->value ) );
+
+ /* a matching item in our list provides a mapping from codeset to
+ * rtl-codeset */
+ if ( language != nullptr )
+ return language->value;
+
+ return RTL_TEXTENCODING_DONTKNOW;
+}
+
+/*****************************************************************************
+ return the current process locale
+ *****************************************************************************/
+
+void imp_getProcessLocale( rtl_Locale ** ppLocale )
+{
+ char const * locale = getenv("LC_ALL");
+ if (locale == nullptr || *locale == '\0') {
+ locale = getenv("LC_CTYPE");
+ if (locale == nullptr || *locale == '\0') {
+ locale = getenv("LANG");
+ if (locale == nullptr || *locale == '\0') {
+ locale = "C";
+ }
+ }
+ }
+ // coverity[overrun-buffer-val : FALSE] - coverity gets this very wrong
+ *ppLocale = parse_locale(locale);
+}
+
+#else /* ifdef LINUX || __sun || MACOSX || NETBSD */
+
+/*
+ * This implementation of osl_getTextEncodingFromLocale maps
+ * from the ISO language codes.
+ */
+
+static const Pair full_locale_list[] = {
+ { "ja_JP.eucJP", RTL_TEXTENCODING_EUC_JP },
+ { "ja_JP.EUC", RTL_TEXTENCODING_EUC_JP },
+ { "ko_KR.EUC", RTL_TEXTENCODING_EUC_KR },
+ { "zh_CN.EUC", RTL_TEXTENCODING_EUC_CN },
+ { "zh_TW.EUC", RTL_TEXTENCODING_EUC_TW }
+};
+
+static const Pair locale_extension_list[] = {
+ { "big5", RTL_TEXTENCODING_BIG5 },
+ { "big5hk", RTL_TEXTENCODING_BIG5_HKSCS },
+ { "gb18030", RTL_TEXTENCODING_GB_18030 },
+ { "euc", RTL_TEXTENCODING_EUC_JP },
+ { "iso8859-1", RTL_TEXTENCODING_ISO_8859_1 },
+ { "iso8859-10", RTL_TEXTENCODING_ISO_8859_10 },
+ { "iso8859-13", RTL_TEXTENCODING_ISO_8859_13 },
+ { "iso8859-14", RTL_TEXTENCODING_ISO_8859_14 },
+ { "iso8859-15", RTL_TEXTENCODING_ISO_8859_15 },
+ { "iso8859-2", RTL_TEXTENCODING_ISO_8859_2 },
+ { "iso8859-3", RTL_TEXTENCODING_ISO_8859_3 },
+ { "iso8859-4", RTL_TEXTENCODING_ISO_8859_4 },
+ { "iso8859-5", RTL_TEXTENCODING_ISO_8859_5 },
+ { "iso8859-6", RTL_TEXTENCODING_ISO_8859_6 },
+ { "iso8859-7", RTL_TEXTENCODING_ISO_8859_7 },
+ { "iso8859-8", RTL_TEXTENCODING_ISO_8859_8 },
+ { "iso8859-9", RTL_TEXTENCODING_ISO_8859_9 },
+ { "koi8-r", RTL_TEXTENCODING_KOI8_R },
+ { "koi8-u", RTL_TEXTENCODING_KOI8_U },
+ { "pck", RTL_TEXTENCODING_MS_932 },
+#if (0)
+ { "sun_eu_greek", RTL_TEXTENCODING_DONTKNOW },
+#endif
+ { "utf-16", RTL_TEXTENCODING_UNICODE },
+ { "utf-7", RTL_TEXTENCODING_UTF7 },
+ { "utf-8", RTL_TEXTENCODING_UTF8 }
+};
+
+static const Pair iso_language_list[] = {
+ { "af", RTL_TEXTENCODING_ISO_8859_1 },
+ { "ar", RTL_TEXTENCODING_ISO_8859_6 },
+ { "az", RTL_TEXTENCODING_ISO_8859_9 },
+ { "be", RTL_TEXTENCODING_ISO_8859_5 },
+ { "bg", RTL_TEXTENCODING_ISO_8859_5 },
+ { "ca", RTL_TEXTENCODING_ISO_8859_1 },
+ { "cs", RTL_TEXTENCODING_ISO_8859_2 },
+ { "da", RTL_TEXTENCODING_ISO_8859_1 },
+ { "de", RTL_TEXTENCODING_ISO_8859_1 },
+ { "el", RTL_TEXTENCODING_ISO_8859_7 },
+ { "en", RTL_TEXTENCODING_ISO_8859_1 },
+ { "es", RTL_TEXTENCODING_ISO_8859_1 },
+ { "et", RTL_TEXTENCODING_ISO_8859_4 },
+ { "eu", RTL_TEXTENCODING_ISO_8859_1 },
+ { "fa", RTL_TEXTENCODING_ISO_8859_6 },
+ { "fi", RTL_TEXTENCODING_ISO_8859_1 },
+ { "fo", RTL_TEXTENCODING_ISO_8859_1 },
+ { "fr", RTL_TEXTENCODING_ISO_8859_1 },
+ { "gr", RTL_TEXTENCODING_ISO_8859_7 },
+ { "he", RTL_TEXTENCODING_ISO_8859_8 },
+ { "hi", RTL_TEXTENCODING_DONTKNOW },
+ { "hr", RTL_TEXTENCODING_ISO_8859_2 },
+ { "hu", RTL_TEXTENCODING_ISO_8859_2 },
+ { "hy", RTL_TEXTENCODING_DONTKNOW },
+ { "id", RTL_TEXTENCODING_ISO_8859_1 },
+ { "is", RTL_TEXTENCODING_ISO_8859_1 },
+ { "it", RTL_TEXTENCODING_ISO_8859_1 },
+ { "iw", RTL_TEXTENCODING_ISO_8859_8 },
+ { "ja", RTL_TEXTENCODING_EUC_JP },
+ { "ka", RTL_TEXTENCODING_DONTKNOW },
+ { "kk", RTL_TEXTENCODING_ISO_8859_5 },
+ { "ko", RTL_TEXTENCODING_EUC_KR },
+ { "lt", RTL_TEXTENCODING_ISO_8859_4 },
+ { "lv", RTL_TEXTENCODING_ISO_8859_4 },
+ { "mk", RTL_TEXTENCODING_ISO_8859_5 },
+ { "mr", RTL_TEXTENCODING_DONTKNOW },
+ { "ms", RTL_TEXTENCODING_ISO_8859_1 },
+ { "nl", RTL_TEXTENCODING_ISO_8859_1 },
+ { "no", RTL_TEXTENCODING_ISO_8859_1 },
+ { "pl", RTL_TEXTENCODING_ISO_8859_2 },
+ { "pt", RTL_TEXTENCODING_ISO_8859_1 },
+ { "ro", RTL_TEXTENCODING_ISO_8859_2 },
+ { "ru", RTL_TEXTENCODING_ISO_8859_5 },
+ { "sa", RTL_TEXTENCODING_DONTKNOW },
+ { "sk", RTL_TEXTENCODING_ISO_8859_2 },
+ { "sl", RTL_TEXTENCODING_ISO_8859_2 },
+ { "sq", RTL_TEXTENCODING_ISO_8859_2 },
+ { "sv", RTL_TEXTENCODING_ISO_8859_1 },
+ { "sw", RTL_TEXTENCODING_ISO_8859_1 },
+ { "ta", RTL_TEXTENCODING_DONTKNOW },
+ { "th", RTL_TEXTENCODING_DONTKNOW },
+ { "tr", RTL_TEXTENCODING_ISO_8859_9 },
+ { "tt", RTL_TEXTENCODING_ISO_8859_5 },
+ { "uk", RTL_TEXTENCODING_ISO_8859_5 },
+ { "ur", RTL_TEXTENCODING_ISO_8859_6 },
+ { "uz", RTL_TEXTENCODING_ISO_8859_9 },
+ { "vi", RTL_TEXTENCODING_DONTKNOW },
+ { "zh", RTL_TEXTENCODING_BIG5 }
+};
+
+/*****************************************************************************
+ return the text encoding corresponding to the given locale
+ *****************************************************************************/
+
+rtl_TextEncoding osl_getTextEncodingFromLocale( rtl_Locale * pLocale )
+{
+ const Pair *language = nullptr;
+ char locale_buf[64] = "";
+
+ /* default to process locale if pLocale == NULL */
+ if( nullptr == pLocale )
+ osl_getProcessLocale( &pLocale );
+
+ /* convert rtl_Locale to locale string */
+ if( compose_locale( pLocale, locale_buf, 64 ) )
+ {
+ /* check special handling list (EUC) first */
+ language = pair_search( locale_buf, full_locale_list, SAL_N_ELEMENTS( full_locale_list ) );
+
+ if( nullptr == language )
+ {
+ /*
+ * check if there is a charset qualifier at the end of the given locale string
+ * e.g. de.ISO8859-15 or de.ISO8859-15@euro which strongly indicates what
+ * charset to use
+ */
+ char* cp = strrchr( locale_buf, '.' );
+
+ if( nullptr != cp )
+ {
+ language = pair_search( cp + 1, locale_extension_list, SAL_N_ELEMENTS( locale_extension_list ) );
+ }
+ }
+
+ /* use iso language code to determine the charset */
+ if( nullptr == language )
+ {
+ /* iso lang codes have 2 characters */
+ locale_buf[2] = '\0';
+
+ language = pair_search( locale_buf, iso_language_list, SAL_N_ELEMENTS( iso_language_list ) );
+ }
+ }
+
+ /* a matching item in our list provides a mapping from codeset to
+ * rtl-codeset */
+ if ( language != nullptr )
+ return language->value;
+
+ return RTL_TEXTENCODING_DONTKNOW;
+}
+
+#if defined(MACOSX) || defined(IOS)
+
+/*****************************************************************************
+ return the current process locale
+ *****************************************************************************/
+
+void imp_getProcessLocale( rtl_Locale ** ppLocale )
+{
+ OUString loc16(macosx_getLocale());
+ OString locale;
+ if (!loc16.convertToString(
+ &locale, RTL_TEXTENCODING_UTF8,
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ SAL_INFO("sal.osl", "Cannot convert \"" << loc16 << "\" to UTF-8");
+ }
+
+ /* handle the case where OS specific method of finding locale fails */
+ if ( locale.isEmpty() )
+ {
+ /* simulate behavior of setlocale */
+ locale = getenv( "LC_ALL" );
+
+ if( locale.isEmpty() )
+ locale = getenv( "LC_CTYPE" );
+
+ if( locale.isEmpty() )
+ locale = getenv( "LANG" );
+
+ if( locale.isEmpty() )
+ locale = "C";
+ }
+
+ /* return the locale */
+ *ppLocale = parse_locale( locale.getStr() );
+}
+#else
+/*****************************************************************************
+ return the current process locale
+ *****************************************************************************/
+
+void imp_getProcessLocale( rtl_Locale ** ppLocale )
+{
+#ifdef ANDROID
+ /* No locale environment variables on Android, so why even bother
+ * with getenv().
+ */
+ char const * locale = "en-US.UTF-8";
+#else
+ /* simulate behavior off setlocale */
+ char * locale = getenv( "LC_ALL" );
+
+ if( NULL == locale )
+ locale = getenv( "LC_CTYPE" );
+
+ if( NULL == locale )
+ locale = getenv( "LANG" );
+
+ if( NULL == locale )
+ locale = "C";
+
+#endif
+ *ppLocale = parse_locale( locale );
+}
+#endif
+
+#endif /* ifdef LINUX || __sun || MACOSX || NETBSD || AIX */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/nlsupport.hxx b/sal/osl/unx/nlsupport.hxx
new file mode 100644
index 000000000..dce5d65c3
--- /dev/null
+++ b/sal/osl/unx/nlsupport.hxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_NLSUPPORT_HXX
+#define INCLUDED_SAL_OSL_UNX_NLSUPPORT_HXX
+
+#include <sal/config.h>
+
+#include <rtl/locale.h>
+
+namespace rtl { class OUString; }
+
+void imp_getProcessLocale( rtl_Locale ** );
+
+#if defined IOS || defined MACOSX
+OUString macosx_getLocale();
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/osxlocale.cxx b/sal/osl/unx/osxlocale.cxx
new file mode 100644
index 000000000..f82ea1436
--- /dev/null
+++ b/sal/osl/unx/osxlocale.cxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/types.h>
+#include <assert.h>
+
+#include <premac.h>
+#ifndef IOS
+#include <CoreServices/CoreServices.h>
+#endif
+#include <CoreFoundation/CoreFoundation.h>
+#include <postmac.h>
+
+#include <rtl/ustrbuf.hxx>
+
+#include "nlsupport.hxx"
+
+namespace
+{
+ template <typename T>
+ class CFGuard
+ {
+ public:
+ explicit CFGuard(T& rT) : rT_(rT) {}
+ ~CFGuard() { if (rT_) CFRelease(rT_); }
+ private:
+ T& rT_;
+ };
+
+ typedef CFGuard<CFArrayRef> CFArrayGuard;
+ typedef CFGuard<CFStringRef> CFStringGuard;
+ typedef CFGuard<CFPropertyListRef> CFPropertyListGuard;
+
+ /** Get the current process locale from system
+ */
+ CFStringRef getProcessLocale()
+ {
+ CFPropertyListRef pref = CFPreferencesCopyAppValue(CFSTR("AppleLanguages"), kCFPreferencesCurrentApplication);
+ CFPropertyListGuard proplGuard(pref);
+
+ if (pref == nullptr) // return fallback value 'en_US'
+ return CFStringCreateWithCString(kCFAllocatorDefault, "en_US", kCFStringEncodingASCII);
+
+ CFStringRef sref = (CFGetTypeID(pref) == CFArrayGetTypeID()) ? static_cast<CFStringRef>(CFArrayGetValueAtIndex(static_cast<CFArrayRef>(pref), 0)) : static_cast<CFStringRef>(pref);
+
+ return CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault, sref);
+ }
+
+ void append(OUStringBuffer & buffer, CFStringRef string) {
+ CFIndex n = CFStringGetLength(string);
+ CFStringGetCharacters(
+ string, CFRangeMake(0, n),
+ reinterpret_cast<UniChar *>(buffer.appendUninitialized(n)));
+ }
+}
+
+/** Grab current locale from system.
+*/
+OUString macosx_getLocale()
+{
+ CFStringRef sref = getProcessLocale();
+ CFStringGuard sGuard(sref);
+
+ assert(sref != nullptr && "osxlocale.cxx: getProcessLocale must return a non-NULL value");
+
+ // split the string into substrings; the first two (if there are two) substrings
+ // are language and country
+ CFArrayRef subs = CFStringCreateArrayBySeparatingStrings(nullptr, sref, CFSTR("-"));
+ CFArrayGuard arrGuard(subs);
+
+ OUStringBuffer buf;
+ append(buf, static_cast<CFStringRef>(CFArrayGetValueAtIndex(subs, 0)));
+
+ // country also available? Assumption: if the array contains more than one
+ // value the second value is always the country!
+ if (CFArrayGetCount(subs) > 1)
+ {
+ buf.append("_");
+ append(buf, static_cast<CFStringRef>(CFArrayGetValueAtIndex(subs, 1)));
+ }
+ // Append 'UTF-8' to the locale because the macOS file
+ // system interface is UTF-8 based and sal tries to determine
+ // the file system locale from the locale information
+ buf.append(".UTF-8");
+ return buf.makeStringAndClear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/pipe.cxx b/sal/osl/unx/pipe.cxx
new file mode 100644
index 000000000..ec9fe4b9d
--- /dev/null
+++ b/sal/osl/unx/pipe.cxx
@@ -0,0 +1,523 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.hxx"
+
+#include <o3tl/safeint.hxx>
+#include <osl/pipe.h>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/interlck.h>
+#include <rtl/string.h>
+#include <rtl/ustring.h>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+
+#include "sockimpl.hxx"
+#include "secimpl.hxx"
+#include "unixerrnostring.hxx"
+
+#include <cassert>
+#include <cstring>
+
+#define PIPEDEFAULTPATH "/tmp"
+#define PIPEALTERNATEPATH "/var/tmp"
+
+static oslPipe osl_psz_createPipe(const char *pszPipeName, oslPipeOptions Options, oslSecurity Security);
+
+static struct
+{
+ int errcode;
+ oslPipeError error;
+} const PipeError[]= {
+ { 0, osl_Pipe_E_None }, /* no error */
+ { EPROTOTYPE, osl_Pipe_E_NoProtocol }, /* Protocol wrong type for socket */
+ { ENOPROTOOPT, osl_Pipe_E_NoProtocol }, /* Protocol not available */
+ { EPROTONOSUPPORT, osl_Pipe_E_NoProtocol }, /* Protocol not supported */
+#ifdef ESOCKTNOSUPPORT
+ { ESOCKTNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Socket type not supported */
+#endif
+ { EPFNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Protocol family not supported */
+ { EAFNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Address family not supported by */
+ /* protocol family */
+ { ENETRESET, osl_Pipe_E_NetworkReset }, /* Network dropped connection because */
+ /* of reset */
+ { ECONNABORTED, osl_Pipe_E_ConnectionAbort }, /* Software caused connection abort */
+ { ECONNRESET, osl_Pipe_E_ConnectionReset }, /* Connection reset by peer */
+ { ENOBUFS, osl_Pipe_E_NoBufferSpace }, /* No buffer space available */
+ { ETIMEDOUT, osl_Pipe_E_TimedOut }, /* Connection timed out */
+ { ECONNREFUSED, osl_Pipe_E_ConnectionRefused }, /* Connection refused */
+ { -1, osl_Pipe_E_invalidError }
+};
+
+static oslPipeError osl_PipeErrorFromNative(int nativeType)
+{
+ int i = 0;
+
+ while ((PipeError[i].error != osl_Pipe_E_invalidError) &&
+ (PipeError[i].errcode != nativeType))
+ {
+ i++;
+ }
+
+ return PipeError[i].error;
+}
+
+static oslPipe createPipeImpl()
+{
+ oslPipe pPipeImpl;
+
+ pPipeImpl = static_cast< oslPipe >(calloc(1, sizeof(struct oslPipeImpl)));
+ if (!pPipeImpl)
+ return nullptr;
+
+ pPipeImpl->m_nRefCount = 1;
+ pPipeImpl->m_bClosed = false;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pPipeImpl->m_bIsInShutdown = false;
+ pPipeImpl->m_bIsAccepting = false;
+#endif
+
+ return pPipeImpl;
+}
+
+static void destroyPipeImpl(oslPipe pImpl)
+{
+ if (pImpl)
+ free(pImpl);
+}
+
+oslPipe SAL_CALL osl_createPipe(rtl_uString *ustrPipeName, oslPipeOptions Options, oslSecurity Security)
+{
+ oslPipe pPipe = nullptr;
+ rtl_String* strPipeName = nullptr;
+
+ if (ustrPipeName)
+ {
+ rtl_uString2String(&strPipeName,
+ rtl_uString_getStr(ustrPipeName),
+ rtl_uString_getLength(ustrPipeName),
+ osl_getThreadTextEncoding(),
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+ char* pszPipeName = rtl_string_getStr(strPipeName);
+ pPipe = osl_psz_createPipe(pszPipeName, Options, Security);
+
+ if (strPipeName)
+ rtl_string_release(strPipeName);
+ }
+
+ return pPipe;
+
+}
+
+static OString
+getBootstrapSocketPath()
+{
+ OUString pValue;
+
+ if (rtl::Bootstrap::get("OSL_SOCKET_PATH", pValue))
+ {
+ return OUStringToOString(pValue, RTL_TEXTENCODING_UTF8);
+ }
+ return "";
+}
+
+static oslPipe osl_psz_createPipe(const char *pszPipeName, oslPipeOptions Options,
+ oslSecurity Security)
+{
+ int Flags;
+ size_t len;
+ struct sockaddr_un addr;
+
+ OString name;
+ oslPipe pPipe;
+
+ if (access(PIPEDEFAULTPATH, W_OK) == 0)
+ name = PIPEDEFAULTPATH;
+ else if (access(PIPEALTERNATEPATH, W_OK) == 0)
+ name = PIPEALTERNATEPATH;
+ else {
+ name = getBootstrapSocketPath ();
+ }
+
+ name += "/";
+
+ if (Security)
+ {
+ char Ident[256];
+
+ Ident[0] = '\0';
+
+ OSL_VERIFY(osl_psz_getUserIdent(Security, Ident, sizeof(Ident)));
+
+ name += OStringLiteral("OSL_PIPE_") + Ident + "_" + pszPipeName;
+ }
+ else
+ {
+ name += OStringLiteral("OSL_PIPE_") + pszPipeName;
+ }
+
+ if (o3tl::make_unsigned(name.getLength()) >= sizeof addr.sun_path)
+ {
+ SAL_WARN("sal.osl.pipe", "osl_createPipe: pipe name too long");
+ return nullptr;
+ }
+
+ /* alloc memory */
+ pPipe = createPipeImpl();
+
+ if (!pPipe)
+ return nullptr;
+
+ /* create socket */
+ pPipe->m_Socket = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (pPipe->m_Socket < 0)
+ {
+ SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno));
+ destroyPipeImpl(pPipe);
+ return nullptr;
+ }
+
+ /* set close-on-exec flag */
+ if ((Flags = fcntl(pPipe->m_Socket, F_GETFD, 0)) != -1)
+ {
+ Flags |= FD_CLOEXEC;
+ if (fcntl(pPipe->m_Socket, F_SETFD, Flags) == -1)
+ {
+ SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno));
+ }
+ }
+
+ memset(&addr, 0, sizeof(addr));
+
+ SAL_INFO("sal.osl.pipe", "new pipe on fd " << pPipe->m_Socket << " '" << name << "'");
+
+ addr.sun_family = AF_UNIX;
+ // coverity[fixed_size_dest : FALSE] - safe, see check above
+ strcpy(addr.sun_path, name.getStr());
+#if defined(FREEBSD)
+ len = SUN_LEN(&addr);
+#else
+ len = sizeof(addr);
+#endif
+
+ if (Options & osl_Pipe_CREATE)
+ {
+ struct stat status;
+
+ /* check if there exists an orphan filesystem entry */
+ if ((stat(name.getStr(), &status) == 0) &&
+ (S_ISSOCK(status.st_mode) || S_ISFIFO(status.st_mode)))
+ {
+ if (connect(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) >= 0)
+ {
+ close (pPipe->m_Socket);
+ destroyPipeImpl(pPipe);
+ return nullptr;
+ }
+
+ unlink(name.getStr());
+ }
+
+ /* ok, fs clean */
+ if (bind(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) < 0)
+ {
+ SAL_WARN("sal.osl.pipe", "bind() failed: " << UnixErrnoString(errno));
+ close(pPipe->m_Socket);
+ destroyPipeImpl(pPipe);
+ return nullptr;
+ }
+
+ /* Only give access to all if no security handle was specified, otherwise security
+ depends on umask */
+
+ if (!Security)
+ (void)chmod(name.getStr(),S_IRWXU | S_IRWXG |S_IRWXO);
+
+ strcpy(pPipe->m_Name, name.getStr()); // safe, see check above
+
+ if (listen(pPipe->m_Socket, 5) < 0)
+ {
+ SAL_WARN("sal.osl.pipe", "listen() failed: " << UnixErrnoString(errno));
+ // cid#1255391 warns about unlink(name) after stat(name, &status)
+ // above, but the intervening call to bind makes those two clearly
+ // unrelated, as it would fail if name existed at that point in
+ // time:
+ // coverity[toctou] - this is bogus
+ unlink(name.getStr()); /* remove filesystem entry */
+ close(pPipe->m_Socket);
+ destroyPipeImpl(pPipe);
+ return nullptr;
+ }
+
+ return pPipe;
+ }
+
+ /* osl_pipe_OPEN */
+ if (access(name.getStr(), F_OK) != -1)
+ {
+ if (connect(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) >= 0)
+ return pPipe;
+
+ SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
+ }
+
+ close (pPipe->m_Socket);
+ destroyPipeImpl(pPipe);
+ return nullptr;
+}
+
+void SAL_CALL osl_acquirePipe(oslPipe pPipe)
+{
+ osl_atomic_increment(&(pPipe->m_nRefCount));
+}
+
+void SAL_CALL osl_releasePipe(oslPipe pPipe)
+{
+ if (!pPipe)
+ return;
+
+ if (osl_atomic_decrement(&(pPipe->m_nRefCount)) == 0)
+ {
+ if (!pPipe->m_bClosed)
+ osl_closePipe(pPipe);
+
+ destroyPipeImpl(pPipe);
+ }
+}
+
+void SAL_CALL osl_closePipe(oslPipe pPipe)
+{
+ int nRet;
+ int ConnFD;
+
+ if (!pPipe)
+ return;
+
+ if (pPipe->m_bClosed)
+ return;
+
+ ConnFD = pPipe->m_Socket;
+
+ /* Thread does not return from accept on linux, so
+ connect to the accepting pipe
+ */
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ struct sockaddr_un addr;
+
+ if (pPipe->m_bIsAccepting)
+ {
+ pPipe->m_bIsInShutdown = true;
+ pPipe->m_Socket = -1;
+
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ {
+ SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno));
+ return;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+
+ SAL_INFO("sal.osl.pipe", "osl_destroyPipe : Pipe Name '" << pPipe->m_Name << "'");
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, pPipe->m_Name); // safe, as both are same size
+
+ nRet = connect(fd, reinterpret_cast< sockaddr* >(&addr), sizeof(addr));
+ if (nRet < 0)
+ SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
+
+ close(fd);
+ }
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+
+ nRet = shutdown(ConnFD, 2);
+ if (nRet < 0)
+ SAL_WARN("sal.osl.pipe", "shutdown() failed: " << UnixErrnoString(errno));
+
+ nRet = close(ConnFD);
+ if (nRet < 0)
+ SAL_WARN("sal.osl.pipe", "close() failed: " << UnixErrnoString(errno));
+
+ /* remove filesystem entry */
+ if (pPipe->m_Name[0] != '\0')
+ unlink(pPipe->m_Name);
+
+ pPipe->m_bClosed = true;
+}
+
+oslPipe SAL_CALL osl_acceptPipe(oslPipe pPipe)
+{
+ int s;
+ oslPipe pAcceptedPipe;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "invalid pipe");
+ if (!pPipe)
+ return nullptr;
+
+ assert(pPipe->m_Name[0] != '\0'); // you cannot have an empty pipe name
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pPipe->m_bIsAccepting = true;
+#endif
+
+ s = accept(pPipe->m_Socket, nullptr, nullptr);
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pPipe->m_bIsAccepting = false;
+#endif
+
+ if (s < 0)
+ {
+ SAL_WARN("sal.osl.pipe", "accept() failed: " << UnixErrnoString(errno));
+ return nullptr;
+ }
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ if (pPipe->m_bIsInShutdown)
+ {
+ close(s);
+ return nullptr;
+ }
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+
+ /* alloc memory */
+ pAcceptedPipe = createPipeImpl();
+
+ assert(pAcceptedPipe); // should never be the case that an oslPipe cannot be initialized
+ if (!pAcceptedPipe)
+ {
+ close(s);
+ return nullptr;
+ }
+
+ /* set close-on-exec flag */
+ int flags;
+ if ((flags = fcntl(s, F_GETFD, 0)) >= 0)
+ {
+ flags |= FD_CLOEXEC;
+ if (fcntl(s, F_SETFD, flags) < 0)
+ SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno));
+ }
+
+ pAcceptedPipe->m_Socket = s;
+
+ return pAcceptedPipe;
+}
+
+sal_Int32 SAL_CALL osl_receivePipe(oslPipe pPipe,
+ void* pBuffer,
+ sal_Int32 BytesToRead)
+{
+ int nRet = 0;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_receivePipe: invalid pipe");
+ if (!pPipe)
+ {
+ SAL_WARN("sal.osl.pipe", "osl_receivePipe: Invalid socket");
+ errno=EINVAL;
+ return -1;
+ }
+
+ nRet = recv(pPipe->m_Socket, pBuffer, BytesToRead, 0);
+
+ if (nRet < 0)
+ SAL_WARN("sal.osl.pipe", "recv() failed: " << UnixErrnoString(errno));
+
+ return nRet;
+}
+
+sal_Int32 SAL_CALL osl_sendPipe(oslPipe pPipe,
+ const void* pBuffer,
+ sal_Int32 BytesToSend)
+{
+ int nRet=0;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_sendPipe: invalid pipe");
+ if (!pPipe)
+ {
+ SAL_WARN("sal.osl.pipe", "osl_sendPipe: Invalid socket");
+ errno=EINVAL;
+ return -1;
+ }
+
+ nRet = send(pPipe->m_Socket, pBuffer, BytesToSend, 0);
+
+ if (nRet <= 0)
+ SAL_WARN("sal.osl.pipe", "send() failed: " << UnixErrnoString(errno));
+
+ return nRet;
+}
+
+oslPipeError SAL_CALL osl_getLastPipeError(SAL_UNUSED_PARAMETER oslPipe)
+{
+ return osl_PipeErrorFromNative(errno);
+}
+
+sal_Int32 SAL_CALL osl_writePipe(oslPipe pPipe, const void *pBuffer, sal_Int32 n)
+{
+ /* loop until all desired bytes were send or an error occurred */
+ sal_Int32 BytesSend = 0;
+ sal_Int32 BytesToSend = n;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_writePipe: invalid pipe"); // osl_sendPipe detects invalid pipe
+ while (BytesToSend > 0)
+ {
+ sal_Int32 RetVal;
+
+ RetVal= osl_sendPipe(pPipe, pBuffer, BytesToSend);
+
+ /* error occurred? */
+ if (RetVal <= 0)
+ break;
+
+ BytesToSend -= RetVal;
+ BytesSend += RetVal;
+ pBuffer= static_cast< char const* >(pBuffer) + RetVal;
+ }
+
+ return BytesSend;
+}
+
+sal_Int32 SAL_CALL osl_readPipe( oslPipe pPipe, void *pBuffer , sal_Int32 n )
+{
+ /* loop until all desired bytes were read or an error occurred */
+ sal_Int32 BytesRead = 0;
+ sal_Int32 BytesToRead = n;
+
+ SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_readPipe: invalid pipe"); // osl_receivePipe detects invalid pipe
+ while (BytesToRead > 0)
+ {
+ sal_Int32 RetVal;
+ RetVal= osl_receivePipe(pPipe, pBuffer, BytesToRead);
+
+ /* error occurred? */
+ if (RetVal <= 0)
+ break;
+
+ BytesToRead -= RetVal;
+ BytesRead += RetVal;
+ pBuffer= static_cast< char* >(pBuffer) + RetVal;
+ }
+
+ return BytesRead;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/process.cxx b/sal/osl/unx/process.cxx
new file mode 100644
index 000000000..39db4936f
--- /dev/null
+++ b/sal/osl/unx/process.cxx
@@ -0,0 +1,1194 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+
+#include <cassert>
+
+/*
+ * ToDo:
+ * - cleanup of process status things
+ * - cleanup of process spawning
+ * - cleanup of resource transfer
+ */
+
+#if defined(__sun)
+ // The procfs may only be used without LFS in 32bits.
+# ifdef _FILE_OFFSET_BITS
+# undef _FILE_OFFSET_BITS
+# endif
+#endif
+
+#if defined(FREEBSD) || defined(NETBSD) || defined(DRAGONFLY)
+#include <machine/param.h>
+#endif
+
+#include "system.hxx"
+#include "unixerrnostring.hxx"
+#if defined(__sun)
+# include <sys/procfs.h>
+#endif
+#include <osl/diagnose.h>
+#include <osl/mutex.h>
+#include <osl/process.h>
+#include <osl/conditn.h>
+#include <osl/thread.h>
+#include <osl/file.h>
+#include <osl/file.hxx>
+#include <osl/signal.h>
+#include <rtl/alloc.h>
+#include <sal/log.hxx>
+
+#include <grp.h>
+
+#include "createfilehandlefromfd.hxx"
+#include "file_url.hxx"
+#include "readwrite_helper.hxx"
+#include "sockimpl.hxx"
+#include "secimpl.hxx"
+
+#define MAX_ARGS 255
+#define MAX_ENVS 255
+
+namespace
+{
+
+struct oslProcessImpl {
+ pid_t m_pid;
+ oslCondition m_terminated;
+ int m_status;
+ oslProcessImpl* m_pnext;
+};
+
+struct ProcessData
+{
+ const char* m_pszArgs[MAX_ARGS + 1];
+ const char* m_pszDir;
+ char* m_pszEnv[MAX_ENVS + 1];
+ uid_t m_uid;
+ gid_t m_gid;
+ char* m_name;
+ oslCondition m_started;
+ oslProcessImpl* m_pProcImpl;
+ oslFileHandle *m_pInputWrite;
+ oslFileHandle *m_pOutputRead;
+ oslFileHandle *m_pErrorRead;
+};
+
+static oslProcessImpl* ChildList;
+static oslMutex ChildListMutex;
+
+} //Anonymous namespace
+
+static oslProcessError osl_psz_executeProcess(char *pszImageName,
+ char *pszArguments[],
+ oslProcessOption Options,
+ oslSecurity Security,
+ char *pszDirectory,
+ char *pszEnvironments[],
+ oslProcess *pProcess,
+ oslFileHandle *pInputWrite,
+ oslFileHandle *pOutputRead,
+ oslFileHandle *pErrorRead );
+
+extern "C" {
+
+static void ChildStatusProc(void *pData)
+{
+ osl_setThreadName("osl_executeProcess");
+
+ pid_t pid = -1;
+ int status = 0;
+ int channel[2] = { -1, -1 };
+ ProcessData data;
+ ProcessData *pdata;
+ int stdOutput[2] = { -1, -1 }, stdInput[2] = { -1, -1 }, stdError[2] = { -1, -1 };
+
+ pdata = static_cast<ProcessData *>(pData);
+
+ /* make a copy of our data, because forking will only copy
+ our local stack of the thread, so the process data will not be accessible
+ in our child process */
+ memcpy(&data, pData, sizeof(data));
+
+#ifdef NO_CHILD_PROCESSES
+#define fork() (errno = EINVAL, -1)
+#endif
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == -1)
+ {
+ status = errno;
+ SAL_WARN("sal.osl", "executeProcess socketpair() errno " << status);
+ }
+
+ (void) fcntl(channel[0], F_SETFD, FD_CLOEXEC);
+ (void) fcntl(channel[1], F_SETFD, FD_CLOEXEC);
+
+ /* Create redirected IO pipes */
+ if ( status == 0 && data.m_pInputWrite && pipe( stdInput ) == -1 )
+ {
+ status = errno;
+ assert(status != 0);
+ SAL_WARN("sal.osl", "executeProcess pipe(stdInput) errno " << status);
+ }
+
+ if ( status == 0 && data.m_pOutputRead && pipe( stdOutput ) == -1 )
+ {
+ status = errno;
+ assert(status != 0);
+ SAL_WARN("sal.osl", "executeProcess pipe(stdOutput) errno " << status);
+ }
+
+ if ( status == 0 && data.m_pErrorRead && pipe( stdError ) == -1 )
+ {
+ status = errno;
+ assert(status != 0);
+ SAL_WARN("sal.osl", "executeProcess pipe(stdError) errno " << status);
+ }
+
+ if ( (status == 0) && ((pid = fork()) == 0) )
+ {
+ /* Child */
+ int chstatus = 0;
+ int errno_copy;
+
+ if (channel[0] != -1) close(channel[0]);
+
+ if ((data.m_uid != uid_t(-1)) && ((data.m_uid != getuid()) || (data.m_gid != getgid())))
+ {
+ OSL_ASSERT(geteuid() == 0); /* must be root */
+
+ if (! INIT_GROUPS(data.m_name, data.m_gid) || (setuid(data.m_uid) != 0))
+ {
+ // ignore; can't do much about it here after fork
+ }
+
+ unsetenv("HOME");
+ }
+
+ if (data.m_pszDir)
+ chstatus = chdir(data.m_pszDir);
+
+ if (chstatus == 0 && ((data.m_uid == uid_t(-1)) || ((data.m_uid == getuid()) && (data.m_gid == getgid()))))
+ {
+ int i;
+ for (i = 0; data.m_pszEnv[i] != nullptr; i++)
+ {
+ if (strchr(data.m_pszEnv[i], '=') == nullptr)
+ {
+ unsetenv(data.m_pszEnv[i]); /*TODO: check error return*/
+ }
+ else
+ {
+ putenv(data.m_pszEnv[i]); /*TODO: check error return*/
+ }
+ }
+
+ /* Connect std IO to pipe ends */
+
+ /* Write end of stdInput not used in child process */
+ if (stdInput[1] != -1) close( stdInput[1] );
+
+ /* Read end of stdOutput not used in child process */
+ if (stdOutput[0] != -1) close( stdOutput[0] );
+
+ /* Read end of stdError not used in child process */
+ if (stdError[0] != -1) close( stdError[0] );
+
+ /* Redirect pipe ends to std IO */
+
+ if ( stdInput[0] != STDIN_FILENO )
+ {
+ dup2( stdInput[0], STDIN_FILENO );
+ if (stdInput[0] != -1) close( stdInput[0] );
+ }
+
+ if ( stdOutput[1] != STDOUT_FILENO )
+ {
+ dup2( stdOutput[1], STDOUT_FILENO );
+ if (stdOutput[1] != -1) close( stdOutput[1] );
+ }
+
+ if ( stdError[1] != STDERR_FILENO )
+ {
+ dup2( stdError[1], STDERR_FILENO );
+ if (stdError[1] != -1) close( stdError[1] );
+ }
+
+ // No need to check the return value of execv. If we return from
+ // it, an error has occurred.
+ execv(data.m_pszArgs[0], const_cast<char **>(data.m_pszArgs));
+ }
+
+ /* if we reach here, something went wrong */
+ errno_copy = errno;
+ if ( !safeWrite(channel[1], &errno_copy, sizeof(errno_copy)) )
+ {
+ // ignore; can't do much about it here after fork
+ }
+
+ if ( channel[1] != -1 )
+ close(channel[1]);
+
+ _exit(255);
+ }
+ else
+ { /* Parent */
+ int i = -1;
+ if (channel[1] != -1) close(channel[1]);
+
+ /* Close unused pipe ends */
+ if (stdInput[0] != -1) close( stdInput[0] );
+ if (stdOutput[1] != -1) close( stdOutput[1] );
+ if (stdError[1] != -1) close( stdError[1] );
+
+ if (pid > 0)
+ {
+ while ((i = read(channel[0], &status, sizeof(status))) < 0)
+ {
+ if (errno != EINTR)
+ break;
+ }
+ }
+
+ if (channel[0] != -1) close(channel[0]);
+
+ if ((pid > 0) && (i == 0))
+ {
+ pid_t child_pid;
+ osl_acquireMutex(ChildListMutex);
+
+ pdata->m_pProcImpl->m_pid = pid;
+ pdata->m_pProcImpl->m_pnext = ChildList;
+ ChildList = pdata->m_pProcImpl;
+
+ /* Store used pipe ends in data structure */
+
+ if ( pdata->m_pInputWrite )
+ *(pdata->m_pInputWrite) = osl::detail::createFileHandleFromFD( stdInput[1] );
+
+ if ( pdata->m_pOutputRead )
+ *(pdata->m_pOutputRead) = osl::detail::createFileHandleFromFD( stdOutput[0] );
+
+ if ( pdata->m_pErrorRead )
+ *(pdata->m_pErrorRead) = osl::detail::createFileHandleFromFD( stdError[0] );
+
+ osl_releaseMutex(ChildListMutex);
+
+ osl_setCondition(pdata->m_started);
+
+ do
+ {
+ child_pid = waitpid(pid, &status, 0);
+ } while ( 0 > child_pid && EINTR == errno );
+
+ if ( child_pid < 0)
+ {
+ SAL_WARN("sal.osl", "Failed to wait for child process: " << UnixErrnoString(errno));
+
+ /*
+ We got another error than EINTR. Anyway we have to wake up the
+ waiting thread under any circumstances */
+
+ child_pid = pid;
+ }
+
+ if ( child_pid > 0 )
+ {
+ oslProcessImpl* pChild;
+
+ osl_acquireMutex(ChildListMutex);
+
+ pChild = ChildList;
+
+ /* check if it is one of our child processes */
+ while (pChild != nullptr)
+ {
+ if (pChild->m_pid == child_pid)
+ {
+ if (WIFEXITED(status))
+ pChild->m_status = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status))
+ pChild->m_status = 128 + WTERMSIG(status);
+ else
+ pChild->m_status = -1;
+
+ osl_setCondition(pChild->m_terminated);
+ }
+
+ pChild = pChild->m_pnext;
+ }
+
+ osl_releaseMutex(ChildListMutex);
+ }
+ }
+ else
+ {
+ SAL_WARN("sal.osl", "ChildStatusProc : starting '" << data.m_pszArgs[0] << "' failed");
+ SAL_WARN("sal.osl", "Failed to launch child process, child reports " << UnixErrnoString(status));
+
+ /* Close pipe ends */
+ if ( pdata->m_pInputWrite )
+ *pdata->m_pInputWrite = nullptr;
+
+ if ( pdata->m_pOutputRead )
+ *pdata->m_pOutputRead = nullptr;
+
+ if ( pdata->m_pErrorRead )
+ *pdata->m_pErrorRead = nullptr;
+
+ if (stdInput[1] != -1) close( stdInput[1] );
+ if (stdOutput[0] != -1) close( stdOutput[0] );
+ if (stdError[0] != -1) close( stdError[0] );
+
+ /* if pid > 0 then a process was created, even if it later failed
+ e.g. bash searching for a command to execute, and we still
+ need to clean it up to avoid "defunct" processes */
+ if (pid > 0)
+ {
+ pid_t child_pid;
+ do
+ {
+ child_pid = waitpid(pid, &status, 0);
+ } while ( 0 > child_pid && EINTR == errno );
+ }
+
+ /* notify (and unblock) parent thread */
+ osl_setCondition(pdata->m_started);
+ }
+ }
+}
+
+}
+
+oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO(
+ rtl_uString *ustrImageName,
+ rtl_uString *ustrArguments[],
+ sal_uInt32 nArguments,
+ oslProcessOption Options,
+ oslSecurity Security,
+ rtl_uString *ustrWorkDir,
+ rtl_uString *ustrEnvironment[],
+ sal_uInt32 nEnvironmentVars,
+ oslProcess *pProcess,
+ oslFileHandle *pInputWrite,
+ oslFileHandle *pOutputRead,
+ oslFileHandle *pErrorRead
+ )
+{
+ OUString image;
+ if (ustrImageName == nullptr)
+ {
+ if (nArguments == 0)
+ {
+ return osl_Process_E_InvalidError;
+ }
+ image = OUString::unacquired(ustrArguments);
+ }
+ else
+ {
+ osl::FileBase::RC e = osl::FileBase::getSystemPathFromFileURL(
+ OUString::unacquired(&ustrImageName), image);
+ if (e != osl::FileBase::E_None)
+ {
+ SAL_INFO(
+ "sal.osl",
+ "getSystemPathFromFileURL("
+ << OUString::unacquired(&ustrImageName)
+ << ") failed with " << e);
+ return osl_Process_E_Unknown;
+ }
+ }
+
+ if ((Options & osl_Process_SEARCHPATH) != 0)
+ {
+ OUString path;
+ if (osl::detail::find_in_PATH(image, path))
+ {
+ image = path;
+ }
+ }
+
+ oslProcessError Error;
+ char* pszWorkDir=nullptr;
+ char** pArguments=nullptr;
+ char** pEnvironment=nullptr;
+ unsigned int idx;
+
+ char szImagePath[PATH_MAX] = "";
+ if (!image.isEmpty()
+ && (UnicodeToText(
+ szImagePath, SAL_N_ELEMENTS(szImagePath), image.getStr(),
+ image.getLength())
+ == 0))
+ {
+ int e = errno;
+ SAL_INFO("sal.osl", "UnicodeToText(" << image << ") failed with " << e);
+ return osl_Process_E_Unknown;
+ }
+
+ char szWorkDir[PATH_MAX] = "";
+ if ( ustrWorkDir != nullptr && ustrWorkDir->length )
+ {
+ oslFileError e = FileURLToPath( szWorkDir, PATH_MAX, ustrWorkDir );
+ if (e != osl_File_E_None)
+ {
+ SAL_INFO(
+ "sal.osl",
+ "FileURLToPath(" << OUString::unacquired(&ustrWorkDir)
+ << ") failed with " << e);
+ return osl_Process_E_Unknown;
+ }
+ pszWorkDir = szWorkDir;
+ }
+
+ if ( pArguments == nullptr && nArguments > 0 )
+ {
+ pArguments = static_cast<char**>(malloc( ( nArguments + 2 ) * sizeof(char*) ));
+ }
+
+ for ( idx = 0 ; idx < nArguments ; ++idx )
+ {
+ rtl_String* strArg =nullptr;
+
+ rtl_uString2String( &strArg,
+ rtl_uString_getStr(ustrArguments[idx]),
+ rtl_uString_getLength(ustrArguments[idx]),
+ osl_getThreadTextEncoding(),
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+
+ pArguments[idx]=strdup(rtl_string_getStr(strArg));
+ rtl_string_release(strArg);
+ pArguments[idx+1]=nullptr;
+ }
+
+ for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
+ {
+ rtl_String* strEnv=nullptr;
+
+ if ( pEnvironment == nullptr )
+ {
+ pEnvironment = static_cast<char**>(malloc( ( nEnvironmentVars + 2 ) * sizeof(char*) ));
+ }
+
+ rtl_uString2String( &strEnv,
+ rtl_uString_getStr(ustrEnvironment[idx]),
+ rtl_uString_getLength(ustrEnvironment[idx]),
+ osl_getThreadTextEncoding(),
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+
+ pEnvironment[idx]=strdup(rtl_string_getStr(strEnv));
+ rtl_string_release(strEnv);
+ pEnvironment[idx+1]=nullptr;
+ }
+
+ Error = osl_psz_executeProcess(szImagePath,
+ pArguments,
+ Options,
+ Security,
+ pszWorkDir,
+ pEnvironment,
+ pProcess,
+ pInputWrite,
+ pOutputRead,
+ pErrorRead
+ );
+
+ if ( pArguments != nullptr )
+ {
+ for ( idx = 0 ; idx < nArguments ; ++idx )
+ {
+ if ( pArguments[idx] != nullptr )
+ {
+ free(pArguments[idx]);
+ }
+ }
+ free(pArguments);
+ }
+
+ if ( pEnvironment != nullptr )
+ {
+ for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
+ {
+ if ( pEnvironment[idx] != nullptr )
+ {
+ free(pEnvironment[idx]);
+ }
+ }
+ free(pEnvironment);
+ }
+
+ return Error;
+}
+
+oslProcessError SAL_CALL osl_executeProcess(
+ rtl_uString *ustrImageName,
+ rtl_uString *ustrArguments[],
+ sal_uInt32 nArguments,
+ oslProcessOption Options,
+ oslSecurity Security,
+ rtl_uString *ustrWorkDir,
+ rtl_uString *ustrEnvironment[],
+ sal_uInt32 nEnvironmentVars,
+ oslProcess *pProcess
+ )
+{
+ return osl_executeProcess_WithRedirectedIO(
+ ustrImageName,
+ ustrArguments,
+ nArguments,
+ Options,
+ Security,
+ ustrWorkDir,
+ ustrEnvironment,
+ nEnvironmentVars,
+ pProcess,
+ nullptr,
+ nullptr,
+ nullptr
+ );
+}
+
+oslProcessError osl_psz_executeProcess(char *pszImageName,
+ char *pszArguments[],
+ oslProcessOption Options,
+ oslSecurity Security,
+ char *pszDirectory,
+ char *pszEnvironments[],
+ oslProcess *pProcess,
+ oslFileHandle *pInputWrite,
+ oslFileHandle *pOutputRead,
+ oslFileHandle *pErrorRead
+ )
+{
+ int i;
+ ProcessData Data;
+ oslThread hThread;
+
+ memset(&Data,0,sizeof(ProcessData));
+ Data.m_pInputWrite = pInputWrite;
+ Data.m_pOutputRead = pOutputRead;
+ Data.m_pErrorRead = pErrorRead;
+
+ OSL_ASSERT(pszImageName != nullptr);
+
+ if ( pszImageName == nullptr )
+ {
+ return osl_Process_E_NotFound;
+ }
+
+ Data.m_pszArgs[0] = strdup(pszImageName);
+ Data.m_pszArgs[1] = nullptr;
+
+ if ( pszArguments != nullptr )
+ {
+ for (i = 0; ((i + 2) < MAX_ARGS) && (pszArguments[i] != nullptr); i++)
+ Data.m_pszArgs[i+1] = strdup(pszArguments[i]);
+ Data.m_pszArgs[i+2] = nullptr;
+ }
+
+ Data.m_pszDir = (pszDirectory != nullptr) ? strdup(pszDirectory) : nullptr;
+
+ if (pszEnvironments != nullptr)
+ {
+ for (i = 0; ((i + 1) < MAX_ENVS) && (pszEnvironments[i] != nullptr); i++)
+ Data.m_pszEnv[i] = strdup(pszEnvironments[i]);
+ Data.m_pszEnv[i+1] = nullptr;
+ }
+ else
+ Data.m_pszEnv[0] = nullptr;
+
+ if (Security != nullptr)
+ {
+ Data.m_uid = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_uid;
+ Data.m_gid = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_gid;
+ Data.m_name = static_cast<oslSecurityImpl*>(Security)->m_pPasswd.pw_name;
+ }
+ else
+ Data.m_uid = uid_t(-1);
+
+ Data.m_pProcImpl = static_cast<oslProcessImpl*>(malloc(sizeof(oslProcessImpl)));
+ Data.m_pProcImpl->m_pid = 0;
+ Data.m_pProcImpl->m_terminated = osl_createCondition();
+ Data.m_pProcImpl->m_pnext = nullptr;
+
+ if (ChildListMutex == nullptr)
+ ChildListMutex = osl_createMutex();
+
+ Data.m_started = osl_createCondition();
+
+ hThread = osl_createThread(ChildStatusProc, &Data);
+
+ if (hThread != nullptr)
+ {
+ osl_waitCondition(Data.m_started, nullptr);
+ }
+ osl_destroyCondition(Data.m_started);
+
+ for (i = 0; Data.m_pszArgs[i] != nullptr; i++)
+ free(const_cast<char *>(Data.m_pszArgs[i]));
+
+ for (i = 0; Data.m_pszEnv[i] != nullptr; i++)
+ free(Data.m_pszEnv[i]);
+
+ if ( Data.m_pszDir != nullptr )
+ {
+ free(const_cast<char *>(Data.m_pszDir));
+ }
+
+ osl_destroyThread(hThread);
+
+ if (Data.m_pProcImpl->m_pid != 0)
+ {
+ assert(hThread != nullptr);
+
+ *pProcess = Data.m_pProcImpl;
+
+ if (Options & osl_Process_WAIT)
+ osl_joinProcess(*pProcess);
+
+ return osl_Process_E_None;
+ }
+
+ osl_destroyCondition(Data.m_pProcImpl->m_terminated);
+ free(Data.m_pProcImpl);
+
+ return osl_Process_E_Unknown;
+}
+
+oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process)
+{
+ if (Process == nullptr)
+ return osl_Process_E_Unknown;
+
+ if (kill(static_cast<oslProcessImpl*>(Process)->m_pid, SIGKILL) != 0)
+ {
+ switch (errno)
+ {
+ case EPERM:
+ return osl_Process_E_NoPermission;
+
+ case ESRCH:
+ return osl_Process_E_NotFound;
+
+ default:
+ return osl_Process_E_Unknown;
+ }
+ }
+
+ return osl_Process_E_None;
+}
+
+oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident)
+{
+ oslProcessImpl *pProcImpl;
+
+ if (kill(Ident, 0) != -1)
+ {
+ oslProcessImpl* pChild;
+
+ if (ChildListMutex == nullptr)
+ ChildListMutex = osl_createMutex();
+
+ osl_acquireMutex(ChildListMutex);
+
+ pChild = ChildList;
+
+ /* check if it is one of our child processes */
+ while (pChild != nullptr)
+ {
+ if (Ident == static_cast<sal_uInt32>(pChild->m_pid))
+ break;
+
+ pChild = pChild->m_pnext;
+ }
+
+ pProcImpl = static_cast<oslProcessImpl*>(malloc(sizeof(oslProcessImpl)));
+ pProcImpl->m_pid = Ident;
+ pProcImpl->m_terminated = osl_createCondition();
+
+ if (pChild != nullptr)
+ {
+ /* process is a child so insert into list */
+ pProcImpl->m_pnext = pChild->m_pnext;
+ pChild->m_pnext = pProcImpl;
+
+ pProcImpl->m_status = pChild->m_status;
+
+ if (osl_checkCondition(pChild->m_terminated))
+ osl_setCondition(pProcImpl->m_terminated);
+ }
+ else
+ pProcImpl->m_pnext = nullptr;
+
+ osl_releaseMutex(ChildListMutex);
+ }
+ else
+ pProcImpl = nullptr;
+
+ return pProcImpl;
+}
+
+void SAL_CALL osl_freeProcessHandle(oslProcess Process)
+{
+ if (Process == nullptr)
+ return;
+
+ oslProcessImpl *pChild, *pPrev = nullptr;
+
+ OSL_ASSERT(ChildListMutex != nullptr);
+
+ if ( ChildListMutex == nullptr )
+ {
+ return;
+ }
+
+ osl_acquireMutex(ChildListMutex);
+
+ pChild = ChildList;
+
+ /* remove process from child list */
+ while (pChild != nullptr)
+ {
+ if (pChild == static_cast<oslProcessImpl*>(Process))
+ {
+ if (pPrev != nullptr)
+ pPrev->m_pnext = pChild->m_pnext;
+ else
+ ChildList = pChild->m_pnext;
+
+ break;
+ }
+
+ pPrev = pChild;
+ pChild = pChild->m_pnext;
+ }
+
+ osl_releaseMutex(ChildListMutex);
+
+ osl_destroyCondition(static_cast<oslProcessImpl*>(Process)->m_terminated);
+
+ free(Process);
+}
+
+#if defined(LINUX)
+namespace {
+
+struct osl_procStat
+{
+ /* from 'stat' */
+ pid_t pid; /* pid */
+ char command[16]; /* 'argv[0]' */ /* mfe: it all right char comm[16] in kernel! */
+ char state; /* state (running, stopped, ...) */
+ pid_t ppid; /* parent pid */
+ pid_t pgrp; /* parent group */
+ int session; /* session ID */
+ int tty; /* no of tty */
+ pid_t tpgid; /* group of process owning the tty */
+ unsigned long flags; /* flags dunno */
+ unsigned long minflt; /* minor page faults */
+ unsigned long cminflt; /* minor page faults with children */
+ unsigned long majflt; /* major page faults */
+ unsigned long cmajflt; /* major page faults with children */
+ unsigned long utime; /* no of jiffies in user mode */
+ unsigned long stime; /* no of jiffies in kernel mode */
+ unsigned long cutime; /* no of jiffies in user mode with children */
+ unsigned long cstime; /* no of jiffies in kernel mode with children */
+ unsigned long priority; /* nice value + 15 (kernel scheduling prio)*/
+ long nice; /* nice value */
+ long timeout; /* no of jiffies of next process timeout */
+ long itrealvalue; /* no jiffies before next SIGALRM */
+ unsigned long starttime; /* process started this no of jiffies after boot */
+ unsigned long vsize; /* virtual memory size (in bytes) */
+ long rss; /* resident set size (in pages) */
+ unsigned long rss_rlim; /* rss limit (in bytes) */
+ unsigned long startcode; /* address above program text can run */
+ unsigned long endcode; /* address below program text can run */
+ unsigned long startstack; /* address of start of stack */
+ unsigned long kstkesp; /* current value of 'esp' (stack pointer) */
+ unsigned long kstkeip; /* current value of 'eip' (instruction pointer) */
+ /* mfe: Linux > 2.1.7x have more signals (88) */
+ char signal[24]; /* pending signals */
+ char blocked[24]; /* blocked signals */
+ char sigignore[24]; /* ignored signals */
+ char sigcatch[24]; /* caught signals */
+ unsigned long wchan; /* 'channel' the process is waiting in */
+ unsigned long nswap; /* ? */
+ unsigned long cnswap; /* ? */
+
+ /* from 'status' */
+ int ruid; /* real uid */
+ int euid; /* effective uid */
+ int suid; /* saved uid */
+ int fuid; /* file access uid */
+ int rgid; /* real gid */
+ int egid; /* effective gid */
+ int sgid; /* saved gid */
+ int fgid; /* file access gid */
+ unsigned long vm_size; /* like vsize but on kb */
+ unsigned long vm_lock; /* locked pages in kb */
+ unsigned long vm_rss; /* like rss but in kb */
+ unsigned long vm_data; /* data size */
+ unsigned long vm_stack; /* stack size */
+ unsigned long vm_exe; /* executable size */
+ unsigned long vm_lib; /* library size */
+};
+
+}
+
+static bool osl_getProcStat(pid_t pid, struct osl_procStat* procstat)
+{
+ int fd = 0;
+ bool bRet = false;
+ char name[PATH_MAX + 1];
+ snprintf(name, sizeof(name), "/proc/%u/stat", pid);
+
+ if ((fd = open(name,O_RDONLY)) >=0 )
+ {
+ char* tmp=nullptr;
+ char prstatbuf[512];
+ memset(prstatbuf,0,512);
+ bRet = safeRead(fd, prstatbuf, 511);
+
+ close(fd);
+
+ if (!bRet)
+ return false;
+
+ tmp = strrchr(prstatbuf, ')');
+ if(tmp)
+ {
+ *tmp = '\0';
+
+ memset(procstat->command, 0, sizeof(procstat->command));
+
+ sscanf(prstatbuf, "%d (%15c", &procstat->pid, procstat->command);
+ sscanf(tmp + 2,
+ "%c"
+ "%i %i %i %i %i"
+ "%lu %lu %lu %lu %lu"
+ "%lu %lu %lu %lu"
+ "%lu %li %li %li"
+ "%lu %lu %li %lu"
+ "%lu %lu %lu %lu %lu"
+ "%23s %23s %23s %23s"
+ "%lu %lu %lu",
+ &procstat->state,
+ &procstat->ppid, &procstat->pgrp, &procstat->session, &procstat->tty, &procstat->tpgid,
+ &procstat->flags, &procstat->minflt, &procstat->cminflt, &procstat->majflt, &procstat->cmajflt,
+ &procstat->utime, &procstat->stime, &procstat->cutime, &procstat->cstime,
+ &procstat->priority, &procstat->nice, &procstat->timeout, &procstat->itrealvalue,
+ &procstat->starttime, &procstat->vsize, &procstat->rss, &procstat->rss_rlim,
+ &procstat->startcode, &procstat->endcode, &procstat->startstack, &procstat->kstkesp, &procstat->kstkeip,
+ procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch,
+ &procstat->wchan, &procstat->nswap, &procstat->cnswap
+ );
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ return bRet;
+}
+
+static bool osl_getProcStatus(pid_t pid, struct osl_procStat* procstat)
+{
+ int fd = 0;
+ char name[PATH_MAX + 1];
+ bool bRet = false;
+
+ snprintf(name, sizeof(name), "/proc/%u/status", pid);
+
+ if ((fd = open(name,O_RDONLY)) >=0 )
+ {
+ char* tmp=nullptr;
+ char prstatusbuf[512];
+ memset(prstatusbuf,0,512);
+ bRet = safeRead(fd, prstatusbuf, 511);
+
+ close(fd);
+
+ if (!bRet)
+ return false;
+
+ tmp = strstr(prstatusbuf,"Uid:");
+ if(tmp)
+ {
+ sscanf(tmp,"Uid:\t%d\t%d\t%d\t%d",
+ &procstat->ruid, &procstat->euid, &procstat->suid, &procstat->fuid
+ );
+ }
+
+ tmp = strstr(prstatusbuf,"Gid:");
+ if(tmp)
+ {
+ sscanf(tmp,"Gid:\t%d\t%d\t%d\t%d",
+ &procstat->rgid, &procstat->egid, &procstat->sgid, &procstat->fgid
+ );
+ }
+
+ tmp = strstr(prstatusbuf,"VmSize:");
+ if(tmp)
+ {
+ sscanf(tmp,
+ "VmSize: %lu kB\n"
+ "VmLck: %lu kB\n"
+ "VmRSS: %lu kB\n"
+ "VmData: %lu kB\n"
+ "VmStk: %lu kB\n"
+ "VmExe: %lu kB\n"
+ "VmLib: %lu kB\n",
+ &procstat->vm_size, &procstat->vm_lock, &procstat->vm_rss, &procstat->vm_data,
+ &procstat->vm_stack, &procstat->vm_exe, &procstat->vm_lib
+ );
+ }
+
+ tmp = strstr(prstatusbuf,"SigPnd:");
+ if(tmp)
+ {
+ sscanf(tmp, "SigPnd: %23s SigBlk: %23s SigIgn: %23s %*s %23s",
+ procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch
+ );
+ }
+ }
+ return bRet;
+}
+
+#endif
+
+oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields, oslProcessInfo* pInfo)
+{
+ pid_t pid;
+
+ if (Process == nullptr)
+ pid = getpid();
+ else
+ pid = static_cast<oslProcessImpl*>(Process)->m_pid;
+
+ if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo)))
+ return osl_Process_E_Unknown;
+
+ pInfo->Fields = 0;
+
+ if (Fields & osl_Process_IDENTIFIER)
+ {
+ pInfo->Ident = pid;
+ pInfo->Fields |= osl_Process_IDENTIFIER;
+ }
+
+ if (Fields & osl_Process_EXITCODE)
+ {
+ if ((Process != nullptr) &&
+ osl_checkCondition(static_cast<oslProcessImpl*>(Process)->m_terminated))
+ {
+ pInfo->Code = static_cast<oslProcessImpl*>(Process)->m_status;
+ pInfo->Fields |= osl_Process_EXITCODE;
+ }
+ }
+
+ if (Fields & (osl_Process_HEAPUSAGE | osl_Process_CPUTIMES))
+ {
+
+#if defined(__sun)
+
+ int fd;
+ char name[PATH_MAX + 1];
+
+ snprintf(name, sizeof(name), "/proc/%ld", (long)pid);
+
+ if ((fd = open(name, O_RDONLY)) >= 0)
+ {
+ prstatus_t prstatus;
+
+ if (ioctl(fd, PIOCSTATUS, &prstatus) >= 0)
+ {
+ if (Fields & osl_Process_CPUTIMES)
+ {
+ pInfo->UserTime.Seconds = prstatus.pr_utime.tv_sec;
+ pInfo->UserTime.Nanosec = prstatus.pr_utime.tv_nsec;
+ pInfo->SystemTime.Seconds = prstatus.pr_stime.tv_sec;
+ pInfo->SystemTime.Nanosec = prstatus.pr_stime.tv_nsec;
+
+ pInfo->Fields |= osl_Process_CPUTIMES;
+ }
+
+ if (Fields & osl_Process_HEAPUSAGE)
+ {
+ pInfo->HeapUsage = prstatus.pr_brksize;
+
+ pInfo->Fields |= osl_Process_HEAPUSAGE;
+ }
+
+ close(fd);
+
+ return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
+ }
+ else
+ close(fd);
+ }
+
+#elif defined(LINUX)
+
+ if ( (Fields & osl_Process_CPUTIMES) || (Fields & osl_Process_HEAPUSAGE) )
+ {
+ struct osl_procStat procstat;
+ memset(&procstat,0,sizeof(procstat));
+
+ if ( (Fields & osl_Process_CPUTIMES) && osl_getProcStat(pid, &procstat) )
+ {
+ /*
+ * mfe:
+ * We calculate only time of the process proper.
+ * Threads are processes, we do not consider their time here!
+ * (For this, cutime and cstime should be used, it seems not
+ * to work in 2.0.36)
+ */
+
+ long clktck;
+ unsigned long hz;
+ unsigned long userseconds;
+ unsigned long systemseconds;
+
+ clktck = sysconf(_SC_CLK_TCK);
+ if (clktck < 0) {
+ return osl_Process_E_Unknown;
+ }
+ hz = static_cast<unsigned long>(clktck);
+
+ userseconds = procstat.utime/hz;
+ systemseconds = procstat.stime/hz;
+
+ pInfo->UserTime.Seconds = userseconds;
+ pInfo->UserTime.Nanosec = procstat.utime - (userseconds * hz);
+ pInfo->SystemTime.Seconds = systemseconds;
+ pInfo->SystemTime.Nanosec = procstat.stime - (systemseconds * hz);
+
+ pInfo->Fields |= osl_Process_CPUTIMES;
+ }
+
+ if ( (Fields & osl_Process_HEAPUSAGE) && osl_getProcStatus(pid, &procstat) )
+ {
+ /*
+ * mfe:
+ * vm_data (found in status) shows the size of the data segment
+ * it a rough approximation of the core heap size
+ */
+ pInfo->HeapUsage = procstat.vm_data*1024;
+
+ pInfo->Fields |= osl_Process_HEAPUSAGE;
+ }
+ }
+
+ return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
+#endif
+
+ }
+
+ return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
+}
+
+/** Helper function for osl_joinProcessWithTimeout
+ */
+
+static bool is_timeout(const struct timeval* tend)
+{
+ struct timeval tcurrent;
+ gettimeofday(&tcurrent, nullptr);
+ return (tcurrent.tv_sec >= tend->tv_sec);
+}
+
+/* kill(pid, 0) is useful for checking if a
+ process is still alive, but remember that
+ kill even returns 0 if the process is already
+ a zombie. */
+
+static bool is_process_dead(pid_t pid)
+{
+ return ((kill(pid, 0) == -1) && (ESRCH == errno));
+}
+
+oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout)
+{
+ oslProcessImpl* pChild = ChildList;
+ oslProcessError osl_error = osl_Process_E_None;
+
+ OSL_PRECOND(Process, "osl_joinProcess: Invalid parameter");
+ OSL_ASSERT(ChildListMutex);
+
+ if (Process == nullptr || ChildListMutex == nullptr)
+ return osl_Process_E_Unknown;
+
+ osl_acquireMutex(ChildListMutex);
+
+ /* check if process is a child of ours */
+ while (pChild != nullptr)
+ {
+ if (pChild == static_cast<oslProcessImpl*>(Process))
+ break;
+
+ pChild = pChild->m_pnext;
+ }
+
+ osl_releaseMutex(ChildListMutex);
+
+ if (pChild != nullptr)
+ {
+ oslConditionResult cond_res = osl_waitCondition(pChild->m_terminated, pTimeout);
+
+ if (cond_res == osl_cond_result_timeout)
+ osl_error = osl_Process_E_TimedOut;
+ else if (cond_res != osl_cond_result_ok)
+ osl_error = osl_Process_E_Unknown;
+ }
+ else /* alien process; StatusThread will not be able
+ to set the condition terminated */
+ {
+ pid_t pid = static_cast<oslProcessImpl*>(Process)->m_pid;
+
+ if (pTimeout)
+ {
+ bool timeout = false;
+ struct timeval tend;
+
+ gettimeofday(&tend, nullptr);
+
+ tend.tv_sec += pTimeout->Seconds;
+
+ while (!is_process_dead(pid) && !(timeout = is_timeout(&tend)))
+ sleep(1);
+
+ if (timeout)
+ osl_error = osl_Process_E_TimedOut;
+ }
+ else /* infinite */
+ {
+ while (!is_process_dead(pid))
+ sleep(1);
+ }
+ }
+ return osl_error;
+}
+
+oslProcessError SAL_CALL osl_joinProcess(oslProcess Process)
+{
+ return osl_joinProcessWithTimeout(Process, nullptr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/process_impl.cxx b/sal/osl/unx/process_impl.cxx
new file mode 100644
index 000000000..5d7f7644f
--- /dev/null
+++ b/sal/osl/unx/process_impl.cxx
@@ -0,0 +1,473 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <osl/process.h>
+
+#include <limits.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <osl/module.h>
+#include <osl/thread.h>
+#include <rtl/alloc.h>
+#include <rtl/ustring.hxx>
+#include <rtl/strbuf.h>
+#include <sal/log.hxx>
+
+#include "file_path_helper.hxx"
+
+#include "uunxapi.hxx"
+#include "nlsupport.hxx"
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+#if defined(MACOSX) || defined(IOS)
+#include <mach-o/dyld.h>
+
+namespace {
+
+oslProcessError bootstrap_getExecutableFile(rtl_uString ** ppFileURL)
+{
+ oslProcessError result = osl_Process_E_NotFound;
+
+ char buffer[PATH_MAX];
+ uint32_t buflen = sizeof(buffer);
+
+ if (_NSGetExecutablePath (buffer, &buflen) == 0)
+ {
+ /* Determine absolute path. */
+ char abspath[PATH_MAX];
+ if (realpath (buffer, abspath) != nullptr)
+ {
+ /* Convert from utf8 to unicode. */
+ rtl_uString * pAbsPath = nullptr;
+ rtl_string2UString (
+ &pAbsPath,
+ abspath, rtl_str_getLength (abspath),
+ RTL_TEXTENCODING_UTF8,
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+
+ if (pAbsPath)
+ {
+ /* Convert from path to url. */
+ if (osl_getFileURLFromSystemPath (pAbsPath, ppFileURL) == osl_File_E_None)
+ {
+ /* Success. */
+ result = osl_Process_E_None;
+ }
+ rtl_uString_release (pAbsPath);
+ }
+ }
+ }
+
+ return result;
+}
+
+}
+
+#else
+#include <dlfcn.h>
+
+namespace {
+
+oslProcessError bootstrap_getExecutableFile(rtl_uString ** ppFileURL)
+{
+ oslProcessError result = osl_Process_E_NotFound;
+
+#ifdef ANDROID
+ /* Now with just a single DSO, this one from lo-bootstrap.c is as good as
+ * any */
+ void * addr = dlsym (RTLD_DEFAULT, "JNI_OnLoad");
+#else
+#if defined __linux
+ // The below code looking for "main" with dlsym() will typically
+ // fail, as there is little reason for "main" to be exported, in
+ // the dlsym() sense, from an executable. But Linux has
+ // /proc/self/exe, try using that.
+ char buf[PATH_MAX];
+ int rc = readlink("/proc/self/exe", buf, sizeof(buf));
+ if (rc > 0 && rc < PATH_MAX)
+ {
+ buf[rc] = '\0';
+ OUString path = OUString::fromUtf8(buf);
+ OUString fileURL;
+ if (osl::File::getFileURLFromSystemPath(path, fileURL) == osl::File::E_None)
+ {
+ rtl_uString_acquire(fileURL.pData);
+ *ppFileURL = fileURL.pData;
+ return osl_Process_E_None;
+ }
+ }
+#endif
+ /* Determine address of "main()" function. */
+ void * addr = dlsym (RTLD_DEFAULT, "main");
+#endif
+ if (addr != nullptr)
+ {
+ /* Determine module URL. */
+ if (osl_getModuleURLFromAddress (addr, ppFileURL))
+ {
+ /* Success. */
+ result = osl_Process_E_None;
+ }
+ }
+
+ return result;
+}
+
+}
+
+#endif
+
+namespace {
+
+struct CommandArgs_Impl
+{
+ pthread_mutex_t m_mutex;
+ sal_uInt32 m_nCount;
+ rtl_uString ** m_ppArgs;
+};
+
+}
+
+static struct CommandArgs_Impl g_command_args =
+{
+ PTHREAD_MUTEX_INITIALIZER,
+ 0,
+ nullptr
+};
+
+oslProcessError SAL_CALL osl_getExecutableFile (rtl_uString ** ppustrFile)
+{
+ pthread_mutex_lock (&(g_command_args.m_mutex));
+ if (g_command_args.m_nCount == 0)
+ {
+ pthread_mutex_unlock (&(g_command_args.m_mutex));
+ return bootstrap_getExecutableFile(ppustrFile);
+ }
+
+ /* CommandArgs set. Obtain argv[0]. */
+ rtl_uString_assign (ppustrFile, g_command_args.m_ppArgs[0]);
+ pthread_mutex_unlock (&(g_command_args.m_mutex));
+ return osl_Process_E_None;
+}
+
+sal_uInt32 SAL_CALL osl_getCommandArgCount()
+{
+ sal_uInt32 result = 0;
+
+ pthread_mutex_lock (&(g_command_args.m_mutex));
+ SAL_INFO_IF(
+ g_command_args.m_nCount == 0, "sal.osl",
+ "osl_getCommandArgCount w/o prior call to osl_setCommandArgs");
+ if (g_command_args.m_nCount > 0)
+ result = g_command_args.m_nCount - 1;
+ pthread_mutex_unlock (&(g_command_args.m_mutex));
+
+ return result;
+}
+
+oslProcessError SAL_CALL osl_getCommandArg (sal_uInt32 nArg, rtl_uString ** strCommandArg)
+{
+ oslProcessError result = osl_Process_E_NotFound;
+
+ pthread_mutex_lock (&(g_command_args.m_mutex));
+ assert(g_command_args.m_nCount > 0);
+ if (g_command_args.m_nCount > (nArg + 1))
+ {
+ rtl_uString_assign (strCommandArg, g_command_args.m_ppArgs[nArg + 1]);
+ result = osl_Process_E_None;
+ }
+ pthread_mutex_unlock (&(g_command_args.m_mutex));
+
+ return result;
+}
+
+void SAL_CALL osl_setCommandArgs (int argc, char ** argv)
+{
+ assert(argc > 0);
+ pthread_mutex_lock (&(g_command_args.m_mutex));
+ SAL_WARN_IF(g_command_args.m_nCount != 0, "sal.osl", "args already set");
+ if (g_command_args.m_nCount == 0)
+ {
+ rtl_uString** ppArgs = static_cast<rtl_uString**>(rtl_allocateZeroMemory (argc * sizeof(rtl_uString*)));
+ if (ppArgs != nullptr)
+ {
+ rtl_TextEncoding encoding = osl_getThreadTextEncoding();
+ for (int i = 0; i < argc; i++)
+ {
+ rtl_string2UString (
+ &(ppArgs[i]),
+ argv[i], rtl_str_getLength (argv[i]), encoding,
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+ }
+ if (ppArgs[0] != nullptr)
+ {
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ // If we are called with a relative path in argv[0] in a sandboxed process
+ // osl::realpath() fails. So just use bootstrap_getExecutableFile() instead.
+ // Somewhat silly to use argv[0] and tediously figure out the absolute path from it
+ // anyway.
+ bootstrap_getExecutableFile(&ppArgs[0]);
+ OUString pArg0(ppArgs[0]);
+ osl_getFileURLFromSystemPath (pArg0.pData, &(ppArgs[0]));
+#else
+#if !defined(ANDROID) && !defined(IOS) // No use searching PATH on Android or iOS
+ /* see @ osl_getExecutableFile(). */
+ if (rtl_ustr_indexOfChar (rtl_uString_getStr(ppArgs[0]), '/') == -1)
+ {
+ const OUString PATH ("PATH");
+
+ rtl_uString * pSearchPath = nullptr;
+ osl_getEnvironment (PATH.pData, &pSearchPath);
+ if (pSearchPath)
+ {
+ rtl_uString * pSearchResult = nullptr;
+ osl_searchPath (ppArgs[0], pSearchPath, &pSearchResult);
+ if (pSearchResult)
+ {
+ rtl_uString_assign (&(ppArgs[0]), pSearchResult);
+ rtl_uString_release (pSearchResult);
+ }
+ rtl_uString_release (pSearchPath);
+ }
+ }
+#endif
+ OUString pArg0;
+ if (osl::realpath (OUString::unacquired(&ppArgs[0]), pArg0))
+ {
+ osl_getFileURLFromSystemPath (pArg0.pData, &(ppArgs[0]));
+ }
+#endif // !HAVE_FEATURE_MACOSX_SANDBOX
+ }
+ g_command_args.m_nCount = argc;
+ g_command_args.m_ppArgs = ppArgs;
+ }
+ }
+ pthread_mutex_unlock (&(g_command_args.m_mutex));
+}
+
+oslProcessError SAL_CALL osl_getEnvironment(rtl_uString* pustrEnvVar, rtl_uString** ppustrValue)
+{
+ oslProcessError result = osl_Process_E_NotFound;
+ rtl_TextEncoding encoding = osl_getThreadTextEncoding();
+ rtl_String* pstr_env_var = nullptr;
+
+ OSL_PRECOND(pustrEnvVar, "osl_getEnvironment(): Invalid parameter");
+ OSL_PRECOND(ppustrValue, "osl_getEnvironment(): Invalid parameter");
+
+ rtl_uString2String(
+ &pstr_env_var,
+ rtl_uString_getStr(pustrEnvVar), rtl_uString_getLength(pustrEnvVar), encoding,
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+ if (pstr_env_var != nullptr)
+ {
+ const char* p_env_var = getenv (rtl_string_getStr (pstr_env_var));
+ if (p_env_var != nullptr)
+ {
+ rtl_string2UString(
+ ppustrValue,
+ p_env_var, strlen(p_env_var), encoding,
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+ OSL_ASSERT(*ppustrValue != nullptr);
+
+ result = osl_Process_E_None;
+ }
+ rtl_string_release(pstr_env_var);
+ }
+
+ return result;
+}
+
+oslProcessError SAL_CALL osl_setEnvironment(rtl_uString* pustrEnvVar, rtl_uString* pustrValue)
+{
+ oslProcessError result = osl_Process_E_Unknown;
+ rtl_TextEncoding encoding = osl_getThreadTextEncoding();
+ rtl_String* pstr_env_var = nullptr;
+ rtl_String* pstr_val = nullptr;
+
+ OSL_PRECOND(pustrEnvVar, "osl_setEnvironment(): Invalid parameter");
+ OSL_PRECOND(pustrValue, "osl_setEnvironment(): Invalid parameter");
+
+ rtl_uString2String(
+ &pstr_env_var,
+ rtl_uString_getStr(pustrEnvVar), rtl_uString_getLength(pustrEnvVar), encoding,
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+
+ rtl_uString2String(
+ &pstr_val,
+ rtl_uString_getStr(pustrValue), rtl_uString_getLength(pustrValue), encoding,
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+
+ if (pstr_env_var != nullptr && pstr_val != nullptr)
+ {
+#if defined (__sun)
+ rtl_String * pBuffer = NULL;
+
+ sal_Int32 nCapacity = rtl_stringbuffer_newFromStringBuffer( &pBuffer,
+ rtl_string_getLength(pstr_env_var) + rtl_string_getLength(pstr_val) + 1,
+ pstr_env_var );
+ rtl_stringbuffer_insert( &pBuffer, &nCapacity, pBuffer->length, "=", 1);
+ rtl_stringbuffer_insert( &pBuffer, &nCapacity, pBuffer->length,
+ rtl_string_getStr(pstr_val), rtl_string_getLength(pstr_val) );
+
+ rtl_string_acquire(pBuffer); // argument to putenv must leak on success
+
+ if (putenv(rtl_string_getStr(pBuffer)) == 0)
+ result = osl_Process_E_None;
+ else
+ rtl_string_release(pBuffer);
+#else
+ if (setenv(rtl_string_getStr(pstr_env_var), rtl_string_getStr(pstr_val), 1) == 0)
+ result = osl_Process_E_None;
+#endif
+ }
+
+ if (pstr_val)
+ rtl_string_release(pstr_val);
+
+ if (pstr_env_var != nullptr)
+ rtl_string_release(pstr_env_var);
+
+ return result;
+}
+
+oslProcessError SAL_CALL osl_clearEnvironment(rtl_uString* pustrEnvVar)
+{
+ oslProcessError result = osl_Process_E_Unknown;
+ rtl_TextEncoding encoding = osl_getThreadTextEncoding();
+ rtl_String* pstr_env_var = nullptr;
+
+ OSL_PRECOND(pustrEnvVar, "osl_setEnvironment(): Invalid parameter");
+
+ rtl_uString2String(
+ &pstr_env_var,
+ rtl_uString_getStr(pustrEnvVar), rtl_uString_getLength(pustrEnvVar), encoding,
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+
+ if (pstr_env_var)
+ {
+#if defined (__sun)
+ rtl_String * pBuffer = NULL;
+
+ sal_Int32 nCapacity = rtl_stringbuffer_newFromStringBuffer( &pBuffer,
+ rtl_string_getLength(pstr_env_var) + 1, pstr_env_var );
+ rtl_stringbuffer_insert( &pBuffer, &nCapacity, pBuffer->length, "=", 1);
+
+ rtl_string_acquire(pBuffer); // argument to putenv must leak on success
+
+ if (putenv(rtl_string_getStr(pBuffer)) == 0)
+ result = osl_Process_E_None;
+ else
+ rtl_string_release(pBuffer);
+#elif (defined(MACOSX) || defined(NETBSD) || defined(FREEBSD))
+ // MacOSX baseline is 10.4, which has an old-school void return
+ // for unsetenv.
+ // See: http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/10.4/man3/unsetenv.3.html?useVersion=10.4
+ unsetenv(rtl_string_getStr(pstr_env_var));
+ result = osl_Process_E_None;
+#else
+ if (unsetenv(rtl_string_getStr(pstr_env_var)) == 0)
+ result = osl_Process_E_None;
+#endif
+ rtl_string_release(pstr_env_var);
+ }
+
+ return result;
+}
+
+oslProcessError SAL_CALL osl_getProcessWorkingDir(rtl_uString **ppustrWorkingDir)
+{
+ oslProcessError result = osl_Process_E_Unknown;
+ char buffer[PATH_MAX];
+
+ OSL_PRECOND(ppustrWorkingDir, "osl_getProcessWorkingDir(): Invalid parameter");
+
+ if (getcwd (buffer, sizeof(buffer)) != nullptr)
+ {
+ rtl_uString* ustrTmp = nullptr;
+
+ rtl_string2UString(
+ &ustrTmp,
+ buffer, strlen(buffer), osl_getThreadTextEncoding(),
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+ if (ustrTmp != nullptr)
+ {
+ if (osl_getFileURLFromSystemPath (ustrTmp, ppustrWorkingDir) == osl_File_E_None)
+ result = osl_Process_E_None;
+ rtl_uString_release (ustrTmp);
+ }
+ }
+
+ return result;
+}
+
+namespace {
+
+struct ProcessLocale_Impl
+{
+ pthread_mutex_t m_mutex;
+ rtl_Locale * m_pLocale;
+};
+
+}
+
+static struct ProcessLocale_Impl g_process_locale =
+{
+ PTHREAD_MUTEX_INITIALIZER,
+ nullptr
+};
+
+oslProcessError SAL_CALL osl_getProcessLocale( rtl_Locale ** ppLocale )
+{
+ oslProcessError result = osl_Process_E_Unknown;
+ OSL_PRECOND(ppLocale, "osl_getProcessLocale(): Invalid parameter.");
+ if (ppLocale)
+ {
+ pthread_mutex_lock(&(g_process_locale.m_mutex));
+
+ if (g_process_locale.m_pLocale == nullptr)
+ imp_getProcessLocale (&(g_process_locale.m_pLocale));
+ *ppLocale = g_process_locale.m_pLocale;
+ result = osl_Process_E_None;
+
+ pthread_mutex_unlock (&(g_process_locale.m_mutex));
+ }
+ return result;
+}
+
+oslProcessError SAL_CALL osl_setProcessLocale( rtl_Locale * pLocale )
+{
+ OSL_PRECOND(pLocale, "osl_setProcessLocale(): Invalid parameter.");
+
+ pthread_mutex_lock(&(g_process_locale.m_mutex));
+ g_process_locale.m_pLocale = pLocale;
+ pthread_mutex_unlock (&(g_process_locale.m_mutex));
+
+ return osl_Process_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/profile.cxx b/sal/osl/unx/profile.cxx
new file mode 100644
index 000000000..ce3742bc8
--- /dev/null
+++ b/sal/osl/unx/profile.cxx
@@ -0,0 +1,1866 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "system.hxx"
+#include "readwrite_helper.hxx"
+#include "file_url.hxx"
+#include "unixerrnostring.hxx"
+
+#include <osl/diagnose.h>
+#include <osl/profile.h>
+#include <osl/process.h>
+#include <osl/thread.h>
+#include <rtl/alloc.h>
+#include <sal/log.hxx>
+
+#define LINES_INI 32
+#define LINES_ADD 10
+#define SECTIONS_INI 5
+#define SECTIONS_ADD 3
+#define ENTRIES_INI 5
+#define ENTRIES_ADD 3
+
+#define STR_INI_BOOLYES "yes"
+#define STR_INI_BOOLON "on"
+#define STR_INI_BOOLONE "1"
+#define STR_INI_BOOLNO "no"
+#define STR_INI_BOOLOFF "off"
+#define STR_INI_BOOLZERO "0"
+
+#define FLG_USER 0x00FF
+#define FLG_AUTOOPEN 0x0100
+#define FLG_MODIFIED 0x0200
+
+#define DEFAULT_PMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
+
+typedef time_t osl_TStamp;
+
+namespace {
+
+enum osl_TLockMode
+{
+ un_lock, read_lock, write_lock
+};
+
+struct osl_TFile
+{
+ int m_Handle;
+ char* m_pReadPtr;
+ char m_ReadBuf[512];
+ char* m_pWriteBuf;
+ sal_uInt32 m_nWriteBufLen;
+ sal_uInt32 m_nWriteBufFree;
+};
+
+struct osl_TProfileEntry
+{
+ sal_uInt32 m_Line;
+ sal_uInt32 m_Offset;
+ sal_uInt32 m_Len;
+};
+
+struct osl_TProfileSection
+{
+ sal_uInt32 m_Line;
+ sal_uInt32 m_Offset;
+ sal_uInt32 m_Len;
+ sal_uInt32 m_NoEntries;
+ sal_uInt32 m_MaxEntries;
+ osl_TProfileEntry* m_Entries;
+};
+
+/* Profile-data structure hidden behind oslProfile: */
+struct osl_TProfileImpl
+{
+ sal_uInt32 m_Flags;
+ osl_TFile* m_pFile;
+ osl_TStamp m_Stamp;
+ char m_FileName[PATH_MAX + 1];
+ sal_uInt32 m_NoLines;
+ sal_uInt32 m_MaxLines;
+ sal_uInt32 m_NoSections;
+ sal_uInt32 m_MaxSections;
+ char** m_Lines;
+ osl_TProfileSection* m_Sections;
+ pthread_mutex_t m_AccessLock;
+ bool m_bIsValid;
+};
+
+}
+
+static osl_TFile* openFileImpl(const char* pszFilename, oslProfileOption ProfileFlags);
+static osl_TStamp closeFileImpl(osl_TFile* pFile, oslProfileOption Flags);
+static bool OslProfile_lockFile(const osl_TFile* pFile, osl_TLockMode eMode);
+static bool OslProfile_rewindFile(osl_TFile* pFile, bool bTruncate);
+static osl_TStamp OslProfile_getFileStamp(osl_TFile* pFile);
+
+static char* OslProfile_getLine(osl_TFile* pFile);
+static bool OslProfile_putLine(osl_TFile* pFile, const char *pszLine);
+static char* stripBlanks(char* String, sal_uInt32* pLen);
+static char* addLine(osl_TProfileImpl* pProfile, const char* Line);
+static char* insertLine(osl_TProfileImpl* pProfile, const char* Line, sal_uInt32 LineNo);
+static void removeLine(osl_TProfileImpl* pProfile, sal_uInt32 LineNo);
+static void setEntry(osl_TProfileImpl* pProfile, osl_TProfileSection* pSection,
+ sal_uInt32 NoEntry, sal_uInt32 Line,
+ char* Entry, sal_uInt32 Len);
+static bool addEntry(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection,
+ int Line, char* Entry, sal_uInt32 Len);
+static void removeEntry(osl_TProfileSection *pSection, sal_uInt32 NoEntry);
+static bool addSection(osl_TProfileImpl* pProfile, int Line, const char* Section, sal_uInt32 Len);
+static void removeSection(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection);
+static osl_TProfileSection* findEntry(osl_TProfileImpl* pProfile, const char* Section,
+ const char* Entry, sal_uInt32 *pNoEntry);
+static bool loadProfile(osl_TFile* pFile, osl_TProfileImpl* pProfile);
+static bool storeProfile(osl_TProfileImpl* pProfile, bool bCleanup);
+static osl_TProfileImpl* acquireProfile(oslProfile Profile, bool bWriteable);
+static bool releaseProfile(osl_TProfileImpl* pProfile);
+
+static bool writeProfileImpl (osl_TFile* pFile);
+static osl_TFile* osl_openTmpProfileImpl(osl_TProfileImpl*);
+static bool osl_ProfileSwapProfileNames(osl_TProfileImpl*);
+static void osl_ProfileGenerateExtension(const char* pszFileName, const char* pszExtension, char* pszTmpName, int BufferMaxLen);
+static oslProfile osl_psz_openProfile(const char *pszProfileName, oslProfileOption Flags);
+
+oslProfile SAL_CALL osl_openProfile(rtl_uString *ustrProfileName, oslProfileOption Options)
+{
+ char profilePath[PATH_MAX] = "";
+ return
+ (ustrProfileName == nullptr
+ || ustrProfileName->buffer[0] == 0
+ || (FileURLToPath(profilePath, PATH_MAX, ustrProfileName)
+ == osl_File_E_None))
+ ? osl_psz_openProfile(profilePath, Options)
+ : nullptr;
+}
+
+static oslProfile osl_psz_openProfile(const char *pszProfileName, oslProfileOption Flags)
+{
+ osl_TFile* pFile;
+ osl_TProfileImpl* pProfile;
+ bool bRet = false;
+
+ if ( ( pFile = openFileImpl(pszProfileName, Flags ) ) == nullptr )
+ {
+ return nullptr;
+ }
+
+ pProfile = static_cast<osl_TProfileImpl*>(calloc(1, sizeof(osl_TProfileImpl)));
+
+ if ( pProfile == nullptr )
+ {
+ closeFileImpl(pFile, Flags);
+ return nullptr;
+ }
+
+ pProfile->m_Flags = Flags & FLG_USER;
+
+ if ( Flags & ( osl_Profile_READLOCK | osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE ) )
+ {
+ pProfile->m_pFile = pFile;
+ }
+
+ pthread_mutex_init(&(pProfile->m_AccessLock),PTHREAD_MUTEXATTR_DEFAULT);
+ pProfile->m_bIsValid = true;
+
+ pProfile->m_Stamp = OslProfile_getFileStamp(pFile);
+ bRet=loadProfile(pFile, pProfile);
+ bRet &= realpath(pszProfileName, pProfile->m_FileName) != nullptr;
+ SAL_WARN_IF(!bRet, "sal.osl", "realpath(pszProfileName, pProfile->m_FileName) != NULL ==> false");
+
+ if (pProfile->m_pFile == nullptr)
+ closeFileImpl(pFile,pProfile->m_Flags);
+
+ // coverity[leaked_storage] - pFile is not leaked
+ return pProfile;
+}
+
+sal_Bool SAL_CALL osl_closeProfile(oslProfile Profile)
+{
+ osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
+ osl_TProfileImpl* pTmpProfile;
+
+ if ( Profile == nullptr )
+ {
+ return false;
+ }
+
+ pthread_mutex_lock(&(pProfile->m_AccessLock));
+
+ if ( !pProfile->m_bIsValid )
+ {
+ SAL_WARN("sal.osl", "!pProfile->m_bIsValid");
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+
+ return false;
+ }
+
+ pProfile->m_bIsValid = false;
+
+ if ( ! ( pProfile->m_Flags & osl_Profile_READLOCK ) && ( pProfile->m_Flags & FLG_MODIFIED ) )
+ {
+ pTmpProfile = acquireProfile(Profile, true);
+
+ if ( pTmpProfile != nullptr )
+ {
+ bool bRet = storeProfile(pTmpProfile, true);
+ SAL_WARN_IF(!bRet, "sal.osl", "storeProfile(pTmpProfile, true) ==> false");
+ }
+ }
+ else
+ {
+ pTmpProfile = acquireProfile(Profile, false);
+ }
+
+ if ( pTmpProfile == nullptr )
+ {
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+
+ SAL_INFO("sal.osl", "Out osl_closeProfile [pProfile==0]");
+ return false;
+ }
+
+ pProfile = pTmpProfile;
+
+ if (pProfile->m_pFile != nullptr)
+ closeFileImpl(pProfile->m_pFile,pProfile->m_Flags);
+
+ pProfile->m_pFile = nullptr;
+ pProfile->m_FileName[0] = '\0';
+
+ /* release whole profile data types memory */
+ if ( pProfile->m_NoLines > 0)
+ {
+ unsigned int idx=0;
+ if ( pProfile->m_Lines != nullptr )
+ {
+ for ( idx = 0 ; idx < pProfile->m_NoLines ; ++idx)
+ {
+ if ( pProfile->m_Lines[idx] != nullptr )
+ {
+ free(pProfile->m_Lines[idx]);
+ pProfile->m_Lines[idx]=nullptr;
+ }
+ }
+ free(pProfile->m_Lines);
+ pProfile->m_Lines=nullptr;
+ }
+ if ( pProfile->m_Sections != nullptr )
+ {
+ /*osl_TProfileSection* pSections=pProfile->m_Sections;*/
+ for ( idx = 0 ; idx < pProfile->m_NoSections ; ++idx )
+ {
+ if ( pProfile->m_Sections[idx].m_Entries != nullptr )
+ {
+ free(pProfile->m_Sections[idx].m_Entries);
+ pProfile->m_Sections[idx].m_Entries=nullptr;
+ }
+ }
+ free(pProfile->m_Sections);
+ pProfile->m_Sections=nullptr;
+ }
+ }
+
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+
+ pthread_mutex_destroy(&(pProfile->m_AccessLock));
+
+ free(pProfile);
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_flushProfile(oslProfile Profile)
+{
+ osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
+ osl_TFile* pFile;
+ bool bRet = false;
+
+ if ( pProfile == nullptr )
+ {
+ return false;
+ }
+
+ pthread_mutex_lock(&(pProfile->m_AccessLock));
+
+ if ( !pProfile->m_bIsValid )
+ {
+ SAL_WARN_IF(!pProfile->m_bIsValid, "sal.osl", "!pProfile->m_bIsValid");
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+ return false;
+ }
+
+ pFile = pProfile->m_pFile;
+ if ( !( pFile != nullptr && pFile->m_Handle >= 0 ) )
+ {
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+
+ return false;
+ }
+
+ if ( pProfile->m_Flags & FLG_MODIFIED )
+ {
+ bRet = storeProfile(pProfile, false);
+ SAL_WARN_IF(!bRet, "sal.osl", "storeProfile(pProfile, false) ==> false");
+ }
+
+ pthread_mutex_unlock(&(pProfile->m_AccessLock));
+ return bRet;
+}
+
+static bool writeProfileImpl(osl_TFile* pFile)
+{
+ if ( !( pFile != nullptr && pFile->m_Handle >= 0 ) || ( pFile->m_pWriteBuf == nullptr ) )
+ {
+ return false;
+ }
+
+ SAL_WARN_IF(
+ (strlen(pFile->m_pWriteBuf)
+ != pFile->m_nWriteBufLen - pFile->m_nWriteBufFree),
+ "sal.osl",
+ strlen(pFile->m_pWriteBuf) << " != "
+ << (pFile->m_nWriteBufLen - pFile->m_nWriteBufFree));
+
+ if ( !safeWrite(pFile->m_Handle, pFile->m_pWriteBuf, pFile->m_nWriteBufLen - pFile->m_nWriteBufFree) )
+ {
+ SAL_INFO("sal.osl", "write failed: " << UnixErrnoString(errno));
+ return false;
+ }
+
+ free(pFile->m_pWriteBuf);
+ pFile->m_pWriteBuf=nullptr;
+ pFile->m_nWriteBufLen=0;
+ pFile->m_nWriteBufFree=0;
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_readProfileString(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ char* pszString,
+ sal_uInt32 MaxLen,
+ const char* pszDefault)
+{
+ sal_uInt32 NoEntry;
+ char* pStr=nullptr;
+ osl_TProfileImpl* pProfile=nullptr;
+ osl_TProfileImpl* pTmpProfile=nullptr;
+ bool bRet = false;
+
+ pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);
+
+ if ( pTmpProfile == nullptr )
+ {
+ return false;
+ }
+
+ pthread_mutex_lock(&(pTmpProfile->m_AccessLock));
+
+ if ( !pTmpProfile->m_bIsValid )
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ pProfile = acquireProfile(Profile, false);
+
+ if ( pProfile == nullptr )
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ osl_TProfileSection* pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry);
+ if ((pSec != nullptr) &&
+ (NoEntry < pSec->m_NoEntries) &&
+ ((pStr = strchr(pProfile->m_Lines[pSec->m_Entries[NoEntry].m_Line],
+ '=')) != nullptr))
+ {
+ pStr++;
+ }
+ else
+ {
+ pStr=const_cast<char*>(pszDefault);
+ }
+
+ if ( pStr != nullptr )
+ {
+ pStr = stripBlanks(pStr, nullptr);
+ MaxLen = (MaxLen - 1 < strlen(pStr)) ? (MaxLen - 1) : strlen(pStr);
+ pStr = stripBlanks(pStr, &MaxLen);
+ strncpy(pszString, pStr, MaxLen);
+ pszString[MaxLen] = '\0';
+ }
+ }
+ else
+ { /* not implemented */ }
+
+ bRet=releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ if ( pStr == nullptr )
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_readProfileBool(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ sal_Bool Default)
+{
+ char Line[32];
+ Line[0] = '\0';
+
+ if (osl_readProfileString(Profile, pszSection, pszEntry, Line, sizeof(Line), ""))
+ {
+ if ((strcasecmp(Line, STR_INI_BOOLYES) == 0) ||
+ (strcasecmp(Line, STR_INI_BOOLON) == 0) ||
+ (strcasecmp(Line, STR_INI_BOOLONE) == 0))
+ Default = true;
+ else
+ if ((strcasecmp(Line, STR_INI_BOOLNO) == 0) ||
+ (strcasecmp(Line, STR_INI_BOOLOFF) == 0) ||
+ (strcasecmp(Line, STR_INI_BOOLZERO) == 0))
+ Default = false;
+ }
+
+ return Default;
+}
+
+sal_uInt32 SAL_CALL osl_readProfileIdent(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ sal_uInt32 FirstId,
+ const char* Strings[],
+ sal_uInt32 Default)
+{
+ sal_uInt32 i;
+ char Line[256];
+ Line[0] = '\0';
+
+ if (osl_readProfileString(Profile, pszSection, pszEntry, Line, sizeof(Line), ""))
+ {
+ i = 0;
+ while (Strings[i] != nullptr)
+ {
+ if (strcasecmp(Line, Strings[i]) == 0)
+ {
+ Default = i + FirstId;
+ break;
+ }
+ i++;
+ }
+ }
+
+ return Default;
+}
+
+sal_Bool SAL_CALL osl_writeProfileString(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ const char* pszString)
+{
+ sal_uInt32 i;
+ bool bRet = false;
+ sal_uInt32 NoEntry;
+ char* pStr;
+ char* Line = nullptr;
+ osl_TProfileSection* pSec;
+ osl_TProfileImpl* pProfile = nullptr;
+ osl_TProfileImpl* pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);
+
+ if ( pTmpProfile == nullptr )
+ {
+ return false;
+ }
+
+ pthread_mutex_lock(&(pTmpProfile->m_AccessLock));
+
+ if ( !pTmpProfile->m_bIsValid )
+ {
+ SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid");
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ pProfile=acquireProfile(Profile, true);
+
+ if (pProfile == nullptr)
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ Line = static_cast<char*>(malloc(strlen(pszEntry)+strlen(pszString)+48));
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ if ((pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry)) == nullptr)
+ {
+ Line[0] = '\0';
+ addLine(pProfile, Line);
+
+ Line[0] = '[';
+ strcpy(&Line[1], pszSection);
+ Line[1 + strlen(pszSection)] = ']';
+ Line[2 + strlen(pszSection)] = '\0';
+
+ pStr = addLine(pProfile, Line);
+ if ((pStr == nullptr) ||
+ (! addSection(pProfile, pProfile->m_NoLines - 1, &pStr[1], strlen(pszSection))))
+ {
+ bRet=releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ free(Line);
+ return false;
+ }
+
+ pSec = &pProfile->m_Sections[pProfile->m_NoSections - 1];
+ NoEntry = pSec->m_NoEntries;
+ }
+
+ Line[0] = '\0';
+ strcpy(&Line[0], pszEntry);
+ Line[0 + strlen(pszEntry)] = '=';
+ strcpy(&Line[1 + strlen(pszEntry)], pszString);
+
+ if (NoEntry >= pSec->m_NoEntries)
+ {
+ if (pSec->m_NoEntries > 0)
+ i = pSec->m_Entries[pSec->m_NoEntries - 1].m_Line + 1;
+ else
+ i = pSec->m_Line + 1;
+
+ pStr = insertLine(pProfile, Line, i);
+ if ((pStr == nullptr) ||
+ (! addEntry(pProfile, pSec, i, pStr, strlen(pszEntry))))
+ {
+ bRet=releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+ free(Line);
+
+ return false;
+ }
+
+ pProfile->m_Flags |= FLG_MODIFIED;
+ }
+ else
+ {
+ i = pSec->m_Entries[NoEntry].m_Line;
+ free(pProfile->m_Lines[i]);
+ pProfile->m_Lines[i] = strdup(Line);
+ setEntry(pProfile, pSec, NoEntry, i, pProfile->m_Lines[i], strlen(pszEntry));
+
+ pProfile->m_Flags |= FLG_MODIFIED;
+ }
+ }
+ else {
+ /* not implemented */
+ }
+
+ bRet = releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+ if ( Line!= nullptr )
+ {
+ free(Line);
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL osl_writeProfileBool(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ sal_Bool Value)
+{
+ bool bRet = false;
+
+ if (Value)
+ bRet=osl_writeProfileString(Profile, pszSection, pszEntry, STR_INI_BOOLONE);
+ else
+ bRet=osl_writeProfileString(Profile, pszSection, pszEntry, STR_INI_BOOLZERO);
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL osl_writeProfileIdent(oslProfile Profile,
+ const char* pszSection,
+ const char* pszEntry,
+ sal_uInt32 FirstId,
+ const char* Strings[],
+ sal_uInt32 Value)
+{
+ int i, n = 0;
+ bool bRet = false;
+
+ while (Strings[n] != nullptr)
+ ++n;
+
+ if ((i = Value - FirstId) >= n)
+ bRet = false;
+ else
+ bRet = osl_writeProfileString(Profile, pszSection, pszEntry, Strings[i]);
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL osl_removeProfileEntry(oslProfile Profile,
+ const char *pszSection,
+ const char *pszEntry)
+{
+ sal_uInt32 NoEntry;
+ osl_TProfileImpl* pProfile = nullptr;
+ osl_TProfileImpl* pTmpProfile = nullptr;
+ bool bRet = false;
+
+ pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);
+
+ if ( pTmpProfile == nullptr )
+ {
+ return false;
+ }
+
+ pthread_mutex_lock(&(pTmpProfile->m_AccessLock));
+
+ if ( !pTmpProfile->m_bIsValid )
+ {
+ SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid");
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+ return false;
+ }
+
+ pProfile = acquireProfile(Profile, true);
+
+ if (pProfile == nullptr)
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return false;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ osl_TProfileSection* pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry);
+ if ((pSec != nullptr) &&
+ (NoEntry < pSec->m_NoEntries))
+ {
+ removeLine(pProfile, pSec->m_Entries[NoEntry].m_Line);
+ removeEntry(pSec, NoEntry);
+ if (pSec->m_NoEntries == 0)
+ {
+ removeLine(pProfile, pSec->m_Line);
+
+ /* remove any empty separation line */
+ if ((pSec->m_Line > 0) && (pProfile->m_Lines[pSec->m_Line - 1][0] == '\0'))
+ removeLine(pProfile, pSec->m_Line - 1);
+
+ removeSection(pProfile, pSec);
+ }
+
+ pProfile->m_Flags |= FLG_MODIFIED;
+ }
+ }
+ else
+ { /* not implemented */ }
+
+ bRet = releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return bRet;
+}
+
+sal_uInt32 SAL_CALL osl_getProfileSectionEntries(oslProfile Profile,
+ const char *pszSection,
+ char* pszBuffer,
+ sal_uInt32 MaxLen)
+{
+ sal_uInt32 i, n = 0;
+ sal_uInt32 NoEntry;
+ osl_TProfileImpl* pProfile = nullptr;
+ osl_TProfileImpl* pTmpProfile = nullptr;
+ bool bRet = false;
+
+ pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);
+
+ if ( pTmpProfile == nullptr )
+ {
+ return 0;
+
+ }
+
+ pthread_mutex_lock(&(pTmpProfile->m_AccessLock));
+
+ if ( !pTmpProfile->m_bIsValid )
+ {
+ SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return 0;
+ }
+
+ pProfile = acquireProfile(Profile, false);
+
+ if (pProfile == nullptr)
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return 0;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ osl_TProfileSection* pSec;
+ if ((pSec = findEntry(pProfile, pszSection, "", &NoEntry)) != nullptr)
+ {
+ if (MaxLen != 0)
+ {
+ for (i = 0; i < pSec->m_NoEntries; i++)
+ {
+ if ((n + pSec->m_Entries[i].m_Len + 1) < MaxLen)
+ {
+ strncpy(&pszBuffer[n], &pProfile->m_Lines[pSec->m_Entries[i].m_Line]
+ [pSec->m_Entries[i].m_Offset], pSec->m_Entries[i].m_Len);
+ n += pSec->m_Entries[i].m_Len;
+ pszBuffer[n++] = '\0';
+ }
+ else
+ break;
+
+ }
+
+ pszBuffer[n++] = '\0';
+ }
+ else
+ {
+ for (i = 0; i < pSec->m_NoEntries; i++)
+ n += pSec->m_Entries[i].m_Len + 1;
+
+ n += 1;
+ }
+ }
+ else
+ n = 0;
+ }
+ else {
+ /* not implemented */
+ }
+
+ bRet=releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return n;
+}
+
+sal_uInt32 SAL_CALL osl_getProfileSections(oslProfile Profile,
+ char* pszBuffer,
+ sal_uInt32 MaxLen)
+{
+ sal_uInt32 i, n = 0;
+ osl_TProfileImpl* pProfile = nullptr;
+ osl_TProfileImpl* pTmpProfile = nullptr;
+ bool bRet = false;
+
+ pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);
+
+ if ( pTmpProfile == nullptr )
+ {
+ return 0;
+ }
+
+ pthread_mutex_lock(&(pTmpProfile->m_AccessLock));
+
+ if ( !pTmpProfile->m_bIsValid )
+ {
+ SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid");
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return 0;
+ }
+
+ pProfile = acquireProfile(Profile, false);
+
+ if (pProfile == nullptr)
+ {
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return 0;
+ }
+
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ if (MaxLen != 0)
+ {
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ osl_TProfileSection* pSec = &pProfile->m_Sections[i];
+
+ if ((n + pSec->m_Len + 1) < MaxLen)
+ {
+ strncpy(&pszBuffer[n], &pProfile->m_Lines[pSec->m_Line][pSec->m_Offset],
+ pSec->m_Len);
+ n += pSec->m_Len;
+ pszBuffer[n++] = '\0';
+ }
+ else
+ break;
+ }
+
+ pszBuffer[n++] = '\0';
+ }
+ else
+ {
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ n += pProfile->m_Sections[i].m_Len + 1;
+
+ n += 1;
+ }
+ }
+ else
+ { /* not implemented */ }
+
+ bRet=releaseProfile(pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");
+
+ pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
+
+ return n;
+}
+
+static osl_TStamp OslProfile_getFileStamp(osl_TFile* pFile)
+{
+ struct stat status;
+
+ if ( (pFile->m_Handle < 0) || (fstat(pFile->m_Handle, &status) < 0) )
+ {
+ return 0;
+ }
+
+ return status.st_mtime;
+}
+
+static bool OslProfile_lockFile(const osl_TFile* pFile, osl_TLockMode eMode)
+{
+ struct flock lock;
+ static bool const bLockingDisabled = getenv( "STAR_PROFILE_LOCKING_DISABLED" ) != nullptr;
+
+ if (pFile->m_Handle < 0)
+ {
+ return false;
+ }
+
+ if ( bLockingDisabled )
+ {
+ return true;
+ }
+
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+
+ switch (eMode)
+ {
+ case un_lock:
+ lock.l_type = F_UNLCK;
+ break;
+
+ case read_lock:
+ lock.l_type = F_RDLCK;
+ break;
+
+ case write_lock:
+ lock.l_type = F_WRLCK;
+ break;
+ }
+
+#ifndef MACOSX
+ if ( fcntl(pFile->m_Handle, F_SETLKW, &lock) == -1 )
+#else
+ /* Mac OSX will return ENOTSUP for webdav drives so we should ignore it */
+ if ( fcntl(pFile->m_Handle, F_SETLKW, &lock) == -1 && errno != ENOTSUP )
+#endif
+ {
+ SAL_INFO("sal.osl", "fcntl failed: " << UnixErrnoString(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static osl_TFile* openFileImpl(const char* pszFilename, oslProfileOption ProfileFlags )
+{
+ int Flags;
+ osl_TFile* pFile = static_cast<osl_TFile*>(calloc(1, sizeof(osl_TFile)));
+ bool bWriteable = false;
+
+ if ( ProfileFlags & ( osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE ) )
+ {
+ bWriteable = true;
+ }
+
+ if (! bWriteable)
+ {
+ pFile->m_Handle = open(pszFilename, O_RDONLY);
+
+ if (pFile->m_Handle == -1)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "open(" << pszFilename << ",O_RDONLY): " << UnixErrnoString(e));
+ }
+ else
+ SAL_INFO("sal.file", "open(" << pszFilename << ",O_RDONLY) => " << pFile->m_Handle);
+
+ /* mfe: argghh!!! do not check if the file could be opened */
+ /* default mode expects it that way!!! */
+ }
+ else
+ {
+ if (((pFile->m_Handle = open(pszFilename, O_RDWR | O_CREAT | O_EXCL, DEFAULT_PMODE)) < 0) &&
+ ((pFile->m_Handle = open(pszFilename, O_RDWR)) < 0))
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "open(" << pszFilename << ",...): " << UnixErrnoString(e));
+ free(pFile);
+ return nullptr;
+ }
+ else
+ SAL_INFO("sal.file", "open(" << pszFilename << ",...) => " << pFile->m_Handle);
+ }
+
+ /* set close-on-exec flag */
+ if ((Flags = fcntl(pFile->m_Handle, F_GETFD, 0)) != -1)
+ {
+ Flags |= FD_CLOEXEC;
+ int e = fcntl(pFile->m_Handle, F_SETFD, Flags);
+ SAL_INFO_IF(
+ e != 0, "sal.osl",
+ "fcntl to set FD_CLOEXEC failed for " << pszFilename);
+ }
+
+ pFile->m_pWriteBuf=nullptr;
+ pFile->m_nWriteBufFree=0;
+ pFile->m_nWriteBufLen=0;
+
+ if ( ProfileFlags & (osl_Profile_WRITELOCK | osl_Profile_READLOCK ) )
+ {
+ OslProfile_lockFile(pFile, bWriteable ? write_lock : read_lock);
+ }
+
+ return pFile;
+}
+
+static osl_TStamp closeFileImpl(osl_TFile* pFile, oslProfileOption Flags)
+{
+ osl_TStamp stamp = 0;
+
+ if ( pFile == nullptr )
+ {
+ return stamp;
+ }
+
+ if ( pFile->m_Handle >= 0 )
+ {
+ stamp = OslProfile_getFileStamp(pFile);
+
+ if ( Flags & (osl_Profile_WRITELOCK | osl_Profile_READLOCK ) )
+ {
+ OslProfile_lockFile(pFile, un_lock);
+ }
+
+ close(pFile->m_Handle);
+ SAL_INFO("sal.file", "close(" << pFile->m_Handle << ")");
+ pFile->m_Handle = -1;
+ }
+
+ if ( pFile->m_pWriteBuf )
+ {
+ free(pFile->m_pWriteBuf);
+ }
+
+ free(pFile);
+
+ return stamp;
+}
+
+static bool OslProfile_rewindFile(osl_TFile* pFile, bool bTruncate)
+{
+ bool bRet = true;
+
+ if (pFile->m_Handle >= 0)
+ {
+ pFile->m_pReadPtr = pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf);
+
+ bRet = (lseek(pFile->m_Handle, SEEK_SET, 0) == 0);
+
+ if (bTruncate)
+ {
+ bRet &= (ftruncate(pFile->m_Handle, 0) == 0);
+ }
+
+ }
+
+ return bRet;
+}
+
+static char* OslProfile_getLine(osl_TFile* pFile)
+{
+ int Max, Free, nLineBytes = 0;
+ char* pChr;
+ char* pLine = nullptr;
+ char* pNewLine;
+
+ if ( pFile == nullptr )
+ {
+ return nullptr;
+ }
+
+ if (pFile->m_Handle < 0)
+ return nullptr;
+
+ do
+ {
+ int Bytes = sizeof(pFile->m_ReadBuf) - (pFile->m_pReadPtr - pFile->m_ReadBuf);
+
+ if (Bytes <= 1)
+ {
+ /* refill buffer */
+ memcpy(pFile->m_ReadBuf, pFile->m_pReadPtr, Bytes);
+ pFile->m_pReadPtr = pFile->m_ReadBuf;
+
+ Free = sizeof(pFile->m_ReadBuf) - Bytes;
+
+ if ((Max = read(pFile->m_Handle, &pFile->m_ReadBuf[Bytes], Free)) < 0)
+ {
+ SAL_INFO("sal.osl", "read failed: " << UnixErrnoString(errno));
+
+ if( pLine )
+ free( pLine );
+ pLine = nullptr;
+ break;
+ }
+
+ if (Max < Free)
+ {
+ if ((Max == 0) && ! pLine)
+ break;
+
+ pFile->m_ReadBuf[Bytes + Max] = '\0';
+ }
+ }
+
+ for (pChr = pFile->m_pReadPtr;
+ (*pChr != '\n') && (*pChr != '\r') && (*pChr != '\0') &&
+ (pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf) - 1));
+ pChr++);
+
+ Max = pChr - pFile->m_pReadPtr;
+ pNewLine = static_cast<char*>(malloc( nLineBytes + Max + 1 ));
+ if( pLine )
+ {
+ memcpy( pNewLine, pLine, nLineBytes );
+ free( pLine );
+ }
+ memcpy(pNewLine+nLineBytes, pFile->m_pReadPtr, Max);
+ nLineBytes += Max;
+ pNewLine[ nLineBytes ] = 0;
+ pLine = pNewLine;
+
+ if (pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf) - 1))
+ {
+ if (*pChr != '\0')
+ {
+ if ((pChr[0] == '\r') && (pChr[1] == '\n'))
+ pChr += 2;
+ else
+ pChr += 1;
+ }
+
+ if ((pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf))) &&
+ (*pChr == '\0'))
+ pChr = pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf);
+
+ /* setting Max to -1 indicates terminating read loop */
+ Max = -1;
+ }
+
+ pFile->m_pReadPtr = pChr;
+ }
+ while (Max > 0) ;
+
+ return pLine;
+}
+
+static bool OslProfile_putLine(osl_TFile* pFile, const char *pszLine)
+{
+ unsigned int Len = strlen(pszLine);
+
+ if ( pFile == nullptr || pFile->m_Handle < 0 )
+ {
+ return false;
+ }
+
+ if ( pFile->m_pWriteBuf == nullptr )
+ {
+ pFile->m_pWriteBuf = static_cast<char*>(malloc(Len+3));
+ pFile->m_nWriteBufLen = Len+3;
+ pFile->m_nWriteBufFree = Len+3;
+ }
+ else
+ {
+ if ( pFile->m_nWriteBufFree <= Len + 3 )
+ {
+ char* pTmp;
+
+ pTmp=static_cast<char*>(realloc(pFile->m_pWriteBuf,( ( pFile->m_nWriteBufLen + Len ) * 2) ));
+ if ( pTmp == nullptr )
+ {
+ return false;
+ }
+ pFile->m_pWriteBuf = pTmp;
+ pFile->m_nWriteBufFree = pFile->m_nWriteBufFree + pFile->m_nWriteBufLen + ( 2 * Len );
+ pFile->m_nWriteBufLen = ( pFile->m_nWriteBufLen + Len ) * 2;
+ memset( (pFile->m_pWriteBuf) + ( pFile->m_nWriteBufLen - pFile->m_nWriteBufFree ), 0, pFile->m_nWriteBufFree);
+ }
+ }
+
+ memcpy(pFile->m_pWriteBuf + ( pFile->m_nWriteBufLen - pFile->m_nWriteBufFree ),pszLine,Len+1);
+ pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len]='\n';
+ pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len + 1]='\0';
+
+ pFile->m_nWriteBufFree-=Len+1;
+
+ return true;
+}
+
+static char* stripBlanks(char* String, sal_uInt32* pLen)
+{
+ if ( ( pLen != nullptr ) && ( *pLen != 0 ) )
+ {
+ while ((String[*pLen - 1] == ' ') || (String[*pLen - 1] == '\t'))
+ (*pLen)--;
+
+ while ( (*String == ' ') || (*String == '\t') )
+ {
+ String++;
+ (*pLen)--;
+ }
+ }
+ else
+ while ( (*String == ' ') || (*String == '\t') )
+ String++;
+
+ return String;
+}
+
+static char* addLine(osl_TProfileImpl* pProfile, const char* Line)
+{
+ if (pProfile->m_NoLines >= pProfile->m_MaxLines)
+ {
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_MaxLines = LINES_INI;
+ pProfile->m_Lines = static_cast<char **>(calloc(pProfile->m_MaxLines, sizeof(char *)));
+ }
+ else
+ {
+ unsigned int idx=0;
+ unsigned int oldmax=pProfile->m_MaxLines;
+
+ pProfile->m_MaxLines += LINES_ADD;
+ pProfile->m_Lines = static_cast<char **>(realloc(pProfile->m_Lines,
+ pProfile->m_MaxLines * sizeof(char *)));
+ for ( idx = oldmax ; idx < pProfile->m_MaxLines ; ++idx )
+ {
+ pProfile->m_Lines[idx]=nullptr;
+ }
+ }
+ }
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_NoLines = 0;
+ pProfile->m_MaxLines = 0;
+ return nullptr;
+ }
+
+ if ( pProfile->m_Lines[pProfile->m_NoLines] != nullptr )
+ {
+ free(pProfile->m_Lines[pProfile->m_NoLines]);
+ }
+ pProfile->m_Lines[pProfile->m_NoLines++] = strdup(Line);
+
+ return pProfile->m_Lines[pProfile->m_NoLines - 1];
+}
+
+static char* insertLine(osl_TProfileImpl* pProfile, const char* Line, sal_uInt32 LineNo)
+{
+ if (pProfile->m_NoLines >= pProfile->m_MaxLines)
+ {
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_MaxLines = LINES_INI;
+ pProfile->m_Lines = static_cast<char **>(calloc(pProfile->m_MaxLines, sizeof(char *)));
+ }
+ else
+ {
+ pProfile->m_MaxLines += LINES_ADD;
+ pProfile->m_Lines = static_cast<char **>(realloc(pProfile->m_Lines,
+ pProfile->m_MaxLines * sizeof(char *)));
+
+ memset(&pProfile->m_Lines[pProfile->m_NoLines],
+ 0,
+ (pProfile->m_MaxLines - pProfile->m_NoLines - 1) * sizeof(char*));
+ }
+
+ if (pProfile->m_Lines == nullptr)
+ {
+ pProfile->m_NoLines = 0;
+ pProfile->m_MaxLines = 0;
+ return nullptr;
+ }
+ }
+
+ LineNo = std::min(LineNo, pProfile->m_NoLines);
+
+ if (LineNo < pProfile->m_NoLines)
+ {
+ sal_uInt32 i, n;
+
+ memmove(&pProfile->m_Lines[LineNo + 1], &pProfile->m_Lines[LineNo],
+ (pProfile->m_NoLines - LineNo) * sizeof(char *));
+
+ /* adjust line references */
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ osl_TProfileSection* pSec = &pProfile->m_Sections[i];
+
+ if (pSec->m_Line >= LineNo)
+ pSec->m_Line++;
+
+ for (n = 0; n < pSec->m_NoEntries; n++)
+ if (pSec->m_Entries[n].m_Line >= LineNo)
+ pSec->m_Entries[n].m_Line++;
+ }
+ }
+
+ pProfile->m_NoLines++;
+
+ pProfile->m_Lines[LineNo] = strdup(Line);
+
+ return pProfile->m_Lines[LineNo];
+}
+
+static void removeLine(osl_TProfileImpl* pProfile, sal_uInt32 LineNo)
+{
+ if (LineNo >= pProfile->m_NoLines)
+ return;
+
+ free(pProfile->m_Lines[LineNo]);
+ pProfile->m_Lines[LineNo]=nullptr;
+ if (pProfile->m_NoLines - LineNo > 1)
+ {
+ sal_uInt32 i, n;
+
+ memmove(&pProfile->m_Lines[LineNo], &pProfile->m_Lines[LineNo + 1],
+ (pProfile->m_NoLines - LineNo - 1) * sizeof(char *));
+
+ memset(&pProfile->m_Lines[pProfile->m_NoLines - 1],
+ 0,
+ (pProfile->m_MaxLines - pProfile->m_NoLines) * sizeof(char*));
+
+ /* adjust line references */
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ osl_TProfileSection* pSec = &pProfile->m_Sections[i];
+
+ if (pSec->m_Line > LineNo)
+ pSec->m_Line--;
+
+ for (n = 0; n < pSec->m_NoEntries; n++)
+ if (pSec->m_Entries[n].m_Line > LineNo)
+ pSec->m_Entries[n].m_Line--;
+ }
+ }
+ else
+ {
+ pProfile->m_Lines[LineNo] = nullptr;
+ }
+
+ pProfile->m_NoLines--;
+}
+
+static void setEntry(osl_TProfileImpl* pProfile, osl_TProfileSection* pSection,
+ sal_uInt32 NoEntry, sal_uInt32 Line,
+ char* Entry, sal_uInt32 Len)
+{
+ Entry = stripBlanks(Entry, &Len);
+ pSection->m_Entries[NoEntry].m_Line = Line;
+ pSection->m_Entries[NoEntry].m_Offset = Entry - pProfile->m_Lines[Line];
+ pSection->m_Entries[NoEntry].m_Len = Len;
+}
+
+static bool addEntry(osl_TProfileImpl* pProfile,
+ osl_TProfileSection *pSection,
+ int Line, char* Entry,
+ sal_uInt32 Len)
+{
+ if (pSection != nullptr)
+ {
+ if (pSection->m_NoEntries >= pSection->m_MaxEntries)
+ {
+ if (pSection->m_Entries == nullptr)
+ {
+ pSection->m_MaxEntries = ENTRIES_INI;
+ pSection->m_Entries = static_cast<osl_TProfileEntry *>(malloc(
+ pSection->m_MaxEntries * sizeof(osl_TProfileEntry)));
+ }
+ else
+ {
+ pSection->m_MaxEntries += ENTRIES_ADD;
+ pSection->m_Entries = static_cast<osl_TProfileEntry *>(realloc(pSection->m_Entries,
+ pSection->m_MaxEntries * sizeof(osl_TProfileEntry)));
+ }
+
+ if (pSection->m_Entries == nullptr)
+ {
+ pSection->m_NoEntries = 0;
+ pSection->m_MaxEntries = 0;
+ return false;
+ }
+ }
+
+ pSection->m_NoEntries++;
+
+ Entry = stripBlanks(Entry, &Len);
+ setEntry(pProfile, pSection, pSection->m_NoEntries - 1, Line,
+ Entry, Len);
+
+ return true;
+ }
+
+ return false;
+}
+
+static void removeEntry(osl_TProfileSection *pSection, sal_uInt32 NoEntry)
+{
+ if (NoEntry >= pSection->m_NoEntries)
+ return;
+
+ if (pSection->m_NoEntries - NoEntry > 1)
+ {
+ memmove(&pSection->m_Entries[NoEntry],
+ &pSection->m_Entries[NoEntry + 1],
+ (pSection->m_NoEntries - NoEntry - 1) * sizeof(osl_TProfileEntry));
+ pSection->m_Entries[pSection->m_NoEntries - 1].m_Line=0;
+ pSection->m_Entries[pSection->m_NoEntries - 1].m_Offset=0;
+ pSection->m_Entries[pSection->m_NoEntries - 1].m_Len=0;
+ }
+
+ pSection->m_NoEntries--;
+
+}
+
+static bool addSection(osl_TProfileImpl* pProfile, int Line, const char* Section, sal_uInt32 Len)
+{
+ if (pProfile->m_NoSections >= pProfile->m_MaxSections)
+ {
+ if (pProfile->m_Sections == nullptr)
+ {
+ pProfile->m_MaxSections = SECTIONS_INI;
+ pProfile->m_Sections = static_cast<osl_TProfileSection *>(calloc(pProfile->m_MaxSections, sizeof(osl_TProfileSection)));
+ }
+ else
+ {
+ unsigned int idx=0;
+ unsigned int oldmax=pProfile->m_MaxSections;
+
+ pProfile->m_MaxSections += SECTIONS_ADD;
+ pProfile->m_Sections = static_cast<osl_TProfileSection *>(realloc(pProfile->m_Sections,
+ pProfile->m_MaxSections * sizeof(osl_TProfileSection)));
+ for ( idx = oldmax ; idx < pProfile->m_MaxSections ; ++idx )
+ {
+ pProfile->m_Sections[idx].m_Entries=nullptr;
+ }
+ }
+
+ if (pProfile->m_Sections == nullptr)
+ {
+ pProfile->m_NoSections = 0;
+ pProfile->m_MaxSections = 0;
+ return false;
+ }
+ }
+
+ pProfile->m_NoSections++;
+
+ if ( pProfile->m_Sections[(pProfile->m_NoSections) - 1].m_Entries != nullptr )
+ {
+ free(pProfile->m_Sections[(pProfile->m_NoSections) - 1].m_Entries);
+ }
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Entries = nullptr;
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_NoEntries = 0;
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_MaxEntries = 0;
+
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Line = Line;
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Offset = Section - pProfile->m_Lines[Line];
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Len = Len;
+
+ return true;
+}
+
+static void removeSection(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection)
+{
+ sal_uInt32 Section;
+
+ if ((Section = pSection - pProfile->m_Sections) >= pProfile->m_NoSections)
+ return;
+
+ free (pSection->m_Entries);
+ pSection->m_Entries=nullptr;
+ if (pProfile->m_NoSections - Section > 1)
+ {
+ memmove(&pProfile->m_Sections[Section], &pProfile->m_Sections[Section + 1],
+ (pProfile->m_NoSections - Section - 1) * sizeof(osl_TProfileSection));
+
+ memset(&pProfile->m_Sections[pProfile->m_NoSections - 1],
+ 0,
+ (pProfile->m_MaxSections - pProfile->m_NoSections) * sizeof(osl_TProfileSection));
+ pProfile->m_Sections[pProfile->m_NoSections - 1].m_Entries = nullptr;
+ }
+ else
+ {
+ pSection->m_Entries = nullptr;
+ }
+
+ pProfile->m_NoSections--;
+}
+
+static osl_TProfileSection* findEntry(osl_TProfileImpl* pProfile,
+ const char* Section,
+ const char* Entry,
+ sal_uInt32 *pNoEntry)
+{
+ static sal_uInt32 Sect = 0;
+ sal_uInt32 i, n;
+ sal_uInt32 Len;
+ osl_TProfileSection* pSec=nullptr;
+
+ Len = strlen(Section);
+
+ n = Sect;
+
+ for (i = 0; i < pProfile->m_NoSections; i++)
+ {
+ n %= pProfile->m_NoSections;
+ pSec = &pProfile->m_Sections[n];
+ if ((Len == pSec->m_Len) &&
+ (strncasecmp(Section, &pProfile->m_Lines[pSec->m_Line][pSec->m_Offset], pSec->m_Len)
+ == 0))
+ break;
+ n++;
+ }
+
+ Sect = n;
+
+ if (i < pProfile->m_NoSections)
+ {
+ Len = strlen(Entry);
+
+ *pNoEntry = pSec->m_NoEntries;
+
+ for (i = 0; i < pSec->m_NoEntries; i++)
+ {
+ const char* pStr = &pProfile->m_Lines[pSec->m_Entries[i].m_Line]
+ [pSec->m_Entries[i].m_Offset];
+ if ((Len == pSec->m_Entries[i].m_Len) &&
+ (strncasecmp(Entry, pStr, pSec->m_Entries[i].m_Len)
+ == 0))
+ {
+ *pNoEntry = i;
+ break;
+ }
+ }
+ }
+ else
+ pSec = nullptr;
+
+ return pSec;
+}
+
+static bool loadProfile(osl_TFile* pFile, osl_TProfileImpl* pProfile)
+{
+ sal_uInt32 i;
+ char* pStr;
+ char* pChar;
+
+ char* pLine;
+
+ if ( !pFile )
+ {
+ return false;
+ }
+
+ if ( !pProfile )
+ {
+ return false;
+ }
+
+ pProfile->m_NoLines = 0;
+ pProfile->m_NoSections = 0;
+
+ OSL_VERIFY(OslProfile_rewindFile(pFile, false));
+
+ while ( ( pLine=OslProfile_getLine(pFile) ) != nullptr )
+ {
+ char* bWasAdded = addLine( pProfile, pLine );
+ free( pLine );
+ SAL_WARN_IF(!bWasAdded, "sal.osl", "addLine( pProfile, pLine ) ==> false");
+ if ( ! bWasAdded )
+ return false;
+ }
+
+ for (i = 0; i < pProfile->m_NoLines; i++)
+ {
+ pStr = stripBlanks(pProfile->m_Lines[i], nullptr);
+
+ if ((*pStr == '\0') || (*pStr == ';'))
+ continue;
+
+ if ((*pStr != '[') || ((pChar = strrchr(pStr, ']')) == nullptr) ||
+ ((pChar - pStr) <= 2))
+ {
+ /* insert entry */
+
+ if (pProfile->m_NoSections < 1)
+ continue;
+
+ if ((pChar = strchr(pStr, '=')) == nullptr)
+ pChar = pStr + strlen(pStr);
+
+ if (! addEntry(pProfile, &pProfile->m_Sections[pProfile->m_NoSections - 1],
+ i, pStr, pChar - pStr))
+ {
+ SAL_WARN("sal.osl", "Adding entry => false");
+ continue;
+ }
+
+ }
+ else
+ {
+ /* new section */
+
+ if (! addSection(pProfile, i, pStr + 1, pChar - pStr - 1))
+ {
+ SAL_WARN("sal.osl", "Adding section => false");
+ continue;
+ }
+
+ }
+ }
+
+ return true;
+}
+
+static bool storeProfile(osl_TProfileImpl* pProfile, bool bCleanup)
+{
+ if (pProfile->m_Lines != nullptr)
+ {
+ if (pProfile->m_Flags & FLG_MODIFIED)
+ {
+ sal_uInt32 i;
+
+ osl_TFile* pTmpFile = osl_openTmpProfileImpl(pProfile);
+
+ if ( pTmpFile == nullptr )
+ {
+ return false;
+ }
+
+ OSL_VERIFY(OslProfile_rewindFile(pTmpFile, true));
+
+ for ( i = 0 ; i < pProfile->m_NoLines ; i++ )
+ {
+ OSL_VERIFY(OslProfile_putLine(pTmpFile, pProfile->m_Lines[i]));
+ }
+
+ if ( ! writeProfileImpl(pTmpFile) )
+ {
+ if ( pTmpFile->m_pWriteBuf != nullptr )
+ {
+ free(pTmpFile->m_pWriteBuf);
+ }
+
+ pTmpFile->m_pWriteBuf=nullptr;
+ pTmpFile->m_nWriteBufLen=0;
+ pTmpFile->m_nWriteBufFree=0;
+
+ closeFileImpl(pTmpFile,pProfile->m_Flags);
+
+ return false;
+ }
+
+ pProfile->m_Flags &= ~FLG_MODIFIED;
+
+ closeFileImpl(pProfile->m_pFile,pProfile->m_Flags);
+ closeFileImpl(pTmpFile,pProfile->m_Flags);
+
+ osl_ProfileSwapProfileNames(pProfile);
+
+ pProfile->m_pFile = openFileImpl(pProfile->m_FileName,pProfile->m_Flags);
+
+ }
+
+ if (bCleanup)
+ {
+ while (pProfile->m_NoLines > 0)
+ removeLine(pProfile, pProfile->m_NoLines - 1);
+
+ free(pProfile->m_Lines);
+ pProfile->m_Lines = nullptr;
+ pProfile->m_NoLines = 0;
+ pProfile->m_MaxLines = 0;
+
+ while (pProfile->m_NoSections > 0)
+ removeSection(pProfile, &pProfile->m_Sections[pProfile->m_NoSections - 1]);
+
+ free(pProfile->m_Sections);
+ pProfile->m_Sections = nullptr;
+ pProfile->m_NoSections = 0;
+ pProfile->m_MaxSections = 0;
+ }
+ }
+
+ return true;
+}
+
+static osl_TFile* osl_openTmpProfileImpl(osl_TProfileImpl* pProfile)
+{
+ osl_TFile* pFile=nullptr;
+ char const * const pszExtension = "tmp";
+ char pszTmpName[PATH_MAX];
+ oslProfileOption PFlags=0;
+
+ pszTmpName[0] = '\0';
+
+ /* generate tmp profilename */
+ osl_ProfileGenerateExtension(pProfile->m_FileName, pszExtension, pszTmpName, PATH_MAX);
+
+ if ( pszTmpName[0] == 0 )
+ {
+ return nullptr;
+ }
+
+ if ( ! ( pProfile->m_Flags & osl_Profile_READLOCK ) )
+ {
+ PFlags |= osl_Profile_WRITELOCK;
+ }
+
+ /* open this file */
+ pFile = openFileImpl(pszTmpName,pProfile->m_Flags | PFlags);
+
+ /* return new pFile */
+ return pFile;
+}
+
+static bool osl_ProfileSwapProfileNames(osl_TProfileImpl* pProfile)
+{
+ char pszBakFile[PATH_MAX];
+ char pszTmpFile[PATH_MAX];
+
+ pszBakFile[0] = '\0';
+ pszTmpFile[0] = '\0';
+
+ osl_ProfileGenerateExtension(pProfile->m_FileName, "bak", pszBakFile, PATH_MAX);
+ osl_ProfileGenerateExtension(pProfile->m_FileName, "tmp", pszTmpFile, PATH_MAX);
+
+ /* unlink bak */
+ unlink( pszBakFile );
+
+ // Rename ini -> bak, then tmp -> ini:
+ bool result = rename( pProfile->m_FileName, pszBakFile ) == 0;
+ if (!result)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "rename(" << pProfile->m_FileName << "," << pszBakFile << "): " << UnixErrnoString(e));
+ }
+ else
+ {
+ SAL_INFO("sal.file", "rename(" << pProfile->m_FileName << "," << pszBakFile << "): OK");
+ result = rename( pszTmpFile, pProfile->m_FileName ) == 0;
+ if (!result)
+ {
+ int e = errno;
+ SAL_INFO("sal.file", "rename(" << pszTmpFile << "," << pProfile->m_FileName << "): " << UnixErrnoString(e));
+ }
+ else
+ {
+ SAL_INFO("sal.file", "rename(" << pszTmpFile << "," << pProfile->m_FileName << "): OK");
+ }
+ }
+ return result;
+}
+
+static void osl_ProfileGenerateExtension(const char* pszFileName, const char* pszExtension, char* pszTmpName, int BufferMaxLen)
+{
+ char* cursor = pszTmpName;
+ int len;
+
+ /* concatenate filename + "." + extension, limited to the size of the
+ * output buffer; in case of overrun, data is truncated at the end...
+ * and the result is always 0-terminated.
+ */
+ len = strlen(pszFileName);
+ if(len < BufferMaxLen)
+ {
+ memcpy(cursor, pszFileName, len);
+ cursor += len;
+ BufferMaxLen -= len;
+ }
+ else
+ {
+ memcpy(cursor, pszFileName, BufferMaxLen - 1);
+ cursor += BufferMaxLen - 1;
+ BufferMaxLen = 1;
+ }
+ if(BufferMaxLen > 1)
+ {
+ *cursor++ = '.';
+ BufferMaxLen -= 1;
+ }
+ len = strlen(pszExtension);
+ if(len < BufferMaxLen)
+ {
+ memcpy(cursor, pszExtension, len);
+ cursor += len;
+ }
+ else
+ {
+ memcpy(cursor, pszExtension, BufferMaxLen - 1);
+ cursor += BufferMaxLen - 1;
+ }
+ *cursor = 0;
+}
+
+static osl_TProfileImpl* acquireProfile(oslProfile Profile, bool bWriteable)
+{
+ osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
+ oslProfileOption PFlags=0;
+
+ if ( bWriteable )
+ {
+ PFlags = osl_Profile_DEFAULT | osl_Profile_WRITELOCK;
+ }
+ else
+ {
+ PFlags = osl_Profile_DEFAULT;
+ }
+
+ if (pProfile == nullptr)
+ {
+ if ( ( pProfile = static_cast<osl_TProfileImpl*>(osl_openProfile(nullptr, PFlags )) ) != nullptr )
+ {
+ pProfile->m_Flags |= FLG_AUTOOPEN;
+ }
+ }
+ else
+ {
+ if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
+ {
+ if (! (pProfile->m_Flags & (osl_Profile_READLOCK | osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE )))
+ {
+ osl_TStamp Stamp;
+
+ if (! (pProfile->m_pFile = openFileImpl(pProfile->m_FileName, pProfile->m_Flags | PFlags )))
+ return nullptr;
+
+ Stamp = OslProfile_getFileStamp(pProfile->m_pFile);
+
+ if (memcmp(&Stamp, &(pProfile->m_Stamp), sizeof(osl_TStamp)))
+ {
+ pProfile->m_Stamp = Stamp;
+ bool bRet = loadProfile(pProfile->m_pFile, pProfile);
+ SAL_WARN_IF(!bRet, "sal.osl", "loadProfile(pProfile->m_pFile, pProfile) ==> false");
+ }
+ }
+ else
+ {
+ /* A readlock file could not be written */
+ if ((pProfile->m_Flags & osl_Profile_READLOCK) && bWriteable)
+ {
+ return nullptr;
+ }
+ }
+ }
+ }
+
+ return pProfile;
+}
+
+static bool releaseProfile(osl_TProfileImpl* pProfile)
+{
+ if ( pProfile == nullptr )
+ {
+ return false;
+ }
+
+ if (pProfile->m_Flags & FLG_AUTOOPEN)
+ {
+ return osl_closeProfile(static_cast<oslProfile>(pProfile));
+ }
+
+ if (! (pProfile->m_Flags & (osl_Profile_READLOCK | osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE )))
+ {
+ if (pProfile->m_Flags & FLG_MODIFIED)
+ {
+ bool bRet = storeProfile(pProfile, false);
+ SAL_WARN_IF(!bRet, "sal.osl", "storeProfile(pProfile, false) ==> false");
+ }
+
+ closeFileImpl(pProfile->m_pFile,pProfile->m_Flags);
+ pProfile->m_pFile = nullptr;
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/random.cxx b/sal/osl/unx/random.cxx
new file mode 100644
index 000000000..5904e7d57
--- /dev/null
+++ b/sal/osl/unx/random.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "system.hxx"
+#include <oslrandom.h>
+
+#include <assert.h>
+
+int osl_get_system_random_data(char* buffer, size_t desired_len)
+{
+ int fd;
+
+ assert(buffer);
+ fd = open("/dev/urandom", O_RDONLY);
+ if(fd != -1)
+ {
+ while(desired_len)
+ {
+ ssize_t nb_read;
+ if ((nb_read = read(fd, buffer, desired_len)) == -1)
+ {
+ if (errno != EINTR)
+ {
+ close(fd);
+ return false;
+ }
+ }
+ else
+ {
+ buffer += nb_read;
+ desired_len -= nb_read;
+ }
+ }
+ close(fd);
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/readwrite_helper.cxx b/sal/osl/unx/readwrite_helper.cxx
new file mode 100644
index 000000000..49c44c2c0
--- /dev/null
+++ b/sal/osl/unx/readwrite_helper.cxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <limits>
+
+#include "readwrite_helper.hxx"
+
+#include "system.hxx"
+
+namespace {
+
+std::size_t cap_ssize_t(std::size_t value) {
+ return std::min(value, std::size_t(std::numeric_limits<ssize_t>::max()));
+}
+
+}
+
+bool safeWrite(int fd, void* data, std::size_t dataSize)
+{
+ auto nToWrite = dataSize;
+ unsigned char* dataToWrite = static_cast<unsigned char *>(data);
+
+ while ( nToWrite ) {
+ auto nWritten = write(fd, dataToWrite, cap_ssize_t(nToWrite));
+ if ( nWritten < 0 ) {
+ if ( errno == EINTR )
+ continue;
+
+ return false;
+
+ }
+
+ assert(nWritten > 0);
+ nToWrite -= nWritten;
+ dataToWrite += nWritten;
+ }
+
+ return true;
+}
+
+bool safeRead( int fd, void* buffer, std::size_t count )
+{
+ auto nToRead = count;
+ unsigned char* bufferForReading = static_cast<unsigned char *>(buffer);
+
+ while ( nToRead ) {
+ auto nRead = read(fd, bufferForReading, cap_ssize_t(nToRead));
+ if ( nRead < 0 ) {
+ // We were interrupted before reading, retry.
+ if (errno == EINTR)
+ continue;
+
+ return false;
+ }
+
+ // If we reach the EOF, we consider this a partial transfer and thus
+ // an error.
+ if ( nRead == 0 )
+ return false;
+
+ nToRead -= nRead;
+ bufferForReading += nRead;
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/readwrite_helper.hxx b/sal/osl/unx/readwrite_helper.hxx
new file mode 100644
index 000000000..d73ce5645
--- /dev/null
+++ b/sal/osl/unx/readwrite_helper.hxx
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_READWRITE_HELPER_HXX
+#define INCLUDED_SAL_OSL_UNX_READWRITE_HELPER_HXX
+
+#include <sal/config.h>
+
+#include <cstddef>
+
+bool safeWrite( int fd, void* data, std::size_t dataSize );
+
+// This function *will* read |count| bytes from |fd|, busy looping
+// if needed. Don't use it when you don't know if you can request enough
+// data. It will return sal_False for any partial transfer or error.
+bool safeRead( int fd, void* buffer, std::size_t count );
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/salinit.cxx b/sal/osl/unx/salinit.cxx
new file mode 100644
index 000000000..c3ac48c57
--- /dev/null
+++ b/sal/osl/unx/salinit.cxx
@@ -0,0 +1,100 @@
+#include <sal/log.hxx>
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <sal/config.h>
+
+#if defined MACOSX
+#include <algorithm>
+#include <cassert>
+#include <limits>
+#include <unistd.h>
+#include <sys/stat.h>
+#endif
+
+#include <config_global.h>
+#include <osl/process.h>
+#include <sal/main.h>
+#include <sal/types.h>
+
+#include "saltime.hxx"
+#include "soffice.hxx"
+#include <salusesyslog.hxx>
+
+#if HAVE_SYSLOG_H
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#endif
+
+extern "C" {
+
+void sal_detail_initialize(int argc, char ** argv) {
+ if (argc == sal::detail::InitializeSoffice)
+ {
+ sal::detail::setSoffice();
+ return;
+ }
+#if defined MACOSX && !HAVE_FEATURE_MACOSX_SANDBOX
+ // On macOS when not sandboxed, soffice can restart itself via exec (see
+ // restartOnMac in desktop/source/app/app.cxx), which leaves all file
+ // descriptors open, which in turn can have unwanted effects (see
+ // <https://bugs.libreoffice.org/show_bug.cgi?id=50603> "Unable to update
+ // LibreOffice without resetting user profile"). But closing fds in
+ // restartOnMac before calling exec does not work, as additional threads
+ // might still be running then, which can still use those fds and cause
+ // crashes. Therefore, the simplest solution is to close fds at process
+ // start (as early as possible, so that no other threads have been created
+ // yet that might already have opened some fds); this is done for all kinds
+ // of processes here, not just soffice, but hopefully none of our processes
+ // rely on being spawned with certain fds already open. Unfortunately, Mac
+ // macOS appears to have no better interface to close all fds (like
+ // closefrom):
+ long openMax = sysconf(_SC_OPEN_MAX);
+ if (openMax == -1) {
+ // Some random value, but hopefully sysconf never returns -1 anyway:
+ openMax = 1024;
+ }
+ // When LibreOffice restarts itself on macOS 11 beta on arm64, for
+ // some reason sysconf(_SC_OPEN_MAX) returns 0x7FFFFFFFFFFFFFFF,
+ // so use a sanity limit here.
+ for (int fd = 3; fd < std::min(100000l, openMax); ++fd) {
+ struct stat s;
+ if (fstat(fd, &s) != -1 && S_ISREG(s.st_mode))
+ close(fd);
+ }
+#endif
+ sal_initGlobalTimer();
+#if HAVE_SYSLOG_H
+ const char *use_syslog = getenv("SAL_LOG_SYSLOG");
+ sal_use_syslog = use_syslog != nullptr && !strcmp(use_syslog, "1");
+ if (sal_use_syslog)
+ openlog("libreoffice", 0, LOG_USER);
+#endif
+
+ osl_setCommandArgs(argc, argv);
+}
+
+void sal_detail_deinitialize() {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/saltime.hxx b/sal/osl/unx/saltime.hxx
new file mode 100644
index 000000000..03e8047e0
--- /dev/null
+++ b/sal/osl/unx/saltime.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_SALTIME_HXX
+#define INCLUDED_SAL_OSL_UNX_SALTIME_HXX
+
+#include <sal/config.h>
+
+void sal_initGlobalTimer();
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/secimpl.hxx b/sal/osl/unx/secimpl.hxx
new file mode 100644
index 000000000..f93b85f8f
--- /dev/null
+++ b/sal/osl/unx/secimpl.hxx
@@ -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 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_SECIMPL_HXX
+#define INCLUDED_SAL_OSL_UNX_SECIMPL_HXX
+
+#include <pwd.h>
+
+struct oslSecurityImpl {
+ struct passwd m_pPasswd;
+ char m_buffer[1]; /* should be a C99 flexible array member */
+};
+
+bool osl_psz_getUserIdent(oslSecurity Security, char *pszIdent, sal_uInt32 nMax);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/security.cxx b/sal/osl/unx/security.cxx
new file mode 100644
index 000000000..f6fc52ce5
--- /dev/null
+++ b/sal/osl/unx/security.cxx
@@ -0,0 +1,535 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <limits>
+
+#ifdef IOS
+#include <premac.h>
+#import <Foundation/Foundation.h>
+#include <postmac.h>
+#endif
+
+#include "system.hxx"
+
+#include <o3tl/safeint.hxx>
+#include <osl/security.h>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+
+#include <osl/thread.h>
+#include <osl/file.h>
+
+#if defined LINUX || defined __sun
+#include <crypt.h>
+#endif
+
+#if defined HAIKU
+#include <fs_info.h>
+#include <FindDirectory.h>
+#endif
+
+#include "secimpl.hxx"
+
+#ifdef ANDROID
+#define getpwuid_r(uid, pwd, buf, buflen, result) (*(result) = getpwuid(uid), (*(result) ? (memcpy (buf, *(result), sizeof (struct passwd)), 0) : errno))
+#endif
+
+static bool osl_psz_getHomeDir(oslSecurity Security, OString* pszDirectory);
+static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory);
+
+static bool sysconf_SC_GETPW_R_SIZE_MAX(std::size_t * value) {
+#if defined _SC_GETPW_R_SIZE_MAX
+ long m;
+ errno = 0;
+ m = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (m == -1) {
+ /* _SC_GETPW_R_SIZE_MAX has no limit; some platforms like certain
+ FreeBSD versions support sysconf(_SC_GETPW_R_SIZE_MAX) in a broken
+ way and always set EINVAL, so be resilient here: */
+ return false;
+ }
+ SAL_WARN_IF( m < 0 || o3tl::make_unsigned(m) >= std::numeric_limits<std::size_t>::max(), "sal.osl",
+ "m < 0 || (unsigned long) m >= std::numeric_limits<std::size_t>::max()");
+ *value = static_cast<std::size_t>(m);
+ return true;
+#else
+ /* some platforms like macOS 1.3 do not define _SC_GETPW_R_SIZE_MAX: */
+ return false;
+#endif
+}
+
+static oslSecurityImpl * growSecurityImpl(
+ oslSecurityImpl * impl, std::size_t * bufSize)
+{
+ std::size_t n = 0;
+ oslSecurityImpl * p = nullptr;
+ if (impl == nullptr) {
+ if (!sysconf_SC_GETPW_R_SIZE_MAX(&n)) {
+ /* choose something sensible (the callers of growSecurityImpl will
+ detect it if the allocated buffer is too small: */
+ n = 1024;
+ }
+ } else if (*bufSize <= std::numeric_limits<std::size_t>::max() / 2) {
+ n = 2 * *bufSize;
+ }
+ if (n != 0) {
+ if (n <= std::numeric_limits<std::size_t>::max()
+ - offsetof(oslSecurityImpl, m_buffer))
+ {
+ *bufSize = n;
+ n += offsetof(oslSecurityImpl, m_buffer);
+ } else {
+ *bufSize = std::numeric_limits<std::size_t>::max()
+ - offsetof(oslSecurityImpl, m_buffer);
+ n = std::numeric_limits<std::size_t>::max();
+ }
+ p = static_cast<oslSecurityImpl *>(realloc(impl, n));
+ memset (p, 0, n);
+ }
+ if (p == nullptr) {
+ free(impl);
+ }
+ return p;
+}
+
+static void deleteSecurityImpl(oslSecurityImpl * impl) {
+ free(impl);
+}
+
+oslSecurity SAL_CALL osl_getCurrentSecurity()
+{
+ std::size_t n = 0;
+ oslSecurityImpl * p = nullptr;
+ for (;;) {
+ struct passwd * found;
+ p = growSecurityImpl(p, &n);
+ if (p == nullptr) {
+ return nullptr;
+ }
+#if defined(IOS) && defined(X86_64)
+ // getpwuid_r() does not work in the iOS simulator
+ (void) found;
+ char * buffer = p->m_buffer;
+ assert(n >= 100);
+ strcpy(buffer, "mobile");
+ p->m_pPasswd.pw_name = buffer;
+ buffer += strlen(buffer) + 1;
+ strcpy(buffer, "*");
+ p->m_pPasswd.pw_passwd = buffer;
+ buffer += strlen(buffer) + 1;
+ p->m_pPasswd.pw_uid = geteuid();
+ p->m_pPasswd.pw_gid = getegid();
+ p->m_pPasswd.pw_change = 0;
+ strcpy(buffer, "");
+ p->m_pPasswd.pw_class = buffer;
+ buffer += strlen(buffer) + 1;
+ strcpy(buffer, "Mobile User");
+ p->m_pPasswd.pw_gecos = buffer;
+ buffer += strlen(buffer) + 1;
+ strcpy(buffer, "/var/mobile"); // ???
+ p->m_pPasswd.pw_dir = buffer;
+ buffer += strlen(buffer) + 1;
+ strcpy(buffer, "");
+ p->m_pPasswd.pw_shell = buffer;
+ buffer += strlen(buffer) + 1;
+ p->m_pPasswd.pw_expire = 0;
+ return p;
+#else
+ switch (getpwuid_r(getuid(), &p->m_pPasswd, p->m_buffer, n, &found)) {
+ case ERANGE:
+ break;
+ case 0:
+ if (found != nullptr) {
+ return p;
+ }
+ [[fallthrough]];
+ default:
+ deleteSecurityImpl(p);
+ return nullptr;
+ }
+#endif
+ }
+}
+
+oslSecurityError SAL_CALL osl_loginUser(
+ SAL_UNUSED_PARAMETER rtl_uString *,
+ SAL_UNUSED_PARAMETER rtl_uString *,
+ SAL_UNUSED_PARAMETER oslSecurity *
+ )
+{
+ return osl_Security_E_None;
+}
+
+oslSecurityError SAL_CALL osl_loginUserOnFileServer(
+ SAL_UNUSED_PARAMETER rtl_uString *,
+ SAL_UNUSED_PARAMETER rtl_uString *,
+ SAL_UNUSED_PARAMETER rtl_uString *,
+ SAL_UNUSED_PARAMETER oslSecurity *
+ )
+{
+ return osl_Security_E_UserUnknown;
+}
+
+sal_Bool SAL_CALL osl_getUserIdent(oslSecurity Security, rtl_uString **ustrIdent)
+{
+ bool bRet = false;
+ char pszIdent[1024];
+
+ pszIdent[0] = '\0';
+
+ bRet = osl_psz_getUserIdent(Security,pszIdent,sizeof(pszIdent));
+
+ rtl_string2UString( ustrIdent, pszIdent, rtl_str_getLength( pszIdent ), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
+ SAL_WARN_IF(*ustrIdent == nullptr, "sal.osl", "*ustrIdent == NULL");
+
+ return bRet;
+}
+
+bool osl_psz_getUserIdent(oslSecurity Security, char *pszIdent, sal_uInt32 nMax)
+{
+ char buffer[32];
+ sal_Int32 nChr;
+
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
+
+ if (pSecImpl == nullptr)
+ return false;
+
+ nChr = snprintf(buffer, sizeof(buffer), "%u", pSecImpl->m_pPasswd.pw_uid);
+ if ( nChr < 0 || sal::static_int_cast<sal_uInt32>(nChr) >= sizeof(buffer)
+ || sal::static_int_cast<sal_uInt32>(nChr) >= nMax )
+ return false; /* leave *pszIdent unmodified in case of failure */
+
+ memcpy(pszIdent, buffer, nChr+1);
+ return true;
+}
+
+sal_Bool SAL_CALL osl_getUserName(oslSecurity Security, rtl_uString **ustrName)
+{
+ bool bRet = false;
+ char * pszName;
+ sal_Int32 len;
+
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
+
+ if (pSecImpl != nullptr && pSecImpl->m_pPasswd.pw_name != nullptr) {
+ pszName = pSecImpl->m_pPasswd.pw_name;
+ auto const n = std::strlen(pszName);
+ if (n <= o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
+ len = n;
+ bRet = true;
+ }
+ }
+
+ if (!bRet) {
+ pszName = nullptr;
+ len = 0;
+ }
+
+ rtl_string2UString( ustrName, pszName, len, osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
+ SAL_WARN_IF(*ustrName == nullptr, "sal.osl", "ustrName == NULL");
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL osl_getShortUserName(oslSecurity Security, rtl_uString **ustrName)
+{
+ return osl_getUserName(Security, ustrName); // No domain name on unix
+}
+
+sal_Bool SAL_CALL osl_getHomeDir(oslSecurity Security, rtl_uString **pustrDirectory)
+{
+ bool bRet = false;
+ OString pszDirectory;
+
+ bRet = osl_psz_getHomeDir(Security,&pszDirectory);
+
+ if ( bRet )
+ {
+ rtl_string2UString( pustrDirectory, pszDirectory.getStr(), pszDirectory.getLength(), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
+ SAL_WARN_IF(*pustrDirectory == nullptr, "sal.osl", "*pustrDirectory == NULL");
+ osl_getFileURLFromSystemPath( *pustrDirectory, pustrDirectory );
+ }
+
+ return bRet;
+}
+
+static bool osl_psz_getHomeDir(oslSecurity Security, OString* pszDirectory)
+{
+ assert(pszDirectory != nullptr);
+
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
+
+ if (pSecImpl == nullptr)
+ return false;
+
+#ifdef HAIKU
+ dev_t volume = dev_for_path("/boot");
+ char homeDir[B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH];
+ status_t result = find_directory(B_USER_DIRECTORY, volume, false, homeDir,
+ sizeof(homeDir));
+ if (result == B_OK) {
+ static_assert(
+ B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH <= std::numeric_limits<sal_Int32>::max());
+ *pszDirectory = OString(homeDir, std::strlen(homeDir));
+ return true;
+ }
+ return false;
+#endif
+
+#ifdef ANDROID
+{
+ OUString pValue;
+
+ if (rtl::Bootstrap::get("HOME", pValue))
+ {
+ auto const pStrValue = OUStringToOString(pValue, RTL_TEXTENCODING_UTF8);
+ if (!pStrValue.isEmpty())
+ {
+ *pszDirectory = pStrValue;
+ return true;
+ }
+ }
+}
+#endif
+
+#ifdef IOS
+ {
+ // Let's pretend the app-specific "Documents" directory is the home directory for now
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *userDirectory = [paths objectAtIndex:0];
+ auto const len = [userDirectory length];
+ if (len <= std::numeric_limits<sal_Int32>::max())
+ {
+ *pszDirectory = OString([userDirectory UTF8String], len);
+ return sal_True;
+ }
+ }
+#endif
+
+ /* if current user, check also environment for HOME */
+ if (getuid() == pSecImpl->m_pPasswd.pw_uid)
+ {
+ char *pStr = nullptr;
+#ifdef __sun
+ char buffer[8192];
+
+ struct passwd pwd;
+ struct passwd *ppwd;
+
+#ifdef _POSIX_PTHREAD_SEMANTICS
+ if ( 0 != getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &ppwd ) )
+ ppwd = NULL;
+#else
+ ppwd = getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer) );
+#endif
+
+ if ( ppwd )
+ pStr = ppwd->pw_dir;
+#else
+ pStr = getenv("HOME");
+#endif
+
+ if (pStr != nullptr && pStr[0] != '\0' && access(pStr, 0) == 0)
+ {
+ auto const len = std::strlen(pStr);
+ if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
+ return false;
+ }
+ *pszDirectory = OString(pStr, len);
+ return true;
+ }
+ }
+ if (pSecImpl->m_pPasswd.pw_dir != nullptr)
+ {
+ auto const len = std::strlen(pSecImpl->m_pPasswd.pw_dir);
+ if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
+ return false;
+ }
+ *pszDirectory = OString(pSecImpl->m_pPasswd.pw_dir, len);
+ }
+ else
+ return false;
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_getConfigDir(oslSecurity Security, rtl_uString **pustrDirectory)
+{
+ bool bRet = false;
+ OString pszDirectory;
+
+ bRet = osl_psz_getConfigDir(Security,&pszDirectory);
+
+ if ( bRet )
+ {
+ rtl_string2UString( pustrDirectory, pszDirectory.getStr(), pszDirectory.getLength(), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
+ SAL_WARN_IF(*pustrDirectory == nullptr, "sal.osl", "*pustrDirectory == NULL");
+ osl_getFileURLFromSystemPath( *pustrDirectory, pustrDirectory );
+ }
+
+ return bRet;
+}
+
+#if defined HAIKU
+
+static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
+{
+ assert(pszDirectory != nullptr);
+ (void) Security;
+ dev_t volume = dev_for_path("/boot");
+ char configDir[B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH];
+ status_t result = find_directory(B_USER_SETTINGS_DIRECTORY, volume, false,
+ configDir, sizeof(configDir));
+ if (result == B_OK) {
+ auto const len = strlen(configDir);
+ if (len <= sal_uInt32(std::numeric_limits<sal_Int32>::max())) {
+ *pszDirectory = OString(configDir, len);
+ return true;
+ }
+ }
+ return false;
+}
+
+#elif !defined(MACOSX) && !defined(IOS)
+
+static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
+{
+ assert(pszDirectory != nullptr);
+
+ char *pStr = getenv("XDG_CONFIG_HOME");
+
+ if (pStr == nullptr || pStr[0] == '\0' || access(pStr, 0) != 0)
+ {
+ // a default equal to $HOME/.config should be used.
+ OString home;
+ if (!osl_psz_getHomeDir(Security, &home))
+ return false;
+ auto const config = OString(home + "/.config");
+
+ // try to create dir if not present
+ bool dirOK = true;
+ if (mkdir(config.getStr(), S_IRWXU) != 0)
+ {
+ int e = errno;
+ if (e != EEXIST)
+ {
+ SAL_WARN(
+ "sal.osl",
+ "mkdir(" << config << "): errno=" << e);
+ dirOK = false;
+ }
+ }
+ if (dirOK)
+ {
+ // check file type and permissions
+ struct stat st;
+ if (stat(config.getStr(), &st) != 0)
+ {
+ SAL_INFO("sal.osl","Could not stat $HOME/.config");
+ dirOK = false;
+ }
+ else
+ {
+ if (!S_ISDIR(st.st_mode))
+ {
+ SAL_INFO("sal.osl", "$HOME/.config is not a directory");
+ dirOK = false;
+ }
+ if (!(st.st_mode & S_IRUSR && st.st_mode & S_IWUSR && st.st_mode & S_IXUSR))
+ {
+ SAL_INFO("sal.osl", "$HOME/.config has bad permissions");
+ dirOK = false;
+ }
+ }
+ }
+
+ // if !dirOK, resort to HOME
+ if (dirOK)
+ home = config;
+ *pszDirectory = home;
+ }
+ else
+ {
+ auto const len = std::strlen(pStr);
+ if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
+ return false;
+ }
+ *pszDirectory = OString(pStr, len);
+ }
+
+ return true;
+}
+
+#else
+
+/*
+ * FIXME: rewrite to use more flexible
+ * NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)
+ * as soon as we can bump the baseline to Tiger (for NSApplicationSupportDirectory) and have
+ * support for Objective-C in the build environment
+ */
+
+static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
+{
+ assert(pszDirectory != nullptr);
+
+ OString home;
+ if( osl_psz_getHomeDir(Security, &home) )
+ {
+ *pszDirectory = home + "/Library/Application Support"; /* Used on iOS, too */
+ return true;
+ }
+
+ return false;
+}
+
+#endif
+
+sal_Bool SAL_CALL osl_isAdministrator(oslSecurity Security)
+{
+ oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
+
+ if (pSecImpl == nullptr)
+ return false;
+
+ if (pSecImpl->m_pPasswd.pw_uid != 0)
+ return false;
+
+ return true;
+}
+
+void SAL_CALL osl_freeSecurityHandle(oslSecurity Security)
+{
+ deleteSecurityImpl(static_cast<oslSecurityImpl *>(Security));
+}
+
+sal_Bool SAL_CALL osl_loadUserProfile(SAL_UNUSED_PARAMETER oslSecurity)
+{
+ return false;
+}
+
+void SAL_CALL osl_unloadUserProfile(SAL_UNUSED_PARAMETER oslSecurity) {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/signal.cxx b/sal/osl/unx/signal.cxx
new file mode 100644
index 000000000..79721def6
--- /dev/null
+++ b/sal/osl/unx/signal.cxx
@@ -0,0 +1,478 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <signalshared.hxx>
+
+#include <config_features.h>
+
+#include "soffice.hxx"
+/* system headers */
+#include "system.hxx"
+
+#if defined( MACOSX )
+
+#if defined( INTEL )
+#include "backtrace.h"
+#define INCLUDE_BACKTRACE
+#endif /* INTEL */
+
+#endif /* MACOSX */
+
+#ifdef LINUX
+#include <execinfo.h>
+#include <link.h>
+#define INCLUDE_BACKTRACE
+#endif
+
+#ifdef __sun
+
+#include "backtrace.h"
+#define INCLUDE_BACKTRACE
+
+#endif /* defined __sun */
+
+#if defined INCLUDE_BACKTRACE
+#define MAX_STACK_FRAMES 256
+#endif
+
+#include <osl/diagnose.h>
+#include <osl/signal.h>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#include <rtl/bootstrap.h>
+#include <rtl/digest.h>
+
+#include "file_path_helper.hxx"
+#define ACT_IGNORE 1
+#define ACT_EXIT 2
+#define ACT_SYSTEM 3
+#define ACT_HIDE 4
+#define ACT_ABORT 5
+
+#if defined HAVE_VALGRIND_HEADERS
+#include <valgrind/memcheck.h>
+#endif
+
+namespace
+{
+extern "C" using Handler1 = void (*)(int);
+extern "C" using Handler2 = void (*)(int, siginfo_t *, void *);
+struct SignalAction
+{
+ int Signal;
+ int Action;
+ Handler1 Handler;
+ bool siginfo; // Handler's type is Handler2
+} Signals[] =
+{
+ { SIGHUP, ACT_HIDE, SIG_DFL, false }, /* hangup */
+ { SIGINT, ACT_EXIT, SIG_DFL, false }, /* interrupt (rubout) */
+ { SIGQUIT, ACT_EXIT, SIG_DFL, false }, /* quit (ASCII FS) */
+ { SIGILL, ACT_SYSTEM, SIG_DFL, false }, /* illegal instruction (not reset when caught) */
+/* changed from ACT_ABOUT to ACT_SYSTEM to try and get collector to run*/
+ { SIGTRAP, ACT_ABORT, SIG_DFL, false }, /* trace trap (not reset when caught) */
+#if ( SIGIOT != SIGABRT )
+ { SIGIOT, ACT_ABORT, SIG_DFL, false }, /* IOT instruction */
+#endif
+#if defined(FORCE_DEFAULT_SIGNAL)
+ { SIGABRT, ACT_SYSTEM, SIG_DFL, false }, /* used by abort, replace SIGIOT in the future */
+#else
+ { SIGABRT, ACT_ABORT, SIG_DFL, false }, /* used by abort, replace SIGIOT in the future */
+#endif
+#ifdef SIGEMT
+ { SIGEMT, ACT_SYSTEM, SIG_DFL, false }, /* EMT instruction */
+/* changed from ACT_ABORT to ACT_SYSTEM to remove handler*/
+/* SIGEMT may also be used by the profiler - so it is probably not a good
+plan to have the new handler use this signal*/
+#endif
+ { SIGFPE, ACT_ABORT, SIG_DFL, false }, /* floating point exception */
+ { SIGKILL, ACT_SYSTEM, SIG_DFL, false }, /* kill (cannot be caught or ignored) */
+ { SIGBUS, ACT_ABORT, SIG_DFL, false }, /* bus error */
+#if defined(FORCE_DEFAULT_SIGNAL)
+ { SIGSEGV, ACT_SYSTEM, SIG_DFL, false }, /* segmentation violation */
+#else
+ { SIGSEGV, ACT_ABORT, SIG_DFL, false }, /* segmentation violation */
+#endif
+#ifdef SIGSYS
+ { SIGSYS, ACT_ABORT, SIG_DFL, false }, /* bad argument to system call */
+#endif
+ { SIGPIPE, ACT_HIDE, SIG_DFL, false }, /* write on a pipe with no one to read it */
+#if defined(FORCE_DEFAULT_SIGNAL)
+ { SIGALRM, ACT_SYSTEM, SIG_DFL, false }, /* alarm clock */
+#else
+ { SIGALRM, ACT_EXIT, SIG_DFL, false }, /* alarm clock */
+#endif
+ { SIGTERM, ACT_EXIT, SIG_DFL, false }, /* software termination signal from kill */
+ { SIGUSR1, ACT_SYSTEM, SIG_DFL, false }, /* user defined signal 1 */
+ { SIGUSR2, ACT_SYSTEM, SIG_DFL, false }, /* user defined signal 2 */
+ { SIGCHLD, ACT_SYSTEM, SIG_DFL, false }, /* child status change */
+#ifdef SIGPWR
+ { SIGPWR, ACT_IGNORE, SIG_DFL, false }, /* power-fail restart */
+#endif
+ { SIGWINCH, ACT_IGNORE, SIG_DFL, false }, /* window size change */
+ { SIGURG, ACT_EXIT, SIG_DFL, false }, /* urgent socket condition */
+#ifdef SIGPOLL
+ { SIGPOLL, ACT_EXIT, SIG_DFL, false }, /* pollable event occurred */
+#endif
+ { SIGSTOP, ACT_SYSTEM, SIG_DFL, false }, /* stop (cannot be caught or ignored) */
+ { SIGTSTP, ACT_SYSTEM, SIG_DFL, false }, /* user stop requested from tty */
+ { SIGCONT, ACT_SYSTEM, SIG_DFL, false }, /* stopped process has been continued */
+ { SIGTTIN, ACT_SYSTEM, SIG_DFL, false }, /* background tty read attempted */
+ { SIGTTOU, ACT_SYSTEM, SIG_DFL, false }, /* background tty write attempted */
+ { SIGVTALRM, ACT_EXIT, SIG_DFL, false }, /* virtual timer expired */
+ { SIGPROF, ACT_SYSTEM, SIG_DFL, false }, /* profiling timer expired */
+/*Change from ACT_EXIT to ACT_SYSTEM for SIGPROF is so that profiling signals do
+not get taken by the new handler - the new handler does not pass on context
+information which causes 'collect' to crash. This is a way of avoiding
+what looks like a bug in the new handler*/
+ { SIGXCPU, ACT_ABORT, SIG_DFL, false }, /* exceeded cpu limit */
+ { SIGXFSZ, ACT_ABORT, SIG_DFL, false } /* exceeded file size limit */
+};
+const int NoSignals = SAL_N_ELEMENTS(Signals);
+
+bool bSetSEGVHandler = false;
+bool bSetWINCHHandler = false;
+bool bSetILLHandler = false;
+
+void signalHandlerFunction(int, siginfo_t *, void *);
+
+#if HAVE_FEATURE_BREAKPAD
+bool is_unset_signal(int signal)
+{
+#ifdef DBG_UTIL
+ return (!bSetSEGVHandler && signal == SIGSEGV) ||
+ (!bSetWINCHHandler && signal == SIGWINCH) ||
+ (!bSetILLHandler && signal == SIGILL);
+#else
+ (void) signal;
+ return false;
+#endif
+}
+#endif
+
+}
+
+bool onInitSignal()
+{
+ if (sal::detail::isSoffice())
+ {
+ // WORKAROUND FOR SEGV HANDLER CONFLICT
+ //
+ // the java jit needs SIGSEGV for proper work
+ // and we need SIGSEGV for the office crashguard
+ //
+ // TEMPORARY SOLUTION:
+ // the office sets the signal handler during startup
+ // java can than overwrite it, if needed
+ bSetSEGVHandler = true;
+
+ // WORKAROUND FOR WINCH HANDLER (SEE ABOVE)
+ bSetWINCHHandler = true;
+
+ // WORKAROUND FOR ILLEGAL INSTRUCTION HANDLER (SEE ABOVE)
+ bSetILLHandler = true;
+ }
+
+#ifdef DBG_UTIL
+ bSetSEGVHandler = bSetWINCHHandler = bSetILLHandler = false;
+#endif
+
+ struct sigaction act;
+ act.sa_sigaction = signalHandlerFunction;
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
+
+ sigfillset(&(act.sa_mask));
+
+ /* Initialize the rest of the signals */
+ for (SignalAction & rSignal : Signals)
+ {
+#if defined HAVE_VALGRIND_HEADERS
+ if (rSignal.Signal == SIGUSR2 && RUNNING_ON_VALGRIND)
+ rSignal.Action = ACT_IGNORE;
+#endif
+
+ /* hack: stomcatd is attaching JavaVM which does not work with an sigaction(SEGV) */
+ if ((bSetSEGVHandler || rSignal.Signal != SIGSEGV)
+ && (bSetWINCHHandler || rSignal.Signal != SIGWINCH)
+ && (bSetILLHandler || rSignal.Signal != SIGILL))
+ {
+ if (rSignal.Action != ACT_SYSTEM)
+ {
+ if (rSignal.Action == ACT_HIDE)
+ {
+ struct sigaction ign;
+
+ ign.sa_handler = SIG_IGN;
+ ign.sa_flags = 0;
+ sigemptyset(&ign.sa_mask);
+
+ struct sigaction oact;
+ if (sigaction(rSignal.Signal, &ign, &oact) == 0) {
+ rSignal.siginfo = (oact.sa_flags & SA_SIGINFO) != 0;
+ if (rSignal.siginfo) {
+ rSignal.Handler = reinterpret_cast<Handler1>(
+ oact.sa_sigaction);
+ } else {
+ rSignal.Handler = oact.sa_handler;
+ }
+ } else {
+ rSignal.Handler = SIG_DFL;
+ rSignal.siginfo = false;
+ }
+ }
+ else
+ {
+ struct sigaction oact;
+ if (sigaction(rSignal.Signal, &act, &oact) == 0) {
+ rSignal.siginfo = (oact.sa_flags & SA_SIGINFO) != 0;
+ if (rSignal.siginfo) {
+ rSignal.Handler = reinterpret_cast<Handler1>(
+ oact.sa_sigaction);
+ } else {
+ rSignal.Handler = oact.sa_handler;
+ }
+ } else {
+ rSignal.Handler = SIG_DFL;
+ rSignal.siginfo = false;
+ }
+ }
+ }
+ }
+ }
+
+ /* Clear signal mask inherited from parent process (on macOS, upon a
+ crash soffice re-execs itself from within the signal handler, so the
+ second soffice would have the guilty signal blocked and would freeze upon
+ encountering a similar crash again): */
+ sigset_t unset;
+ if (sigemptyset(&unset) < 0 ||
+ pthread_sigmask(SIG_SETMASK, &unset, nullptr) < 0)
+ {
+ SAL_WARN("sal.osl", "sigemptyset or pthread_sigmask failed");
+ }
+
+ return true;
+}
+
+bool onDeInitSignal()
+{
+ struct sigaction act;
+
+ sigemptyset(&(act.sa_mask));
+
+ /* Initialize the rest of the signals */
+ for (int i = NoSignals - 1; i >= 0; i--)
+ if (Signals[i].Action != ACT_SYSTEM
+ && ((bSetSEGVHandler || Signals[i].Signal != SIGSEGV)
+ && (bSetWINCHHandler || Signals[i].Signal != SIGWINCH)
+ && (bSetILLHandler || Signals[i].Signal != SIGILL)))
+ {
+ if (Signals[i].siginfo) {
+ act.sa_sigaction = reinterpret_cast<Handler2>(
+ Signals[i].Handler);
+ act.sa_flags = SA_SIGINFO;
+ } else {
+ act.sa_handler = Signals[i].Handler;
+ act.sa_flags = 0;
+ }
+
+ sigaction(Signals[i].Signal, &act, nullptr);
+ }
+
+ return false;
+}
+
+namespace
+{
+void printStack(int sig)
+{
+#ifdef INCLUDE_BACKTRACE
+ void *buffer[MAX_STACK_FRAMES];
+ int size = backtrace( buffer, SAL_N_ELEMENTS(buffer) );
+#endif
+
+ fprintf( stderr, "\n\nFatal exception: Signal %d\n", sig );
+
+#if defined( MACOSX ) && !defined( INCLUDE_BACKTRACE )
+ fprintf( stderr, "Please turn on Enable Crash Reporting and\nAutomatic Display of Crashlogs in the Console application\n" );
+#else
+#ifdef INCLUDE_BACKTRACE
+ if ( size > 0 )
+ {
+ fputs( "Stack:\n", stderr );
+ backtrace_symbols_fd( buffer, size, fileno(stderr) );
+ }
+#endif
+#endif
+}
+
+void callSystemHandler(int signal, siginfo_t * info, void * context)
+{
+ int i;
+
+ for (i = 0; i < NoSignals; i++)
+ {
+ if (Signals[i].Signal == signal)
+ break;
+ }
+
+ if (i >= NoSignals)
+ return;
+
+ if ((Signals[i].Handler == SIG_DFL) ||
+ (Signals[i].Handler == SIG_IGN) ||
+ (Signals[i].Handler == SIG_ERR))
+ {
+ switch (Signals[i].Action)
+ {
+ case ACT_EXIT: /* terminate */
+ /* prevent dumping core on exit() */
+ _exit(255);
+ break;
+
+ case ACT_ABORT: /* terminate with core dump */
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = 0;
+ sigemptyset(&(act.sa_mask));
+ sigaction(SIGABRT, &act, nullptr);
+ printStack( signal );
+ abort();
+ break;
+
+ case ACT_IGNORE: /* ignore */
+ break;
+
+ default: /* should never happen */
+ OSL_ASSERT(false);
+ }
+ }
+ else if (Signals[i].siginfo) {
+ (*reinterpret_cast<Handler2>(Signals[i].Handler))(
+ signal, info, context);
+ } else {
+ (*Signals[i].Handler)(signal);
+ }
+}
+
+#if defined HAVE_VALGRIND_HEADERS
+void DUMPCURRENTALLOCS()
+{
+ VALGRIND_PRINTF( "=== start memcheck dump of active allocations ===\n" );
+
+#if __GNUC__ && !defined(__clang__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
+
+ VALGRIND_DO_LEAK_CHECK;
+
+#if __GNUC__ && !defined(__clang__)
+# pragma GCC diagnostic pop
+#endif
+
+ VALGRIND_PRINTF( "=== end memcheck dump of active allocations ===\n" );
+}
+#endif
+
+void signalHandlerFunction(int signal, siginfo_t * info, void * context)
+{
+ oslSignalInfo Info;
+
+ Info.UserSignal = signal;
+ Info.UserData = nullptr;
+
+ switch (signal)
+ {
+ case SIGBUS:
+ case SIGILL:
+ case SIGSEGV:
+ case SIGIOT:
+#if ( SIGIOT != SIGABRT )
+ case SIGABRT:
+#endif
+ Info.Signal = osl_Signal_AccessViolation;
+ break;
+
+ case -1:
+ Info.Signal = osl_Signal_IntegerDivideByZero;
+ break;
+
+ case SIGFPE:
+ Info.Signal = osl_Signal_FloatDivideByZero;
+ break;
+
+ case SIGINT:
+ case SIGTERM:
+ case SIGQUIT:
+ Info.Signal = osl_Signal_Terminate;
+ break;
+
+#if defined HAVE_VALGRIND_HEADERS
+ case SIGUSR2:
+ if (RUNNING_ON_VALGRIND)
+ DUMPCURRENTALLOCS();
+ Info.Signal = osl_Signal_System;
+ break;
+#endif
+
+ default:
+ Info.Signal = osl_Signal_System;
+ break;
+ }
+
+#if HAVE_FEATURE_BREAKPAD
+ if ((Info.Signal == osl_Signal_AccessViolation ||
+ Info.Signal == osl_Signal_IntegerDivideByZero ||
+ Info.Signal == osl_Signal_FloatDivideByZero) && !is_unset_signal(signal))
+ {
+ callSystemHandler(signal, info, context);
+ }
+#endif
+
+ switch (callSignalHandler(&Info))
+ {
+ case osl_Signal_ActCallNextHdl:
+ callSystemHandler(signal, info, context);
+ break;
+
+ case osl_Signal_ActAbortApp:
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = 0;
+ sigemptyset(&(act.sa_mask));
+ sigaction(SIGABRT, &act, nullptr);
+ printStack( signal );
+ abort();
+ break;
+
+ case osl_Signal_ActKillApp:
+ /* prevent dumping core on exit() */
+ _exit(255);
+ break;
+ default:
+ break;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/socket.cxx b/sal/osl/unx/socket.cxx
new file mode 100644
index 000000000..4f67795a6
--- /dev/null
+++ b/sal/osl/unx/socket.cxx
@@ -0,0 +1,2240 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <utility>
+
+#include "system.hxx"
+
+#include <osl/socket.h>
+#include <osl/mutex.h>
+#include <osl/signal.h>
+
+#include <rtl/alloc.h>
+#include <rtl/byteseq.h>
+#include <rtl/ustring.hxx>
+#include <assert.h>
+#include <sal/types.h>
+#include <sal/log.hxx>
+
+#include "sockimpl.hxx"
+#include "unixerrnostring.hxx"
+
+/* defines for poll */
+#ifdef HAVE_POLL_H
+#undef HAVE_POLL_H
+#endif
+
+#if defined(LINUX) || defined(NETBSD) || defined ( FREEBSD ) || \
+ defined (MACOSX) || defined (OPENBSD) || defined(DRAGONFLY)
+#include <sys/poll.h>
+#define HAVE_POLL_H
+#endif /* HAVE_POLL_H */
+
+#if defined(__sun)
+#include <poll.h>
+#define HAVE_POLL_H
+#endif /* __sun */
+
+#ifndef HAVE_POLL_H
+#define POLLIN 0x0001
+#define POLLOUT 0x0002
+#define POLLPRI 0x0004
+#endif /* HAVE_POLL_H */
+
+/* defines for shutdown */
+#define SD_RECEIVE 0
+#define SD_SEND 1
+#define SD_BOTH 2
+
+/*
+ oslSocketAddr is a pointer to a Berkeley struct sockaddr.
+ I refrained from using sockaddr_in because of possible further
+ extensions of this socket-interface (IP-NG?).
+ The intention was to hide all Berkeley data-structures from
+ direct access past the osl-interface.
+
+ The current implementation is internet (IP) centered. All
+ the constructor-functions (osl_create...) take parameters
+ that will probably make sense only in the IP-environment
+ (e.g. because of using the dotted-address-format).
+
+ If the interface will be extended to host other protocol-
+ families, I expect no externally visible changes in the
+ existing functions. You'll probably need only new
+ constructor-functions who take the different address
+ formats into consideration (maybe a long dotted address
+ or whatever).
+*/
+
+/* _Note_ that I rely on the fact that oslSocketAddr and struct sockaddr */
+/* are the same! I don't like it very much but see no other easy way to */
+/* conceal the struct sockaddr from the eyes of the user. */
+
+#define OSL_INVALID_SOCKET -1
+#define OSL_SOCKET_ERROR -1
+
+/* Buffer size for getnameinfo */
+#define MAX_HOSTBUFFER_SIZE 2048
+
+static const unsigned long FamilyMap[]= {
+ AF_INET, /* osl_Socket_FamilyInet */
+ AF_IPX, /* osl_Socket_FamilyIpx */
+ 0 /* osl_Socket_FamilyInvalid */
+};
+
+static oslAddrFamily osl_AddrFamilyFromNative(sal_uInt32 nativeType)
+{
+ oslAddrFamily i= oslAddrFamily(0);
+
+ while(i != osl_Socket_FamilyInvalid)
+ {
+ if(FamilyMap[i] == nativeType)
+ return i;
+ i = static_cast<oslAddrFamily>( i + 1 );
+ }
+
+ return i;
+}
+
+#define FAMILY_FROM_NATIVE(y) osl_AddrFamilyFromNative(y)
+#define FAMILY_TO_NATIVE(x) static_cast<short>(FamilyMap[x])
+
+static const sal_uInt32 ProtocolMap[]= {
+ 0, /* osl_Socket_ProtocolIp */
+ NSPROTO_IPX, /* osl_Socket_ProtocolIpx */
+ NSPROTO_SPX, /* osl_Socket_ProtocolSpx */
+ NSPROTO_SPXII, /* osl_Socket_ProtocolSpxII */
+ 0 /* osl_Socket_ProtocolInvalid */
+};
+
+#define PROTOCOL_TO_NATIVE(x) ProtocolMap[x]
+
+static const sal_uInt32 TypeMap[]= {
+ SOCK_STREAM, /* osl_Socket_TypeStream */
+ SOCK_DGRAM, /* osl_Socket_TypeDgram */
+ SOCK_RAW, /* osl_Socket_TypeRaw */
+ SOCK_RDM, /* osl_Socket_TypeRdm */
+ SOCK_SEQPACKET, /* osl_Socket_TypeSeqPacket */
+ 0 /* osl_Socket_TypeInvalid */
+};
+
+static oslSocketType osl_SocketTypeFromNative(sal_uInt32 nativeType)
+{
+ oslSocketType i= oslSocketType(0);
+
+ while(i != osl_Socket_TypeInvalid)
+ {
+ if(TypeMap[i] == nativeType)
+ return i;
+ i = static_cast<oslSocketType>(i + 1);
+ }
+
+ return i;
+}
+
+#define TYPE_TO_NATIVE(x) TypeMap[x]
+#define TYPE_FROM_NATIVE(y) osl_SocketTypeFromNative(y)
+
+static const sal_uInt32 OptionMap[]= {
+ SO_DEBUG, /* osl_Socket_OptionDebug */
+ SO_ACCEPTCONN, /* osl_Socket_OptionAcceptConn */
+ SO_REUSEADDR, /* osl_Socket_OptionReuseAddr */
+ SO_KEEPALIVE, /* osl_Socket_OptionKeepAlive */
+ SO_DONTROUTE, /* osl_Socket_OptionDontRoute */
+ SO_BROADCAST, /* osl_Socket_OptionBroadcast */
+ SO_USELOOPBACK, /* osl_Socket_OptionUseLoopback */
+ SO_LINGER, /* osl_Socket_OptionLinger */
+ SO_OOBINLINE, /* osl_Socket_OptionOOBinLine */
+ SO_SNDBUF, /* osl_Socket_OptionSndBuf */
+ SO_RCVBUF, /* osl_Socket_OptionRcvBuf */
+ SO_SNDLOWAT, /* osl_Socket_OptionSndLowat */
+ SO_RCVLOWAT, /* osl_Socket_OptionRcvLowat */
+ SO_SNDTIMEO, /* osl_Socket_OptionSndTimeo */
+ SO_RCVTIMEO, /* osl_Socket_OptionRcvTimeo */
+ SO_ERROR, /* osl_Socket_OptionError */
+ SO_TYPE, /* osl_Socket_OptionType */
+ TCP_NODELAY, /* osl_Socket_OptionTcpNoDelay */
+ 0 /* osl_Socket_OptionInvalid */
+};
+
+#define OPTION_TO_NATIVE(x) OptionMap[x]
+
+static const sal_uInt32 OptionLevelMap[]= {
+ SOL_SOCKET, /* osl_Socket_LevelSocket */
+ IPPROTO_TCP, /* osl_Socket_LevelTcp */
+ 0 /* osl_Socket_LevelInvalid */
+};
+
+#define OPTION_LEVEL_TO_NATIVE(x) OptionLevelMap[x]
+
+static const sal_uInt32 SocketMsgFlagMap[]= {
+ 0, /* osl_Socket_MsgNormal */
+ MSG_OOB, /* osl_Socket_MsgOOB */
+ MSG_PEEK, /* osl_Socket_MsgPeek */
+ MSG_DONTROUTE, /* osl_Socket_MsgDontRoute */
+ MSG_MAXIOVLEN, /* osl_Socket_MsgMaxIOVLen */
+ 0 /* osl_Socket_MsgInvalid */
+};
+
+#define MSG_FLAG_TO_NATIVE(x) SocketMsgFlagMap[x]
+
+static const sal_uInt32 SocketDirection[]= {
+ SD_RECEIVE, /* osl_Socket_DirRead */
+ SD_SEND, /* osl_Socket_DirWrite */
+ SD_BOTH, /* osl_Socket_DirReadWrite */
+ 0 /* osl_Socket_DirInvalid */
+};
+
+#define DIRECTION_TO_NATIVE(x) SocketDirection[x]
+
+static const struct
+{
+ int errcode;
+ oslSocketError error;
+} SocketError[]= {
+ { 0, osl_Socket_E_None }, /* no error */
+ { ENOTSOCK, osl_Socket_E_NotSocket }, /* Socket operation on non-socket */
+ { EDESTADDRREQ, osl_Socket_E_DestAddrReq }, /* Destination address required */
+ { EMSGSIZE, osl_Socket_E_MsgSize }, /* Message too long */
+ { EPROTOTYPE, osl_Socket_E_Prototype }, /* Protocol wrong type for socket */
+ { ENOPROTOOPT, osl_Socket_E_NoProtocol }, /* Protocol not available */
+ { EPROTONOSUPPORT, osl_Socket_E_ProtocolNoSupport }, /* Protocol not supported */
+#ifdef ESOCKTNOSUPPORT
+ { ESOCKTNOSUPPORT, osl_Socket_E_TypeNoSupport }, /* Socket type not supported */
+#endif
+ { EOPNOTSUPP, osl_Socket_E_OpNotSupport }, /* Operation not supported on socket */
+ { EPFNOSUPPORT, osl_Socket_E_PfNoSupport }, /* Protocol family not supported */
+ { EAFNOSUPPORT, osl_Socket_E_AfNoSupport }, /* Address family not supported by
+ protocol family */
+ { EADDRINUSE, osl_Socket_E_AddrInUse }, /* Address already in use */
+ { EADDRNOTAVAIL, osl_Socket_E_AddrNotAvail }, /* Can't assign requested address */
+ { ENETDOWN, osl_Socket_E_NetDown }, /* Network is down */
+ { ENETUNREACH, osl_Socket_E_NetUnreachable }, /* Network is unreachable */
+ { ENETRESET, osl_Socket_E_NetReset }, /* Network dropped connection because
+ of reset */
+ { ECONNABORTED, osl_Socket_E_ConnAborted }, /* Software caused connection abort */
+ { ECONNRESET, osl_Socket_E_ConnReset }, /* Connection reset by peer */
+ { ENOBUFS, osl_Socket_E_NoBufferSpace }, /* No buffer space available */
+ { EISCONN, osl_Socket_E_IsConnected }, /* Socket is already connected */
+ { ENOTCONN, osl_Socket_E_NotConnected }, /* Socket is not connected */
+ { ESHUTDOWN, osl_Socket_E_Shutdown }, /* Can't send after socket shutdown */
+#ifdef ETOOMANYREFS
+ { ETOOMANYREFS, osl_Socket_E_TooManyRefs }, /* Too many references: can't splice */
+#endif
+ { ETIMEDOUT, osl_Socket_E_TimedOut }, /* Connection timed out */
+ { ECONNREFUSED, osl_Socket_E_ConnRefused }, /* Connection refused */
+ { EHOSTDOWN, osl_Socket_E_HostDown }, /* Host is down */
+ { EHOSTUNREACH, osl_Socket_E_HostUnreachable }, /* No route to host */
+ { EWOULDBLOCK, osl_Socket_E_WouldBlock }, /* call would block on non-blocking socket */
+ { EALREADY, osl_Socket_E_Already }, /* operation already in progress */
+ { EINPROGRESS, osl_Socket_E_InProgress }, /* operation now in progress */
+ { EAGAIN, osl_Socket_E_WouldBlock }, /* same as EWOULDBLOCK */
+ { -1, osl_Socket_E_InvalidError }
+};
+
+static oslSocketError osl_SocketErrorFromNative(int nativeType)
+{
+ int i = 0;
+
+ while ((SocketError[i].error != osl_Socket_E_InvalidError) &&
+ (SocketError[i].errcode != nativeType)) i++;
+
+ return SocketError[i].error;
+}
+
+#define ERROR_FROM_NATIVE(y) osl_SocketErrorFromNative(y)
+
+static oslSocketAddr osl_psz_createInetSocketAddr (
+ const char* pszDottedAddr, sal_Int32 Port);
+
+static oslHostAddr osl_psz_createHostAddr (
+ const char *pszHostname, const oslSocketAddr Addr);
+
+static oslHostAddr osl_psz_createHostAddrByName (
+ const char *pszHostname);
+
+static const char* osl_psz_getHostnameOfHostAddr (
+ const oslHostAddr Addr);
+
+static oslSocketAddr osl_psz_resolveHostname (
+ const char* pszHostname);
+
+static sal_Int32 osl_psz_getServicePort (
+ const char* pszServicename, const char* pszProtocol);
+
+static void osl_psz_getLastSocketErrorDescription (
+ oslSocket Socket, char* pBuffer, sal_uInt32 BufferSize);
+
+static oslSocket createSocketImpl()
+{
+ oslSocket pSocket;
+
+ pSocket = static_cast<oslSocket>(calloc(1, sizeof(struct oslSocketImpl)));
+
+ pSocket->m_Socket = OSL_INVALID_SOCKET;
+ pSocket->m_nLastError = 0;
+ pSocket->m_nRefCount = 1;
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pSocket->m_bIsAccepting = false;
+#endif
+
+ return pSocket;
+}
+
+static void destroySocketImpl(oslSocket Socket)
+{
+ if ( Socket != nullptr)
+ free(Socket);
+}
+
+static oslSocketAddr createSocketAddr()
+{
+ oslSocketAddr pAddr = static_cast<oslSocketAddr>(rtl_allocateZeroMemory( sizeof( struct oslSocketAddrImpl )));
+ return pAddr;
+}
+
+static oslSocketAddr createSocketAddrWithFamily(
+ oslAddrFamily family, sal_Int32 port, sal_uInt32 nAddr )
+{
+ oslSocketAddr pAddr;
+
+ SAL_WARN_IF( family != osl_Socket_FamilyInet, "sal.osl", "creating socket for non-IP address family" );
+
+ pAddr = createSocketAddr();
+ switch( family )
+ {
+ case osl_Socket_FamilyInet:
+ {
+ struct sockaddr_in* pInetAddr= reinterpret_cast<sockaddr_in*>(&pAddr->m_sockaddr);
+
+ pInetAddr->sin_family = FAMILY_TO_NATIVE(osl_Socket_FamilyInet);
+ pInetAddr->sin_addr.s_addr = nAddr;
+ pInetAddr->sin_port = static_cast<sal_uInt16>(port&0xffff);
+ break;
+ }
+ default:
+ pAddr->m_sockaddr.sa_family = FAMILY_TO_NATIVE(family);
+ }
+ return pAddr;
+}
+
+static oslSocketAddr createSocketAddrFromSystem( struct sockaddr *pSystemSockAddr )
+{
+ oslSocketAddr pAddr = createSocketAddr();
+ memcpy( &(pAddr->m_sockaddr), pSystemSockAddr, sizeof( struct sockaddr ) );
+ return pAddr;
+}
+
+static void destroySocketAddr( oslSocketAddr addr )
+{
+ free( addr );
+}
+
+oslSocketAddr SAL_CALL osl_createEmptySocketAddr(oslAddrFamily Family)
+{
+ oslSocketAddr pAddr = nullptr;
+
+ /* is it an internet-Addr? */
+ if (Family == osl_Socket_FamilyInet)
+ {
+ pAddr = createSocketAddrWithFamily(Family, 0 , htonl(INADDR_ANY) );
+ }
+ else
+ {
+ pAddr = createSocketAddrWithFamily( Family , 0 , 0 );
+ }
+
+ return pAddr;
+}
+
+oslSocketAddr SAL_CALL osl_copySocketAddr(oslSocketAddr Addr)
+{
+ oslSocketAddr pCopy = nullptr;
+ if (Addr)
+ {
+ pCopy = createSocketAddr();
+
+ if (pCopy)
+ memcpy(&(pCopy->m_sockaddr),&(Addr->m_sockaddr), sizeof(struct sockaddr));
+ }
+ return pCopy;
+}
+
+sal_Bool SAL_CALL osl_isEqualSocketAddr (
+ oslSocketAddr Addr1,
+ oslSocketAddr Addr2)
+{
+ struct sockaddr* pAddr1 = nullptr;
+ struct sockaddr* pAddr2 = nullptr;
+
+ assert(Addr1 && Addr2);
+ pAddr1 = &(Addr1->m_sockaddr);
+ pAddr2 = &(Addr2->m_sockaddr);
+
+ if (pAddr1 == pAddr2)
+ {
+ return true;
+ }
+
+ if (pAddr1->sa_family == pAddr2->sa_family)
+ {
+ switch (pAddr1->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in* pInetAddr1= reinterpret_cast<sockaddr_in*>(pAddr1);
+ struct sockaddr_in* pInetAddr2= reinterpret_cast<sockaddr_in*>(pAddr2);
+
+ if ((pInetAddr1->sin_family == pInetAddr2->sin_family) &&
+ (pInetAddr1->sin_addr.s_addr == pInetAddr2->sin_addr.s_addr) &&
+ (pInetAddr1->sin_port == pInetAddr2->sin_port))
+ return true;
+ [[fallthrough]];
+ }
+
+ default:
+ {
+ return (memcmp(pAddr1, pAddr2, sizeof(struct sockaddr)) == 0);
+ }
+ }
+ }
+
+ return false;
+}
+
+oslSocketAddr SAL_CALL osl_createInetBroadcastAddr (
+ rtl_uString *strDottedAddr,
+ sal_Int32 Port)
+{
+ sal_uInt32 nAddr = OSL_INADDR_NONE;
+ oslSocketAddr pAddr;
+
+ if (strDottedAddr && strDottedAddr->length)
+ {
+ /* Dotted host address for limited broadcast */
+ rtl_String *pDottedAddr = nullptr;
+
+ rtl_uString2String (
+ &pDottedAddr, strDottedAddr->buffer, strDottedAddr->length,
+ RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS);
+
+ nAddr = inet_addr (pDottedAddr->buffer);
+ rtl_string_release (pDottedAddr);
+ }
+
+ if (nAddr != OSL_INADDR_NONE)
+ {
+ /* Limited broadcast */
+ nAddr = ntohl(nAddr);
+ if (IN_CLASSA(nAddr))
+ {
+ nAddr &= IN_CLASSA_NET;
+ nAddr |= IN_CLASSA_HOST;
+ }
+ else if (IN_CLASSB(nAddr))
+ {
+ nAddr &= IN_CLASSB_NET;
+ nAddr |= IN_CLASSB_HOST;
+ }
+ else if (IN_CLASSC(nAddr))
+ {
+ nAddr &= IN_CLASSC_NET;
+ nAddr |= IN_CLASSC_HOST;
+ }
+ else
+ {
+ /* No broadcast in class D */
+ return nullptr;
+ }
+ nAddr = htonl(nAddr);
+ }
+
+ pAddr = createSocketAddrWithFamily( osl_Socket_FamilyInet, htons(Port), nAddr );
+ return pAddr;
+}
+
+oslSocketAddr SAL_CALL osl_createInetSocketAddr (
+ rtl_uString *ustrDottedAddr,
+ sal_Int32 Port)
+{
+ rtl_String* strDottedAddr=nullptr;
+ oslSocketAddr Addr;
+ char* pszDottedAddr=nullptr;
+
+ if ( ustrDottedAddr != nullptr )
+ {
+ rtl_uString2String( &strDottedAddr,
+ rtl_uString_getStr(ustrDottedAddr),
+ rtl_uString_getLength(ustrDottedAddr),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS);
+ pszDottedAddr = rtl_string_getStr(strDottedAddr);
+ }
+
+ Addr = pszDottedAddr ? osl_psz_createInetSocketAddr(pszDottedAddr, Port) : nullptr;
+
+ if ( strDottedAddr != nullptr )
+ {
+ rtl_string_release(strDottedAddr);
+ }
+
+ return Addr;
+}
+
+oslSocketAddr osl_psz_createInetSocketAddr (
+ const char* pszDottedAddr,
+ sal_Int32 Port)
+{
+ oslSocketAddr pAddr = nullptr;
+ sal_Int32 Addr = inet_addr(pszDottedAddr);
+ if(Addr != -1)
+ {
+ /* valid dotted addr */
+ pAddr = createSocketAddrWithFamily( osl_Socket_FamilyInet, htons(Port) , Addr );
+ }
+ return pAddr;
+}
+
+oslSocketResult SAL_CALL osl_setAddrOfSocketAddr( oslSocketAddr pAddr, sal_Sequence *pByteSeq )
+{
+ oslSocketResult res = osl_Socket_Error;
+
+ SAL_WARN_IF( !pAddr, "sal.osl", "setting address of undefined socket address" );
+ SAL_WARN_IF( !pByteSeq, "sal.osl", "setting undefined address for socket address" );
+
+ if( pAddr && pByteSeq )
+ {
+ struct sockaddr_in * pSystemInetAddr;
+
+ assert( pAddr->m_sockaddr.sa_family == FAMILY_TO_NATIVE( osl_Socket_FamilyInet ) );
+ assert( pByteSeq->nElements == 4 );
+
+ pSystemInetAddr = reinterpret_cast<sockaddr_in *>(&pAddr->m_sockaddr);
+ memcpy( &(pSystemInetAddr->sin_addr) , pByteSeq->elements , 4 );
+ res = osl_Socket_Ok;
+ }
+ return res;
+}
+
+oslSocketResult SAL_CALL osl_getAddrOfSocketAddr( oslSocketAddr pAddr, sal_Sequence **ppByteSeq )
+{
+ oslSocketResult res = osl_Socket_Error;
+
+ SAL_WARN_IF( !pAddr, "sal.osl", "getting address of undefined socket address" );
+ SAL_WARN_IF( !ppByteSeq, "sal.osl", "getting address to undefined address pointer" );
+
+ if( pAddr && ppByteSeq )
+ {
+ struct sockaddr_in * pSystemInetAddr = reinterpret_cast<sockaddr_in *>(&pAddr->m_sockaddr);
+ rtl_byte_sequence_constructFromArray( ppByteSeq, reinterpret_cast<sal_Int8 *>(&pSystemInetAddr->sin_addr), 4);
+ res = osl_Socket_Ok;
+ }
+ return res;
+}
+
+/** try to figure out a full-qualified hostname, by adding the current domain
+ as given by the domainname program to the given hostname.
+ This function MUST NOT call gethostbyname since pHostName already points
+ to data returned by gethostname and would be garbled: use gethostname_r
+ instead!
+ */
+
+namespace {
+
+struct oslAddrInfo
+{
+ addrinfo* pAddrInfoList = nullptr;
+ int nError;
+
+ oslAddrInfo(const char* name, bool isInet = false)
+ {
+ addrinfo aHints;
+ memset(&aHints, 0, sizeof(addrinfo));
+ if (isInet)
+ aHints.ai_family = AF_INET;
+ aHints.ai_flags = AI_CANONNAME;
+
+ nError = getaddrinfo(name, nullptr, &aHints, &pAddrInfoList);
+ }
+
+ ~oslAddrInfo()
+ {
+ if (nError == 0)
+ freeaddrinfo(pAddrInfoList);
+ }
+
+ const char* getHostName() const
+ {
+ assert(pAddrInfoList);
+ return pAddrInfoList->ai_canonname;
+ }
+};
+
+}
+static bool isFullQualifiedDomainName (const char *pHostName)
+{
+ /* a FQDN (aka 'hostname.domain.top_level_domain' )
+ * is a name which contains a dot '.' in it ( would
+ * match as well for 'hostname.' but is good enough
+ * for now )*/
+ return strchr( pHostName, int('.') ) != nullptr;
+}
+
+static char* getFullQualifiedDomainName (const char *pHostName)
+{
+ char *pFullQualifiedName = nullptr;
+
+ if (isFullQualifiedDomainName(pHostName))
+ {
+ pFullQualifiedName = strdup(pHostName);
+ }
+ else
+ {
+ oslAddrInfo aAddrInfo(pHostName);
+ if (aAddrInfo.nError == 0)
+ pFullQualifiedName = strdup(aAddrInfo.getHostName());
+ }
+
+ return pFullQualifiedName;
+}
+
+struct oslHostAddrImpl
+{
+ char *pHostName;
+ oslSocketAddr pSockAddr;
+};
+
+static oslHostAddr addrinfoToHostAddr (const addrinfo* ai)
+{
+ if (!ai || !ai->ai_canonname || !ai->ai_addr)
+ return nullptr;
+
+ char* cn = getFullQualifiedDomainName(ai->ai_canonname);
+ SAL_WARN_IF( !cn, "sal.osl", "couldn't get full qualified domain name" );
+ if (cn == nullptr)
+ return nullptr;
+
+ oslSocketAddr pSockAddr = createSocketAddr();
+ SAL_WARN_IF( !pSockAddr, "sal.osl", "insufficient memory" );
+ if (pSockAddr == nullptr)
+ {
+ free(cn);
+ return nullptr;
+ }
+
+ if (ai->ai_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ {
+ memcpy (
+ &(pSockAddr->m_sockaddr),
+ ai->ai_addr,
+ ai->ai_addrlen);
+ }
+ else
+ {
+ /* unknown address family */
+ /* future extensions for new families might be implemented here */
+
+ SAL_WARN( "sal.osl", "unknown address family" );
+
+ destroySocketAddr( pSockAddr );
+ free (cn);
+ return nullptr;
+ }
+
+ oslHostAddr pAddr = static_cast<oslHostAddr>(malloc(sizeof(struct oslHostAddrImpl)));
+ SAL_WARN_IF( !pAddr, "sal.osl", "allocation error" );
+ if (pAddr == nullptr)
+ {
+ destroySocketAddr( pSockAddr );
+ free (cn);
+ return nullptr;
+ }
+
+ pAddr->pHostName= cn;
+ pAddr->pSockAddr= pSockAddr;
+
+ return pAddr;
+}
+
+oslHostAddr SAL_CALL osl_createHostAddr (
+ rtl_uString *ustrHostname,
+ const oslSocketAddr Addr)
+{
+ oslHostAddr HostAddr;
+ rtl_String* strHostname=nullptr;
+ char* pszHostName=nullptr;
+
+ if ( ustrHostname != nullptr )
+ {
+ rtl_uString2String( &strHostname,
+ rtl_uString_getStr(ustrHostname),
+ rtl_uString_getLength(ustrHostname),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+ pszHostName = rtl_string_getStr(strHostname);
+ }
+
+ HostAddr = osl_psz_createHostAddr(pszHostName,Addr);
+
+ if ( strHostname != nullptr )
+ {
+ rtl_string_release(strHostname);
+ }
+
+ return HostAddr;
+}
+
+oslHostAddr osl_psz_createHostAddr (
+ const char *pszHostname,
+ const oslSocketAddr pAddr)
+{
+ oslHostAddr pHostAddr;
+ char *cn;
+
+ SAL_WARN_IF( !pszHostname, "sal.osl", "undefined hostname" );
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+ if ((pszHostname == nullptr) || (pAddr == nullptr))
+ return nullptr;
+
+ cn = strdup(pszHostname);
+ SAL_WARN_IF( !cn, "sal.osl", "insufficient memory" );
+ if (cn == nullptr)
+ return nullptr;
+
+ pHostAddr= static_cast<oslHostAddr>(malloc(sizeof(struct oslHostAddrImpl)));
+ SAL_WARN_IF( !pHostAddr, "sal.osl", "allocation error" );
+ if (pHostAddr == nullptr)
+ {
+ free (cn);
+ return nullptr;
+ }
+
+ pHostAddr->pHostName= cn;
+ pHostAddr->pSockAddr= osl_copySocketAddr( pAddr );
+
+ return pHostAddr;
+}
+
+oslHostAddr SAL_CALL osl_createHostAddrByName(rtl_uString *ustrHostname)
+{
+ oslHostAddr HostAddr;
+ rtl_String* strHostname=nullptr;
+ char* pszHostName=nullptr;
+
+ if ( ustrHostname != nullptr )
+ {
+ rtl_uString2String( &strHostname,
+ rtl_uString_getStr(ustrHostname),
+ rtl_uString_getLength(ustrHostname),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+ pszHostName=rtl_string_getStr(strHostname);
+ }
+
+ HostAddr = osl_psz_createHostAddrByName(pszHostName);
+
+ if ( strHostname != nullptr )
+ {
+ rtl_string_release(strHostname);
+ }
+
+ return HostAddr;
+}
+
+oslHostAddr osl_psz_createHostAddrByName (const char *pszHostname)
+{
+ oslAddrInfo aAddrInfo(pszHostname, /* isInet */ true);
+
+ return addrinfoToHostAddr (aAddrInfo.pAddrInfoList);
+}
+
+oslHostAddr SAL_CALL osl_createHostAddrByAddr (const oslSocketAddr pAddr)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if (pAddr == nullptr)
+ return nullptr;
+
+ if (pAddr->m_sockaddr.sa_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ {
+ const struct sockaddr_in *sin = reinterpret_cast<sockaddr_in *>(&pAddr->m_sockaddr);
+ if (sin->sin_addr.s_addr == htonl(INADDR_ANY))
+ return nullptr;
+
+ char host[MAX_HOSTBUFFER_SIZE];
+ int res = getnameinfo(&pAddr->m_sockaddr, sizeof(struct sockaddr_in),
+ host, sizeof(host), nullptr, 0, NI_NAMEREQD);
+ if (res != 0)
+ return nullptr;
+
+ char *cn = getFullQualifiedDomainName(host);
+ SAL_WARN_IF( !cn, "sal.osl", "couldn't get full qualified domain name" );
+ if (cn == nullptr)
+ return nullptr;
+
+ oslSocketAddr pSockAddr = createSocketAddr();
+ SAL_WARN_IF( !pSockAddr, "sal.osl", "insufficient memory" );
+ if (pSockAddr == nullptr)
+ {
+ free(cn);
+ return nullptr;
+ }
+
+ memcpy(&pSockAddr->m_sockaddr, &pAddr->m_sockaddr, sizeof(pAddr->m_sockaddr));
+
+ oslHostAddr pHostAddr = static_cast<oslHostAddr>(malloc(sizeof(struct oslHostAddrImpl)));
+ SAL_WARN_IF( !pAddr, "sal.osl", "allocation error" );
+ if (pHostAddr == nullptr)
+ {
+ destroySocketAddr(pSockAddr);
+ free(cn);
+ return nullptr;
+ }
+
+ pHostAddr->pHostName = cn;
+ pHostAddr->pSockAddr = pSockAddr;
+
+ return pHostAddr;
+ }
+
+ return nullptr;
+}
+
+oslHostAddr SAL_CALL osl_copyHostAddr (const oslHostAddr pAddr)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if (pAddr)
+ return osl_psz_createHostAddr (pAddr->pHostName, pAddr->pSockAddr);
+ return nullptr;
+}
+
+void SAL_CALL osl_getHostnameOfHostAddr (
+ const oslHostAddr Addr,
+ rtl_uString **ustrHostname)
+{
+ const char* pHostname = osl_psz_getHostnameOfHostAddr(Addr);
+
+ rtl_uString_newFromAscii (ustrHostname, pHostname);
+}
+
+const char* osl_psz_getHostnameOfHostAddr (const oslHostAddr pAddr)
+{
+ if (pAddr)
+ return pAddr->pHostName;
+ return nullptr;
+}
+
+oslSocketAddr SAL_CALL osl_getSocketAddrOfHostAddr (const oslHostAddr pAddr)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if (pAddr)
+ return pAddr->pSockAddr;
+ return nullptr;
+}
+
+void SAL_CALL osl_destroyHostAddr (oslHostAddr pAddr)
+{
+ if (pAddr)
+ {
+ if (pAddr->pHostName)
+ free (pAddr->pHostName);
+ if (pAddr->pSockAddr)
+ osl_destroySocketAddr (pAddr->pSockAddr);
+ free (pAddr);
+ }
+}
+
+oslSocketResult SAL_CALL osl_getLocalHostname(rtl_uString **ustrLocalHostname)
+{
+ static auto const init = []() -> std::pair<oslSocketResult, OUString> {
+ char LocalHostname[256] = "";
+
+#ifdef SYSV
+ struct utsname uts;
+
+ if (uname(&uts) < 0)
+ return {osl_Socket_Error, OUString()};
+
+ if ((strlen(uts.nodename) + 1) > nBufLen)
+ return {osl_Socket_Error, OUString()};
+
+ strncpy(LocalHostname, uts.nodename, sizeof( LocalHostname ));
+#else /* BSD compatible */
+ if (gethostname(LocalHostname, sizeof(LocalHostname)-1) != 0)
+ return {osl_Socket_Error, OUString()};
+#endif /* SYSV */
+ LocalHostname[sizeof(LocalHostname)-1] = 0;
+
+ /* check if we have an FQDN */
+ if (strchr(LocalHostname, '.') == nullptr)
+ {
+ oslHostAddr Addr;
+
+ /* no, determine it via dns */
+ Addr = osl_psz_createHostAddrByName(LocalHostname);
+
+ const char *pStr;
+ if ((pStr = osl_psz_getHostnameOfHostAddr(Addr)) != nullptr)
+ {
+ strncpy(LocalHostname, pStr, sizeof( LocalHostname ));
+ LocalHostname[sizeof(LocalHostname)-1] = 0;
+ }
+ osl_destroyHostAddr(Addr);
+ }
+
+ if (LocalHostname[0] != '\0')
+ {
+ return {osl_Socket_Ok, OUString::createFromAscii(LocalHostname)};
+ }
+
+ return {osl_Socket_Error, OUString()};
+ }();
+
+ rtl_uString_assign(ustrLocalHostname,init.second.pData);
+
+ return init.first;
+}
+
+oslSocketAddr SAL_CALL osl_resolveHostname(rtl_uString *ustrHostname)
+{
+ oslSocketAddr Addr;
+ rtl_String* strHostname=nullptr;
+ char* pszHostName=nullptr;
+
+ if ( ustrHostname != nullptr )
+ {
+ rtl_uString2String( &strHostname,
+ rtl_uString_getStr(ustrHostname),
+ rtl_uString_getLength(ustrHostname),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+ pszHostName = rtl_string_getStr(strHostname);
+ }
+
+ Addr = osl_psz_resolveHostname(pszHostName);
+
+ if ( strHostname != nullptr )
+ {
+ rtl_string_release(strHostname);
+ }
+
+ return Addr;
+}
+
+oslSocketAddr osl_psz_resolveHostname(const char* pszHostname)
+{
+ struct oslHostAddrImpl *pAddr = osl_psz_createHostAddrByName(pszHostname);
+
+ if (pAddr)
+ {
+ oslSocketAddr SockAddr = osl_copySocketAddr(pAddr->pSockAddr);
+
+ osl_destroyHostAddr(pAddr);
+
+ return SockAddr;
+ }
+
+ return nullptr;
+}
+
+sal_Int32 SAL_CALL osl_getServicePort(rtl_uString *ustrServicename, rtl_uString *ustrProtocol)
+{
+ sal_Int32 nPort;
+ rtl_String* strServicename=nullptr;
+ rtl_String* strProtocol=nullptr;
+ char* pszServiceName=nullptr;
+ char* pszProtocol=nullptr;
+
+ if ( ustrServicename != nullptr )
+ {
+ rtl_uString2String( &strServicename,
+ rtl_uString_getStr(ustrServicename),
+ rtl_uString_getLength(ustrServicename),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+ pszServiceName = rtl_string_getStr(strServicename);
+ }
+
+ if ( ustrProtocol != nullptr )
+ {
+ rtl_uString2String( &strProtocol,
+ rtl_uString_getStr(ustrProtocol),
+ rtl_uString_getLength(ustrProtocol),
+ RTL_TEXTENCODING_UTF8,
+ OUSTRING_TO_OSTRING_CVTFLAGS );
+ pszProtocol = rtl_string_getStr(strProtocol);
+ }
+
+ nPort = osl_psz_getServicePort(pszServiceName,pszProtocol);
+
+ if ( strServicename != nullptr )
+ {
+ rtl_string_release(strServicename);
+ }
+
+ if ( strProtocol != nullptr )
+ {
+ rtl_string_release(strProtocol);
+ }
+
+ return nPort;
+}
+
+sal_Int32 osl_psz_getServicePort(const char* pszServicename,
+ const char* pszProtocol)
+{
+ struct servent* ps;
+
+ ps= getservbyname(pszServicename, pszProtocol);
+
+ if (ps != nullptr)
+ return ntohs(ps->s_port);
+
+ return OSL_INVALID_PORT;
+}
+
+void SAL_CALL osl_destroySocketAddr(oslSocketAddr pAddr)
+{
+ destroySocketAddr( pAddr );
+}
+
+oslAddrFamily SAL_CALL osl_getFamilyOfSocketAddr(oslSocketAddr pAddr)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if (pAddr)
+ return FAMILY_FROM_NATIVE(pAddr->m_sockaddr.sa_family);
+ return osl_Socket_FamilyInvalid;
+}
+
+sal_Int32 SAL_CALL osl_getInetPortOfSocketAddr(oslSocketAddr pAddr)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if( pAddr )
+ {
+ struct sockaddr_in* pSystemInetAddr= reinterpret_cast<sockaddr_in*>(&pAddr->m_sockaddr);
+
+ if ( pSystemInetAddr->sin_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ return ntohs(pSystemInetAddr->sin_port);
+ }
+ return OSL_INVALID_PORT;
+}
+
+sal_Bool SAL_CALL osl_setInetPortOfSocketAddr(oslSocketAddr pAddr, sal_Int32 Port)
+{
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+
+ if( pAddr )
+ {
+ struct sockaddr_in* pSystemInetAddr= reinterpret_cast<sockaddr_in*>(&pAddr->m_sockaddr);
+ if ( pSystemInetAddr->sin_family == FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ {
+ pSystemInetAddr->sin_port= htons(static_cast<short>(Port));
+ return true;
+ }
+ }
+
+ /* this is not a inet-addr => can't set port */
+ return false;
+}
+
+oslSocketResult SAL_CALL osl_getHostnameOfSocketAddr(oslSocketAddr Addr, rtl_uString **ustrHostname)
+{
+ oslHostAddr pHostAddr= osl_createHostAddrByAddr(Addr);
+
+ if (!pHostAddr)
+ {
+ return osl_Socket_Error;
+ }
+
+ rtl_uString_newFromAscii(ustrHostname,pHostAddr->pHostName);
+
+ osl_destroyHostAddr(pHostAddr);
+
+ return osl_Socket_Ok;
+}
+
+oslSocketResult SAL_CALL osl_getDottedInetAddrOfSocketAddr(oslSocketAddr Addr, rtl_uString **ustrDottedInetAddr)
+{
+ if( !Addr )
+ {
+ return osl_Socket_Error;
+ }
+
+ struct sockaddr_in* pSystemInetAddr = reinterpret_cast<sockaddr_in *>(&Addr->m_sockaddr);
+
+ if (pSystemInetAddr->sin_family != FAMILY_TO_NATIVE(osl_Socket_FamilyInet))
+ {
+ return osl_Socket_Error;
+ }
+
+ rtl_uString_newFromAscii(ustrDottedInetAddr,inet_ntoa(pSystemInetAddr->sin_addr));
+
+ return osl_Socket_Ok;
+
+}
+
+oslSocket SAL_CALL osl_createSocket(
+ oslAddrFamily Family,
+ oslSocketType Type,
+ oslProtocol Protocol)
+{
+ oslSocket pSocket;
+
+ /* alloc memory */
+ pSocket= createSocketImpl();
+
+ /* create socket */
+ pSocket->m_Socket= socket(FAMILY_TO_NATIVE(Family),
+ TYPE_TO_NATIVE(Type),
+ PROTOCOL_TO_NATIVE(Protocol));
+
+ /* creation failed => free memory */
+ if(pSocket->m_Socket == OSL_INVALID_SOCKET)
+ {
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "socket creation failed: " << UnixErrnoString(nErrno) );
+
+ destroySocketImpl(pSocket);
+ pSocket= nullptr;
+ }
+ else
+ {
+ sal_Int32 nFlags=0;
+ /* set close-on-exec flag */
+ if ((nFlags = fcntl(pSocket->m_Socket, F_GETFD, 0)) != -1)
+ {
+ nFlags |= FD_CLOEXEC;
+ if (fcntl(pSocket->m_Socket, F_SETFD, nFlags) == -1)
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "failed changing socket flags: " << UnixErrnoString(nErrno) );
+ }
+ }
+ else
+ {
+ pSocket->m_nLastError=errno;
+ }
+ }
+
+ return pSocket;
+}
+
+void SAL_CALL osl_acquireSocket(oslSocket pSocket)
+{
+ osl_atomic_increment(&(pSocket->m_nRefCount));
+}
+
+void SAL_CALL osl_releaseSocket(oslSocket pSocket)
+{
+ if (pSocket && osl_atomic_decrement(&(pSocket->m_nRefCount)) == 0)
+ {
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ if (pSocket->m_bIsAccepting)
+ {
+ SAL_WARN( "sal.osl", "attempt to destroy socket while accepting" );
+ return;
+ }
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+ osl_closeSocket(pSocket);
+ destroySocketImpl(pSocket);
+ }
+}
+
+void SAL_CALL osl_closeSocket(oslSocket pSocket)
+{
+ /* socket already invalid */
+ if (!pSocket)
+ return;
+
+ pSocket->m_nLastError=0;
+ sal_Int32 nFD = pSocket->m_Socket;
+
+ if (nFD == OSL_INVALID_SOCKET)
+ return;
+
+ pSocket->m_Socket = OSL_INVALID_SOCKET;
+
+ sal_Int32 nRet=0;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pSocket->m_bIsInShutdown = true;
+
+ if (pSocket->m_bIsAccepting)
+ {
+ union {
+ struct sockaddr aSockAddr;
+ struct sockaddr_in aSockAddrIn;
+ } s;
+ socklen_t nSockLen = sizeof(s.aSockAddr);
+
+ nRet = getsockname(nFD, &s.aSockAddr, &nSockLen);
+ if (nRet < 0)
+ {
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "getsockname call failed: " << UnixErrnoString(nErrno) );
+ }
+
+ if (s.aSockAddr.sa_family == AF_INET)
+ {
+ if (s.aSockAddrIn.sin_addr.s_addr == htonl(INADDR_ANY))
+ {
+ s.aSockAddrIn.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ }
+
+ int nConnFD = socket(AF_INET, SOCK_STREAM, 0);
+ if (nConnFD < 0)
+ {
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "socket call failed: " << UnixErrnoString(nErrno) );
+ }
+ else
+ {
+ nRet = connect(nConnFD, &s.aSockAddr, sizeof(s.aSockAddr));
+ if (nRet < 0)
+ {
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "connect call failed: " << UnixErrnoString(nErrno) );
+ }
+ close(nConnFD);
+ }
+ }
+ pSocket->m_bIsAccepting = false;
+ }
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+
+ nRet=close(nFD);
+ if (nRet != 0)
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "closeSocket close failed: " << UnixErrnoString(nErrno) );
+ }
+
+ pSocket->m_Socket = OSL_INVALID_SOCKET;
+}
+
+/* Note from function creator: I rely on the fact that oslSocketAddr and struct sockaddr
+ are the same! I don't like it very much but see no other easy way to conceal
+ the struct sockaddr from the eyes of the user. */
+oslSocketAddr SAL_CALL osl_getLocalAddrOfSocket(oslSocket pSocket)
+{
+ socklen_t AddrLen;
+ struct sockaddr Addr;
+ oslSocketAddr pAddr;
+
+ if (pSocket == nullptr) /* ENOTSOCK */
+ return nullptr;
+
+ AddrLen= sizeof(struct sockaddr);
+
+ if (getsockname(pSocket->m_Socket, &Addr, &AddrLen) == OSL_SOCKET_ERROR)
+ return nullptr;
+
+ pAddr = createSocketAddrFromSystem( &Addr );
+ return pAddr;
+}
+
+oslSocketAddr SAL_CALL osl_getPeerAddrOfSocket(oslSocket pSocket)
+{
+ socklen_t AddrLen;
+ struct sockaddr Addr;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return nullptr;
+ }
+
+ pSocket->m_nLastError=0;
+ AddrLen= sizeof(struct sockaddr);
+
+ if(getpeername(pSocket->m_Socket, &Addr, &AddrLen) == OSL_SOCKET_ERROR)
+ {
+ pSocket->m_nLastError=errno;
+ return nullptr;
+ }
+ return createSocketAddrFromSystem( &Addr );
+}
+
+sal_Bool SAL_CALL osl_bindAddrToSocket(oslSocket pSocket,
+ oslSocketAddr pAddr)
+{
+ int nRet;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ SAL_WARN_IF( !pAddr, "sal.osl", "undefined address" );
+ if ( pSocket == nullptr || pAddr == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ nRet = bind(pSocket->m_Socket, &(pAddr->m_sockaddr), sizeof(struct sockaddr));
+
+ if ( nRet == OSL_SOCKET_ERROR)
+ {
+ pSocket->m_nLastError=errno;
+ return false;
+ }
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_listenOnSocket(oslSocket pSocket,
+ sal_Int32 MaxPendingConnections)
+{
+ int nRet;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ nRet = listen(pSocket->m_Socket,
+ MaxPendingConnections == -1 ?
+ SOMAXCONN :
+ MaxPendingConnections);
+ if ( nRet == OSL_SOCKET_ERROR)
+ {
+ pSocket->m_nLastError=errno;
+ return false;
+ }
+
+ return true;
+}
+
+oslSocketResult SAL_CALL osl_connectSocketTo(oslSocket pSocket,
+ oslSocketAddr pAddr,
+ const TimeValue* pTimeout)
+{
+ fd_set WriteSet;
+ fd_set ExcptSet;
+ int ReadyHandles;
+ struct timeval tv;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+
+ if ( pSocket == nullptr )
+ {
+ return osl_Socket_Error;
+ }
+
+ pSocket->m_nLastError=0;
+
+ if (osl_isNonBlockingMode(pSocket))
+ {
+ if (connect(pSocket->m_Socket,
+ &(pAddr->m_sockaddr),
+ sizeof(struct sockaddr)) != OSL_SOCKET_ERROR)
+ return osl_Socket_Ok;
+
+ if (errno == EWOULDBLOCK || errno == EINPROGRESS)
+ {
+ pSocket->m_nLastError=EINPROGRESS;
+ return osl_Socket_InProgress;
+ }
+
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "connection failed: " << UnixErrnoString(nErrno) );
+ return osl_Socket_Error;
+ }
+
+ /* set socket temporarily to non-blocking */
+ if( !osl_enableNonBlockingMode(pSocket, true) )
+ SAL_WARN( "sal.osl", "failed to enable non-blocking mode" );
+
+ /* initiate connect */
+ if(connect(pSocket->m_Socket,
+ &(pAddr->m_sockaddr),
+ sizeof(struct sockaddr)) != OSL_SOCKET_ERROR)
+ {
+ /* immediate connection */
+ osl_enableNonBlockingMode(pSocket, false);
+
+ return osl_Socket_Ok;
+ }
+
+ /* really an error or just delayed? */
+ if (errno != EINPROGRESS)
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "connection failed: " << UnixErrnoString(nErrno) );
+
+ osl_enableNonBlockingMode(pSocket, false);
+ return osl_Socket_Error;
+ }
+
+ /* prepare select set for socket */
+ FD_ZERO(&WriteSet);
+ FD_ZERO(&ExcptSet);
+ FD_SET(pSocket->m_Socket, &WriteSet);
+ FD_SET(pSocket->m_Socket, &ExcptSet);
+
+ /* prepare timeout */
+ if (pTimeout)
+ {
+ /* divide milliseconds into seconds and microseconds */
+ tv.tv_sec= pTimeout->Seconds;
+ tv.tv_usec= pTimeout->Nanosec / 1000;
+ }
+
+ /* select */
+ ReadyHandles= select(pSocket->m_Socket+1,
+ nullptr,
+ PTR_FD_SET(WriteSet),
+ PTR_FD_SET(ExcptSet),
+ pTimeout ? &tv : nullptr);
+
+ if (ReadyHandles > 0) /* connected */
+ {
+ if ( FD_ISSET(pSocket->m_Socket, &WriteSet ) )
+ {
+ int nErrorCode = 0;
+ socklen_t nErrorSize = sizeof( nErrorCode );
+
+ int nSockOpt;
+
+ nSockOpt = getsockopt ( pSocket->m_Socket, SOL_SOCKET, SO_ERROR,
+ &nErrorCode, &nErrorSize );
+ if ( (nSockOpt == 0) && (nErrorCode == 0))
+ {
+ osl_enableNonBlockingMode(pSocket, false);
+ return osl_Socket_Ok;
+ }
+ else
+ {
+ pSocket->m_nLastError = (nSockOpt == 0) ? nErrorCode : errno;
+ return osl_Socket_Error;
+ }
+ }
+ else
+ {
+ return osl_Socket_Error;
+ }
+ }
+ else if (ReadyHandles < 0) /* error */
+ {
+ if (errno == EBADF) /* most probably interrupted by close() */
+ {
+ /* do not access pSockImpl because it is about to be or */
+ /* already destroyed */
+ return osl_Socket_Interrupted;
+ }
+ pSocket->m_nLastError=errno;
+ return osl_Socket_Error;
+ }
+ else /* timeout */
+ {
+ pSocket->m_nLastError=errno;
+ return osl_Socket_TimedOut;
+ }
+}
+
+oslSocket SAL_CALL osl_acceptConnectionOnSocket(oslSocket pSocket,
+ oslSocketAddr* ppAddr)
+{
+ struct sockaddr Addr;
+ int Connection, Flags;
+ oslSocket pConnectionSockImpl;
+
+ socklen_t AddrLen = sizeof(struct sockaddr);
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return nullptr;
+ }
+
+ pSocket->m_nLastError=0;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pSocket->m_bIsAccepting = true;
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+
+ if( ppAddr && *ppAddr )
+ {
+ osl_destroySocketAddr( *ppAddr );
+ *ppAddr = nullptr;
+ }
+
+ /* prevent Linux EINTR behaviour */
+ do
+ {
+ Connection = accept(pSocket->m_Socket, &Addr, &AddrLen);
+ } while (Connection == -1 && errno == EINTR);
+
+ /* accept failed? */
+ if( Connection == OSL_SOCKET_ERROR )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "accept connection failed: " << UnixErrnoString(nErrno) );
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pSocket->m_bIsAccepting = false;
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+ return nullptr;
+ }
+
+ assert(AddrLen == sizeof(struct sockaddr));
+
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ if ( pSocket->m_bIsInShutdown )
+ {
+ close(Connection);
+ SAL_WARN( "sal.osl", "close while accept" );
+ return nullptr;
+ }
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+
+ if(ppAddr)
+ {
+ *ppAddr= createSocketAddrFromSystem(&Addr);
+ }
+
+ /* alloc memory */
+ pConnectionSockImpl= createSocketImpl();
+
+ /* set close-on-exec flag */
+ if ((Flags = fcntl(Connection, F_GETFD, 0)) != -1)
+ {
+ Flags |= FD_CLOEXEC;
+ if (fcntl(Connection, F_SETFD, Flags) == -1)
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "fcntl failed: " << UnixErrnoString(nErrno) );
+ }
+
+ }
+
+ pConnectionSockImpl->m_Socket = Connection;
+ pConnectionSockImpl->m_nLastError = 0;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ pConnectionSockImpl->m_bIsAccepting = false;
+
+ pSocket->m_bIsAccepting = false;
+#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
+ return pConnectionSockImpl;
+}
+
+sal_Int32 SAL_CALL osl_receiveSocket(oslSocket pSocket,
+ void* pBuffer,
+ sal_uInt32 BytesToRead,
+ oslSocketMsgFlag Flag)
+{
+ int nRead;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return -1;
+ }
+
+ pSocket->m_nLastError=0;
+
+ do
+ {
+ nRead = recv(pSocket->m_Socket,
+ pBuffer,
+ BytesToRead,
+ MSG_FLAG_TO_NATIVE(Flag));
+ } while ( nRead < 0 && errno == EINTR );
+
+ if ( nRead < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "receive socket [" << nRead << "] failed: " << UnixErrnoString(nErrno) );
+ }
+ else if ( nRead == 0 )
+ {
+ SAL_WARN( "sal.osl", "receive socket [" << nRead << "] failed: EOL" );
+ }
+
+ return nRead;
+}
+
+sal_Int32 SAL_CALL osl_receiveFromSocket(oslSocket pSocket,
+ oslSocketAddr pSenderAddr,
+ void* pBuffer,
+ sal_uInt32 BufferSize,
+ oslSocketMsgFlag Flag)
+{
+ int nRead;
+ struct sockaddr *pSystemSockAddr = nullptr;
+ socklen_t AddrLen = 0;
+ if( pSenderAddr )
+ {
+ AddrLen = sizeof( struct sockaddr );
+ pSystemSockAddr = &(pSenderAddr->m_sockaddr);
+ }
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return -1;
+ }
+
+ pSocket->m_nLastError=0;
+
+ nRead = recvfrom(pSocket->m_Socket,
+ pBuffer,
+ BufferSize,
+ MSG_FLAG_TO_NATIVE(Flag),
+ pSystemSockAddr,
+ &AddrLen);
+
+ if ( nRead < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "receive socket [" << nRead << "] failed: " << UnixErrnoString(nErrno) );
+ }
+ else if ( nRead == 0 )
+ {
+ SAL_WARN( "sal.osl", "receive socket [" << nRead << "] failed: EOL" );
+ }
+
+ return nRead;
+}
+
+sal_Int32 SAL_CALL osl_sendSocket(oslSocket pSocket,
+ const void* pBuffer,
+ sal_uInt32 BytesToSend,
+ oslSocketMsgFlag Flag)
+{
+ int nWritten;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return -1;
+ }
+
+ pSocket->m_nLastError=0;
+
+ do
+ {
+ nWritten = send(pSocket->m_Socket,
+ pBuffer,
+ BytesToSend,
+ MSG_FLAG_TO_NATIVE(Flag));
+ } while ( nWritten < 0 && errno == EINTR );
+
+ if ( nWritten < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "send socket [" << nWritten << "] failed: " << UnixErrnoString(nErrno) );
+ }
+ else if ( nWritten == 0 )
+ {
+ SAL_WARN( "sal.osl", "send socket [" << nWritten << "] failed: EOL" );
+ }
+
+ return nWritten;
+}
+
+sal_Int32 SAL_CALL osl_sendToSocket(oslSocket pSocket,
+ oslSocketAddr ReceiverAddr,
+ const void* pBuffer,
+ sal_uInt32 BytesToSend,
+ oslSocketMsgFlag Flag)
+{
+ int nWritten;
+
+ struct sockaddr *pSystemSockAddr = nullptr;
+ int AddrLen = 0;
+ if( ReceiverAddr )
+ {
+ pSystemSockAddr = &(ReceiverAddr->m_sockaddr);
+ AddrLen = sizeof( struct sockaddr );
+ }
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return -1;
+ }
+
+ pSocket->m_nLastError=0;
+
+ /* ReceiverAddr might be 0 when used on a connected socket. */
+ /* Then sendto should behave like send. */
+
+ nWritten = sendto(pSocket->m_Socket,
+ pBuffer,
+ BytesToSend,
+ MSG_FLAG_TO_NATIVE(Flag),
+ pSystemSockAddr,
+ AddrLen);
+
+ if ( nWritten < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "send socket [" << nWritten << "] failed: " << UnixErrnoString(nErrno) );
+ }
+ else if ( nWritten == 0 )
+ {
+ SAL_WARN( "sal.osl", "send socket [" << nWritten << "] failed: EOL" );
+ }
+
+ return nWritten;
+}
+
+sal_Int32 SAL_CALL osl_readSocket (
+ oslSocket pSocket, void *pBuffer, sal_Int32 n )
+{
+ sal_uInt8 * Ptr = static_cast<sal_uInt8 *>(pBuffer);
+ sal_uInt32 BytesRead= 0;
+ sal_uInt32 BytesToRead= n;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+
+ /* loop until all desired bytes were read or an error occurred */
+ while (BytesToRead > 0)
+ {
+ sal_Int32 RetVal;
+ RetVal= osl_receiveSocket(pSocket,
+ Ptr,
+ BytesToRead,
+ osl_Socket_MsgNormal);
+
+ /* error occurred? */
+ if(RetVal <= 0)
+ {
+ break;
+ }
+
+ BytesToRead -= RetVal;
+ BytesRead += RetVal;
+ Ptr += RetVal;
+ }
+
+ return BytesRead;
+}
+
+sal_Int32 SAL_CALL osl_writeSocket(
+ oslSocket pSocket, const void *pBuffer, sal_Int32 n )
+{
+ /* loop until all desired bytes were send or an error occurred */
+ sal_uInt32 BytesSend= 0;
+ sal_uInt32 BytesToSend= n;
+ sal_uInt8 const *Ptr = static_cast<sal_uInt8 const *>(pBuffer);
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+
+ while (BytesToSend > 0)
+ {
+ sal_Int32 RetVal;
+
+ RetVal= osl_sendSocket( pSocket,Ptr,BytesToSend,osl_Socket_MsgNormal);
+
+ /* error occurred? */
+ if(RetVal <= 0)
+ {
+ break;
+ }
+
+ BytesToSend -= RetVal;
+ BytesSend += RetVal;
+ Ptr += RetVal;
+
+ }
+ return BytesSend;
+}
+
+#ifdef HAVE_POLL_H /* poll() */
+
+static bool socket_poll (
+ oslSocket pSocket,
+ const TimeValue* pTimeout,
+ short nEvent)
+{
+ struct pollfd fds;
+ int timeout;
+ int result;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if (pSocket == nullptr)
+ return false; /* EINVAL */
+
+ pSocket->m_nLastError = 0;
+
+ fds.fd = pSocket->m_Socket;
+ fds.events = nEvent;
+ fds.revents = 0;
+
+ timeout = -1;
+ if (pTimeout)
+ {
+ /* Convert to [ms] */
+ timeout = pTimeout->Seconds * 1000;
+ timeout += pTimeout->Nanosec / (1000 * 1000);
+ }
+
+ result = poll (&fds, 1, timeout);
+ if (result < 0)
+ {
+ pSocket->m_nLastError = errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "poll failed: " << UnixErrnoString(nErrno) );
+ return false;
+ }
+ if (result == 0)
+ {
+ /* Timeout */
+ return false;
+ }
+
+ return ((fds.revents & nEvent) == nEvent);
+}
+
+#else /* select() */
+
+static sal_Bool socket_poll (
+ oslSocket pSocket,
+ const TimeValue* pTimeout,
+ short nEvent)
+{
+ fd_set fds;
+ struct timeval tv;
+ int result;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if (0 == pSocket)
+ return sal_False; /* EINVAL */
+
+ pSocket->m_nLastError = 0;
+
+ FD_ZERO(&fds);
+ FD_SET(pSocket->m_Socket, &fds);
+
+ if (pTimeout)
+ {
+ /* Convert to 'timeval' */
+ tv.tv_sec = pTimeout->Seconds;
+ tv.tv_usec = pTimeout->Nanosec / 1000;
+ }
+
+ result = select (
+ pSocket->m_Socket + 1,
+ (nEvent == POLLIN ) ? PTR_FD_SET(fds) : NULL,
+ (nEvent == POLLOUT) ? PTR_FD_SET(fds) : NULL,
+ (nEvent == POLLPRI) ? PTR_FD_SET(fds) : NULL,
+ (pTimeout) ? &tv : NULL);
+
+ if (result < 0)
+ {
+ pSocket->m_nLastError = errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "select failed: " << UnixErrnoString(nErrno) );
+ return sal_False;
+ }
+ if (result == 0)
+ {
+ /* Timeout */
+ return sal_False;
+ }
+
+ return (FD_ISSET(pSocket->m_Socket, &fds) ? sal_True : sal_False);
+}
+
+#endif /* HAVE_POLL_H */
+
+sal_Bool SAL_CALL osl_isReceiveReady (
+ oslSocket pSocket, const TimeValue* pTimeout)
+{
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if (pSocket == nullptr)
+ {
+ /* ENOTSOCK */
+ return false;
+ }
+
+ return socket_poll (pSocket, pTimeout, POLLIN);
+}
+
+sal_Bool SAL_CALL osl_isSendReady (
+ oslSocket pSocket, const TimeValue* pTimeout)
+{
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if (pSocket == nullptr)
+ {
+ /* ENOTSOCK */
+ return false;
+ }
+
+ return socket_poll (pSocket, pTimeout, POLLOUT);
+}
+
+sal_Bool SAL_CALL osl_isExceptionPending (
+ oslSocket pSocket, const TimeValue* pTimeout)
+{
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if (pSocket == nullptr)
+ {
+ /* ENOTSOCK */
+ return false;
+ }
+
+ return socket_poll (pSocket, pTimeout, POLLPRI);
+}
+
+sal_Bool SAL_CALL osl_shutdownSocket(oslSocket pSocket,
+ oslSocketDirection Direction)
+{
+ int nRet;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ nRet=shutdown(pSocket->m_Socket, DIRECTION_TO_NATIVE(Direction));
+ if (nRet != 0 )
+ {
+ pSocket->m_nLastError=errno;
+ int nErrno = errno;
+ SAL_WARN( "sal.osl", "shutdown failed: " << UnixErrnoString(nErrno) );
+ }
+ return (nRet==0);
+}
+
+sal_Int32 SAL_CALL osl_getSocketOption(oslSocket pSocket,
+ oslSocketOptionLevel Level,
+ oslSocketOption Option,
+ void* pBuffer,
+ sal_uInt32 BufferLen)
+{
+ socklen_t nOptLen = static_cast<socklen_t>(BufferLen);
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return -1;
+ }
+
+ pSocket->m_nLastError=0;
+
+ if(getsockopt(pSocket->m_Socket,
+ OPTION_LEVEL_TO_NATIVE(Level),
+ OPTION_TO_NATIVE(Option),
+ pBuffer,
+ &nOptLen) == -1)
+ {
+ pSocket->m_nLastError=errno;
+ return -1;
+ }
+
+ return nOptLen;
+}
+
+sal_Bool SAL_CALL osl_setSocketOption(oslSocket pSocket,
+ oslSocketOptionLevel Level,
+ oslSocketOption Option,
+ void* pBuffer,
+ sal_uInt32 BufferLen)
+{
+ int nRet;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ nRet = setsockopt(pSocket->m_Socket,
+ OPTION_LEVEL_TO_NATIVE(Level),
+ OPTION_TO_NATIVE(Option),
+ pBuffer,
+ BufferLen);
+
+ if ( nRet < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ return false;
+ }
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_enableNonBlockingMode(oslSocket pSocket,
+ sal_Bool On)
+{
+ int flags;
+ int nRet;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ flags = fcntl(pSocket->m_Socket, F_GETFL, 0);
+
+ if (On)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~(O_NONBLOCK);
+
+ nRet = fcntl(pSocket->m_Socket, F_SETFL, flags);
+
+ if ( nRet < 0 )
+ {
+ pSocket->m_nLastError=errno;
+ return false;
+ }
+
+ return true;
+}
+
+sal_Bool SAL_CALL osl_isNonBlockingMode(oslSocket pSocket)
+{
+ int flags;
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return false;
+ }
+
+ pSocket->m_nLastError=0;
+
+ flags = fcntl(pSocket->m_Socket, F_GETFL, 0);
+
+ if (flags == -1 || !(flags & O_NONBLOCK))
+ return false;
+
+ return true;
+}
+
+oslSocketType SAL_CALL osl_getSocketType(oslSocket pSocket)
+{
+ int Type=0;
+ socklen_t TypeSize= sizeof(Type);
+
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( pSocket == nullptr )
+ {
+ return osl_Socket_TypeInvalid;
+ }
+
+ pSocket->m_nLastError=0;
+
+ if(getsockopt(pSocket->m_Socket,
+ OPTION_LEVEL_TO_NATIVE(osl_Socket_LevelSocket),
+ OPTION_TO_NATIVE(osl_Socket_OptionType),
+ &Type,
+ &TypeSize) == -1)
+ {
+ /* error */
+ pSocket->m_nLastError=errno;
+ return osl_Socket_TypeInvalid;
+ }
+
+ return TYPE_FROM_NATIVE(Type);
+
+}
+
+void SAL_CALL osl_getLastSocketErrorDescription(oslSocket Socket, rtl_uString **ustrError)
+{
+ char pszError[1024];
+
+ pszError[0] = '\0';
+
+ osl_psz_getLastSocketErrorDescription(Socket,pszError,sizeof(pszError));
+
+ rtl_uString_newFromAscii(ustrError,pszError);
+}
+
+void osl_psz_getLastSocketErrorDescription(oslSocket pSocket, char* pBuffer, sal_uInt32 BufferSize)
+{
+ /* make sure pBuffer will be a zero-terminated string even when strncpy has to cut */
+ pBuffer[BufferSize-1]= '\0';
+
+ if ( pSocket == nullptr )
+ {
+ strncpy(pBuffer, strerror(EINVAL), BufferSize-1);
+ return;
+ }
+
+ strncpy(pBuffer, strerror(pSocket->m_nLastError), BufferSize-1);
+}
+
+oslSocketError SAL_CALL osl_getLastSocketError(oslSocket pSocket)
+{
+ if ( pSocket == nullptr )
+ {
+ return ERROR_FROM_NATIVE(EINVAL);
+ }
+
+ return ERROR_FROM_NATIVE(pSocket->m_nLastError);
+}
+
+struct oslSocketSetImpl
+{
+ int m_MaxHandle; /* for select(), the largest descriptor in the set */
+ fd_set m_Set; /* the set of descriptors */
+
+};
+
+oslSocketSet SAL_CALL osl_createSocketSet()
+{
+ oslSocketSetImpl* pSet;
+
+ pSet= static_cast<oslSocketSetImpl*>(malloc(sizeof(oslSocketSetImpl)));
+
+ SAL_WARN_IF( !pSet, "sal.osl", "allocation error" );
+
+ if(pSet)
+ {
+ pSet->m_MaxHandle= 0;
+ FD_ZERO(&pSet->m_Set);
+ }
+
+ return pSet;
+}
+
+void SAL_CALL osl_destroySocketSet(oslSocketSet Set)
+{
+ if(Set)
+ free(Set);
+}
+
+void SAL_CALL osl_clearSocketSet(oslSocketSet Set)
+{
+ SAL_WARN_IF( !Set, "sal.osl", "undefined socket set" );
+ if ( Set == nullptr )
+ {
+ return;
+ }
+
+ Set->m_MaxHandle= 0;
+
+ FD_ZERO(&Set->m_Set);
+}
+
+void SAL_CALL osl_addToSocketSet(oslSocketSet Set, oslSocket pSocket)
+{
+ SAL_WARN_IF( !Set, "sal.osl", "undefined socket set" );
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+
+ if ( Set == nullptr || pSocket == nullptr)
+ {
+ return;
+ }
+
+ /* correct max handle */
+ if(pSocket->m_Socket > Set->m_MaxHandle)
+ Set->m_MaxHandle= pSocket->m_Socket;
+ FD_SET(pSocket->m_Socket, &Set->m_Set);
+
+}
+
+void SAL_CALL osl_removeFromSocketSet(oslSocketSet Set, oslSocket pSocket)
+{
+ SAL_WARN_IF( !Set, "sal.osl", "undefined socket set" );
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+
+ if ( Set == nullptr || pSocket == nullptr)
+ {
+ return;
+ }
+
+ /* correct max handle */
+ if(pSocket->m_Socket == Set->m_MaxHandle)
+ {
+ /* not optimal, since the next used descriptor might be */
+ /* much smaller than m_Socket-1, but it will do */
+ Set->m_MaxHandle--;
+ if(Set->m_MaxHandle < 0)
+ {
+ Set->m_MaxHandle= 0; /* avoid underflow */
+ }
+ }
+
+ FD_CLR(pSocket->m_Socket, &Set->m_Set);
+}
+
+sal_Bool SAL_CALL osl_isInSocketSet(oslSocketSet Set, oslSocket pSocket)
+{
+ SAL_WARN_IF( !Set, "sal.osl", "undefined socket set" );
+ SAL_WARN_IF( !pSocket, "sal.osl", "undefined socket" );
+ if ( Set == nullptr || pSocket == nullptr )
+ {
+ return false;
+ }
+
+ return bool(FD_ISSET(pSocket->m_Socket, &Set->m_Set));
+}
+
+sal_Int32 SAL_CALL osl_demultiplexSocketEvents(oslSocketSet IncomingSet,
+ oslSocketSet OutgoingSet,
+ oslSocketSet OutOfBandSet,
+ const TimeValue* pTimeout)
+{
+ int MaxHandle= 0;
+ struct timeval tv;
+
+ if (pTimeout)
+ {
+ /* non-blocking call */
+ tv.tv_sec = pTimeout->Seconds;
+ tv.tv_usec = pTimeout->Nanosec / 1000;
+ }
+
+ /* get max handle from all sets */
+ if (IncomingSet)
+ MaxHandle= IncomingSet->m_MaxHandle;
+
+ if (OutgoingSet && (OutgoingSet->m_MaxHandle > MaxHandle))
+ MaxHandle= OutgoingSet->m_MaxHandle;
+
+ if (OutOfBandSet && (OutOfBandSet->m_MaxHandle > MaxHandle))
+ MaxHandle= OutOfBandSet->m_MaxHandle;
+
+ return select(MaxHandle+1,
+ IncomingSet ? PTR_FD_SET(IncomingSet->m_Set) : nullptr,
+ OutgoingSet ? PTR_FD_SET(OutgoingSet->m_Set) : nullptr,
+ OutOfBandSet ? PTR_FD_SET(OutOfBandSet->m_Set) : nullptr,
+ pTimeout ? &tv : nullptr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/sockimpl.hxx b/sal/osl/unx/sockimpl.hxx
new file mode 100644
index 000000000..1a9bc1f9e
--- /dev/null
+++ b/sal/osl/unx/sockimpl.hxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_SOCKIMPL_HXX
+#define INCLUDED_SAL_OSL_UNX_SOCKIMPL_HXX
+
+#include <osl/pipe.h>
+#include <osl/socket.h>
+#include <osl/interlck.h>
+
+#include "system.hxx"
+
+#if defined(LINUX) || defined(FREEBSD) || defined(NETBSD)
+#define CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT 1
+#endif
+
+struct oslSocketImpl {
+ int m_Socket;
+ int m_nLastError;
+ oslInterlockedCount m_nRefCount;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ bool m_bIsAccepting;
+ bool m_bIsInShutdown;
+#endif
+};
+
+struct oslSocketAddrImpl
+{
+ struct sockaddr m_sockaddr;
+};
+
+struct oslPipeImpl {
+ int m_Socket;
+ char m_Name[sizeof sockaddr_un::sun_path];
+ oslInterlockedCount m_nRefCount;
+ bool m_bClosed;
+#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
+ bool m_bIsAccepting;
+ bool m_bIsInShutdown;
+#endif
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/soffice.cxx b/sal/osl/unx/soffice.cxx
new file mode 100644
index 000000000..bcead388b
--- /dev/null
+++ b/sal/osl/unx/soffice.cxx
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <atomic>
+
+#include "soffice.hxx"
+
+namespace
+{
+std::atomic<bool> flag(false);
+}
+
+void sal::detail::setSoffice() { flag = true; }
+
+bool sal::detail::isSoffice() { return flag; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sal/osl/unx/soffice.hxx b/sal/osl/unx/soffice.hxx
new file mode 100644
index 000000000..da1b684af
--- /dev/null
+++ b/sal/osl/unx/soffice.hxx
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_SOFFICE_HXX
+#define INCLUDED_SAL_OSL_UNX_SOFFICE_HXX
+
+#include <sal/config.h>
+
+// Used to communicate special sal::detail::InitializeSoffice sal_detail_initialize call:
+
+namespace sal::detail
+{
+void setSoffice();
+
+bool isSoffice();
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sal/osl/unx/system.cxx b/sal/osl/unx/system.cxx
new file mode 100644
index 000000000..875f41016
--- /dev/null
+++ b/sal/osl/unx/system.cxx
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <config_features.h>
+
+#include "system.hxx"
+
+#ifdef NO_PTHREAD_RTL
+
+#if defined(MACOSX)
+
+#include <premac.h>
+#include <Foundation/Foundation.h>
+#include <postmac.h>
+
+/*
+ * Add support for resolving Mac native alias files (not the same as unix alias files)
+ * (what are "unix alias files"?)
+ * returns 0 on success.
+ */
+int macxp_resolveAlias(char *path, int buflen)
+{
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ /* Avoid unnecessary messages in the system.log:
+ *
+ * soffice(57342) deny file-read-data /Users/tml/Documents/b.odt/..namedfork/rsrc
+ * etc.
+ *
+ * Just don't bother with resolving aliases. I doubt its usefulness anyway.
+ */
+ (void) path;
+ (void) buflen;
+ return 0;
+#else
+ CFStringRef cfpath;
+ CFURLRef cfurl;
+ CFErrorRef cferror;
+ CFDataRef cfbookmark;
+
+ // Don't even try anything for files inside the app bundle. Just a
+ // waste of time.
+
+ static const char * const appBundle = [[[NSBundle mainBundle] bundlePath] UTF8String];
+
+ const size_t appBundleLen = strlen(appBundle);
+ if (strncmp(path, appBundle, appBundleLen) == 0 && path[appBundleLen] == '/')
+ return 0;
+
+ char *unprocessedPath = path;
+
+ if ( *unprocessedPath == '/' )
+ unprocessedPath++;
+
+ int nRet = 0;
+ while ( !nRet && unprocessedPath && *unprocessedPath )
+ {
+ unprocessedPath = strchr( unprocessedPath, '/' );
+ if ( unprocessedPath )
+ *unprocessedPath = '\0';
+
+ cfpath = CFStringCreateWithCString( nullptr, path, kCFStringEncodingUTF8 );
+ cfurl = CFURLCreateWithFileSystemPath( nullptr, cfpath, kCFURLPOSIXPathStyle, false );
+ CFRelease( cfpath );
+ cferror = nullptr;
+ cfbookmark = CFURLCreateBookmarkDataFromFile( nullptr, cfurl, &cferror );
+ CFRelease( cfurl );
+
+ if ( cfbookmark == nullptr )
+ {
+ if(cferror)
+ {
+ CFRelease( cferror );
+ }
+ }
+ else
+ {
+ Boolean isStale;
+ cfurl = CFURLCreateByResolvingBookmarkData( nullptr, cfbookmark, kCFBookmarkResolutionWithoutUIMask,
+ nullptr, nullptr, &isStale, &cferror );
+ CFRelease( cfbookmark );
+ if ( cfurl == nullptr )
+ {
+ CFRelease( cferror );
+ }
+ else
+ {
+ cfpath = CFURLCopyFileSystemPath( cfurl, kCFURLPOSIXPathStyle );
+ CFRelease( cfurl );
+ if ( cfpath != nullptr )
+ {
+ char tmpPath[ PATH_MAX ];
+ if ( CFStringGetCString( cfpath, tmpPath, PATH_MAX, kCFStringEncodingUTF8 ) )
+ {
+ int nLen = strlen( tmpPath ) + ( unprocessedPath ? strlen( unprocessedPath + 1 ) + 1 : 0 );
+ if ( nLen < buflen && nLen < PATH_MAX )
+ {
+ if ( unprocessedPath )
+ {
+ int nTmpPathLen = strlen( tmpPath );
+ strcat( tmpPath, "/" );
+ strcat( tmpPath, unprocessedPath + 1 );
+ strcpy( path, tmpPath);
+ unprocessedPath = path + nTmpPathLen;
+ }
+ else if ( !unprocessedPath )
+ {
+ strcpy( path, tmpPath );
+ }
+ }
+ else
+ {
+ errno = ENAMETOOLONG;
+ nRet = -1;
+ }
+ }
+ CFRelease( cfpath );
+ }
+ }
+ }
+
+ if ( unprocessedPath )
+ *unprocessedPath++ = '/';
+ }
+
+ return nRet;
+#endif
+}
+
+#endif /* defined MACOSX */
+
+#endif /* NO_PTHREAD_RTL */
+
+//might be useful on other platforms, but doesn't compiler under MACOSX anyway
+#if defined(__GNUC__) && defined(LINUX)
+//force the __data_start symbol to exist in any executables that link against
+//libuno_sal so that dlopening of the libgcj provided libjvm.so on some
+//platforms where it needs that symbol will succeed. e.g. Debian mips/lenny
+//with gcc 4.3. With this in place the smoketest succeeds with libgcj provided
+//java. Quite possibly also required/helpful for s390x/s390 and maybe some
+//others. Without it the dlopen of libjvm.so will fail with __data_start
+//not found
+extern int __data_start[] __attribute__((weak));
+extern int data_start[] __attribute__((weak));
+extern int _end[] __attribute__((weak));
+static void *dummy[] __attribute__((used)) = {__data_start, data_start, _end};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/system.hxx b/sal/osl/unx/system.hxx
new file mode 100644
index 000000000..bbf9c0449
--- /dev/null
+++ b/sal/osl/unx/system.hxx
@@ -0,0 +1,384 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_SYSTEM_HXX
+#define INCLUDED_SAL_OSL_UNX_SYSTEM_HXX
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <signal.h>
+#include <utime.h>
+
+#include <pwd.h>
+
+#include <netdb.h>
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <sys/types.h>
+
+/* Make sockets of type AF_UNIX use underlying FS rights */
+#if defined(__sun) && !defined(_XOPEN_SOURCE)
+# define _XOPEN_SOURCE 500
+# include <sys/socket.h>
+# undef _XOPEN_SOURCE
+#else
+# include <sys/socket.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef SYSV
+# include <sys/utsname.h>
+#endif
+
+#ifdef LINUX
+# ifndef __USE_GNU
+# define __USE_GNU
+# endif
+
+# include <shadow.h>
+# include <pthread.h>
+# include <sys/file.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <dlfcn.h>
+# include <endian.h>
+# include <sys/time.h>
+# define IORESOURCE_TRANSFER_BSD
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define pthread_testcancel()
+# define NO_PTHREAD_PRIORITY
+# define PTHREAD_SIGACTION pthread_sigaction
+
+# ifndef ETIME
+# define ETIME ETIMEDOUT
+# endif
+
+#endif
+
+#ifdef HAIKU
+# include <shadow.h>
+# include <pthread.h>
+# include <sys/file.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <dlfcn.h>
+# include <endian.h>
+# include <sys/time.h>
+# define IORESOURCE_TRANSFER_BSD
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define pthread_testcancel()
+# define NO_PTHREAD_PRIORITY
+# define NO_PTHREAD_RTL
+# define PTHREAD_SIGACTION pthread_sigaction
+
+# ifndef ETIME
+# define ETIME ETIMEDOUT
+# endif
+# define SIGIOT SIGABRT
+# define SOCK_RDM 0
+// hack: Haiku defines SOL_SOCKET as -1, but this makes GCC complain about
+// narrowing conversion
+# undef SOL_SOCKET
+# define SOL_SOCKET (sal_uInt32) -1
+
+#endif
+
+#if defined(ANDROID)
+# include <pthread.h>
+# include <sys/file.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <dlfcn.h>
+# include <endian.h>
+# include <sys/time.h>
+# define IORESOURCE_TRANSFER_BSD
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define pthread_testcancel()
+# define NO_PTHREAD_PRIORITY
+#endif
+
+#ifdef NETBSD
+# define NO_PTHREAD_RTL
+#endif
+
+#ifdef FREEBSD
+# define ETIME ETIMEDOUT
+# include <pthread.h>
+# include <sys/sem.h>
+# include <dlfcn.h>
+# include <sys/filio.h>
+# include <sys/ioctl.h>
+# include <sys/param.h>
+# include <sys/time.h>
+# include <sys/uio.h>
+# include <sys/exec.h>
+# include <vm/vm.h>
+# include <vm/vm_param.h>
+# include <vm/pmap.h>
+# include <vm/swap_pager.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# define IORESOURCE_TRANSFER_BSD
+# include <machine/endian.h>
+# define NO_PTHREAD_RTL
+#endif
+
+#ifdef OPENBSD
+# define ETIME ETIMEDOUT
+# include <pthread.h>
+# include <sys/sem.h>
+# include <dlfcn.h>
+# include <sys/filio.h>
+# include <sys/ioctl.h>
+# include <sys/param.h>
+# include <sys/time.h>
+# include <sys/uio.h>
+# include <sys/exec.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# define IORESOURCE_TRANSFER_BSD
+# include <machine/endian.h>
+# define PTR_SIZE_T(s) ((size_t *)&(s))
+# define IORESOURCE_TRANSFER_BSD
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define pthread_testcancel()
+# define NO_PTHREAD_PRIORITY
+# define NO_PTHREAD_RTL
+# define PTHREAD_SIGACTION pthread_sigaction
+#endif
+
+#if defined(DRAGONFLY) || defined(NETBSD)
+# define ETIME ETIMEDOUT
+# include <pthread.h>
+# include <sys/sem.h>
+# include <dlfcn.h>
+# include <sys/filio.h>
+# include <sys/ioctl.h>
+# include <sys/param.h>
+# include <sys/time.h>
+# include <sys/uio.h>
+# include <sys/exec.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <machine/endian.h>
+# define IORESOURCE_TRANSFER_BSD
+# define IOCHANNEL_TRANSFER_BSD_RENO
+#endif
+
+#ifdef AIX
+# define AF_IPX -1
+# include <strings.h>
+# include <pthread.h>
+# include <dlfcn.h>
+# include <sys/time.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <sys/machine.h>
+# define SLEEP_TIMESPEC(timespec) nsleep(&timespec, 0)
+# define LIBPATH "LIBPATH"
+#endif
+
+#ifdef __sun
+# include <shadow.h>
+# include <sys/un.h>
+# include <stropts.h>
+# include <pthread.h>
+# include <netinet/tcp.h>
+# include <sys/filio.h>
+# include <dlfcn.h>
+# include <sys/isa_defs.h>
+# define IORESOURCE_TRANSFER_SYSV
+# define IOCHANNEL_TRANSFER_BSD
+# define LIBPATH "LD_LIBRARY_PATH"
+#endif
+
+#ifdef MACOSX
+#define TimeValue CFTimeValue // Do not conflict with TimeValue in sal/inc/osl/time.h
+#include <Carbon/Carbon.h>
+#undef TimeValue
+# ifndef ETIME
+# define ETIME ETIMEDOUT
+# endif
+# include <dlfcn.h>
+# include <pthread.h>
+# include <sys/file.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <machine/endian.h>
+# include <sys/time.h>
+# include <mach-o/dyld.h>
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define NO_PTHREAD_RTL
+/* for NSGetArgc/Argv/Environ */
+# include <crt_externs.h>
+int macxp_resolveAlias(char *path, int buflen);
+#endif
+
+#ifdef IOS
+# ifndef ETIME
+# define ETIME ETIMEDOUT
+# endif
+# include <dlfcn.h>
+# include <pthread.h>
+# include <sys/file.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <sys/un.h>
+# include <netinet/tcp.h>
+# include <machine/endian.h>
+# include <sys/time.h>
+# define IOCHANNEL_TRANSFER_BSD_RENO
+# define NO_PTHREAD_RTL
+#endif
+
+#if !defined(_WIN32) && \
+ !defined(LINUX) && !defined(NETBSD) && !defined(FREEBSD) && \
+ !defined(AIX) && \
+ !defined(__sun) && !defined(MACOSX) && \
+ !defined(OPENBSD) && !defined(DRAGONFLY) && \
+ !defined(IOS) && !defined(ANDROID) && \
+ !defined(HAIKU)
+# error "Target platform not specified!"
+#endif
+
+#ifndef PTR_FD_SET
+# define PTR_FD_SET(s) (&(s))
+#endif
+
+#ifndef NORMALIZE_TIMESPEC
+# define NORMALIZE_TIMESPEC(timespec) \
+ timespec . tv_sec += timespec . tv_nsec / 1000000000; \
+ timespec . tv_nsec %= 1000000000;
+#endif
+
+#ifndef SET_TIMESPEC
+# define SET_TIMESPEC(timespec, sec, nsec) \
+ timespec . tv_sec = (sec); \
+ timespec . tv_nsec = (nsec); \
+ NORMALIZE_TIMESPEC(timespec);
+#endif
+
+#ifndef SLEEP_TIMESPEC
+# define SLEEP_TIMESPEC(timespec) nanosleep(&timespec, nullptr)
+#endif
+
+#ifndef INIT_GROUPS
+# define INIT_GROUPS(name, gid) ((setgid((gid)) == 0) && (initgroups((name), (gid)) == 0))
+#endif
+
+#ifndef PTHREAD_NONE
+# define PTHREAD_NONE _pthread_none_
+# ifndef PTHREAD_NONE_INIT
+# define PTHREAD_NONE_INIT ((pthread_t)-1)
+# endif
+#endif
+
+#ifndef PTHREAD_ATTR_DEFAULT
+# define PTHREAD_ATTR_DEFAULT nullptr
+#endif
+#ifndef PTHREAD_MUTEXATTR_DEFAULT
+# define PTHREAD_MUTEXATTR_DEFAULT nullptr
+#endif
+#ifndef PTHREAD_CONDATTR_DEFAULT
+# define PTHREAD_CONDATTR_DEFAULT nullptr
+#endif
+
+#ifndef PTHREAD_SIGACTION
+# define PTHREAD_SIGACTION sigaction
+#endif
+
+#ifndef STAT_PARENT
+# define STAT_PARENT lstat
+#endif
+
+/* socket options which might not be defined on all unx flavors */
+#ifndef SO_ACCEPTCONN
+# define SO_ACCEPTCONN 0
+#endif
+#ifndef SO_SNDLOWAT
+# define SO_SNDLOWAT 0
+#endif
+#ifndef SO_RCVLOWAT
+# define SO_RCVLOWAT 0
+#endif
+#ifndef SO_SNDTIMEO
+# define SO_SNDTIMEO 0
+#endif
+#ifndef SO_RCVTIMEO
+# define SO_RCVTIMEO 0
+#endif
+#ifndef SO_USELOOPBACK
+# define SO_USELOOPBACK 0
+#endif
+#ifndef MSG_MAXIOVLEN
+# define MSG_MAXIOVLEN 0
+#endif
+
+/* BEGIN HACK */
+/* dummy define and declarations for IPX should be replaced by */
+/* original ipx headers when these are available for this platform */
+
+#ifndef SA_FAMILY_DECL
+# define SA_FAMILY_DECL short sa_family
+#endif
+
+#define NSPROTO_IPX 1000
+#define NSPROTO_SPX 1256
+#define NSPROTO_SPXII 1257
+
+/* END HACK */
+
+#ifdef NO_PTHREAD_RTL
+#if !defined FREEBSD
+#if !defined NETBSD
+struct passwd *getpwent_r(struct passwd *pwd, char *buffer, int buflen);
+#endif
+extern struct spwd *getspnam_r(const char *name, struct spwd *result,
+ char *buffer, int buflen);
+
+#if !defined MACOSX
+struct tm *localtime_r(const time_t *timep, struct tm *buffer);
+struct tm *gmtime_r(const time_t *timep, struct tm *buffer);
+#endif
+#endif /* !defined(FREEBSD) */
+#endif
+
+#endif // INCLUDED_SAL_OSL_UNX_SYSTEM_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/system.mm b/sal/osl/unx/system.mm
new file mode 100644
index 000000000..7495a3e73
--- /dev/null
+++ b/sal/osl/unx/system.mm
@@ -0,0 +1 @@
+#include "system.cxx"
diff --git a/sal/osl/unx/tempfile.cxx b/sal/osl/unx/tempfile.cxx
new file mode 100644
index 000000000..84f44cbd4
--- /dev/null
+++ b/sal/osl/unx/tempfile.cxx
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "system.hxx"
+#include <osl/file.h>
+#include <osl/thread.h>
+#include <rtl/ustrbuf.h>
+#include <osl/diagnose.h>
+#include <sal/macros.h>
+
+#include "file_url.hxx"
+#include "file_impl.hxx"
+
+#include <cassert>
+
+oslFileError SAL_CALL osl_getTempDirURL( rtl_uString** pustrTempDir )
+{
+ oslFileError error;
+ /* described in environ(7) */
+ const char *pValue = getenv( "TMPDIR" );
+ rtl_uString *ustrTempPath = nullptr;
+
+ if ( !pValue )
+ pValue = getenv( "TEMP" );
+
+ if ( !pValue )
+ pValue = getenv( "TMP" );
+
+ if ( !pValue )
+ pValue = "/tmp";
+
+ rtl_string2UString( &ustrTempPath, pValue, strlen( pValue ), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
+ assert(ustrTempPath);
+ error = osl_getFileURLFromSystemPath( ustrTempPath, pustrTempDir );
+ rtl_uString_release( ustrTempPath );
+
+ return error;
+}
+
+/******************************************************************
+ * Generates a random unique file name. We're using the scheme
+ * from the standard c-lib function mkstemp to generate a more
+ * or less random unique file name
+ *
+ * @param rand_name
+ * receives the random name
+ ******************************************************************/
+
+static const char LETTERS[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+static const int COUNT_OF_LETTERS = SAL_N_ELEMENTS(LETTERS) - 1;
+
+#define RAND_NAME_LENGTH 6
+
+static void osl_gen_random_name_impl_(rtl_uString** rand_name)
+{
+ static uint64_t value;
+
+ char buffer[RAND_NAME_LENGTH];
+ struct timeval tv;
+ uint64_t v;
+ int i;
+
+ gettimeofday(&tv, nullptr);
+
+ value += (static_cast<uint64_t>(tv.tv_usec) << 16) ^ tv.tv_sec ^ getpid();
+
+ v = value;
+
+ for (i = 0; i < RAND_NAME_LENGTH; i++)
+ {
+ buffer[i] = LETTERS[v % COUNT_OF_LETTERS];
+ v /= COUNT_OF_LETTERS;
+ }
+
+ rtl_string2UString(
+ rand_name,
+ buffer,
+ RAND_NAME_LENGTH,
+ RTL_TEXTENCODING_ASCII_US,
+ OSTRING_TO_OUSTRING_CVTFLAGS);
+
+ assert(*rand_name);
+}
+
+/*****************************************************************
+ * Helper function
+ * Either use the directory provided or the result of
+ * osl_getTempDirUrl and return it as system path and file url
+ ****************************************************************/
+
+static oslFileError osl_setup_base_directory_impl_(
+ rtl_uString* pustrDirectoryURL,
+ rtl_uString** ppustr_base_dir)
+{
+ rtl_uString* dir_url = nullptr;
+ rtl_uString* dir = nullptr;
+ oslFileError error = osl_File_E_None;
+
+ if (pustrDirectoryURL)
+ rtl_uString_assign(&dir_url, pustrDirectoryURL);
+ else
+ error = osl_getTempDirURL(&dir_url);
+
+ if (error == osl_File_E_None)
+ {
+ error = osl_getSystemPathFromFileURL_Ex(dir_url, &dir);
+ rtl_uString_release(dir_url);
+ }
+
+ if (error == osl_File_E_None)
+ {
+ rtl_uString_assign(ppustr_base_dir, dir);
+ rtl_uString_release(dir);
+ }
+
+ return error;
+}
+
+/*****************************************************************
+ * osl_setup_createTempFile_impl
+ * validate input parameter, setup variables
+ ****************************************************************/
+
+ static oslFileError osl_setup_createTempFile_impl_(
+ rtl_uString* pustrDirectoryURL,
+ oslFileHandle* pHandle,
+ rtl_uString** ppustrTempFileURL,
+ rtl_uString** ppustr_base_dir,
+ bool* b_delete_on_close)
+ {
+ oslFileError osl_error;
+
+ OSL_PRECOND(((nullptr != pHandle) || (nullptr != ppustrTempFileURL)), "Invalid parameter!");
+
+ if ((pHandle == nullptr) && (ppustrTempFileURL == nullptr))
+ {
+ osl_error = osl_File_E_INVAL;
+ }
+ else
+ {
+ osl_error = osl_setup_base_directory_impl_(
+ pustrDirectoryURL, ppustr_base_dir);
+
+ *b_delete_on_close = (ppustrTempFileURL == nullptr);
+ }
+
+ return osl_error;
+ }
+
+/*****************************************************************
+ * Create a unique file in the specified directory and return
+ * its name
+ ****************************************************************/
+
+static oslFileError osl_create_temp_file_impl_(
+ const rtl_uString* pustr_base_directory,
+ oslFileHandle* file_handle,
+ rtl_uString** ppustr_temp_file_name)
+{
+ rtl_uString* rand_name = nullptr;
+ sal_uInt32 len_base_dir = 0;
+ rtl_uString* tmp_file_path = nullptr;
+ rtl_uString* tmp_file_url = nullptr;
+ sal_Int32 capacity = 0;
+ oslFileError osl_error = osl_File_E_None;
+ sal_Int32 offset_file_name;
+ const sal_Unicode* puchr;
+
+ OSL_PRECOND(pustr_base_directory, "Invalid Parameter");
+ OSL_PRECOND(file_handle, "Invalid Parameter");
+ OSL_PRECOND(ppustr_temp_file_name, "Invalid Parameter");
+
+ len_base_dir = rtl_uString_getLength(pustr_base_directory);
+
+ rtl_uString_new_WithLength(
+ &tmp_file_path,
+ (len_base_dir + 1 + RAND_NAME_LENGTH));
+ capacity = len_base_dir + 1 + RAND_NAME_LENGTH;
+
+ rtl_uStringbuffer_insert(
+ &tmp_file_path,
+ &capacity,
+ 0,
+ rtl_uString_getStr(const_cast<rtl_uString*>(pustr_base_directory)),
+ len_base_dir);
+
+ offset_file_name = len_base_dir;
+
+ puchr = rtl_uString_getStr(tmp_file_path);
+
+ /* ensure that the last character is a '/' */
+
+ if (puchr[len_base_dir - 1] != '/')
+ {
+ rtl_uStringbuffer_insert_ascii(
+ &tmp_file_path,
+ &capacity,
+ len_base_dir,
+ "/",
+ 1);
+
+ offset_file_name++;
+ }
+
+ while(true) /* try until success */
+ {
+ osl_gen_random_name_impl_(&rand_name);
+
+ rtl_uStringbuffer_insert(
+ &tmp_file_path,
+ &capacity,
+ offset_file_name,
+ rtl_uString_getStr(rand_name),
+ rtl_uString_getLength(rand_name));
+
+ osl_error = osl_getFileURLFromSystemPath(
+ tmp_file_path, &tmp_file_url);
+
+ if (osl_error == osl_File_E_None)
+ {
+ osl_error = openFile(
+ tmp_file_url,
+ file_handle,
+ osl_File_OpenFlag_Read |
+ osl_File_OpenFlag_Write |
+ osl_File_OpenFlag_Create,
+ S_IRUSR | S_IWUSR);
+ }
+
+ /* in case of error osl_File_E_EXIST we simply try again else we give up */
+
+ if (osl_error != osl_File_E_EXIST)
+ {
+ rtl_uString_release(rand_name);
+
+ if (tmp_file_url)
+ rtl_uString_release(tmp_file_url);
+
+ break;
+ }
+ } /* while(1) */
+
+ if (osl_error == osl_File_E_None)
+ rtl_uString_assign(ppustr_temp_file_name, tmp_file_path);
+
+ rtl_uString_release(tmp_file_path);
+
+ return osl_error;
+}
+
+oslFileError SAL_CALL osl_createTempFile(
+ rtl_uString* pustrDirectoryURL,
+ oslFileHandle* pHandle,
+ rtl_uString** ppustrTempFileURL)
+{
+ rtl_uString* base_directory = nullptr;
+ oslFileHandle temp_file_handle;
+ bool b_delete_on_close;
+ oslFileError osl_error;
+
+ osl_error = osl_setup_createTempFile_impl_(
+ pustrDirectoryURL,
+ pHandle,
+ ppustrTempFileURL,
+ &base_directory,
+ &b_delete_on_close);
+
+ if (osl_error != osl_File_E_None)
+ return osl_error;
+
+ rtl_uString* temp_file_name = nullptr;
+ osl_error = osl_create_temp_file_impl_(
+ base_directory, &temp_file_handle, &temp_file_name);
+
+ rtl_uString* temp_file_url = nullptr;
+ if (osl_error == osl_File_E_None)
+ {
+ osl_error = osl_getFileURLFromSystemPath(temp_file_name, &temp_file_url);
+ rtl_uString_release(temp_file_name);
+ }
+
+ if (osl_error == osl_File_E_None)
+ {
+ if (b_delete_on_close)
+ {
+ osl_error = osl_removeFile(temp_file_url);
+
+ if (osl_error == osl_File_E_None)
+ {
+ *pHandle = temp_file_handle;
+ temp_file_handle = nullptr;
+ }
+ }
+ else
+ {
+ if (pHandle)
+ {
+ *pHandle = temp_file_handle;
+ temp_file_handle = nullptr;
+ }
+
+ rtl_uString_assign(ppustrTempFileURL, temp_file_url);
+ }
+
+ rtl_uString_release(temp_file_url);
+ }
+
+ if (temp_file_handle)
+ osl_closeFile(temp_file_handle);
+
+ rtl_uString_release(base_directory);
+
+ return osl_error;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/thread.cxx b/sal/osl/unx/thread.cxx
new file mode 100644
index 000000000..ab19bdc8a
--- /dev/null
+++ b/sal/osl/unx/thread.cxx
@@ -0,0 +1,1100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstddef>
+#include <limits>
+#include <functional>
+
+#include "system.hxx"
+#include "unixerrnostring.hxx"
+#include <string.h>
+#if defined(OPENBSD)
+#include <sched.h>
+#endif
+#include <config_options.h>
+#include <o3tl/safeint.hxx>
+#include <osl/thread.h>
+#include <osl/nlsupport.h>
+#include <rtl/textenc.h>
+#include <rtl/alloc.h>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#ifdef ANDROID
+#include <jni.h>
+#include <android/log.h>
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+#if defined LINUX && ! defined __FreeBSD_kernel__
+#include <sys/syscall.h>
+#endif
+
+/****************************************************************************
+ * @@@ TODO @@@
+ *
+ * (1) 'osl_thread_priority_init_Impl()'
+ * - insane assumption that initializing caller is main thread
+ * - use _POSIX_THREAD_PRIORITY_SCHEDULING, not NO_PTHREAD_PRIORITY (?)
+ * - POSIX doesn't require defined prio's for SCHED_OTHER (!)
+ * - use SCHED_RR instead of SCHED_OTHER for defined behaviour (?)
+ * (2) 'oslThreadIdentifier' and '{insert|remove|lookup}ThreadId()'
+ * - cannot reliably be applied to 'alien' threads;
+ * - memory leak for 'alien' thread 'HashEntry's;
+ * - use 'reinterpret_cast<unsigned long>(pthread_t)' as identifier
+ * instead (?)
+ * - if yes, change 'oslThreadIdentifier' to 'intptr_t' or similar
+ * (3) 'oslSigAlarmHandler()' (#71232#)
+ * - [Under Solaris we get SIGALRM in e.g. pthread_join which terminates
+ * the process. So we initialize our signal handling module and do
+ * register a SIGALRM Handler which catches and ignores it]
+ * - should this still happen, 'signal.c' needs to be fixed instead.
+ *
+ ****************************************************************************/
+
+#define THREADIMPL_FLAGS_TERMINATE 0x00001
+#define THREADIMPL_FLAGS_STARTUP 0x00002
+#define THREADIMPL_FLAGS_SUSPENDED 0x00004
+#define THREADIMPL_FLAGS_ACTIVE 0x00008
+#define THREADIMPL_FLAGS_ATTACHED 0x00010
+#define THREADIMPL_FLAGS_DESTROYED 0x00020
+
+namespace {
+
+typedef struct osl_thread_impl_st
+{
+ pthread_t m_hThread;
+ oslThreadIdentifier m_Ident; /* @@@ see TODO @@@ */
+ short m_Flags;
+ oslWorkerFunction m_WorkerFunction;
+ void* m_pData;
+ pthread_mutex_t m_Lock;
+ pthread_cond_t m_Cond;
+} Thread_Impl;
+
+#if !defined NO_PTHREAD_PRIORITY
+struct osl_thread_priority_st
+{
+ int m_Highest;
+ int m_Above_Normal;
+ int m_Normal;
+ int m_Below_Normal;
+ int m_Lowest;
+};
+#endif
+
+}
+
+#if !defined NO_PTHREAD_PRIORITY
+#define OSL_THREAD_PRIORITY_INITIALIZER { 127, 96, 64, 32, 0 }
+#endif
+
+static void osl_thread_priority_init_Impl();
+
+namespace {
+
+struct osl_thread_textencoding_st
+{
+ pthread_key_t m_key; /* key to store thread local text encoding */
+ rtl_TextEncoding m_default; /* the default text encoding */
+};
+
+}
+
+#define OSL_THREAD_TEXTENCODING_INITIALIZER { 0, RTL_TEXTENCODING_DONTKNOW }
+static void osl_thread_textencoding_init_Impl();
+
+namespace {
+
+struct osl_thread_global_st
+{
+ pthread_once_t m_once;
+#if !defined NO_PTHREAD_PRIORITY
+ struct osl_thread_priority_st m_priority;
+#endif
+ struct osl_thread_textencoding_st m_textencoding;
+};
+
+}
+
+static struct osl_thread_global_st g_thread =
+{
+ PTHREAD_ONCE_INIT,
+#if !defined NO_PTHREAD_PRIORITY
+ OSL_THREAD_PRIORITY_INITIALIZER,
+#endif
+ OSL_THREAD_TEXTENCODING_INITIALIZER
+};
+
+static void osl_thread_init_Impl();
+
+static Thread_Impl* osl_thread_construct_Impl();
+static void osl_thread_destruct_Impl (Thread_Impl ** ppImpl);
+
+static void* osl_thread_start_Impl (void * pData);
+static void osl_thread_cleanup_Impl (Thread_Impl * pImpl);
+
+static oslThread osl_thread_create_Impl (
+ oslWorkerFunction pWorker, void * pThreadData, short nFlags);
+
+/* @@@ see TODO @@@ */
+static oslThreadIdentifier insertThreadId (pthread_t hThread);
+static oslThreadIdentifier lookupThreadId (pthread_t hThread);
+static void removeThreadId (pthread_t hThread);
+
+static void osl_thread_init_Impl()
+{
+ osl_thread_priority_init_Impl();
+ osl_thread_textencoding_init_Impl();
+}
+
+Thread_Impl* osl_thread_construct_Impl()
+{
+ Thread_Impl* pImpl = new Thread_Impl;
+ memset (pImpl, 0, sizeof(Thread_Impl));
+
+ pthread_mutex_init (&(pImpl->m_Lock), PTHREAD_MUTEXATTR_DEFAULT);
+ pthread_cond_init (&(pImpl->m_Cond), PTHREAD_CONDATTR_DEFAULT);
+ return pImpl;
+}
+
+static void osl_thread_destruct_Impl (Thread_Impl ** ppImpl)
+{
+ assert(ppImpl);
+ if (*ppImpl)
+ {
+ pthread_cond_destroy (&((*ppImpl)->m_Cond));
+ pthread_mutex_destroy (&((*ppImpl)->m_Lock));
+
+ delete *ppImpl;
+ (*ppImpl) = nullptr;
+ }
+}
+
+static void osl_thread_cleanup_Impl (Thread_Impl * pImpl)
+{
+ pthread_t thread;
+ bool attached;
+ bool destroyed;
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ thread = pImpl->m_hThread;
+ attached = (pImpl->m_Flags & THREADIMPL_FLAGS_ATTACHED) != 0;
+ destroyed = (pImpl->m_Flags & THREADIMPL_FLAGS_DESTROYED) != 0;
+ pImpl->m_Flags &= ~(THREADIMPL_FLAGS_ACTIVE | THREADIMPL_FLAGS_ATTACHED);
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+
+ /* release oslThreadIdentifier @@@ see TODO @@@ */
+ removeThreadId (thread);
+
+ if (attached)
+ {
+ pthread_detach (thread);
+ }
+
+ if (destroyed)
+ {
+ osl_thread_destruct_Impl (&pImpl);
+ }
+}
+
+static void* osl_thread_start_Impl (void* pData)
+{
+ bool terminate;
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(pData);
+
+ assert(pImpl);
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ /* request oslThreadIdentifier @@@ see TODO @@@ */
+ pImpl->m_Ident = insertThreadId (pImpl->m_hThread);
+
+ /* signal change from STARTUP to ACTIVE state */
+ pImpl->m_Flags &= ~THREADIMPL_FLAGS_STARTUP;
+ pImpl->m_Flags |= THREADIMPL_FLAGS_ACTIVE;
+ pthread_cond_signal (&(pImpl->m_Cond));
+
+ /* Check if thread is started in SUSPENDED state */
+ while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
+ {
+ /* wait until SUSPENDED flag is cleared */
+ pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
+ }
+
+ /* check for SUSPENDED to TERMINATE state change */
+ terminate = ((pImpl->m_Flags & THREADIMPL_FLAGS_TERMINATE) > 0);
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+
+ if (!terminate)
+ {
+#ifdef ANDROID
+ JNIEnv* env = 0;
+ int res = (*lo_get_javavm()).AttachCurrentThread(&env, NULL);
+ __android_log_print(ANDROID_LOG_INFO, "LibreOffice", "New sal thread started and attached res=%d", res);
+#endif
+ /* call worker function */
+ pImpl->m_WorkerFunction(pImpl->m_pData);
+
+#ifdef ANDROID
+ res = (*lo_get_javavm()).DetachCurrentThread();
+ __android_log_print(ANDROID_LOG_INFO, "LibreOffice", "Detached finished sal thread res=%d", res);
+#endif
+ }
+
+ osl_thread_cleanup_Impl (pImpl);
+ return nullptr;
+}
+
+static oslThread osl_thread_create_Impl (
+ oslWorkerFunction pWorker,
+ void* pThreadData,
+ short nFlags)
+{
+ Thread_Impl* pImpl;
+#if defined OPENBSD || ((defined MACOSX || defined LINUX) && !ENABLE_RUNTIME_OPTIMIZATIONS)
+ pthread_attr_t attr;
+ size_t stacksize;
+#endif
+ int nRet=0;
+
+ pImpl = osl_thread_construct_Impl();
+ if (!pImpl)
+ return nullptr; /* ENOMEM */
+
+ pImpl->m_WorkerFunction = pWorker;
+ pImpl->m_pData = pThreadData;
+ pImpl->m_Flags = nFlags | THREADIMPL_FLAGS_STARTUP;
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+#if defined OPENBSD || ((defined MACOSX || defined LINUX) && !ENABLE_RUNTIME_OPTIMIZATIONS)
+ if (pthread_attr_init(&attr) != 0)
+ return nullptr;
+
+#if defined OPENBSD
+ stacksize = 262144;
+#else
+ stacksize = 12 * 1024 * 1024; // 8MB is not enough for ASAN on x86-64
+#endif
+ if (pthread_attr_setstacksize(&attr, stacksize) != 0) {
+ pthread_attr_destroy(&attr);
+ return nullptr;
+ }
+#endif
+
+ if ((nRet = pthread_create (
+ &(pImpl->m_hThread),
+#if defined OPENBSD || ((defined MACOSX || defined LINUX) && !ENABLE_RUNTIME_OPTIMIZATIONS)
+ &attr,
+#else
+ PTHREAD_ATTR_DEFAULT,
+#endif
+ osl_thread_start_Impl,
+ static_cast<void*>(pImpl))) != 0)
+ {
+ SAL_WARN(
+ "sal.osl",
+ "pthread_create failed: " << UnixErrnoString(nRet));
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+ osl_thread_destruct_Impl (&pImpl);
+
+ return nullptr;
+ }
+
+#if defined OPENBSD || ((defined MACOSX || defined LINUX) && !ENABLE_RUNTIME_OPTIMIZATIONS)
+ pthread_attr_destroy(&attr);
+#endif
+
+ /* wait for change from STARTUP to ACTIVE state */
+ while (pImpl->m_Flags & THREADIMPL_FLAGS_STARTUP)
+ {
+ /* wait until STARTUP flag is cleared */
+ pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
+ }
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+
+ return static_cast<oslThread>(pImpl);
+}
+
+oslThread osl_createThread (
+ oslWorkerFunction pWorker,
+ void * pThreadData)
+{
+ return osl_thread_create_Impl (
+ pWorker,
+ pThreadData,
+ THREADIMPL_FLAGS_ATTACHED);
+}
+
+oslThread osl_createSuspendedThread (
+ oslWorkerFunction pWorker,
+ void * pThreadData)
+{
+ return osl_thread_create_Impl (
+ pWorker,
+ pThreadData,
+ THREADIMPL_FLAGS_ATTACHED |
+ THREADIMPL_FLAGS_SUSPENDED );
+}
+
+void SAL_CALL osl_destroyThread(oslThread Thread)
+{
+ if (Thread != nullptr) {
+ Thread_Impl * impl = static_cast<Thread_Impl *>(Thread);
+ bool active;
+ pthread_mutex_lock(&impl->m_Lock);
+ active = (impl->m_Flags & THREADIMPL_FLAGS_ACTIVE) != 0;
+ impl->m_Flags |= THREADIMPL_FLAGS_DESTROYED;
+ pthread_mutex_unlock(&impl->m_Lock);
+ if (!active) {
+ osl_thread_destruct_Impl(&impl);
+ }
+ }
+}
+
+void SAL_CALL osl_resumeThread(oslThread Thread)
+{
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_resumeThread(nullptr) call");
+ return; /* EINVAL */
+ }
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ if (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
+ {
+ /* clear SUSPENDED flag */
+ pImpl->m_Flags &= ~THREADIMPL_FLAGS_SUSPENDED;
+ pthread_cond_signal (&(pImpl->m_Cond));
+ }
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+}
+
+void SAL_CALL osl_suspendThread(oslThread Thread)
+{
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_suspendThread(nullptr) call");
+ return; /* EINVAL */
+ }
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ pImpl->m_Flags |= THREADIMPL_FLAGS_SUSPENDED;
+
+ if (pthread_equal (pthread_self(), pImpl->m_hThread))
+ {
+ /* self suspend */
+ while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
+ {
+ /* wait until SUSPENDED flag is cleared */
+ pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
+ }
+ }
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+}
+
+sal_Bool SAL_CALL osl_isThreadRunning(const oslThread Thread)
+{
+ bool active;
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ return false;
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+ active = ((pImpl->m_Flags & THREADIMPL_FLAGS_ACTIVE) > 0);
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+
+ return active;
+}
+
+void SAL_CALL osl_joinWithThread(oslThread Thread)
+{
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ return;
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ pthread_t const thread = pImpl->m_hThread;
+ bool const attached = ((pImpl->m_Flags & THREADIMPL_FLAGS_ATTACHED) > 0);
+
+ /* check this only if *this* thread is still attached - if it's not,
+ then it could have terminated and another newly created thread could
+ have recycled the same id as m_hThread! */
+ if (attached && pthread_equal(pthread_self(), pImpl->m_hThread))
+ {
+ assert(false); /* Win32 implementation would deadlock here! */
+ /* self join */
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+ return; /* EDEADLK */
+ }
+
+ pImpl->m_Flags &= ~THREADIMPL_FLAGS_ATTACHED;
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+
+ if (attached)
+ {
+ pthread_join (thread, nullptr);
+ }
+}
+
+void SAL_CALL osl_terminateThread(oslThread Thread)
+{
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_terminateThread(nullptr) call");
+ return; /* EINVAL */
+ }
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ if (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
+ {
+ /* clear SUSPENDED flag */
+ pImpl->m_Flags &= ~THREADIMPL_FLAGS_SUSPENDED;
+ pthread_cond_signal (&(pImpl->m_Cond));
+ }
+
+ pImpl->m_Flags |= THREADIMPL_FLAGS_TERMINATE;
+
+ pthread_mutex_unlock (&(pImpl->m_Lock));
+}
+
+sal_Bool SAL_CALL osl_scheduleThread(oslThread Thread)
+{
+ bool terminate;
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_scheduleThread(nullptr) call");
+ return false; /* EINVAL */
+ }
+
+ if (!(pthread_equal (pthread_self(), pImpl->m_hThread)))
+ {
+ SAL_WARN("sal.osl", "invalid osl_scheduleThread(non-self) call");
+ return false; /* EINVAL */
+ }
+
+ pthread_mutex_lock (&(pImpl->m_Lock));
+
+ while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
+ {
+ /* wait until SUSPENDED flag is cleared */
+ pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
+ }
+
+ terminate = ((pImpl->m_Flags & THREADIMPL_FLAGS_TERMINATE) > 0);
+
+ pthread_mutex_unlock(&(pImpl->m_Lock));
+
+ return !terminate;
+}
+
+void SAL_CALL osl_waitThread(const TimeValue* pDelay)
+{
+ if (pDelay)
+ {
+ struct timespec delay;
+
+ SET_TIMESPEC(delay, pDelay->Seconds, pDelay->Nanosec);
+
+ SLEEP_TIMESPEC(delay);
+ }
+}
+
+/** Yields thread
+
+ @attention Note that POSIX scheduling @em really requires threads to call this
+ function, since a thread only reschedules to other thread, when
+ it blocks (sleep, blocking I/O) OR calls sched_yield().
+*/
+void SAL_CALL osl_yieldThread()
+{
+ sched_yield();
+}
+
+void SAL_CALL osl_setThreadName(char const * name)
+{
+ assert( name );
+#if defined LINUX && ! defined __FreeBSD_kernel__
+ const int LINUX_THREAD_NAME_MAXLEN = 15;
+ if ( strlen( name ) > LINUX_THREAD_NAME_MAXLEN )
+ SAL_INFO( "sal.osl", "osl_setThreadName truncated thread name to "
+ << LINUX_THREAD_NAME_MAXLEN << " chars from name '"
+ << name << "'" );
+ char shortname[ LINUX_THREAD_NAME_MAXLEN + 1 ];
+ shortname[ LINUX_THREAD_NAME_MAXLEN ] = '\0';
+ strncpy( shortname, name, LINUX_THREAD_NAME_MAXLEN );
+ int err = pthread_setname_np( pthread_self(), shortname );
+ if ( 0 != err )
+ SAL_WARN("sal.osl", "pthread_setname_np failed with errno " << err);
+#elif defined __FreeBSD_kernel__
+ pthread_setname_np( pthread_self(), name );
+#elif defined MACOSX || defined IOS
+ pthread_setname_np( name );
+#else
+ (void) name;
+#endif
+}
+
+/* osl_getThreadIdentifier @@@ see TODO @@@ */
+
+namespace {
+
+struct HashEntry
+{
+ pthread_t Handle;
+ oslThreadIdentifier Ident;
+ HashEntry * Next;
+};
+
+}
+
+static HashEntry* HashTable[31];
+static const int HashSize = SAL_N_ELEMENTS(HashTable);
+
+static pthread_mutex_t HashLock = PTHREAD_MUTEX_INITIALIZER;
+
+#if ! ((defined LINUX && !defined __FreeBSD_kernel__) || defined MACOSX || defined IOS)
+static oslThreadIdentifier LastIdent = 0;
+#endif
+
+namespace {
+
+std::size_t HASHID(pthread_t x)
+{ return std::hash<pthread_t>()(x) % HashSize; }
+
+}
+
+static oslThreadIdentifier lookupThreadId (pthread_t hThread)
+{
+ HashEntry *pEntry;
+
+ pthread_mutex_lock(&HashLock);
+
+ pEntry = HashTable[HASHID(hThread)];
+ while (pEntry != nullptr)
+ {
+ if (pthread_equal(pEntry->Handle, hThread))
+ {
+ pthread_mutex_unlock(&HashLock);
+ return pEntry->Ident;
+ }
+ pEntry = pEntry->Next;
+ }
+
+ pthread_mutex_unlock(&HashLock);
+
+ return 0;
+}
+
+static oslThreadIdentifier insertThreadId (pthread_t hThread)
+{
+ HashEntry *pEntry, *pInsert = nullptr;
+
+ pthread_mutex_lock(&HashLock);
+
+ pEntry = HashTable[HASHID(hThread)];
+
+ while (pEntry != nullptr)
+ {
+ if (pthread_equal(pEntry->Handle, hThread))
+ break;
+
+ pInsert = pEntry;
+ pEntry = pEntry->Next;
+ }
+
+ if (pEntry == nullptr)
+ {
+ pEntry = static_cast<HashEntry*>(calloc(sizeof(HashEntry), 1));
+
+ pEntry->Handle = hThread;
+
+#if defined LINUX && ! defined __FreeBSD_kernel__
+#if defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30))
+ // gettid returns a pid_t, which POSIX defines to be a signed integer type; assume that all
+ // valid pid_t values on Linux are positive (zero is filtered out in the generic code
+ // below):
+ pid_t const tid = gettid();
+ assert(tid >= 0);
+#else
+ long const tid = syscall(SYS_gettid);
+ if (tid < 0 || o3tl::make_unsigned(tid) > std::numeric_limits<sal_uInt32>::max()) {
+ std::abort();
+ }
+#endif
+ pEntry->Ident = tid;
+#elif defined MACOSX || defined IOS
+ // currently the value of pthread_threadid_np is the same then
+ // syscall(SYS_thread_selfid), which returns an int as the TID.
+ // may change, as the syscall interface was deprecated.
+ uint64_t mac_tid;
+ pthread_threadid_np(nullptr, &mac_tid);
+ if (mac_tid > SAL_MAX_UINT32)
+ std::abort();
+ pEntry->Ident = mac_tid;
+#else
+ ++LastIdent;
+ if (0 == LastIdent)
+ LastIdent = 1;
+ pEntry->Ident = LastIdent;
+#endif
+ if (0 == pEntry->Ident)
+ std::abort();
+
+ if (pInsert)
+ pInsert->Next = pEntry;
+ else
+ HashTable[HASHID(hThread)] = pEntry;
+ }
+
+ pthread_mutex_unlock(&HashLock);
+
+ return pEntry->Ident;
+}
+
+static void removeThreadId (pthread_t hThread)
+{
+ HashEntry *pEntry, *pRemove = nullptr;
+
+ pthread_mutex_lock(&HashLock);
+
+ pEntry = HashTable[HASHID(hThread)];
+ while (pEntry != nullptr)
+ {
+ if (pthread_equal(pEntry->Handle, hThread))
+ break;
+
+ pRemove = pEntry;
+ pEntry = pEntry->Next;
+ }
+
+ if (pEntry != nullptr)
+ {
+ if (pRemove)
+ pRemove->Next = pEntry->Next;
+ else
+ HashTable[HASHID(hThread)] = pEntry->Next;
+
+ free(pEntry);
+ }
+
+ pthread_mutex_unlock(&HashLock);
+}
+
+oslThreadIdentifier SAL_CALL osl_getThreadIdentifier(oslThread Thread)
+{
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+ oslThreadIdentifier Ident;
+
+ if (pImpl)
+ Ident = pImpl->m_Ident;
+ else
+ {
+ /* current thread */
+ pthread_t current = pthread_self();
+
+ Ident = lookupThreadId (current);
+ if (Ident == 0)
+ /* @@@ see TODO: alien pthread_self() @@@ */
+ Ident = insertThreadId (current);
+ }
+
+ return Ident;
+}
+
+/*****************************************************************************
+ @@@ see TODO @@@
+ osl_thread_priority_init_Impl
+
+ set the base-priority of the main-thread to
+ oslThreadPriorityNormal (64) since 0 (lowest) is
+ the system default. This behaviour collides with
+ our enum-priority definition (highest..normal..lowest).
+ A normaluser will expect the main-thread of an app.
+ to have the "normal" priority.
+
+*****************************************************************************/
+static void osl_thread_priority_init_Impl()
+{
+#ifndef NO_PTHREAD_PRIORITY
+ struct sched_param param;
+ int policy=0;
+ int nRet=0;
+
+/* @@@ see TODO: calling thread may not be main thread @@@ */
+
+ if ((nRet = pthread_getschedparam(pthread_self(), &policy, &param)) != 0)
+ {
+ SAL_WARN(
+ "sal.osl",
+ "pthread_getschedparam failed: " << UnixErrnoString(nRet));
+ return;
+ }
+
+#if defined (__sun)
+ if ( policy >= _SCHED_NEXT)
+ {
+ /* mfe: pthread_getschedparam on Solaris has a possible Bug */
+ /* one gets 959917873 as the policy */
+ /* so set the policy to a default one */
+ policy=SCHED_OTHER;
+ }
+#endif /* __sun */
+
+ if ((nRet = sched_get_priority_min(policy) ) != -1)
+ {
+ SAL_INFO(
+ "sal.osl", "Min Prioriy for policy " << policy << " == " << nRet);
+ g_thread.m_priority.m_Lowest=nRet;
+ }
+ else
+ {
+ int e = errno;
+ SAL_WARN(
+ "sal.osl",
+ "sched_get_priority_min failed: " << UnixErrnoString(e));
+ }
+
+ if ((nRet = sched_get_priority_max(policy) ) != -1)
+ {
+ SAL_INFO(
+ "sal.osl", "Max Prioriy for policy " << policy << " == " << nRet);
+ g_thread.m_priority.m_Highest=nRet;
+ }
+ else
+ {
+ int e = errno;
+ SAL_WARN(
+ "sal.osl",
+ "sched_get_priority_max failed: " << UnixErrnoString(e));
+ }
+
+ g_thread.m_priority.m_Normal =
+ (g_thread.m_priority.m_Lowest + g_thread.m_priority.m_Highest) / 2;
+ g_thread.m_priority.m_Below_Normal =
+ (g_thread.m_priority.m_Lowest + g_thread.m_priority.m_Normal) / 2;
+ g_thread.m_priority.m_Above_Normal =
+ (g_thread.m_priority.m_Normal + g_thread.m_priority.m_Highest) / 2;
+
+/* @@@ set prio of calling (not main) thread (?) @@@ */
+
+ param.sched_priority= g_thread.m_priority.m_Normal;
+
+ if ((nRet = pthread_setschedparam(pthread_self(), policy, &param)) != 0)
+ {
+ SAL_WARN(
+ "sal.osl",
+ "pthread_setschedparam failed: " << UnixErrnoString(nRet));
+ SAL_INFO(
+ "sal.osl",
+ "Thread ID " << pthread_self() << ", Policy " << policy
+ << ", Priority " << param.sched_priority);
+ }
+
+#endif /* NO_PTHREAD_PRIORITY */
+}
+
+/**
+ Impl-Notes: contrary to solaris-docu, which claims
+ valid priority-levels from 0 .. INT_MAX, only the
+ range 0..127 is accepted. (0 lowest, 127 highest)
+*/
+void SAL_CALL osl_setThreadPriority (
+ oslThread Thread,
+ oslThreadPriority Priority)
+{
+#ifndef NO_PTHREAD_PRIORITY
+
+ struct sched_param Param;
+ int policy;
+ int nRet;
+
+#endif /* NO_PTHREAD_PRIORITY */
+
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_setThreadPriority(nullptr, ...) call");
+ return; /* EINVAL */
+ }
+
+#ifdef NO_PTHREAD_PRIORITY
+ (void) Priority; /* unused */
+#else /* NO_PTHREAD_PRIORITY */
+
+ if (pthread_getschedparam(pImpl->m_hThread, &policy, &Param) != 0)
+ return; /* ESRCH */
+
+#if defined (__sun)
+ if ( policy >= _SCHED_NEXT)
+ {
+ /* mfe: pthread_getschedparam on Solaris has a possible Bug */
+ /* one gets 959917873 as the policy */
+ /* so set the policy to a default one */
+ policy=SCHED_OTHER;
+ }
+#endif /* __sun */
+
+ pthread_once (&(g_thread.m_once), osl_thread_init_Impl);
+
+ switch(Priority)
+ {
+ case osl_Thread_PriorityHighest:
+ Param.sched_priority= g_thread.m_priority.m_Highest;
+ break;
+
+ case osl_Thread_PriorityAboveNormal:
+ Param.sched_priority= g_thread.m_priority.m_Above_Normal;
+ break;
+
+ case osl_Thread_PriorityNormal:
+ Param.sched_priority= g_thread.m_priority.m_Normal;
+ break;
+
+ case osl_Thread_PriorityBelowNormal:
+ Param.sched_priority= g_thread.m_priority.m_Below_Normal;
+ break;
+
+ case osl_Thread_PriorityLowest:
+ Param.sched_priority= g_thread.m_priority.m_Lowest;
+ break;
+
+ case osl_Thread_PriorityUnknown:
+ SAL_WARN(
+ "sal.osl",
+ "invalid osl_setThreadPriority(..., osl_Thread_PriorityUnknown)"
+ " call");
+ return;
+
+ default:
+ SAL_WARN(
+ "sal.osl",
+ "invalid osl_setThreadPriority(..., " << Priority << ") call");
+ return;
+ }
+
+ if ((nRet = pthread_setschedparam(pImpl->m_hThread, policy, &Param)) != 0)
+ {
+ SAL_WARN(
+ "sal.osl",
+ "pthread_setschedparam failed: " << UnixErrnoString(nRet));
+ }
+
+#endif /* NO_PTHREAD_PRIORITY */
+}
+
+oslThreadPriority SAL_CALL osl_getThreadPriority(const oslThread Thread)
+{
+#ifndef NO_PTHREAD_PRIORITY
+
+ struct sched_param Param;
+ int Policy;
+
+#endif /* NO_PTHREAD_PRIORITY */
+
+ oslThreadPriority Priority = osl_Thread_PriorityNormal;
+ Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
+
+ if (!pImpl)
+ {
+ SAL_WARN("sal.osl", "invalid osl_getThreadPriority(nullptr) call");
+ return osl_Thread_PriorityUnknown; /* EINVAL */
+ }
+
+#ifndef NO_PTHREAD_PRIORITY
+
+ if (pthread_getschedparam(pImpl->m_hThread, &Policy, &Param) != 0)
+ return osl_Thread_PriorityUnknown; /* ESRCH */
+
+ pthread_once (&(g_thread.m_once), osl_thread_init_Impl);
+
+ /* map pthread priority to enum */
+ if (Param.sched_priority==g_thread.m_priority.m_Highest)
+ {
+ /* 127 - highest */
+ Priority= osl_Thread_PriorityHighest;
+ }
+ else if (Param.sched_priority > g_thread.m_priority.m_Normal)
+ {
+ /* 65..126 - above normal */
+ Priority= osl_Thread_PriorityAboveNormal;
+ }
+ else if (Param.sched_priority == g_thread.m_priority.m_Normal)
+ {
+ /* normal */
+ Priority= osl_Thread_PriorityNormal;
+ }
+ else if (Param.sched_priority > g_thread.m_priority.m_Lowest)
+ {
+ /* 63..1 -below normal */
+ Priority= osl_Thread_PriorityBelowNormal;
+ }
+ else if (Param.sched_priority == g_thread.m_priority.m_Lowest)
+ {
+ /* 0 - lowest */
+ Priority= osl_Thread_PriorityLowest;
+ }
+ else
+ {
+ /* unknown */
+ Priority= osl_Thread_PriorityUnknown;
+ }
+
+#endif /* NO_PTHREAD_PRIORITY */
+
+ return Priority;
+}
+
+namespace {
+
+struct wrapper_pthread_key
+{
+ pthread_key_t m_key;
+ oslThreadKeyCallbackFunction pfnCallback;
+};
+
+}
+
+oslThreadKey SAL_CALL osl_createThreadKey( oslThreadKeyCallbackFunction pCallback )
+{
+ wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(malloc(sizeof(wrapper_pthread_key)));
+
+ if (pKey)
+ {
+ pKey->pfnCallback = pCallback;
+
+ if (pthread_key_create(&(pKey->m_key), pKey->pfnCallback) != 0)
+ {
+ free(pKey);
+ pKey = nullptr;
+ }
+ }
+
+ return static_cast<oslThreadKey>(pKey);
+}
+
+void SAL_CALL osl_destroyThreadKey(oslThreadKey Key)
+{
+ wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
+ if (pKey)
+ {
+ pthread_key_delete(pKey->m_key);
+ free(pKey);
+ }
+}
+
+void* SAL_CALL osl_getThreadKeyData(oslThreadKey Key)
+{
+ wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
+ return pKey ? pthread_getspecific(pKey->m_key) : nullptr;
+}
+
+sal_Bool SAL_CALL osl_setThreadKeyData(oslThreadKey Key, void *pData)
+{
+ bool bRet;
+ void *pOldData = nullptr;
+ wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
+ if (!pKey)
+ return false;
+
+ if (pKey->pfnCallback)
+ pOldData = pthread_getspecific(pKey->m_key);
+
+ bRet = (pthread_setspecific(pKey->m_key, pData) == 0);
+
+ if (bRet && pKey->pfnCallback && pOldData)
+ pKey->pfnCallback(pOldData);
+
+ return bRet;
+}
+
+static void osl_thread_textencoding_init_Impl()
+{
+ rtl_TextEncoding defaultEncoding;
+
+ /* create thread specific data key */
+ pthread_key_create (&(g_thread.m_textencoding.m_key), nullptr);
+
+ /* determine default text encoding */
+ defaultEncoding = osl_getTextEncodingFromLocale(nullptr);
+ // Tools string functions call abort() on an unknown encoding so ASCII is a
+ // meaningful fallback:
+ if ( RTL_TEXTENCODING_DONTKNOW == defaultEncoding )
+ {
+ SAL_WARN("sal.osl", "RTL_TEXTENCODING_DONTKNOW -> _ASCII_US");
+ defaultEncoding = RTL_TEXTENCODING_ASCII_US;
+ }
+
+ g_thread.m_textencoding.m_default = defaultEncoding;
+}
+
+rtl_TextEncoding SAL_CALL osl_getThreadTextEncoding()
+{
+ rtl_TextEncoding threadEncoding;
+
+ pthread_once (&(g_thread.m_once), osl_thread_init_Impl);
+
+ /* check for thread specific encoding, use default if not set */
+ threadEncoding = static_cast<rtl_TextEncoding>(
+ reinterpret_cast<sal_uIntPtr>(pthread_getspecific(g_thread.m_textencoding.m_key)));
+ if (threadEncoding == 0)
+ threadEncoding = g_thread.m_textencoding.m_default;
+
+ return threadEncoding;
+}
+
+rtl_TextEncoding osl_setThreadTextEncoding(rtl_TextEncoding Encoding)
+{
+ rtl_TextEncoding oldThreadEncoding = osl_getThreadTextEncoding();
+
+ /* save encoding in thread local storage */
+ pthread_setspecific (
+ g_thread.m_textencoding.m_key,
+ reinterpret_cast<void*>(static_cast<sal_uIntPtr>(Encoding)));
+
+ return oldThreadEncoding;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/time.cxx b/sal/osl/unx/time.cxx
new file mode 100644
index 000000000..a77015614
--- /dev/null
+++ b/sal/osl/unx/time.cxx
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "saltime.hxx"
+#include "system.hxx"
+
+#include <osl/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef __MACH__
+#include <mach/clock.h>
+#include <mach/mach.h>
+#endif
+
+/* FIXME: detection should be done in configure script */
+#if defined(MACOSX) || defined(IOS) || defined(FREEBSD) || defined(NETBSD) || \
+ defined(LINUX) || defined(OPENBSD) || defined(DRAGONFLY)
+#define STRUCT_TM_HAS_GMTOFF 1
+
+#elif defined(__sun)
+#define HAS_ALTZONE 1
+#endif
+
+#ifdef __MACH__
+typedef mach_timespec_t osl_time_t;
+#else
+#if defined(_POSIX_TIMERS)
+#define USE_CLOCK_GETTIME
+typedef struct timespec osl_time_t;
+#else
+typedef struct timeval osl_time_t;
+#endif
+#endif
+static osl_time_t startTime;
+
+sal_Bool SAL_CALL osl_getSystemTime(TimeValue* tv)
+{
+#ifdef __MACH__
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ mach_port_deallocate(mach_task_self(), cclock);
+
+ tv->Seconds = mts.tv_sec;
+ tv->Nanosec = mts.tv_nsec;
+#else
+ int res;
+ osl_time_t tp;
+#if defined(USE_CLOCK_GETTIME)
+ res = clock_gettime(CLOCK_REALTIME, &tp);
+#else
+ res = gettimeofday(&tp, NULL);
+#endif
+
+ if (res != 0)
+ {
+ return false;
+ }
+
+ tv->Seconds = tp.tv_sec;
+ #if defined(USE_CLOCK_GETTIME)
+ tv->Nanosec = tp.tv_nsec;
+ #else
+ tv->Nanosec = tp.tv_usec * 1000;
+ #endif
+#endif
+ return true;
+}
+
+sal_Bool SAL_CALL osl_getDateTimeFromTimeValue( const TimeValue* pTimeVal, oslDateTime* pDateTime )
+{
+ struct tm *pSystemTime;
+ struct tm tmBuf;
+ time_t atime;
+
+ atime = static_cast<time_t>(pTimeVal->Seconds);
+
+ /* Convert time from type time_t to struct tm */
+ pSystemTime = gmtime_r( &atime, &tmBuf );
+
+ /* Convert struct tm to struct oslDateTime */
+ if ( pSystemTime != nullptr )
+ {
+ pDateTime->NanoSeconds = pTimeVal->Nanosec;
+ pDateTime->Seconds = pSystemTime->tm_sec;
+ pDateTime->Minutes = pSystemTime->tm_min;
+ pDateTime->Hours = pSystemTime->tm_hour;
+ pDateTime->Day = pSystemTime->tm_mday;
+ pDateTime->DayOfWeek = pSystemTime->tm_wday;
+ pDateTime->Month = pSystemTime->tm_mon + 1;
+ pDateTime->Year = pSystemTime->tm_year + 1900;
+
+ return true;
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL osl_getTimeValueFromDateTime( const oslDateTime* pDateTime, TimeValue* pTimeVal )
+{
+ struct tm aTime;
+ time_t nSeconds;
+
+ /* Convert struct oslDateTime to struct tm */
+ aTime.tm_sec = pDateTime->Seconds;
+ aTime.tm_min = pDateTime->Minutes;
+ aTime.tm_hour = pDateTime->Hours;
+ aTime.tm_mday = pDateTime->Day;
+
+ if ( pDateTime->Month > 0 )
+ aTime.tm_mon = pDateTime->Month - 1;
+ else
+ return false;
+
+ aTime.tm_year = pDateTime->Year - 1900;
+
+ aTime.tm_isdst = -1;
+ aTime.tm_wday = 0;
+ aTime.tm_yday = 0;
+
+#if defined(STRUCT_TM_HAS_GMTOFF)
+ aTime.tm_gmtoff = 0;
+#endif
+
+ /* Convert time to calendar value */
+ nSeconds = mktime( &aTime );
+
+ /*
+ * mktime expects the struct tm to be in local timezone, so we have to adjust
+ * the returned value to be timezone neutral.
+ */
+
+ if ( nSeconds != time_t(-1) )
+ {
+ time_t bias;
+
+ /* timezone corrections */
+ tzset();
+
+#if defined(STRUCT_TM_HAS_GMTOFF)
+ /* members of struct tm are corrected by mktime */
+ bias = 0 - aTime.tm_gmtoff;
+
+#elif defined(HAS_ALTZONE)
+ /* check if daylight saving time is in effect */
+ bias = aTime.tm_isdst > 0 ? altzone : timezone;
+#else
+ /* expect daylight saving time to be one hour */
+ bias = aTime.tm_isdst > 0 ? timezone - 3600 : timezone;
+#endif
+
+ pTimeVal->Seconds = nSeconds;
+ pTimeVal->Nanosec = pDateTime->NanoSeconds;
+
+ if ( nSeconds > bias )
+ pTimeVal->Seconds -= bias;
+
+ return true;
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL osl_getLocalTimeFromSystemTime( const TimeValue* pSystemTimeVal, TimeValue* pLocalTimeVal )
+{
+ struct tm *pLocalTime;
+ struct tm tmBuf;
+ time_t bias;
+ time_t atime;
+
+ atime = static_cast<time_t>(pSystemTimeVal->Seconds);
+ pLocalTime = localtime_r( &atime, &tmBuf );
+
+#if defined(STRUCT_TM_HAS_GMTOFF)
+ /* members of struct tm are corrected by mktime */
+ bias = -pLocalTime->tm_gmtoff;
+
+#elif defined(HAS_ALTZONE)
+ /* check if daylight saving time is in effect */
+ bias = pLocalTime->tm_isdst > 0 ? altzone : timezone;
+#else
+ /* expect daylight saving time to be one hour */
+ bias = pLocalTime->tm_isdst > 0 ? timezone - 3600 : timezone;
+#endif
+
+ if ( static_cast<sal_Int64>(pSystemTimeVal->Seconds) > bias )
+ {
+ pLocalTimeVal->Seconds = pSystemTimeVal->Seconds - bias;
+ pLocalTimeVal->Nanosec = pSystemTimeVal->Nanosec;
+
+ return true;
+ }
+
+ return false;
+}
+
+sal_Bool SAL_CALL osl_getSystemTimeFromLocalTime( const TimeValue* pLocalTimeVal, TimeValue* pSystemTimeVal )
+{
+ struct tm *pLocalTime;
+ struct tm tmBuf;
+ time_t bias;
+ time_t atime;
+
+ atime = static_cast<time_t>(pLocalTimeVal->Seconds);
+
+ /* Convert atime, which is a local time, to its GMT equivalent. Then, get
+ * the timezone offset for the local time for the GMT equivalent time. Note
+ * that we cannot directly use local time to determine the timezone offset
+ * because GMT is the only reliable time that we can determine timezone
+ * offset from.
+ */
+
+ atime = mktime( gmtime_r( &atime, &tmBuf ) );
+ pLocalTime = localtime_r( &atime, &tmBuf );
+
+#if defined(STRUCT_TM_HAS_GMTOFF)
+ /* members of struct tm are corrected by mktime */
+ bias = 0 - pLocalTime->tm_gmtoff;
+
+#elif defined(HAS_ALTZONE)
+ /* check if daylight saving time is in effect */
+ bias = pLocalTime->tm_isdst > 0 ? altzone : timezone;
+#else
+ /* expect daylight saving time to be one hour */
+ bias = pLocalTime->tm_isdst > 0 ? timezone - 3600 : timezone;
+#endif
+
+ if ( static_cast<sal_Int64>(pLocalTimeVal->Seconds) + bias > 0 )
+ {
+ pSystemTimeVal->Seconds = pLocalTimeVal->Seconds + bias;
+ pSystemTimeVal->Nanosec = pLocalTimeVal->Nanosec;
+
+ return true;
+ }
+
+ return false;
+}
+
+void sal_initGlobalTimer()
+{
+#ifdef __MACH__
+ clock_serv_t cclock;
+
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &startTime);
+ mach_port_deallocate(mach_task_self(), cclock);
+#else /* ! (MACOSX || IOS) */
+#if defined(USE_CLOCK_GETTIME)
+ clock_gettime(CLOCK_REALTIME, &startTime);
+#else /* Ndef USE_CLOCK_GETTIME */
+ gettimeofday( &startTime, NULL );
+#endif /* NDef USE_CLOCK_GETTIME */
+#endif /* ! (MACOSX || IOS) */
+}
+
+sal_uInt32 SAL_CALL osl_getGlobalTimer()
+{
+ sal_uInt32 nSeconds;
+
+#ifdef __MACH__
+ clock_serv_t cclock;
+ mach_timespec_t currentTime;
+
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &currentTime);
+ mach_port_deallocate(mach_task_self(), cclock);
+
+ nSeconds = ( currentTime.tv_sec - startTime.tv_sec );
+ nSeconds = ( nSeconds * 1000 ) + static_cast<long>(( currentTime.tv_nsec - startTime.tv_nsec) / 1000000 );
+#else
+ osl_time_t currentTime;
+
+#if defined(USE_CLOCK_GETTIME)
+ clock_gettime(CLOCK_REALTIME, &currentTime);
+#else
+ gettimeofday( &currentTime, NULL );
+#endif
+
+ nSeconds = static_cast<sal_uInt32>( currentTime.tv_sec - startTime.tv_sec );
+#if defined(USE_CLOCK_GETTIME)
+ nSeconds = ( nSeconds * 1000 ) + static_cast<long>(( currentTime.tv_nsec - startTime.tv_nsec) / 1000000 );
+#else
+ nSeconds = ( nSeconds * 1000 ) + (long) (( currentTime.tv_usec - startTime.tv_usec) / 1000 );
+#endif
+#endif
+ return nSeconds;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/unixerrnostring.hxx b/sal/osl/unx/unixerrnostring.hxx
new file mode 100644
index 000000000..9e13b04e6
--- /dev/null
+++ b/sal/osl/unx/unixerrnostring.hxx
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_UNIXERRNOSTRING_HXX
+#define INCLUDED_SAL_OSL_UNX_UNIXERRNOSTRING_HXX
+
+#include <string>
+
+// Return the symbolic name of an errno value, like "ENOENT".
+
+// Rationale why to use this and not strerror(): This is intended to be used in SAL_INFO() and
+// SAL_WARN(). Such messages are intended to be read by developers, not end-users. Developers are
+// (or should be) familiar with symbolic errno names in code anyway. strerror() is localized and the
+// localised error strings might be less familiar to a developer that happens to run a localised
+// environment.
+
+std::string UnixErrnoString(int nErrno);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/uunxapi.cxx b/sal/osl/unx/uunxapi.cxx
new file mode 100644
index 000000000..e8901784c
--- /dev/null
+++ b/sal/osl/unx/uunxapi.cxx
@@ -0,0 +1,896 @@
+/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include "uunxapi.hxx"
+#include "system.hxx"
+#include "unixerrnostring.hxx"
+#include <limits.h>
+#include <rtl/ustring.hxx>
+#include <osl/thread.h>
+#include <sal/log.hxx>
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+OString osl::OUStringToOString(const OUString& s)
+{
+ return OUStringToOString(s, osl_getThreadTextEncoding());
+}
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+
+#include <Foundation/Foundation.h>
+#include <Security/Security.h>
+#include <mach-o/dyld.h>
+
+static NSUserDefaults *userDefaults = NULL;
+static bool isSandboxed = false;
+
+static void do_once()
+{
+ SecCodeRef code;
+ OSStatus rc = SecCodeCopySelf(kSecCSDefaultFlags, &code);
+
+ SecStaticCodeRef staticCode;
+ if (rc == errSecSuccess)
+ rc = SecCodeCopyStaticCode(code, kSecCSDefaultFlags, &staticCode);
+
+ CFDictionaryRef signingInformation;
+ if (rc == errSecSuccess)
+ rc = SecCodeCopySigningInformation(staticCode, kSecCSRequirementInformation, &signingInformation);
+
+ CFDictionaryRef entitlements = NULL;
+ if (rc == errSecSuccess)
+ entitlements = (CFDictionaryRef) CFDictionaryGetValue(signingInformation, kSecCodeInfoEntitlementsDict);
+
+ if (entitlements != NULL)
+ if (CFDictionaryGetValue(entitlements, CFSTR("com.apple.security.app-sandbox")) != NULL)
+ isSandboxed = true;
+
+ if (isSandboxed)
+ userDefaults = [NSUserDefaults standardUserDefaults];
+}
+
+typedef struct {
+ NSURL *scopeURL;
+ NSAutoreleasePool *pool;
+} accessFilePathState;
+
+static accessFilePathState *
+prepare_to_access_file_path( const char *cpFilePath )
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, &do_once);
+ NSURL *fileURL = nil;
+ NSData *data = nil;
+ BOOL stale;
+ accessFilePathState *state;
+
+ if (!isSandboxed)
+ return NULL;
+
+ // If malloc() fails we are screwed anyway
+ state = (accessFilePathState*) malloc(sizeof(accessFilePathState));
+
+ state->pool = [[NSAutoreleasePool alloc] init];
+ state->scopeURL = nil;
+
+ if (userDefaults != nil)
+ fileURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:cpFilePath]];
+
+ if (fileURL != nil)
+ data = [userDefaults dataForKey:[@"bookmarkFor:" stringByAppendingString:[fileURL absoluteString]]];
+
+ if (data != nil)
+ state->scopeURL = [NSURL URLByResolvingBookmarkData:data
+ options:NSURLBookmarkResolutionWithSecurityScope
+ relativeToURL:nil
+ bookmarkDataIsStale:&stale
+ error:nil];
+ if (state->scopeURL != nil)
+ [state->scopeURL startAccessingSecurityScopedResource];
+
+ return state;
+}
+
+static void
+done_accessing_file_path( const char * /*cpFilePath*/, accessFilePathState *state )
+{
+ if (!isSandboxed)
+ return;
+
+ int saved_errno = errno;
+
+ if (state->scopeURL != nil)
+ [state->scopeURL stopAccessingSecurityScopedResource];
+ [state->pool release];
+ free(state);
+
+ errno = saved_errno;
+}
+
+#else
+
+typedef void accessFilePathState;
+
+#define prepare_to_access_file_path( cpFilePath ) nullptr
+
+#define done_accessing_file_path( cpFilePath, state ) ((void) cpFilePath, (void) state)
+
+#endif
+
+#ifdef MACOSX
+/*
+ * Helper function for resolving Mac native alias files (not the same as unix alias files)
+ * and to return the resolved alias as OString
+ */
+static OString macxp_resolveAliasAndConvert(OString const & p)
+{
+ char path[PATH_MAX];
+ if (p.getLength() < PATH_MAX)
+ {
+ strcpy(path, p.getStr());
+ macxp_resolveAlias(path, PATH_MAX);
+ return path;
+ }
+ return p;
+}
+#endif /* MACOSX */
+
+int osl::access(const OString& pstrPath, int mode)
+{
+ OString fn = pstrPath;
+#ifdef ANDROID
+ if (fn == "/assets" || fn.startsWith("/assets/"))
+ {
+ struct stat stat;
+ if (lo_apk_lstat(fn.getStr(), &stat) == -1)
+ return -1;
+ if (mode & W_OK)
+ {
+ errno = EACCES;
+ return -1;
+ }
+ return 0;
+ }
+#endif
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(fn.getStr());
+
+ int result = ::access(fn.getStr(), mode);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "access(" << fn << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "access(" << fn << ",0" << std::oct << mode << std::dec << "): OK");
+
+ done_accessing_file_path(fn.getStr(), state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+namespace {
+
+OString toOString(OString const & s) { return s; }
+
+OString toOString(OUString const & s) { return osl::OUStringToOString(s); }
+
+template<typename T> T fromOString(OString const &) = delete;
+
+template<> OString fromOString(OString const & s) { return s; }
+
+template<> OUString fromOString(OString const & s)
+{ return OStringToOUString(s, osl_getThreadTextEncoding()); }
+
+template<typename T> bool realpath_(const T& pstrFileName, T& ppstrResolvedName)
+{
+ OString fn = toOString(pstrFileName);
+#ifdef ANDROID
+ if (fn == "/assets" || fn.startsWith("/assets/"))
+ {
+ if (osl::access(fn, F_OK) == -1)
+ return false;
+
+ ppstrResolvedName = pstrFileName;
+
+ return true;
+ }
+#endif
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(fn.getStr());
+
+ char rp[PATH_MAX];
+ bool bRet = realpath(fn.getStr(), rp);
+ int saved_errno = errno;
+ if (!bRet)
+ SAL_INFO("sal.file", "realpath(" << fn << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "realpath(" << fn << "): OK");
+
+ done_accessing_file_path(fn.getStr(), state);
+
+ if (bRet)
+ {
+ ppstrResolvedName = fromOString<T>(OString(rp));
+ }
+
+ errno = saved_errno;
+
+ return bRet;
+}
+
+}
+
+bool osl::realpath(const OUString& pustrFileName, OUString& ppustrResolvedName)
+{
+ return realpath_(pustrFileName, ppustrResolvedName);
+}
+
+bool osl::realpath(const OString& pstrFileName, OString& ppstrResolvedName)
+{
+ return realpath_(pstrFileName, ppstrResolvedName);
+}
+
+int stat_c(const char* cpPath, struct stat* buf)
+{
+#ifdef ANDROID
+ if (strncmp(cpPath, "/assets", sizeof("/assets")-1) == 0 &&
+ (cpPath[sizeof("/assets")-1] == '\0' ||
+ cpPath[sizeof("/assets")-1] == '/'))
+ return lo_apk_lstat(cpPath, buf);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(cpPath);
+
+ int result = stat(cpPath, buf);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "stat(" << cpPath << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "stat(" << cpPath << "): OK");
+
+ done_accessing_file_path(cpPath, state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+int lstat_c(const char* cpPath, struct stat* buf)
+{
+#ifdef ANDROID
+ if (strncmp(cpPath, "/assets", sizeof("/assets")-1) == 0 &&
+ (cpPath[sizeof("/assets")-1] == '\0' ||
+ cpPath[sizeof("/assets")-1] == '/'))
+ return lo_apk_lstat(cpPath, buf);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(cpPath);
+
+ int result = lstat(cpPath, buf);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "lstat(" << cpPath << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "lstat(" << cpPath << "): OK");
+
+ done_accessing_file_path(cpPath, state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+namespace {
+
+template<typename T> int lstat_(const T& pstrPath, struct stat& buf)
+{
+ OString fn = toOString(pstrPath);
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ return lstat_c(fn.getStr(), &buf);
+}
+
+}
+
+int osl::lstat(const OUString& pustrPath, struct stat& buf)
+{
+ return lstat_(pustrPath, buf);
+}
+
+int osl::lstat(const OString& pstrPath, struct stat& buf)
+{
+ return lstat_(pstrPath, buf);
+}
+
+int osl::mkdir(const OString& path, mode_t mode)
+{
+ accessFilePathState *state = prepare_to_access_file_path(path.getStr());
+
+ int result = ::mkdir(path.getStr(), mode);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "mkdir(" << path << ",0" << std::oct << mode << std::dec << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "mkdir(" << path << ",0" << std::oct << mode << std::dec << "): OK");
+
+ done_accessing_file_path(path.getStr(), state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+int open_c(const char *cpPath, int oflag, int mode)
+{
+ accessFilePathState *state = prepare_to_access_file_path(cpPath);
+
+ int result = open(cpPath, oflag, mode);
+ int saved_errno = errno;
+ if (result == -1)
+ SAL_INFO("sal.file", "open(" << cpPath << ",0" << std::oct << oflag << ",0" << mode << std::dec << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "open(" << cpPath << ",0" << std::oct << oflag << ",0" << mode << std::dec << ") => " << result);
+
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ if (isSandboxed && result != -1 && (oflag & O_CREAT) && (oflag & O_EXCL))
+ {
+ // A new file was created. Check if it is outside the sandbox.
+ // (In that case it must be one the user selected as export or
+ // save destination in a file dialog, otherwise we wouldn't
+ // have been able to create it.) Create and store a security
+ // scoped bookmark for it so that we can access the file in
+ // the future, too. (For the "Recent Files" functionality.)
+ const char *sandbox = [NSHomeDirectory() UTF8String];
+ if (!(strncmp(sandbox, cpPath, strlen(sandbox)) == 0 &&
+ cpPath[strlen(sandbox)] == '/'))
+ {
+ NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:cpPath]];
+ NSData *data = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
+ includingResourceValuesForKeys:nil
+ relativeToURL:nil
+ error:nil];
+ if (data != NULL)
+ {
+ [userDefaults setObject:data
+ forKey:[@"bookmarkFor:" stringByAppendingString:[url absoluteString]]];
+ }
+ }
+ }
+#endif
+
+ done_accessing_file_path(cpPath, state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+int utime_c(const char *cpPath, struct utimbuf *times)
+{
+ accessFilePathState *state = prepare_to_access_file_path(cpPath);
+
+ int result = utime(cpPath, times);
+
+ done_accessing_file_path(cpPath, state);
+
+ return result;
+}
+
+int ftruncate_with_name(int fd, sal_uInt64 uSize, rtl_String* path)
+{
+ /* When sandboxed on macOS, ftruncate(), even if it takes an
+ * already open file descriptor which was returned from an open()
+ * call already checked by the sandbox, still requires a security
+ * scope bookmark for the file to be active in case the file is
+ * one that the sandbox doesn't otherwise allow access to. Luckily
+ * LibreOffice usually calls ftruncate() through the helpful C++
+ * abstraction layer that keeps the pathname around.
+ */
+
+ OString fn(path);
+
+#ifdef MACOSX
+ fn = macxp_resolveAliasAndConvert(fn);
+#endif
+
+ accessFilePathState *state = prepare_to_access_file_path(fn.getStr());
+
+ int result = ftruncate(fd, uSize);
+ int saved_errno = errno;
+ if (result < 0)
+ SAL_INFO("sal.file", "ftruncate(" << fd << "," << uSize << "): " << UnixErrnoString(saved_errno));
+ else
+ SAL_INFO("sal.file", "ftruncate(" << fd << "," << uSize << "): OK");
+
+ done_accessing_file_path(fn.getStr(), state);
+
+ errno = saved_errno;
+
+ return result;
+}
+
+
+std::string UnixErrnoString(int nErrno)
+{
+ // Errnos from <asm-generic/errno-base.h> and <asm-generic/errno.h> on Linux and <sys/errno.h>
+ // on macOS.
+ switch (nErrno)
+ {
+ case EPERM:
+ return "EPERM";
+ case ENOENT:
+ return "ENOENT";
+ case ESRCH:
+ return "ESRCH";
+ case EINTR:
+ return "EINTR";
+ case EIO:
+ return "EIO";
+ case ENXIO:
+ return "ENXIO";
+ case E2BIG:
+ return "E2BIG";
+ case ENOEXEC:
+ return "ENOEXEC";
+ case EBADF:
+ return "EBADF";
+ case ECHILD:
+ return "ECHILD";
+ case EAGAIN:
+ return "EAGAIN";
+ case ENOMEM:
+ return "ENOMEM";
+ case EACCES:
+ return "EACCES";
+ case EFAULT:
+ return "EFAULT";
+#ifdef ENOTBLK
+ case ENOTBLK:
+ return "ENOTBLK";
+#endif
+ case EBUSY:
+ return "EBUSY";
+ case EEXIST:
+ return "EEXIST";
+ case EXDEV:
+ return "EXDEV";
+ case ENODEV:
+ return "ENODEV";
+ case ENOTDIR:
+ return "ENOTDIR";
+ case EISDIR:
+ return "EISDIR";
+ case EINVAL:
+ return "EINVAL";
+ case ENFILE:
+ return "ENFILE";
+ case EMFILE:
+ return "EMFILE";
+ case ENOTTY:
+ return "ENOTTY";
+ case ETXTBSY:
+ return "ETXTBSY";
+ case EFBIG:
+ return "EFBIG";
+ case ENOSPC:
+ return "ENOSPC";
+ case ESPIPE:
+ return "ESPIPE";
+ case EROFS:
+ return "EROFS";
+ case EMLINK:
+ return "EMLINK";
+ case EPIPE:
+ return "EPIPE";
+ case EDOM:
+ return "EDOM";
+ case ERANGE:
+ return "ERANGE";
+ case EDEADLK:
+ return "EDEADLK";
+ case ENAMETOOLONG:
+ return "ENAMETOOLONG";
+ case ENOLCK:
+ return "ENOLCK";
+ case ENOSYS:
+ return "ENOSYS";
+ case ENOTEMPTY:
+ return "ENOTEMPTY";
+ case ELOOP:
+ return "ELOOP";
+ case ENOMSG:
+ return "ENOMSG";
+ case EIDRM:
+ return "EIDRM";
+#ifdef ECHRNG
+ case ECHRNG:
+ return "ECHRNG";
+#endif
+#ifdef EL2NSYNC
+ case EL2NSYNC:
+ return "EL2NSYNC";
+#endif
+#ifdef EL3HLT
+ case EL3HLT:
+ return "EL3HLT";
+#endif
+#ifdef EL3RST
+ case EL3RST:
+ return "EL3RST";
+#endif
+#ifdef ELNRNG
+ case ELNRNG:
+ return "ELNRNG";
+#endif
+#ifdef EUNATCH
+ case EUNATCH:
+ return "EUNATCH";
+#endif
+#ifdef ENOCSI
+ case ENOCSI:
+ return "ENOCSI";
+#endif
+#ifdef EL2HLT
+ case EL2HLT:
+ return "EL2HLT";
+#endif
+#ifdef EBADE
+ case EBADE:
+ return "EBADE";
+#endif
+#ifdef EBADR
+ case EBADR:
+ return "EBADR";
+#endif
+#ifdef EXFULL
+ case EXFULL:
+ return "EXFULL";
+#endif
+#ifdef ENOANO
+ case ENOANO:
+ return "ENOANO";
+#endif
+#ifdef EBADRQC
+ case EBADRQC:
+ return "EBADRQC";
+#endif
+#ifdef EBADSLT
+ case EBADSLT:
+ return "EBADSLT";
+#endif
+#ifdef EBFONT
+ case EBFONT:
+ return "EBFONT";
+#endif
+ case ENOSTR:
+ return "ENOSTR";
+ case ENODATA:
+ return "ENODATA";
+ case ETIME:
+ return "ETIME";
+ case ENOSR:
+ return "ENOSR";
+#ifdef ENONET
+ case ENONET:
+ return "ENONET";
+#endif
+#ifdef ENOPKG
+ case ENOPKG:
+ return "ENOPKG";
+#endif
+#ifdef EREMOTE
+ case EREMOTE:
+ return "EREMOTE";
+#endif
+ case ENOLINK:
+ return "ENOLINK";
+#ifdef EADV
+ case EADV:
+ return "EADV";
+#endif
+#ifdef ESRMNT
+ case ESRMNT:
+ return "ESRMNT";
+#endif
+#ifdef ECOMM
+ case ECOMM:
+ return "ECOMM";
+#endif
+ case EPROTO:
+ return "EPROTO";
+ case EMULTIHOP:
+ return "EMULTIHOP";
+#ifdef EDOTDOT
+ case EDOTDOT:
+ return "EDOTDOT";
+#endif
+ case EBADMSG:
+ return "EBADMSG";
+ case EOVERFLOW:
+ return "EOVERFLOW";
+#ifdef ENOTUNIQ
+ case ENOTUNIQ:
+ return "ENOTUNIQ";
+#endif
+#ifdef EBADFD
+ case EBADFD:
+ return "EBADFD";
+#endif
+#ifdef EREMCHG
+ case EREMCHG:
+ return "EREMCHG";
+#endif
+#ifdef ELIBACC
+ case ELIBACC:
+ return "ELIBACC";
+#endif
+#ifdef ELIBBAD
+ case ELIBBAD:
+ return "ELIBBAD";
+#endif
+#ifdef ELIBSCN
+ case ELIBSCN:
+ return "ELIBSCN";
+#endif
+#ifdef ELIBMAX
+ case ELIBMAX:
+ return "ELIBMAX";
+#endif
+#ifdef ELIBEXEC
+ case ELIBEXEC:
+ return "ELIBEXEC";
+#endif
+ case EILSEQ:
+ return "EILSEQ";
+#ifdef ERESTART
+ case ERESTART:
+ return "ERESTART";
+#endif
+#ifdef ESTRPIPE
+ case ESTRPIPE:
+ return "ESTRPIPE";
+#endif
+#ifdef EUSERS
+ case EUSERS:
+ return "EUSERS";
+#endif
+ case ENOTSOCK:
+ return "ENOTSOCK";
+ case EDESTADDRREQ:
+ return "EDESTADDRREQ";
+ case EMSGSIZE:
+ return "EMSGSIZE";
+ case EPROTOTYPE:
+ return "EPROTOTYPE";
+ case ENOPROTOOPT:
+ return "ENOPROTOOPT";
+ case EPROTONOSUPPORT:
+ return "EPROTONOSUPPORT";
+#ifdef ESOCKTNOSUPPORT
+ case ESOCKTNOSUPPORT:
+ return "ESOCKTNOSUPPORT";
+#endif
+#ifdef EOPNOTSUPP
+ case EOPNOTSUPP:
+ return "EOPNOTSUPP";
+#endif
+ case EPFNOSUPPORT:
+ return "EPFNOSUPPORT";
+ case EAFNOSUPPORT:
+ return "EAFNOSUPPORT";
+ case EADDRINUSE:
+ return "EADDRINUSE";
+ case EADDRNOTAVAIL:
+ return "EADDRNOTAVAIL";
+ case ENETDOWN:
+ return "ENETDOWN";
+ case ENETUNREACH:
+ return "ENETUNREACH";
+ case ENETRESET:
+ return "ENETRESET";
+ case ECONNABORTED:
+ return "ECONNABORTED";
+ case ECONNRESET:
+ return "ECONNRESET";
+ case ENOBUFS:
+ return "ENOBUFS";
+ case EISCONN:
+ return "EISCONN";
+ case ENOTCONN:
+ return "ENOTCONN";
+#ifdef ESHUTDOWN
+ case ESHUTDOWN:
+ return "ESHUTDOWN";
+#endif
+#ifdef ETOOMANYREFS
+ case ETOOMANYREFS:
+ return "ETOOMANYREFS";
+#endif
+ case ETIMEDOUT:
+ return "ETIMEDOUT";
+ case ECONNREFUSED:
+ return "ECONNREFUSED";
+#ifdef EHOSTDOWN
+ case EHOSTDOWN:
+ return "EHOSTDOWN";
+#endif
+ case EHOSTUNREACH:
+ return "EHOSTUNREACH";
+ case EALREADY:
+ return "EALREADY";
+ case EINPROGRESS:
+ return "EINPROGRESS";
+ case ESTALE:
+ return "ESTALE";
+#ifdef EUCLEAN
+ case EUCLEAN:
+ return "EUCLEAN";
+#endif
+#ifdef ENOTNAM
+ case ENOTNAM:
+ return "ENOTNAM";
+#endif
+#ifdef ENAVAIL
+ case ENAVAIL:
+ return "ENAVAIL";
+#endif
+#ifdef EISNAM
+ case EISNAM:
+ return "EISNAM";
+#endif
+#ifdef EREMOTEIO
+ case EREMOTEIO:
+ return "EREMOTEIO";
+#endif
+ case EDQUOT:
+ return "EDQUOT";
+#ifdef ENOMEDIUM
+ case ENOMEDIUM:
+ return "ENOMEDIUM";
+#endif
+#ifdef EMEDIUMTYPE
+ case EMEDIUMTYPE:
+ return "EMEDIUMTYPE";
+#endif
+ case ECANCELED:
+ return "ECANCELED";
+#ifdef ENOKEY
+ case ENOKEY:
+ return "ENOKEY";
+#endif
+#ifdef EKEYEXPIRED
+ case EKEYEXPIRED:
+ return "EKEYEXPIRED";
+#endif
+#ifdef EKEYREVOKED
+ case EKEYREVOKED:
+ return "EKEYREVOKED";
+#endif
+#ifdef EKEYREJECTED
+ case EKEYREJECTED:
+ return "EKEYREJECTED";
+#endif
+#ifdef EOWNERDEAD
+ case EOWNERDEAD:
+ return "EOWNERDEAD";
+#endif
+#ifdef ENOTRECOVERABLE
+ case ENOTRECOVERABLE:
+ return "ENOTRECOVERABLE";
+#endif
+#ifdef ERFKILL
+ case ERFKILL:
+ return "ERFKILL";
+#endif
+#ifdef EHWPOISON
+ case EHWPOISON:
+ return "EHWPOISON";
+#endif
+#ifdef EPROCLIM
+ case EPROCLIM:
+ return "EPROCLIM";
+#endif
+#ifdef EBADRPC
+ case EBADRPC:
+ return "EBADRPC";
+#endif
+#ifdef ERPCMISMATCH
+ case ERPCMISMATCH:
+ return "ERPCMISMATCH";
+#endif
+#ifdef EPROGUNAVAIL
+ case EPROGUNAVAIL:
+ return "EPROGUNAVAIL";
+#endif
+#ifdef EPROGMISMATCH
+ case EPROGMISMATCH:
+ return "EPROGMISMATCH";
+#endif
+#ifdef EPROCUNAVAIL
+ case EPROCUNAVAIL:
+ return "EPROCUNAVAIL";
+#endif
+#ifdef EFTYPE
+ case EFTYPE:
+ return "EFTYPE";
+#endif
+#ifdef EAUTH
+ case EAUTH:
+ return "EAUTH";
+#endif
+#ifdef ENEEDAUTH
+ case ENEEDAUTH:
+ return "ENEEDAUTH";
+#endif
+#ifdef EPWROFF
+ case EPWROFF:
+ return "EPWROFF";
+#endif
+#ifdef EDEVERR
+ case EDEVERR:
+ return "EDEVERR";
+#endif
+#ifdef EBADEXEC
+ case EBADEXEC:
+ return "EBADEXEC";
+#endif
+#ifdef EBADARCH
+ case EBADARCH:
+ return "EBADARCH";
+#endif
+#ifdef ESHLIBVERS
+ case ESHLIBVERS:
+ return "ESHLIBVERS";
+#endif
+#ifdef EBADMACHO
+ case EBADMACHO:
+ return "EBADMACHO";
+#endif
+#ifdef ENOATTR
+ case ENOATTR:
+ return "ENOATTR";
+#endif
+#ifdef EQFULL
+ case EQFULL:
+ return "EQFULL";
+#endif
+ default:
+ char* str = strerror(nErrno);
+ return std::to_string(nErrno) + " (" + std::string(str) + ")";
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/uunxapi.hxx b/sal/osl/unx/uunxapi.hxx
new file mode 100644
index 000000000..f182b755e
--- /dev/null
+++ b/sal/osl/unx/uunxapi.hxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_OSL_UNX_UUNXAPI_HXX
+#define INCLUDED_SAL_OSL_UNX_UUNXAPI_HXX
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rtl/ustring.h>
+
+#include <rtl/ustring.hxx>
+
+int stat_c(const char *cpPath, struct stat* buf);
+
+int lstat_c(const char *cpPath, struct stat* buf);
+
+int mkdir_c(OString const & path, mode_t mode);
+
+int open_c(const char *cpPath, int oflag, int mode);
+
+int utime_c(const char *cpPath, struct utimbuf *times);
+
+int ftruncate_with_name(int fd, sal_uInt64 uSize, rtl_String* path);
+
+namespace osl
+{
+ OString OUStringToOString(const OUString& s);
+
+ int access(const OString& strPath, int mode);
+
+ /***********************************
+ osl::realpath
+
+ @descr
+ The return value differs from the
+ realpath function
+
+ @returns sal_True on success else
+ sal_False
+
+ @see realpath
+ **********************************/
+
+ bool realpath(
+ const OUString& ustrFileName,
+ OUString& ustrResolvedName);
+
+ bool realpath(
+ const OString& strFileName,
+ OString& strResolvedName);
+
+ int lstat(const OUString& ustrPath, struct stat& buf);
+
+ int lstat(const OString& strPath, struct stat& buf);
+
+ int mkdir(const OString& aPath, mode_t aMode);
+} // end namespace osl
+
+#endif // INCLUDED_SAL_OSL_UNX_UUNXAPI_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/osl/unx/uunxapi.mm b/sal/osl/unx/uunxapi.mm
new file mode 100644
index 000000000..5a7e17f34
--- /dev/null
+++ b/sal/osl/unx/uunxapi.mm
@@ -0,0 +1 @@
+#include "uunxapi.cxx"