diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /tools/source/stream | |
parent | Initial commit. (diff) | |
download | libreoffice-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 'tools/source/stream')
-rw-r--r-- | tools/source/stream/GenericTypeSerializer.cxx | 186 | ||||
-rw-r--r-- | tools/source/stream/stream.cxx | 2115 | ||||
-rw-r--r-- | tools/source/stream/strmunx.cxx | 487 | ||||
-rw-r--r-- | tools/source/stream/strmwnt.cxx | 420 | ||||
-rw-r--r-- | tools/source/stream/vcompat.cxx | 67 |
5 files changed, 3275 insertions, 0 deletions
diff --git a/tools/source/stream/GenericTypeSerializer.cxx b/tools/source/stream/GenericTypeSerializer.cxx new file mode 100644 index 000000000..c099713d2 --- /dev/null +++ b/tools/source/stream/GenericTypeSerializer.cxx @@ -0,0 +1,186 @@ +/* -*- 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 <tools/GenericTypeSerializer.hxx> +#include <vector> + +namespace tools +{ +constexpr sal_uInt16 COL_NAME_USER = 0x8000; + +constexpr sal_Int32 RECT_EMPTY_VALUE_RIGHT_BOTTOM = -32767; + +void GenericTypeSerializer::readColor(Color& rColor) +{ + sal_uInt16 nColorNameID(0); + + mrStream.ReadUInt16(nColorNameID); + + if (nColorNameID & COL_NAME_USER) + { + sal_uInt16 nRed; + sal_uInt16 nGreen; + sal_uInt16 nBlue; + + mrStream.ReadUInt16(nRed); + mrStream.ReadUInt16(nGreen); + mrStream.ReadUInt16(nBlue); + + rColor = Color(nRed >> 8, nGreen >> 8, nBlue >> 8); + } + else + { + static const std::vector<Color> staticColorArray = { + COL_BLACK, // COL_BLACK + COL_BLUE, // COL_BLUE + COL_GREEN, // COL_GREEN + COL_CYAN, // COL_CYAN + COL_RED, // COL_RED + COL_MAGENTA, // COL_MAGENTA + COL_BROWN, // COL_BROWN + COL_GRAY, // COL_GRAY + COL_LIGHTGRAY, // COL_LIGHTGRAY + COL_LIGHTBLUE, // COL_LIGHTBLUE + COL_LIGHTGREEN, // COL_LIGHTGREEN + COL_LIGHTCYAN, // COL_LIGHTCYAN + COL_LIGHTRED, // COL_LIGHTRED + COL_LIGHTMAGENTA, // COL_LIGHTMAGENTA + COL_YELLOW, // COL_YELLOW + COL_WHITE, // COL_WHITE + COL_WHITE, // COL_MENUBAR + COL_BLACK, // COL_MENUBARTEXT + COL_WHITE, // COL_POPUPMENU + COL_BLACK, // COL_POPUPMENUTEXT + COL_BLACK, // COL_WINDOWTEXT + COL_WHITE, // COL_WINDOWWORKSPACE + COL_BLACK, // COL_HIGHLIGHT + COL_WHITE, // COL_HIGHLIGHTTEXT + COL_BLACK, // COL_3DTEXT + COL_LIGHTGRAY, // COL_3DFACE + COL_WHITE, // COL_3DLIGHT + COL_GRAY, // COL_3DSHADOW + COL_LIGHTGRAY, // COL_SCROLLBAR + COL_WHITE, // COL_FIELD + COL_BLACK // COL_FIELDTEXT + }; + + if (nColorNameID < staticColorArray.size()) + rColor = staticColorArray[nColorNameID]; + else + rColor = COL_BLACK; + } +} + +void GenericTypeSerializer::writeColor(const Color& rColor) +{ + mrStream.WriteUInt16(COL_NAME_USER); + + sal_uInt16 nR = rColor.GetRed(); + sal_uInt16 nG = rColor.GetGreen(); + sal_uInt16 nB = rColor.GetBlue(); + + mrStream.WriteUInt16((nR << 8) + nR); + mrStream.WriteUInt16((nG << 8) + nG); + mrStream.WriteUInt16((nB << 8) + nB); +} + +void GenericTypeSerializer::readPoint(Point& rPoint) +{ + sal_Int32 nX(0); + sal_Int32 nY(0); + + mrStream.ReadInt32(nX); + mrStream.ReadInt32(nY); + + rPoint.setX(nX); + rPoint.setY(nY); +} + +void GenericTypeSerializer::writePoint(const Point& rPoint) +{ + mrStream.WriteInt32(rPoint.getX()); + mrStream.WriteInt32(rPoint.getY()); +} + +void GenericTypeSerializer::readSize(Size& rSize) +{ + sal_Int32 nWidth(0); + sal_Int32 nHeight(0); + + mrStream.ReadInt32(nWidth); + mrStream.ReadInt32(nHeight); + + rSize.setWidth(nWidth); + rSize.setHeight(nHeight); +} + +void GenericTypeSerializer::writeSize(const Size& rSize) +{ + mrStream.WriteInt32(rSize.getWidth()); + mrStream.WriteInt32(rSize.getHeight()); +} + +void GenericTypeSerializer::readRectangle(Rectangle& rRectangle) +{ + sal_Int32 nLeft(0); + sal_Int32 nTop(0); + sal_Int32 nRight(0); + sal_Int32 nBottom(0); + + mrStream.ReadInt32(nLeft); + mrStream.ReadInt32(nTop); + mrStream.ReadInt32(nRight); + mrStream.ReadInt32(nBottom); + + if (nRight == RECT_EMPTY_VALUE_RIGHT_BOTTOM || nBottom == RECT_EMPTY_VALUE_RIGHT_BOTTOM) + { + rRectangle.SetEmpty(); + } + else + { + rRectangle.SetLeft(nLeft); + rRectangle.SetTop(nTop); + rRectangle.SetRight(nRight); + rRectangle.SetBottom(nBottom); + } +} + +void GenericTypeSerializer::writeRectangle(const Rectangle& rRectangle) +{ + if (rRectangle.IsEmpty()) + { + mrStream.WriteInt32(0); + mrStream.WriteInt32(0); + mrStream.WriteInt32(RECT_EMPTY_VALUE_RIGHT_BOTTOM); + mrStream.WriteInt32(RECT_EMPTY_VALUE_RIGHT_BOTTOM); + } + else + { + mrStream.WriteInt32(rRectangle.Left()); + mrStream.WriteInt32(rRectangle.Top()); + mrStream.WriteInt32(rRectangle.Right()); + mrStream.WriteInt32(rRectangle.Bottom()); + } +} + +} // end namespace tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/tools/source/stream/stream.cxx b/tools/source/stream/stream.cxx new file mode 100644 index 000000000..404b9eb67 --- /dev/null +++ b/tools/source/stream/stream.cxx @@ -0,0 +1,2115 @@ +/* -*- 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 . + */ + +// TODO: Read->RefreshBuffer-> React to changes from m_nBufActualLen + +#include <sal/config.h> + +#include <cassert> +#include <cstddef> +#include <memory> + +#include <string.h> +#include <stdio.h> + +#include <o3tl/safeint.hxx> +#include <osl/endian.h> +#include <osl/diagnose.h> +#include <rtl/strbuf.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include <comphelper/fileformat.h> + +static void swapNibbles(unsigned char &c) +{ + unsigned char nSwapTmp=c; + nSwapTmp <<= 4; + c >>= 4; + c |= nSwapTmp; +} + +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <osl/thread.h> +#include <algorithm> + +// !!! Do not inline if already the operators <<,>> are inline +static void SwapUShort( sal_uInt16& r ) + { r = OSL_SWAPWORD(r); } +static void SwapShort( short& r ) + { r = OSL_SWAPWORD(r); } +static void SwapULong( sal_uInt32& r ) + { r = OSL_SWAPDWORD(r); } +static void SwapLongInt( sal_Int32& r ) + { r = OSL_SWAPDWORD(r); } + +static void SwapUInt64( sal_uInt64& r ) + { + union + { + sal_uInt64 n; + sal_uInt32 c[2]; + } s; + + s.n = r; + s.c[0] ^= s.c[1]; // swap the 32 bit words + s.c[1] ^= s.c[0]; + s.c[0] ^= s.c[1]; + // swap the bytes in the words + s.c[0] = OSL_SWAPDWORD(s.c[0]); + s.c[1] = OSL_SWAPDWORD(s.c[1]); + r = s.n; + } +static void SwapInt64( sal_Int64& r ) + { + union + { + sal_Int64 n; + sal_Int32 c[2]; + } s; + + s.n = r; + s.c[0] ^= s.c[1]; // swap the 32 bit words + s.c[1] ^= s.c[0]; + s.c[0] ^= s.c[1]; + // swap the bytes in the words + s.c[0] = OSL_SWAPDWORD(s.c[0]); + s.c[1] = OSL_SWAPDWORD(s.c[1]); + r = s.n; + } + +#ifdef UNX +static void SwapFloat( float& r ) + { + union + { + float f; + sal_uInt32 c; + } s; + + s.f = r; + s.c = OSL_SWAPDWORD( s.c ); + r = s.f; + } + +static void SwapDouble( double& r ) + { + if( sizeof(double) != 8 ) + { + SAL_WARN( "tools.stream", "Can only swap 8-Byte-doubles" ); + } + else + { + union + { + double d; + sal_uInt32 c[2]; + } s; + + s.d = r; + s.c[0] ^= s.c[1]; // swap 32-bit values in situ + s.c[1] ^= s.c[0]; + s.c[0] ^= s.c[1]; + s.c[0] = OSL_SWAPDWORD(s.c[0]); // swap dword itself in situ + s.c[1] = OSL_SWAPDWORD(s.c[1]); + r = s.d; + } + } +#endif + +static void SwapUnicode(sal_Unicode & r) { r = OSL_SWAPWORD(r); } + +//SDO + +void SvStream::readNumberWithoutSwap_(void * pDataDest, int nDataSize) +{ + if (m_isIoRead && nDataSize <= m_nBufFree) + { + for (int i = 0; i < nDataSize; i++) + static_cast<char*>(pDataDest)[i] = m_pBufPos[i]; + m_nBufActualPos += nDataSize; + m_pBufPos += nDataSize; + m_nBufFree -= nDataSize; + } + else + { + ReadBytes( pDataDest, nDataSize ); + } +} + + +void SvStream::writeNumberWithoutSwap_(const void * pDataSrc, int nDataSize) +{ + if (m_isIoWrite && nDataSize <= m_nBufFree) + { + for (int i = 0; i < nDataSize; i++) + m_pBufPos[i] = static_cast<const char*>(pDataSrc)[i]; + m_nBufFree -= nDataSize; + m_nBufActualPos += nDataSize; + if (m_nBufActualPos > m_nBufActualLen) + m_nBufActualLen = m_nBufActualPos; + m_pBufPos += nDataSize; + m_isDirty = true; + } + else + { + WriteBytes( pDataSrc, nDataSize ); + } +} + + +void SvLockBytes::close() +{ + if (m_bOwner) + delete m_pStream; + m_pStream = nullptr; +} + + +// virtual +ErrCode SvLockBytes::ReadAt(sal_uInt64 const nPos, void * pBuffer, std::size_t nCount, + std::size_t * pRead) const +{ + if (!m_pStream) + { + OSL_FAIL("SvLockBytes::ReadAt(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->Seek(nPos); + std::size_t nTheRead = m_pStream->ReadBytes(pBuffer, nCount); + if (pRead) + *pRead = nTheRead; + return m_pStream->GetErrorCode(); +} + +// virtual +ErrCode SvLockBytes::WriteAt(sal_uInt64 const nPos, const void * pBuffer, std::size_t nCount, + std::size_t * pWritten) +{ + if (!m_pStream) + { + OSL_FAIL("SvLockBytes::WriteAt(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->Seek(nPos); + std::size_t nTheWritten = m_pStream->WriteBytes(pBuffer, nCount); + if (pWritten) + *pWritten = nTheWritten; + return m_pStream->GetErrorCode(); +} + +// virtual +ErrCode SvLockBytes::Flush() const +{ + if (!m_pStream) + { + OSL_FAIL("SvLockBytes::Flush(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->Flush(); + return m_pStream->GetErrorCode(); +} + +// virtual +ErrCode SvLockBytes::SetSize(sal_uInt64 const nSize) +{ + if (!m_pStream) + { + OSL_FAIL("SvLockBytes::SetSize(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->SetStreamSize(nSize); + return m_pStream->GetErrorCode(); +} + +ErrCode SvLockBytes::Stat(SvLockBytesStat * pStat) const +{ + if (!m_pStream) + { + OSL_FAIL("SvLockBytes::Stat(): Bad stream"); + return ERRCODE_NONE; + } + + if (pStat) + { + sal_uInt64 const nPos = m_pStream->Tell(); + pStat->nSize = m_pStream->Seek(STREAM_SEEK_TO_END); + m_pStream->Seek(nPos); + } + return ERRCODE_NONE; +} + + +std::size_t SvStream::GetData( void* pData, std::size_t nSize ) +{ + if( !GetError() ) + { + DBG_ASSERT( m_xLockBytes.is(), "pure virtual function" ); + std::size_t nRet(0); + m_nError = m_xLockBytes->ReadAt(m_nActPos, pData, nSize, &nRet); + m_nActPos += nRet; + return nRet; + } + else return 0; +} + +std::size_t SvStream::PutData( const void* pData, std::size_t nSize ) +{ + if( !GetError() ) + { + DBG_ASSERT( m_xLockBytes.is(), "pure virtual function" ); + std::size_t nRet(0); + m_nError = m_xLockBytes->WriteAt(m_nActPos, pData, nSize, &nRet); + m_nActPos += nRet; + return nRet; + } + else return 0; +} + +sal_uInt64 SvStream::SeekPos(sal_uInt64 const nPos) +{ + // check if a truncated STREAM_SEEK_TO_END was passed + assert(nPos != SAL_MAX_UINT32); + if( !GetError() && nPos == STREAM_SEEK_TO_END ) + { + DBG_ASSERT( m_xLockBytes.is(), "pure virtual function" ); + SvLockBytesStat aStat; + m_xLockBytes->Stat( &aStat ); + m_nActPos = aStat.nSize; + } + else + m_nActPos = nPos; + return m_nActPos; +} + +void SvStream::FlushData() +{ + if( !GetError() ) + { + DBG_ASSERT( m_xLockBytes.is(), "pure virtual function" ); + m_nError = m_xLockBytes->Flush(); + } +} + +void SvStream::SetSize(sal_uInt64 const nSize) +{ + DBG_ASSERT( m_xLockBytes.is(), "pure virtual function" ); + m_nError = m_xLockBytes->SetSize( nSize ); +} + +SvStream::SvStream() : + m_nActPos(0) + + , m_pBufPos(nullptr) + , m_nBufSize(0) + , m_nBufActualLen(0) + , m_nBufActualPos(0) + , m_nBufFree(0) + , m_isIoRead(false) + , m_isIoWrite(false) + + , m_isDirty(false) + , m_isEof(false) + + , m_nCompressMode(SvStreamCompressFlags::NONE) +#if defined UNX + , m_eLineDelimiter(LINEEND_LF) // UNIX-Format +#else + , m_eLineDelimiter(LINEEND_CRLF) // DOS-Format +#endif + , m_eStreamCharSet(osl_getThreadTextEncoding()) + + , m_nCryptMask(0) + + , m_nVersion(0) + + , m_nBufFilePos(0) + , m_eStreamMode(StreamMode::NONE) + , m_isWritable(true) + +{ + SetEndian( SvStreamEndian::LITTLE ); + + ClearError(); +} + +SvStream::SvStream( SvLockBytes* pLockBytesP ) : SvStream() +{ + m_xLockBytes = pLockBytesP; + if( pLockBytesP ) { + const SvStream* pStrm = pLockBytesP->GetStream(); + if( pStrm ) { + SetError( pStrm->GetErrorCode() ); + } + } + SetBufferSize( 256 ); +} + +SvStream::~SvStream() +{ + if (m_xLockBytes.is()) + Flush(); +} + +void SvStream::ClearError() +{ + m_isEof = false; + m_nError = ERRCODE_NONE; +} + +void SvStream::SetError( ErrCode nErrorCode ) +{ + if (m_nError == ERRCODE_NONE) + m_nError = nErrorCode; +} + +void SvStream::SetEndian( SvStreamEndian nNewFormat ) +{ + m_nEndian = nNewFormat; + m_isSwap = false; +#ifdef OSL_BIGENDIAN + if (m_nEndian == SvStreamEndian::LITTLE) + m_isSwap = true; +#else + if (m_nEndian == SvStreamEndian::BIG) + m_isSwap = true; +#endif +} + +void SvStream::SetBufferSize( sal_uInt16 nBufferSize ) +{ + sal_uInt64 const nActualFilePos = Tell(); + bool bDontSeek = (m_pRWBuf == nullptr); + + if (m_isDirty && m_isWritable) // due to Windows NT: Access denied + Flush(); + + if (m_nBufSize) + { + m_pRWBuf.reset(); + m_nBufFilePos += m_nBufActualPos; + } + + m_pRWBuf = nullptr; + m_nBufActualLen = 0; + m_nBufActualPos = 0; + m_nBufSize = nBufferSize; + if (m_nBufSize) + m_pRWBuf.reset(new sal_uInt8[ m_nBufSize ]); + m_pBufPos = m_pRWBuf.get(); + m_isIoRead = m_isIoWrite = false; + if( !bDontSeek ) + SeekPos( nActualFilePos ); +} + +void SvStream::ClearBuffer() +{ + m_nBufActualLen = 0; + m_nBufActualPos = 0; + m_nBufFilePos = 0; + m_pBufPos = m_pRWBuf.get(); + m_isDirty = false; + m_isIoRead = m_isIoWrite = false; + + m_isEof = false; +} + +void SvStream::ResetError() +{ + ClearError(); +} + +bool SvStream::ReadByteStringLine( OUString& rStr, rtl_TextEncoding eSrcCharSet, + sal_Int32 nMaxBytesToRead ) +{ + OString aStr; + bool bRet = ReadLine( aStr, nMaxBytesToRead); + rStr = OStringToOUString(aStr, eSrcCharSet); + return bRet; +} + +bool SvStream::ReadLine( OString& rStr, sal_Int32 nMaxBytesToRead ) +{ + char buf[256+1]; + bool bEnd = false; + sal_uInt64 nOldFilePos = Tell(); + char c = 0; + std::size_t nTotalLen = 0; + + OStringBuffer aBuf(4096); + while( !bEnd && !GetError() ) // Don't test for EOF as we + // are reading block-wise! + { + sal_uInt16 nLen = static_cast<sal_uInt16>(ReadBytes(buf, sizeof(buf)-1)); + if ( !nLen ) + { + if ( aBuf.isEmpty() ) + { + // Exit on first block-read error + m_isEof = true; + rStr.clear(); + return false; + } + else + break; + } + + sal_uInt16 j, n; + for( j = n = 0; j < nLen ; ++j ) + { + c = buf[j]; + if ( c == '\n' || c == '\r' ) + { + bEnd = true; + break; + } + if ( n < j ) + buf[n] = c; + ++n; + } + nTotalLen += j; + if (nTotalLen > o3tl::make_unsigned(nMaxBytesToRead)) + { + n -= nTotalLen - nMaxBytesToRead; + nTotalLen = nMaxBytesToRead; + bEnd = true; + } + if ( n ) + aBuf.append(buf, n); + } + + if ( !bEnd && !GetError() && !aBuf.isEmpty() ) + bEnd = true; + + nOldFilePos += nTotalLen; + if( Tell() > nOldFilePos ) + nOldFilePos++; + Seek( nOldFilePos ); // Seek pointer due to BlockRead above + + if ( bEnd && (c=='\r' || c=='\n') ) // Special treatment for DOS files + { + char cTemp; + std::size_t nLen = ReadBytes(&cTemp, sizeof(cTemp)); + if ( nLen ) { + if( cTemp == c || (cTemp != '\n' && cTemp != '\r') ) + Seek( nOldFilePos ); + } + } + + if ( bEnd ) + m_isEof = false; + rStr = aBuf.makeStringAndClear(); + return bEnd; +} + +bool SvStream::ReadUniStringLine( OUString& rStr, sal_Int32 nMaxCodepointsToRead ) +{ + sal_Unicode buf[256+1]; + bool bEnd = false; + sal_uInt64 nOldFilePos = Tell(); + sal_Unicode c = 0; + std::size_t nTotalLen = 0; + + DBG_ASSERT( sizeof(sal_Unicode) == sizeof(sal_uInt16), "ReadUniStringLine: swapping sizeof(sal_Unicode) not implemented" ); + + OUStringBuffer aBuf(4096); + while( !bEnd && !GetError() ) // Don't test for EOF as we + // are reading block-wise! + { + sal_uInt16 nLen = static_cast<sal_uInt16>(ReadBytes( buf, sizeof(buf)-sizeof(sal_Unicode))); + nLen /= sizeof(sal_Unicode); + if ( !nLen ) + { + if ( aBuf.isEmpty() ) + { + // exit on first BlockRead error + m_isEof = true; + rStr.clear(); + return false; + } + else + break; + } + + sal_uInt16 j, n; + for( j = n = 0; j < nLen ; ++j ) + { + if (m_isSwap) + SwapUnicode( buf[n] ); + c = buf[j]; + if ( c == '\n' || c == '\r' ) + { + bEnd = true; + break; + } + // erAck 26.02.01: Old behavior was no special treatment of '\0' + // character here, but a following rStr+=c did ignore it. Is this + // really intended? Or should a '\0' better terminate a line? + // The nOldFilePos stuff wasn't correct then anyways. + if ( c ) + { + if ( n < j ) + buf[n] = c; + ++n; + } + } + nTotalLen += j; + if (nTotalLen > o3tl::make_unsigned(nMaxCodepointsToRead)) + { + n -= nTotalLen - nMaxCodepointsToRead; + nTotalLen = nMaxCodepointsToRead; + bEnd = true; + } + if ( n ) + aBuf.append( buf, n ); + } + + if ( !bEnd && !GetError() && !aBuf.isEmpty() ) + bEnd = true; + + nOldFilePos += nTotalLen * sizeof(sal_Unicode); + if( Tell() > nOldFilePos ) + nOldFilePos += sizeof(sal_Unicode); + Seek( nOldFilePos ); // seek due to BlockRead above + + if ( bEnd && (c=='\r' || c=='\n') ) // special treatment for DOS files + { + sal_Unicode cTemp; + ReadBytes( &cTemp, sizeof(cTemp) ); + if (m_isSwap) + SwapUnicode( cTemp ); + if( cTemp == c || (cTemp != '\n' && cTemp != '\r') ) + Seek( nOldFilePos ); + } + + if ( bEnd ) + m_isEof = false; + rStr = aBuf.makeStringAndClear(); + return bEnd; +} + +bool SvStream::ReadUniOrByteStringLine( OUString& rStr, rtl_TextEncoding eSrcCharSet, + sal_Int32 nMaxCodepointsToRead ) +{ + if ( eSrcCharSet == RTL_TEXTENCODING_UNICODE ) + return ReadUniStringLine( rStr, nMaxCodepointsToRead ); + else + return ReadByteStringLine( rStr, eSrcCharSet, nMaxCodepointsToRead ); +} + +OString read_zeroTerminated_uInt8s_ToOString(SvStream& rStream) +{ + OStringBuffer aOutput(256); + + char buf[ 256 + 1 ]; + bool bEnd = false; + sal_uInt64 nFilePos = rStream.Tell(); + + while( !bEnd && !rStream.GetError() ) + { + std::size_t nLen = rStream.ReadBytes(buf, sizeof(buf)-1); + if (!nLen) + break; + + std::size_t nReallyRead = nLen; + const char* pPtr = buf; + while (nLen && *pPtr) + { + ++pPtr; + --nLen; + } + + bEnd = ( nReallyRead < sizeof(buf)-1 ) // read less than attempted to read + || ( ( nLen > 0 ) // OR it is inside the block we read + && ( 0 == *pPtr ) // AND found a string terminator + ); + + aOutput.append(buf, pPtr - buf); + } + + nFilePos += aOutput.getLength(); + if (rStream.Tell() > nFilePos) + rStream.Seek(nFilePos+1); // seek due to FileRead above + return aOutput.makeStringAndClear(); +} + +OUString read_zeroTerminated_uInt8s_ToOUString(SvStream& rStream, rtl_TextEncoding eEnc) +{ + return OStringToOUString( + read_zeroTerminated_uInt8s_ToOString(rStream), eEnc); +} + +/** Attempt to write a prefixed sequence of nUnits 16bit units from an OUString, + returned value is number of bytes written */ +std::size_t write_uInt16s_FromOUString(SvStream& rStrm, const OUString& rStr, + std::size_t nUnits) +{ + DBG_ASSERT( sizeof(sal_Unicode) == sizeof(sal_uInt16), "write_uInt16s_FromOUString: swapping sizeof(sal_Unicode) not implemented" ); + std::size_t nWritten; + if (!rStrm.IsEndianSwap()) + nWritten = rStrm.WriteBytes(rStr.getStr(), nUnits * sizeof(sal_Unicode)); + else + { + std::size_t nLen = nUnits; + sal_Unicode aBuf[384]; + sal_Unicode* const pTmp = ( nLen > 384 ? new sal_Unicode[nLen] : aBuf); + memcpy( pTmp, rStr.getStr(), nLen * sizeof(sal_Unicode) ); + sal_Unicode* p = pTmp; + const sal_Unicode* const pStop = pTmp + nLen; + while ( p < pStop ) + { + SwapUnicode( *p ); + p++; + } + nWritten = rStrm.WriteBytes( pTmp, nLen * sizeof(sal_Unicode) ); + if ( pTmp != aBuf ) + delete [] pTmp; + } + return nWritten; +} + +bool SvStream::WriteUnicodeOrByteText( const OUString& rStr, rtl_TextEncoding eDestCharSet ) +{ + if ( eDestCharSet == RTL_TEXTENCODING_UNICODE ) + { + write_uInt16s_FromOUString(*this, rStr, rStr.getLength()); + return m_nError == ERRCODE_NONE; + } + else + { + OString aStr(OUStringToOString(rStr, eDestCharSet)); + write_uInt8s_FromOString(*this, aStr, aStr.getLength()); + return m_nError == ERRCODE_NONE; + } +} + +bool SvStream::WriteByteStringLine( const OUString& rStr, rtl_TextEncoding eDestCharSet ) +{ + return WriteLine(OUStringToOString(rStr, eDestCharSet)); +} + +bool SvStream::WriteLine(const OString& rStr) +{ + WriteBytes(rStr.getStr(), rStr.getLength()); + endl(*this); + return m_nError == ERRCODE_NONE; +} + +bool SvStream::WriteUniOrByteChar( sal_Unicode ch, rtl_TextEncoding eDestCharSet ) +{ + if ( eDestCharSet == RTL_TEXTENCODING_UNICODE ) + WriteUnicode(ch); + else + { + OString aStr(&ch, 1, eDestCharSet); + WriteBytes(aStr.getStr(), aStr.getLength()); + } + return m_nError == ERRCODE_NONE; +} + +void SvStream::StartWritingUnicodeText() +{ + // BOM, Byte Order Mark, U+FEFF, see + // http://www.unicode.org/faq/utf_bom.html#BOM + // Upon read: 0xfeff(-257) => no swap; 0xfffe(-2) => swap + writeNumberWithoutSwap(sal_uInt16(0xfeff)); // write native format +} + +void SvStream::StartReadingUnicodeText( rtl_TextEncoding eReadBomCharSet ) +{ + if (!( eReadBomCharSet == RTL_TEXTENCODING_DONTKNOW || + eReadBomCharSet == RTL_TEXTENCODING_UNICODE || + eReadBomCharSet == RTL_TEXTENCODING_UTF8)) + return; // nothing to read + + bool bTryUtf8 = false; + sal_uInt16 nFlag(0); + sal_sSize nBack = sizeof(nFlag); + ReadUInt16( nFlag ); + switch ( nFlag ) + { + case 0xfeff : + // native UTF-16 + if ( eReadBomCharSet == RTL_TEXTENCODING_DONTKNOW || + eReadBomCharSet == RTL_TEXTENCODING_UNICODE) + nBack = 0; + break; + case 0xfffe : + // swapped UTF-16 + if ( eReadBomCharSet == RTL_TEXTENCODING_DONTKNOW || + eReadBomCharSet == RTL_TEXTENCODING_UNICODE) + { + SetEndian( m_nEndian == SvStreamEndian::BIG ? SvStreamEndian::LITTLE : SvStreamEndian::BIG ); + nBack = 0; + } + break; + case 0xefbb : + if (m_nEndian == SvStreamEndian::BIG && + (eReadBomCharSet == RTL_TEXTENCODING_DONTKNOW || + eReadBomCharSet == RTL_TEXTENCODING_UTF8)) + bTryUtf8 = true; + break; + case 0xbbef : + if (m_nEndian == SvStreamEndian::LITTLE && + (eReadBomCharSet == RTL_TEXTENCODING_DONTKNOW || + eReadBomCharSet == RTL_TEXTENCODING_UTF8)) + bTryUtf8 = true; + break; + default: + ; // nothing + } + if (bTryUtf8) + { + unsigned char nChar(0); + nBack += sizeof(nChar); + ReadUChar( nChar ); + if (nChar == 0xbf) + nBack = 0; // it is UTF-8 + } + if (nBack) + SeekRel( -nBack ); // no BOM, pure data +} + +sal_uInt64 SvStream::SeekRel(sal_Int64 const nPos) +{ + sal_uInt64 nActualPos = Tell(); + + if ( nPos >= 0 ) + { + if (SAL_MAX_UINT64 - nActualPos > o3tl::make_unsigned(nPos)) + nActualPos += nPos; + } + else + { + sal_uInt64 const nAbsPos = static_cast<sal_uInt64>(-nPos); + if ( nActualPos >= nAbsPos ) + nActualPos -= nAbsPos; + } + + assert((m_pBufPos != nullptr) == bool(m_pRWBuf)); + if (m_pRWBuf) + { + m_pBufPos = m_pRWBuf.get() + nActualPos; + } + return Seek( nActualPos ); +} + +SvStream& SvStream::ReadUInt16(sal_uInt16& r) +{ + sal_uInt16 n = 0; + readNumberWithoutSwap(n); + if (good()) + { + if (m_isSwap) + SwapUShort(n); + r = n; + } + return *this; +} + +SvStream& SvStream::ReadUInt32(sal_uInt32& r) +{ + sal_uInt32 n = 0; + readNumberWithoutSwap(n); + if (good()) + { + if (m_isSwap) + SwapULong(n); + r = n; + } + return *this; +} + +SvStream& SvStream::ReadUInt64(sal_uInt64& r) +{ + sal_uInt64 n = 0; + readNumberWithoutSwap(n); + if (good()) + { + if (m_isSwap) + SwapUInt64(n); + r = n; + } + return *this; +} + +SvStream& SvStream::ReadInt16(sal_Int16& r) +{ + sal_Int16 n = 0; + readNumberWithoutSwap(n); + if (good()) + { + if (m_isSwap) + SwapShort(n); + r = n; + } + return *this; +} + +SvStream& SvStream::ReadInt32(sal_Int32& r) +{ + sal_Int32 n = 0; + readNumberWithoutSwap(n); + if (good()) + { + if (m_isSwap) + SwapLongInt(n); + r = n; + } + return *this; +} + +SvStream& SvStream::ReadInt64(sal_Int64& r) +{ + sal_Int64 n = 0; + readNumberWithoutSwap(n); + if (good()) + { + if (m_isSwap) + SwapInt64(n); + r = n; + } + return *this; +} + +SvStream& SvStream::ReadSChar( signed char& r ) +{ + if (m_isIoRead && sizeof(signed char) <= m_nBufFree) + { + r = *m_pBufPos; + m_nBufActualPos += sizeof(signed char); + m_pBufPos += sizeof(signed char); + m_nBufFree -= sizeof(signed char); + } + else + ReadBytes( &r, sizeof(signed char) ); + return *this; +} + +// Special treatment for Chars due to PutBack + +SvStream& SvStream::ReadChar( char& r ) +{ + if (m_isIoRead && sizeof(char) <= m_nBufFree) + { + r = *m_pBufPos; + m_nBufActualPos += sizeof(char); + m_pBufPos += sizeof(char); + m_nBufFree -= sizeof(char); + } + else + ReadBytes( &r, sizeof(char) ); + return *this; +} + +SvStream& SvStream::ReadUChar( unsigned char& r ) +{ + if (m_isIoRead && sizeof(char) <= m_nBufFree) + { + r = *m_pBufPos; + m_nBufActualPos += sizeof(char); + m_pBufPos += sizeof(char); + m_nBufFree -= sizeof(char); + } + else + ReadBytes( &r, sizeof(char) ); + return *this; +} + +SvStream& SvStream::ReadUtf16(sal_Unicode& r) +{ + sal_uInt16 n = 0; + readNumberWithoutSwap(n); + if (good()) + { + if (m_isSwap) + SwapUShort(n); + r = sal_Unicode(n); + } + return *this; +} + +SvStream& SvStream::ReadCharAsBool( bool& r ) +{ + if (m_isIoRead && sizeof(char) <= m_nBufFree) + { + SAL_WARN_IF( + *m_pBufPos > 1, "tools.stream", unsigned(*m_pBufPos) << " not 0/1"); + r = *m_pBufPos != 0; + m_nBufActualPos += sizeof(char); + m_pBufPos += sizeof(char); + m_nBufFree -= sizeof(char); + } + else + { + unsigned char c; + if (ReadBytes(&c, 1) == 1) + { + SAL_WARN_IF(c > 1, "tools.stream", unsigned(c) << " not 0/1"); + r = c != 0; + } + } + return *this; +} + +SvStream& SvStream::ReadFloat(float& r) +{ + float n = 0; + readNumberWithoutSwap(n); + if (good()) + { +#if defined UNX + if (m_isSwap) + SwapFloat(n); +#endif + r = n; + } + return *this; +} + +SvStream& SvStream::ReadDouble(double& r) +{ + double n = 0; + readNumberWithoutSwap(n); + if (good()) + { +#if defined UNX + if (m_isSwap) + SwapDouble(n); +#endif + r = n; + } + return *this; +} + +SvStream& SvStream::ReadStream( SvStream& rStream ) +{ + const sal_uInt32 cBufLen = 0x8000; + std::unique_ptr<char[]> pBuf( new char[ cBufLen ] ); + + sal_uInt32 nCount; + do { + nCount = ReadBytes( pBuf.get(), cBufLen ); + rStream.WriteBytes( pBuf.get(), nCount ); + } while( nCount == cBufLen ); + + return *this; +} + +SvStream& SvStream::WriteUInt16( sal_uInt16 v ) +{ + if (m_isSwap) + SwapUShort(v); + writeNumberWithoutSwap(v); + return *this; +} + +SvStream& SvStream::WriteUInt32( sal_uInt32 v ) +{ + if (m_isSwap) + SwapULong(v); + writeNumberWithoutSwap(v); + return *this; +} + +SvStream& SvStream::WriteUInt64( sal_uInt64 v ) +{ + if (m_isSwap) + SwapUInt64(v); + writeNumberWithoutSwap(v); + return *this; +} + +SvStream& SvStream::WriteInt16( sal_Int16 v ) +{ + if (m_isSwap) + SwapShort(v); + writeNumberWithoutSwap(v); + return *this; +} + +SvStream& SvStream::WriteInt32( sal_Int32 v ) +{ + if (m_isSwap) + SwapLongInt(v); + writeNumberWithoutSwap(v); + return *this; +} + +SvStream& SvStream::WriteInt64 (sal_Int64 v) +{ + if (m_isSwap) + SwapInt64(v); + writeNumberWithoutSwap(v); + return *this; +} + +SvStream& SvStream::WriteSChar( signed char v ) +{ + //SDO + if (m_isIoWrite && sizeof(signed char) <= m_nBufFree) + { + *m_pBufPos = v; + m_pBufPos++; // sizeof(char); + m_nBufActualPos++; + if (m_nBufActualPos > m_nBufActualLen) // Append ? + m_nBufActualLen = m_nBufActualPos; + m_nBufFree--; // = sizeof(char); + m_isDirty = true; + } + else + WriteBytes( &v, sizeof(signed char) ); + return *this; +} + +// Special treatment for Chars due to PutBack + +SvStream& SvStream::WriteChar( char v ) +{ + //SDO + if (m_isIoWrite && sizeof(char) <= m_nBufFree) + { + *m_pBufPos = v; + m_pBufPos++; // sizeof(char); + m_nBufActualPos++; + if (m_nBufActualPos > m_nBufActualLen) // Append ? + m_nBufActualLen = m_nBufActualPos; + m_nBufFree--; // = sizeof(char); + m_isDirty = true; + } + else + WriteBytes( &v, sizeof(char) ); + return *this; +} + +SvStream& SvStream::WriteUChar( unsigned char v ) +{ +//SDO + if (m_isIoWrite && sizeof(char) <= m_nBufFree) + { + *reinterpret_cast<unsigned char*>(m_pBufPos) = v; + m_pBufPos++; // = sizeof(char); + m_nBufActualPos++; // = sizeof(char); + if (m_nBufActualPos > m_nBufActualLen) // Append ? + m_nBufActualLen = m_nBufActualPos; + m_nBufFree--; + m_isDirty = true; + } + else + WriteBytes( &v, sizeof(char) ); + return *this; +} + +SvStream& SvStream::WriteUInt8( sal_uInt8 v ) +{ + return WriteUChar(v); +} + +SvStream& SvStream::WriteUnicode( sal_Unicode v ) +{ + return WriteUInt16(v); +} + +SvStream& SvStream::WriteFloat( float v ) +{ +#ifdef UNX + if (m_isSwap) + SwapFloat(v); +#endif + writeNumberWithoutSwap(v); + return *this; +} + +SvStream& SvStream::WriteDouble ( const double& r ) +{ +#if defined UNX + if (m_isSwap) + { + double nHelp = r; + SwapDouble(nHelp); + writeNumberWithoutSwap(nHelp); + return *this; + } + else +#endif + { + writeNumberWithoutSwap(r); + } + return *this; +} + +SvStream& SvStream::WriteCharPtr( const char* pBuf ) +{ + WriteBytes( pBuf, strlen(pBuf) ); + return *this; +} + +SvStream& SvStream::WriteStream( SvStream& rStream ) +{ + const sal_uInt32 cBufLen = 0x8000; + std::unique_ptr<char[]> pBuf( new char[ cBufLen ] ); + sal_uInt32 nCount; + do { + nCount = rStream.ReadBytes( pBuf.get(), cBufLen ); + WriteBytes( pBuf.get(), nCount ); + } while( nCount == cBufLen ); + + return *this; +} + +sal_uInt64 SvStream::WriteStream( SvStream& rStream, sal_uInt64 nSize ) +{ + const sal_uInt32 cBufLen = 0x8000; + std::unique_ptr<char[]> pBuf( new char[ cBufLen ] ); + sal_uInt32 nCurBufLen = cBufLen; + sal_uInt32 nCount; + sal_uInt64 nWriteSize = nSize; + + do + { + nCurBufLen = std::min<sal_uInt64>(nCurBufLen, nWriteSize); + nCount = rStream.ReadBytes(pBuf.get(), nCurBufLen); + WriteBytes( pBuf.get(), nCount ); + nWriteSize -= nCount; + } + while( nWriteSize && nCount == nCurBufLen ); + + return nSize - nWriteSize; +} + +OUString SvStream::ReadUniOrByteString( rtl_TextEncoding eSrcCharSet ) +{ + // read UTF-16 string directly from stream ? + if (eSrcCharSet == RTL_TEXTENCODING_UNICODE) + return read_uInt32_lenPrefixed_uInt16s_ToOUString(*this); + return read_uInt16_lenPrefixed_uInt8s_ToOUString(*this, eSrcCharSet); +} + +SvStream& SvStream::WriteUniOrByteString( const OUString& rStr, rtl_TextEncoding eDestCharSet ) +{ + // write UTF-16 string directly into stream ? + if (eDestCharSet == RTL_TEXTENCODING_UNICODE) + write_uInt32_lenPrefixed_uInt16s_FromOUString(*this, rStr); + else + write_uInt16_lenPrefixed_uInt8s_FromOUString(*this, rStr, eDestCharSet); + return *this; +} + +void SvStream::FlushBuffer() +{ + if (m_isDirty) // Does stream require a flush? + { + SeekPos(m_nBufFilePos); + if (m_nCryptMask) + CryptAndWriteBuffer(m_pRWBuf.get(), m_nBufActualLen); + else if (PutData(m_pRWBuf.get(), m_nBufActualLen) != m_nBufActualLen) + SetError(SVSTREAM_WRITE_ERROR); + m_isDirty = false; + } +} + +std::size_t SvStream::ReadBytes( void* pData, std::size_t nCount ) +{ + std::size_t nSaveCount = nCount; + + if (!m_pRWBuf) + { + nCount = GetData( pData,nCount); + if (m_nCryptMask) + EncryptBuffer(pData, nCount); + m_nBufFilePos += nCount; + } + else + { + // check if block is completely within buffer + m_isIoRead = true; + m_isIoWrite = false; + if (nCount <= o3tl::make_unsigned(m_nBufActualLen - m_nBufActualPos)) + { + // => yes + if (nCount != 0) + memcpy(pData, m_pBufPos, nCount); + m_nBufActualPos = m_nBufActualPos + static_cast<sal_uInt16>(nCount); + m_pBufPos += nCount; + m_nBufFree = m_nBufFree - static_cast<sal_uInt16>(nCount); + } + else + { + FlushBuffer(); + + // Does data block fit into buffer? + if (nCount > m_nBufSize) + { + // => No! Thus read directly + // into target area without using the buffer + + m_isIoRead = false; + + SeekPos(m_nBufFilePos + m_nBufActualPos); + m_nBufActualLen = 0; + m_pBufPos = m_pRWBuf.get(); + nCount = GetData( pData, nCount ); + if (m_nCryptMask) + EncryptBuffer(pData, nCount); + m_nBufFilePos += nCount; + m_nBufFilePos += m_nBufActualPos; + m_nBufActualPos = 0; + } + else + { + // => Yes. Fill buffer first, then copy to target area + + m_nBufFilePos += m_nBufActualPos; + SeekPos(m_nBufFilePos); + + // TODO: Typecast before GetData, sal_uInt16 nCountTmp + std::size_t nCountTmp = GetData( m_pRWBuf.get(), m_nBufSize ); + if (m_nCryptMask) + EncryptBuffer(m_pRWBuf.get(), nCountTmp); + m_nBufActualLen = static_cast<sal_uInt16>(nCountTmp); + if( nCount > nCountTmp ) + { + nCount = nCountTmp; // trim count back, EOF see below + } + memcpy( pData, m_pRWBuf.get(), nCount ); + m_nBufActualPos = static_cast<sal_uInt16>(nCount); + m_pBufPos = m_pRWBuf.get() + nCount; + } + } + } + m_isEof = false; + m_nBufFree = m_nBufActualLen - m_nBufActualPos; + if (nCount != nSaveCount && m_nError != ERRCODE_IO_PENDING) + m_isEof = true; + if (nCount == nSaveCount && m_nError == ERRCODE_IO_PENDING) + m_nError = ERRCODE_NONE; + return nCount; +} + +std::size_t SvStream::WriteBytes( const void* pData, std::size_t nCount ) +{ + if( !nCount ) + return 0; + + if (!m_isWritable) + { + SetError( ERRCODE_IO_CANTWRITE ); + return 0; + } + + if (!m_pRWBuf) + { + if (m_nCryptMask) + nCount = CryptAndWriteBuffer( pData, nCount ); + else + nCount = PutData( pData, nCount ); + m_nBufFilePos += nCount; + return nCount; + } + + m_isIoRead = false; + m_isIoWrite = true; + if (nCount <= o3tl::make_unsigned(m_nBufSize - m_nBufActualPos)) + { + memcpy( m_pBufPos, pData, nCount ); + m_nBufActualPos = m_nBufActualPos + static_cast<sal_uInt16>(nCount); + // Update length if buffer was updated + if (m_nBufActualPos > m_nBufActualLen) + m_nBufActualLen = m_nBufActualPos; + + m_pBufPos += nCount; + m_isDirty = true; + } + else + { + FlushBuffer(); + + // Does data block fit into buffer? + if (nCount > m_nBufSize) + { + m_isIoWrite = false; + m_nBufFilePos += m_nBufActualPos; + m_nBufActualLen = 0; + m_nBufActualPos = 0; + m_pBufPos = m_pRWBuf.get(); + SeekPos(m_nBufFilePos); + if (m_nCryptMask) + nCount = CryptAndWriteBuffer( pData, nCount ); + else + nCount = PutData( pData, nCount ); + m_nBufFilePos += nCount; + } + else + { + // Copy block to buffer + memcpy( m_pRWBuf.get(), pData, nCount ); + + // Mind the order! + m_nBufFilePos += m_nBufActualPos; + m_nBufActualPos = static_cast<sal_uInt16>(nCount); + m_pBufPos = m_pRWBuf.get() + nCount; + m_nBufActualLen = static_cast<sal_uInt16>(nCount); + m_isDirty = true; + } + } + m_nBufFree = m_nBufSize - m_nBufActualPos; + return nCount; +} + +sal_uInt64 SvStream::Seek(sal_uInt64 const nFilePos) +{ + m_isIoRead = m_isIoWrite = false; + m_isEof = false; + if (!m_pRWBuf) + { + m_nBufFilePos = SeekPos( nFilePos ); + DBG_ASSERT(Tell() == m_nBufFilePos,"Out Of Sync!"); + return m_nBufFilePos; + } + + // Is seek position within buffer? + if (nFilePos >= m_nBufFilePos && nFilePos <= (m_nBufFilePos + m_nBufActualLen)) + { + m_nBufActualPos = static_cast<sal_uInt16>(nFilePos - m_nBufFilePos); + m_pBufPos = m_pRWBuf.get() + m_nBufActualPos; + // Update m_nBufFree to avoid crash upon PutBack + m_nBufFree = m_nBufActualLen - m_nBufActualPos; + } + else + { + FlushBuffer(); + m_nBufActualLen = 0; + m_nBufActualPos = 0; + m_pBufPos = m_pRWBuf.get(); + m_nBufFilePos = SeekPos( nFilePos ); + } + return m_nBufFilePos + m_nBufActualPos; +} + +bool checkSeek(SvStream &rSt, sal_uInt64 nOffset) +{ + const sal_uInt64 nMaxSeek(rSt.Tell() + rSt.remainingSize()); + return (nOffset <= nMaxSeek && rSt.Seek(nOffset) == nOffset); +} + +//STREAM_SEEK_TO_END in some of the Seek backends is special cased to be +//efficient, in others e.g. SotStorageStream it's really horribly slow, and in +//those this should be overridden +sal_uInt64 SvStream::remainingSize() +{ + sal_uInt64 const nCurr = Tell(); + sal_uInt64 const nEnd = TellEnd(); + sal_uInt64 nMaxAvailable = nEnd > nCurr ? (nEnd-nCurr) : 0; + Seek(nCurr); + return nMaxAvailable; +} + +sal_uInt64 SvStream::TellEnd() +{ + FlushBuffer(); + sal_uInt64 const nCurr = Tell(); + sal_uInt64 const nEnd = Seek(STREAM_SEEK_TO_END); + Seek(nCurr); + return nEnd; +} + +void SvStream::Flush() +{ + FlushBuffer(); + if (m_isWritable) + FlushData(); +} + +void SvStream::RefreshBuffer() +{ + FlushBuffer(); + SeekPos(m_nBufFilePos); + m_nBufActualLen = static_cast<sal_uInt16>(GetData( m_pRWBuf.get(), m_nBufSize )); + if (m_nBufActualLen && m_nError == ERRCODE_IO_PENDING) + m_nError = ERRCODE_NONE; + if (m_nCryptMask) + EncryptBuffer(m_pRWBuf.get(), static_cast<std::size_t>(m_nBufActualLen)); + m_isIoRead = m_isIoWrite = false; +} + +SvStream& SvStream::WriteInt32AsString(sal_Int32 nInt32) +{ + char buffer[12]; + std::size_t nLen = sprintf(buffer, "%" SAL_PRIdINT32, nInt32); + WriteBytes(buffer, nLen); + return *this; +} + +SvStream& SvStream::WriteUInt32AsString(sal_uInt32 nUInt32) +{ + char buffer[11]; + std::size_t nLen = sprintf(buffer, "%" SAL_PRIuUINT32, nUInt32); + WriteBytes(buffer, nLen); + return *this; +} + +#define CRYPT_BUFSIZE 1024 + +/// Encrypt and write +std::size_t SvStream::CryptAndWriteBuffer( const void* pStart, std::size_t nLen) +{ + unsigned char pTemp[CRYPT_BUFSIZE]; + unsigned char const * pDataPtr = static_cast<unsigned char const *>(pStart); + std::size_t nCount = 0; + std::size_t nBufCount; + unsigned char nMask = m_nCryptMask; + do + { + if( nLen >= CRYPT_BUFSIZE ) + nBufCount = CRYPT_BUFSIZE; + else + nBufCount = nLen; + nLen -= nBufCount; + memcpy( pTemp, pDataPtr, static_cast<sal_uInt16>(nBufCount) ); + // ******** Encrypt ******** + for (unsigned char & rn : pTemp) + { + unsigned char aCh = rn; + aCh ^= nMask; + swapNibbles(aCh); + rn = aCh; + } + // ************************* + nCount += PutData( pTemp, nBufCount ); + pDataPtr += nBufCount; + } + while ( nLen ); + return nCount; +} + +void SvStream::EncryptBuffer(void* pStart, std::size_t nLen) const +{ + unsigned char* pTemp = static_cast<unsigned char*>(pStart); + unsigned char nMask = m_nCryptMask; + + for ( std::size_t n=0; n < nLen; n++, pTemp++ ) + { + unsigned char aCh = *pTemp; + swapNibbles(aCh); + aCh ^= nMask; + *pTemp = aCh; + } +} + +static unsigned char implGetCryptMask(const char* pStr, sal_Int32 nLen, long nVersion) +{ + unsigned char nCryptMask = 0; + + if (!nLen) + return nCryptMask; + + if( nVersion <= SOFFICE_FILEFORMAT_31 ) + { + while( nLen ) + { + nCryptMask ^= *pStr; + pStr++; + nLen--; + } + } + else // BugFix #25888# + { + for( sal_Int32 i = 0; i < nLen; i++ ) { + nCryptMask ^= pStr[i]; + if( nCryptMask & 0x80 ) { + nCryptMask <<= 1; + nCryptMask++; + } + else + nCryptMask <<= 1; + } + } + + if( !nCryptMask ) + nCryptMask = 67; + + return nCryptMask; +} + +void SvStream::SetCryptMaskKey(const OString& rCryptMaskKey) +{ + m_aCryptMaskKey = rCryptMaskKey; + m_nCryptMask = implGetCryptMask(m_aCryptMaskKey.getStr(), + m_aCryptMaskKey.getLength(), GetVersion()); +} + +bool SvStream::SetStreamSize(sal_uInt64 const nSize) +{ +#ifdef DBG_UTIL + sal_uInt64 nFPos = Tell(); +#endif + sal_uInt16 nBuf = m_nBufSize; + SetBufferSize( 0 ); + SetSize( nSize ); + if (nSize < m_nBufFilePos) + { + m_nBufFilePos = nSize; + } + SetBufferSize( nBuf ); +#ifdef DBG_UTIL + DBG_ASSERT(Tell()==nFPos,"SetStreamSize failed"); +#endif + return (m_nError == ERRCODE_NONE); +} + +SvStream& endl( SvStream& rStr ) +{ + LineEnd eDelim = rStr.GetLineDelimiter(); + if ( eDelim == LINEEND_CR ) + rStr.WriteChar('\r'); + else if( eDelim == LINEEND_LF ) + rStr.WriteChar('\n'); + else + rStr.WriteChar('\r').WriteChar('\n'); + return rStr; +} + +SvStream& endlu( SvStream& rStrm ) +{ + switch ( rStrm.GetLineDelimiter() ) + { + case LINEEND_CR : + rStrm.WriteUnicode('\r'); + break; + case LINEEND_LF : + rStrm.WriteUnicode('\n'); + break; + default: + rStrm.WriteUnicode('\r').WriteUnicode('\n'); + } + return rStrm; +} + +SvStream& endlub( SvStream& rStrm ) +{ + if ( rStrm.GetStreamCharSet() == RTL_TEXTENCODING_UNICODE ) + return endlu( rStrm ); + else + return endl( rStrm ); +} + +SvMemoryStream::SvMemoryStream( void* pBuffer, std::size_t bufSize, + StreamMode eMode ) +{ + if( eMode & StreamMode::WRITE ) + m_isWritable = true; + else + m_isWritable = false; + nEndOfData = bufSize; + bOwnsData = false; + pBuf = static_cast<sal_uInt8 *>(pBuffer); + nResize = 0; + nSize = bufSize; + nPos = 0; + SetBufferSize( 0 ); +} + +SvMemoryStream::SvMemoryStream( std::size_t nInitSize, std::size_t nResizeOffset ) +{ + m_isWritable = true; + bOwnsData = true; + nEndOfData = 0; + nResize = nResizeOffset; + nPos = 0; + pBuf = nullptr; + if( nResize != 0 && nResize < 16 ) + nResize = 16; + if( nInitSize ) + AllocateMemory( nInitSize ); + nSize = nInitSize; + SetBufferSize( 64 ); +} + +SvMemoryStream::~SvMemoryStream() +{ + if( pBuf ) + { + if( bOwnsData ) + FreeMemory(); + else + Flush(); + } +} + +sal_uInt64 SvMemoryStream::GetSize() +{ + Flush(); + sal_uInt64 const nTemp = Tell(); + sal_uInt64 const nLength = Seek( STREAM_SEEK_TO_END ); + Seek( nTemp ); + return nLength; +} + +void SvMemoryStream::SetBuffer( void* pNewBuf, std::size_t nCount, + std::size_t nEOF ) +{ + SetBufferSize( 0 ); // Init buffering in the base class + Seek( 0 ); + if( bOwnsData && pNewBuf != pBuf ) + FreeMemory(); + + pBuf = static_cast<sal_uInt8 *>(pNewBuf); + nPos = 0; + nSize = nCount; + nResize = 0; + bOwnsData = false; + + if( nEOF > nCount ) + nEOF = nCount; + nEndOfData = nEOF; + + ResetError(); +} + +std::size_t SvMemoryStream::GetData( void* pData, std::size_t nCount ) +{ + std::size_t nMaxCount = nEndOfData-nPos; + if( nCount > nMaxCount ) + nCount = nMaxCount; + if (nCount != 0) + { + memcpy( pData, pBuf+nPos, nCount ); + } + nPos += nCount; + return nCount; +} + +std::size_t SvMemoryStream::PutData( const void* pData, std::size_t nCount ) +{ + if( GetError() ) + return 0; + + std::size_t nMaxCount = nSize-nPos; + + // check for overflow + if( nCount > nMaxCount ) + { + if( nResize == 0 ) + { + // copy as much as possible + nCount = nMaxCount; + SetError( SVSTREAM_OUTOFMEMORY ); + } + else + { + long nNewResize; + if( nSize && nSize > nResize ) + nNewResize = nSize; + else + nNewResize = nResize; + + if( (nCount-nMaxCount) < nResize ) + { + // lacking memory is smaller than nResize, + // resize accordingly + if( !ReAllocateMemory( nNewResize) ) + { + nCount = 0; + SetError( SVSTREAM_WRITE_ERROR ); + } + } + else + { + // lacking memory is larger than nResize, + // resize by (nCount-nMaxCount) + resize offset + if( !ReAllocateMemory( nCount-nMaxCount+nNewResize ) ) + { + nCount = 0; + SetError( SVSTREAM_WRITE_ERROR ); + } + } + } + } + assert(pBuf && "Possibly Reallocate failed"); + memcpy( pBuf+nPos, pData, nCount); + + nPos += nCount; + if( nPos > nEndOfData ) + nEndOfData = nPos; + return nCount; +} + +sal_uInt64 SvMemoryStream::SeekPos(sal_uInt64 const nNewPos) +{ + // nEndOfData: First position in stream not allowed to read from + // nSize: Size of allocated buffer + + // check if a truncated STREAM_SEEK_TO_END was passed + assert(nNewPos != SAL_MAX_UINT32); + if( nNewPos < nEndOfData ) + nPos = nNewPos; + else if( nNewPos == STREAM_SEEK_TO_END ) + nPos = nEndOfData; + else + { + if( nNewPos >= nSize ) // Does buffer need extension? + { + if( nResize ) // Is extension possible? + { + long nDiff = static_cast<long>(nNewPos - nSize + 1); + nDiff += static_cast<long>(nResize); + ReAllocateMemory( nDiff ); + nPos = nNewPos; + nEndOfData = nNewPos; + } + else // Extension not possible, set pos to end of data + { + // SetError( SVSTREAM_OUTOFMEMORY ); + nPos = nEndOfData; + } + } + else // Expand buffer size + { + nPos = nNewPos; + nEndOfData = nNewPos; + } + } + return nPos; +} + +void SvMemoryStream::FlushData() +{ +} + +void SvMemoryStream::ResetError() +{ + SvStream::ClearError(); +} + +void SvMemoryStream::AllocateMemory( std::size_t nNewSize ) +{ + pBuf = new sal_uInt8[nNewSize]; +} + +// (using Bozo algorithm) +bool SvMemoryStream::ReAllocateMemory( long nDiff ) +{ + if (!m_isWritable || !bOwnsData) + return false; + + bool bRetVal = false; + long nTemp = static_cast<long>(nSize); + nTemp += nDiff; + std::size_t nNewSize = static_cast<std::size_t>(nTemp); + + if( nNewSize ) + { + sal_uInt8* pNewBuf = new sal_uInt8[nNewSize]; + + bRetVal = true; // Success! + if( nNewSize < nSize ) // Are we shrinking? + { + memcpy( pNewBuf, pBuf, nNewSize ); + if( nPos > nNewSize ) + nPos = 0; + if( nEndOfData >= nNewSize ) + nEndOfData = nNewSize-1; + } + else + { + if (nSize != 0) + { + memcpy( pNewBuf, pBuf, nSize ); + } + memset(pNewBuf + nSize, 0x00, nNewSize - nSize); + } + + FreeMemory(); + + pBuf = pNewBuf; + nSize = nNewSize; + } + else + { + bRetVal = true; + FreeMemory(); + pBuf = nullptr; + nSize = 0; + nEndOfData = 0; + nPos = 0; + } + + return bRetVal; +} + +void SvMemoryStream::FreeMemory() +{ + assert(bOwnsData); + if (bOwnsData) + { + delete[] pBuf; + pBuf = nullptr; + } +} + +void* SvMemoryStream::SwitchBuffer() +{ + Flush(); + if( !bOwnsData ) + return nullptr; + Seek( STREAM_SEEK_TO_BEGIN ); + + void* pRetVal = pBuf; + pBuf = nullptr; + nEndOfData = 0; + nResize = 64; + nPos = 0; + + ResetError(); + + std::size_t nInitSize = 512; + AllocateMemory(nInitSize); + nSize = nInitSize; + + SetBufferSize( 64 ); + return pRetVal; +} + +void SvMemoryStream::SetSize(sal_uInt64 const nNewSize) +{ + if (!m_isWritable) + { + SetError(SVSTREAM_INVALID_HANDLE); + return; + } + + long nDiff = static_cast<long>(nNewSize) - static_cast<long>(nSize); + ReAllocateMemory( nDiff ); +} + +//Create an OString of nLen bytes from rStream +OString read_uInt8s_ToOString(SvStream& rStrm, std::size_t nLen) +{ + rtl_String *pStr = nullptr; + if (nLen) + { + nLen = std::min<std::size_t>(nLen, SAL_MAX_INT32); + //limit allocation to size of file, but + 1 to set eof state + nLen = std::min<sal_uInt64>(nLen, rStrm.remainingSize() + 1); + //alloc a (ref-count 1) rtl_String of the desired length. + //rtl_String's buffer is uninitialized, except for null termination + pStr = rtl_string_alloc(sal::static_int_cast<sal_Int32>(nLen)); + SAL_WARN_IF(!pStr, "tools.stream", "allocation failed"); + if (pStr) + { + std::size_t nWasRead = rStrm.ReadBytes(pStr->buffer, nLen); + if (nWasRead != nLen) + { + //on (typically unlikely) short read set length to what we could + //read, and null terminate. Excess buffer capacity remains of + //course, could create a (true) replacement OString if it matters. + pStr->length = sal::static_int_cast<sal_Int32>(nWasRead); + pStr->buffer[pStr->length] = 0; + } + } + } + + //take ownership of buffer and return, otherwise return empty string + return pStr ? OString(pStr, SAL_NO_ACQUIRE) : OString(); +} + +//Create an OUString of nLen sal_Unicode code units from rStream +OUString read_uInt16s_ToOUString(SvStream& rStrm, std::size_t nLen) +{ + rtl_uString *pStr = nullptr; + if (nLen) + { + nLen = std::min<std::size_t>(nLen, SAL_MAX_INT32); + //limit allocation to size of file, but + 1 to set eof state + nLen = std::min<sal_uInt64>(nLen, (rStrm.remainingSize() + 2) / 2); + //alloc a (ref-count 1) rtl_uString of the desired length. + //rtl_String's buffer is uninitialized, except for null termination + pStr = rtl_uString_alloc(sal::static_int_cast<sal_Int32>(nLen)); + SAL_WARN_IF(!pStr, "tools.stream", "allocation failed"); + if (pStr) + { + std::size_t nWasRead = rStrm.ReadBytes(pStr->buffer, nLen*2)/2; + if (nWasRead != nLen) + { + //on (typically unlikely) short read set length to what we could + //read, and null terminate. Excess buffer capacity remains of + //course, could create a (true) replacement OUString if it matters. + pStr->length = sal::static_int_cast<sal_Int32>(nWasRead); + pStr->buffer[pStr->length] = 0; + } + if (rStrm.IsEndianSwap()) + { + for (sal_Int32 i = 0; i < pStr->length; ++i) + pStr->buffer[i] = OSL_SWAPWORD(pStr->buffer[i]); + } + } + } + + //take ownership of buffer and return, otherwise return empty string + return pStr ? OUString(pStr, SAL_NO_ACQUIRE) : OUString(); +} + +namespace +{ + template <typename T, typename O> T tmpl_convertLineEnd(const T &rIn, LineEnd eLineEnd) + { + // Determine linebreaks and compute length + bool bConvert = false; // Needs conversion + sal_Int32 nStrLen = rIn.getLength(); + sal_Int32 nLineEndLen = (eLineEnd == LINEEND_CRLF) ? 2 : 1; + sal_Int32 nLen = 0; // Target length + sal_Int32 i = 0; // Source counter + + while (i < nStrLen) + { + // \r or \n causes linebreak + if ( (rIn[i] == '\r') || (rIn[i] == '\n') ) + { + nLen = nLen + nLineEndLen; + + // If set already, skip expensive test + if ( !bConvert ) + { + // Do we need to convert? + if ( ((eLineEnd != LINEEND_LF) && (rIn[i] == '\n')) || + ((eLineEnd == LINEEND_CRLF) && (i+1) < nStrLen && (rIn[i+1] != '\n')) || + ((eLineEnd == LINEEND_LF) && + ((rIn[i] == '\r') || ((i+1) < nStrLen && rIn[i+1] == '\r'))) || + ((eLineEnd == LINEEND_CR) && + ((rIn[i] == '\n') || ((i+1) < nStrLen && rIn[i+1] == '\n'))) ) + bConvert = true; + } + + // skip char if \r\n or \n\r + if ( (i+1) < nStrLen && ((rIn[i+1] == '\r') || (rIn[i+1] == '\n')) && + (rIn[i] != rIn[i+1]) ) + ++i; + } + else + ++nLen; + ++i; + } + + if (!bConvert) + return rIn; + + // convert linebreaks, insert string + O aNewData(nLen); + i = 0; + while (i < nStrLen) + { + // \r or \n causes linebreak + if ( (rIn[i] == '\r') || (rIn[i] == '\n') ) + { + if ( eLineEnd == LINEEND_CRLF ) + { + aNewData.append('\r'); + aNewData.append('\n'); + } + else + { + if ( eLineEnd == LINEEND_CR ) + aNewData.append('\r'); + else + aNewData.append('\n'); + } + + if ( (i+1) < nStrLen && ((rIn[i+1] == '\r') || (rIn[i+1] == '\n')) && + (rIn[i] != rIn[i+1]) ) + ++i; + } + else + { + aNewData.append(rIn[i]); + } + + ++i; + } + + return aNewData.makeStringAndClear(); + } +} + +OString convertLineEnd(const OString &rIn, LineEnd eLineEnd) +{ + return tmpl_convertLineEnd<OString, OStringBuffer>(rIn, eLineEnd); +} + +OUString convertLineEnd(const OUString &rIn, LineEnd eLineEnd) +{ + return tmpl_convertLineEnd<OUString, OUStringBuffer>(rIn, eLineEnd); +} + +std::size_t write_uInt32_lenPrefixed_uInt16s_FromOUString(SvStream& rStrm, + const OUString &rStr) +{ + std::size_t nWritten = 0; + sal_uInt32 nUnits = std::min<std::size_t>(rStr.getLength(), std::numeric_limits<sal_uInt32>::max()); + SAL_WARN_IF(static_cast<std::size_t>(nUnits) != static_cast<std::size_t>(rStr.getLength()), + "tools.stream", + "string too long for prefix count to fit in output type"); + rStrm.WriteUInt32(nUnits); + if (rStrm.good()) + { + nWritten += sizeof(sal_uInt32); + nWritten += write_uInt16s_FromOUString(rStrm, rStr, nUnits); + } + return nWritten; +} + +std::size_t write_uInt16_lenPrefixed_uInt16s_FromOUString(SvStream& rStrm, + const OUString &rStr) +{ + std::size_t nWritten = 0; + sal_uInt16 nUnits = std::min<std::size_t>(rStr.getLength(), std::numeric_limits<sal_uInt16>::max()); + SAL_WARN_IF(nUnits != rStr.getLength(), + "tools.stream", + "string too long for prefix count to fit in output type"); + rStrm.WriteUInt16(nUnits); + if (rStrm.good()) + { + nWritten += sizeof(nUnits); + nWritten += write_uInt16s_FromOUString(rStrm, rStr, nUnits); + } + return nWritten; +} + +std::size_t write_uInt16_lenPrefixed_uInt8s_FromOString(SvStream& rStrm, + const OString &rStr) +{ + std::size_t nWritten = 0; + sal_uInt16 nUnits = std::min<std::size_t>(rStr.getLength(), std::numeric_limits<sal_uInt16>::max()); + SAL_WARN_IF(static_cast<std::size_t>(nUnits) != static_cast<std::size_t>(rStr.getLength()), + "tools.stream", + "string too long for sal_uInt16 count to fit in output type"); + rStrm.WriteUInt16( nUnits ); + if (rStrm.good()) + { + nWritten += sizeof(sal_uInt16); + nWritten += write_uInt8s_FromOString(rStrm, rStr, nUnits); + } + return nWritten; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/tools/source/stream/strmunx.cxx b/tools/source/stream/strmunx.cxx new file mode 100644 index 000000000..9a55831e2 --- /dev/null +++ b/tools/source/stream/strmunx.cxx @@ -0,0 +1,487 @@ +/* -*- 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 <fcntl.h> +#include <errno.h> + +#include <tools/stream.hxx> +#include <map> + +#include <osl/mutex.hxx> +#include <osl/thread.h> +#include <sal/log.hxx> + +#include <osl/file.hxx> +#include <osl/detail/file.h> +#include <rtl/instance.hxx> + +using namespace osl; + +// InternalLock ---------------------------------------------------------------- + +namespace { + +struct LockMutex : public rtl::Static< osl::Mutex, LockMutex > {}; + +struct Locks : public rtl::Static< std::map<SvFileStream const *, osl::DirectoryItem>, Locks > {}; + +bool lockFile( SvFileStream* pStream ) +{ + osl::DirectoryItem aItem; + if (osl::DirectoryItem::get( pStream->GetFileName(), aItem) != osl::FileBase::E_None ) + { + SAL_INFO("tools.stream", "Failed to lookup stream for locking"); + return true; + } + + osl::FileStatus aStatus( osl_FileStatus_Mask_Type ); + if ( aItem.getFileStatus( aStatus ) != osl::FileBase::E_None ) + { + SAL_INFO("tools.stream", "Failed to stat stream for locking"); + return true; + } + if( aStatus.getFileType() == osl::FileStatus::Directory ) + return true; + + osl::MutexGuard aGuard( LockMutex::get() ); + auto &rLocks = Locks::get(); + for( const auto& [rLockStream, rLockItem] : rLocks ) + { + if( aItem.isIdenticalTo( rLockItem ) ) + { + StreamMode nLockMode = rLockStream->GetStreamMode(); + StreamMode nNewMode = pStream->GetStreamMode(); + bool bDenyByOptions = (nLockMode & StreamMode::SHARE_DENYALL) || + ( (nLockMode & StreamMode::SHARE_DENYWRITE) && (nNewMode & StreamMode::WRITE) ) || + ( (nLockMode & StreamMode::SHARE_DENYREAD) && (nNewMode & StreamMode::READ) ); + + if( bDenyByOptions ) + { + return false; // file is already locked + } + } + } + rLocks[pStream] = aItem; + return true; +} + +void unlockFile( SvFileStream const * pStream ) +{ + osl::MutexGuard aGuard( LockMutex::get() ); + auto &rLocks = Locks::get(); + rLocks.erase(pStream); +} + +} + +// StreamData ------------------------------------------------------------------ + +class StreamData +{ +public: + oslFileHandle rHandle; + + StreamData() : rHandle( nullptr ) { } +}; + +static ErrCode GetSvError( int nErrno ) +{ + static struct { int nErr; ErrCode sv; } const errArr[] = + { + { 0, ERRCODE_NONE }, + { EACCES, SVSTREAM_ACCESS_DENIED }, + { EBADF, SVSTREAM_INVALID_HANDLE }, +#if defined(NETBSD) || \ + defined(FREEBSD) || defined(MACOSX) || defined(OPENBSD) || \ + defined(__FreeBSD_kernel__) || defined (AIX) || defined(DRAGONFLY) || \ + defined(IOS) || defined(HAIKU) + { EDEADLK, SVSTREAM_LOCKING_VIOLATION }, +#else + { EDEADLOCK, SVSTREAM_LOCKING_VIOLATION }, +#endif + { EINVAL, SVSTREAM_INVALID_PARAMETER }, + { EMFILE, SVSTREAM_TOO_MANY_OPEN_FILES }, + { ENFILE, SVSTREAM_TOO_MANY_OPEN_FILES }, + { ENOENT, SVSTREAM_FILE_NOT_FOUND }, + { EPERM, SVSTREAM_ACCESS_DENIED }, + { EROFS, SVSTREAM_ACCESS_DENIED }, + { EAGAIN, SVSTREAM_LOCKING_VIOLATION }, + { EISDIR, SVSTREAM_PATH_NOT_FOUND }, + { ELOOP, SVSTREAM_PATH_NOT_FOUND }, +#if !defined(NETBSD) && !defined (FREEBSD) && \ + !defined(MACOSX) && !defined(OPENBSD) && !defined(__FreeBSD_kernel__) && \ + !defined(DRAGONFLY) + { EMULTIHOP, SVSTREAM_PATH_NOT_FOUND }, + { ENOLINK, SVSTREAM_PATH_NOT_FOUND }, +#endif + { ENOTDIR, SVSTREAM_PATH_NOT_FOUND }, + { ETXTBSY, SVSTREAM_ACCESS_DENIED }, + { EEXIST, SVSTREAM_CANNOT_MAKE }, + { ENOSPC, SVSTREAM_DISK_FULL }, + { int(0xFFFF), SVSTREAM_GENERALERROR } + }; + + ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error + int i=0; + do + { + if ( errArr[i].nErr == nErrno ) + { + nRetVal = errArr[i].sv; + break; + } + ++i; + } + while( errArr[i].nErr != 0xFFFF ); + return nRetVal; +} + +static ErrCode GetSvError( oslFileError nErrno ) +{ + static struct { oslFileError nErr; ErrCode sv; } const errArr[] = + { + { osl_File_E_None, ERRCODE_NONE }, + { osl_File_E_ACCES, SVSTREAM_ACCESS_DENIED }, + { osl_File_E_BADF, SVSTREAM_INVALID_HANDLE }, + { osl_File_E_DEADLK, SVSTREAM_LOCKING_VIOLATION }, + { osl_File_E_INVAL, SVSTREAM_INVALID_PARAMETER }, + { osl_File_E_MFILE, SVSTREAM_TOO_MANY_OPEN_FILES }, + { osl_File_E_NFILE, SVSTREAM_TOO_MANY_OPEN_FILES }, + { osl_File_E_NOENT, SVSTREAM_FILE_NOT_FOUND }, + { osl_File_E_PERM, SVSTREAM_ACCESS_DENIED }, + { osl_File_E_ROFS, SVSTREAM_ACCESS_DENIED }, + { osl_File_E_AGAIN, SVSTREAM_LOCKING_VIOLATION }, + { osl_File_E_ISDIR, SVSTREAM_PATH_NOT_FOUND }, + { osl_File_E_LOOP, SVSTREAM_PATH_NOT_FOUND }, + { osl_File_E_MULTIHOP, SVSTREAM_PATH_NOT_FOUND }, + { osl_File_E_NOLINK, SVSTREAM_PATH_NOT_FOUND }, + { osl_File_E_NOTDIR, SVSTREAM_PATH_NOT_FOUND }, + { osl_File_E_EXIST, SVSTREAM_CANNOT_MAKE }, + { osl_File_E_NOSPC, SVSTREAM_DISK_FULL }, + { oslFileError(0xFFFF), SVSTREAM_GENERALERROR } + }; + + ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error + int i=0; + do + { + if ( errArr[i].nErr == nErrno ) + { + nRetVal = errArr[i].sv; + break; + } + ++i; + } + while( errArr[i].nErr != oslFileError(0xFFFF) ); + return nRetVal; +} + +SvFileStream::SvFileStream( const OUString& rFileName, StreamMode nOpenMode ) +{ + bIsOpen = false; + m_isWritable = false; + pInstanceData.reset(new StreamData); + + SetBufferSize( 1024 ); + // convert URL to SystemPath, if necessary + OUString aSystemFileName; + if( FileBase::getSystemPathFromFileURL( rFileName , aSystemFileName ) + != FileBase::E_None ) + { + aSystemFileName = rFileName; + } + Open( aSystemFileName, nOpenMode ); +} + +SvFileStream::SvFileStream() +{ + bIsOpen = false; + m_isWritable = false; + pInstanceData.reset(new StreamData); + SetBufferSize( 1024 ); +} + +SvFileStream::~SvFileStream() +{ + Close(); +} + +std::size_t SvFileStream::GetData( void* pData, std::size_t nSize ) +{ + SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes from " << aFilename); + + sal_uInt64 nRead = 0; + if ( IsOpen() ) + { + oslFileError rc = osl_readFile(pInstanceData->rHandle,pData,static_cast<sal_uInt64>(nSize),&nRead); + if ( rc != osl_File_E_None ) + { + SetError( ::GetSvError( rc )); + return -1; + } + } + return static_cast<std::size_t>(nRead); +} + +std::size_t SvFileStream::PutData( const void* pData, std::size_t nSize ) +{ + SAL_INFO("tools", OString::number(static_cast<sal_Int64>(nSize)) << " Bytes to " << aFilename); + + sal_uInt64 nWrite = 0; + if ( IsOpen() ) + { + oslFileError rc = osl_writeFile(pInstanceData->rHandle,pData,static_cast<sal_uInt64>(nSize),&nWrite); + if ( rc != osl_File_E_None ) + { + SetError( ::GetSvError( rc ) ); + return -1; + } + else if( !nWrite ) + SetError( SVSTREAM_DISK_FULL ); + } + return static_cast<std::size_t>(nWrite); +} + +sal_uInt64 SvFileStream::SeekPos(sal_uInt64 const nPos) +{ + // check if a truncated STREAM_SEEK_TO_END was passed + assert(nPos != sal_uInt64(sal_uInt32(STREAM_SEEK_TO_END))); + if ( IsOpen() ) + { + oslFileError rc; + sal_uInt64 nNewPos; + if ( nPos != STREAM_SEEK_TO_END ) + rc = osl_setFilePos( pInstanceData->rHandle, osl_Pos_Absolut, nPos ); + else + rc = osl_setFilePos( pInstanceData->rHandle, osl_Pos_End, 0 ); + + if ( rc != osl_File_E_None ) + { + SetError( SVSTREAM_SEEK_ERROR ); + return 0L; + } + if ( nPos != STREAM_SEEK_TO_END ) + return nPos; + osl_getFilePos( pInstanceData->rHandle, &nNewPos ); + return nNewPos; + } + SetError( SVSTREAM_GENERALERROR ); + return 0L; +} + +void SvFileStream::FlushData() +{ + // does not exist locally +} + +bool SvFileStream::LockFile() +{ + int nLockMode = 0; + + if ( ! IsOpen() ) + return false; + + if (m_eStreamMode & StreamMode::SHARE_DENYALL) + { + if (m_isWritable) + nLockMode = F_WRLCK; + else + nLockMode = F_RDLCK; + } + + if (m_eStreamMode & StreamMode::SHARE_DENYREAD) + { + if (m_isWritable) + nLockMode = F_WRLCK; + else + { + SetError(SVSTREAM_LOCKING_VIOLATION); + return false; + } + } + + if (m_eStreamMode & StreamMode::SHARE_DENYWRITE) + { + if (m_isWritable) + nLockMode = F_WRLCK; + else + nLockMode = F_RDLCK; + } + + if (!nLockMode) + return true; + + if( !lockFile( this ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "InternalLock on %s failed\n", + OUStringToOString(aFilename, osl_getThreadTextEncoding()).getStr() ); +#endif + return false; + } + + return true; +} + +void SvFileStream::UnlockFile() +{ + if ( ! IsOpen() ) + return; + + unlockFile( this ); +} + +void SvFileStream::Open( const OUString& rFilename, StreamMode nOpenMode ) +{ + sal_uInt32 uFlags; + oslFileHandle nHandleTmp; + + Close(); + errno = 0; + m_eStreamMode = nOpenMode; + m_eStreamMode &= ~StreamMode::TRUNC; // don't truncate on reopen + + aFilename = rFilename; + + SAL_INFO("tools", aFilename); + + OUString aFileURL; + osl::DirectoryItem aItem; + osl::FileStatus aStatus( osl_FileStatus_Mask_Type | osl_FileStatus_Mask_LinkTargetURL ); + + // FIXME: we really need to switch to a pure URL model ... + if ( osl::File::getFileURLFromSystemPath( aFilename, aFileURL ) != osl::FileBase::E_None ) + aFileURL = aFilename; + bool bStatValid = ( osl::DirectoryItem::get( aFileURL, aItem) == osl::FileBase::E_None && + aItem.getFileStatus( aStatus ) == osl::FileBase::E_None ); + + // SvFileStream can't open a directory + if( bStatValid && aStatus.getFileType() == osl::FileStatus::Directory ) + { + SetError( ::GetSvError( EISDIR ) ); + return; + } + + if ( !( nOpenMode & StreamMode::WRITE ) ) + uFlags = osl_File_OpenFlag_Read; + else if ( !( nOpenMode & StreamMode::READ ) ) + uFlags = osl_File_OpenFlag_Write; + else + uFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write; + + // Fix (MDA, 18.01.95): Don't open with O_CREAT upon RD_ONLY + // Important for Read-Only-Filesystems (e.g, CDROM) + if ( (!( nOpenMode & StreamMode::NOCREATE )) && ( uFlags != osl_File_OpenFlag_Read ) ) + uFlags |= osl_File_OpenFlag_Create; + if ( nOpenMode & StreamMode::TRUNC ) + uFlags |= osl_File_OpenFlag_Trunc; + + uFlags |= osl_File_OpenFlag_NoExcl | osl_File_OpenFlag_NoLock; + + if ( nOpenMode & StreamMode::WRITE) + { + if ( nOpenMode & StreamMode::COPY_ON_SYMLINK ) + { + if ( bStatValid && aStatus.getFileType() == osl::FileStatus::Link && + aStatus.getLinkTargetURL().getLength() > 0 ) + { + // delete the symbolic link, and replace it with the contents of the link + if (osl::File::remove( aFileURL ) == osl::FileBase::E_None ) + { + File::copy( aStatus.getLinkTargetURL(), aFileURL ); +#if OSL_DEBUG_LEVEL > 0 + fprintf( stderr, + "Removing link and replacing with file contents (%s) -> (%s).\n", + OUStringToOString( aStatus.getLinkTargetURL(), + RTL_TEXTENCODING_UTF8).getStr(), + OUStringToOString( aFileURL, + RTL_TEXTENCODING_UTF8).getStr() ); +#endif + } + } + } + } + + oslFileError rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags ); + if ( rc != osl_File_E_None ) + { + if ( uFlags & osl_File_OpenFlag_Write ) + { + // Change to read-only + uFlags &= ~osl_File_OpenFlag_Write; + rc = osl_openFile( aFileURL.pData, &nHandleTmp, uFlags ); + } + } + if ( rc == osl_File_E_None ) + { + pInstanceData->rHandle = nHandleTmp; + bIsOpen = true; + if ( uFlags & osl_File_OpenFlag_Write ) + m_isWritable = true; + + if ( !LockFile() ) // whole file + { + osl_closeFile( nHandleTmp ); + bIsOpen = false; + m_isWritable = false; + pInstanceData->rHandle = nullptr; + } + } + else + SetError( ::GetSvError( rc ) ); +} + +void SvFileStream::Close() +{ + UnlockFile(); + + if ( IsOpen() ) + { + SAL_INFO("tools", "Closing " << aFilename); + Flush(); + osl_closeFile( pInstanceData->rHandle ); + pInstanceData->rHandle = nullptr; + } + + bIsOpen = false; + m_isWritable = false; + SvStream::ClearBuffer(); + SvStream::ClearError(); +} + +/// set filepointer to beginning of file +void SvFileStream::ResetError() +{ + SvStream::ClearError(); +} + +void SvFileStream::SetSize (sal_uInt64 const nSize) +{ + if (IsOpen()) + { + oslFileError rc = osl_setFileSize( pInstanceData->rHandle, nSize ); + if (rc != osl_File_E_None ) + { + SetError ( ::GetSvError( rc )); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/tools/source/stream/strmwnt.cxx b/tools/source/stream/strmwnt.cxx new file mode 100644 index 000000000..d85ce3a0c --- /dev/null +++ b/tools/source/stream/strmwnt.cxx @@ -0,0 +1,420 @@ +/* -*- 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 . + */ + +// TODO: StreamMode <-> AllocateMemory + +#include <string.h> +#include <limits.h> + +#ifdef _WIN32 +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#endif + +#include <osl/thread.h> +#include <tools/stream.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#include <osl/file.hxx> +using namespace osl; + +class StreamData +{ +public: + HANDLE hFile; + + StreamData() : hFile(nullptr) + { + } +}; + +static ErrCode GetSvError( DWORD nWntError ) +{ + static struct { DWORD wnt; ErrCode sv; } errArr[] = + { + { ERROR_SUCCESS, ERRCODE_NONE }, + { ERROR_ACCESS_DENIED, SVSTREAM_ACCESS_DENIED }, + { ERROR_ACCOUNT_DISABLED, SVSTREAM_ACCESS_DENIED }, + { ERROR_ACCOUNT_EXPIRED, SVSTREAM_ACCESS_DENIED }, + { ERROR_ACCOUNT_RESTRICTION, SVSTREAM_ACCESS_DENIED }, + { ERROR_ATOMIC_LOCKS_NOT_SUPPORTED, SVSTREAM_INVALID_PARAMETER }, + { ERROR_BAD_PATHNAME, SVSTREAM_PATH_NOT_FOUND }, + // Filename too long + { ERROR_BUFFER_OVERFLOW, SVSTREAM_INVALID_PARAMETER }, + { ERROR_DIRECTORY, SVSTREAM_INVALID_PARAMETER }, + { ERROR_DRIVE_LOCKED, SVSTREAM_LOCKING_VIOLATION }, + { ERROR_FILE_NOT_FOUND, SVSTREAM_FILE_NOT_FOUND }, + { ERROR_FILENAME_EXCED_RANGE, SVSTREAM_INVALID_PARAMETER }, + { ERROR_INVALID_ACCESS, SVSTREAM_INVALID_ACCESS }, + { ERROR_INVALID_DRIVE, SVSTREAM_PATH_NOT_FOUND }, + { ERROR_INVALID_HANDLE, SVSTREAM_INVALID_HANDLE }, + { ERROR_INVALID_NAME, SVSTREAM_PATH_NOT_FOUND }, + { ERROR_INVALID_PARAMETER, SVSTREAM_INVALID_PARAMETER }, + { ERROR_IS_SUBST_PATH, SVSTREAM_INVALID_PARAMETER }, + { ERROR_IS_SUBST_TARGET, SVSTREAM_INVALID_PARAMETER }, + { ERROR_LOCK_FAILED, SVSTREAM_LOCKING_VIOLATION }, + { ERROR_LOCK_VIOLATION, SVSTREAM_LOCKING_VIOLATION }, + { ERROR_NEGATIVE_SEEK, SVSTREAM_SEEK_ERROR }, + { ERROR_PATH_NOT_FOUND, SVSTREAM_PATH_NOT_FOUND }, + { ERROR_READ_FAULT, SVSTREAM_READ_ERROR }, + { ERROR_SEEK, SVSTREAM_SEEK_ERROR }, + { ERROR_SEEK_ON_DEVICE, SVSTREAM_SEEK_ERROR }, + { ERROR_SHARING_BUFFER_EXCEEDED,SVSTREAM_SHARE_BUFF_EXCEEDED }, + { ERROR_SHARING_PAUSED, SVSTREAM_SHARING_VIOLATION }, + { ERROR_SHARING_VIOLATION, SVSTREAM_SHARING_VIOLATION }, + { ERROR_TOO_MANY_OPEN_FILES, SVSTREAM_TOO_MANY_OPEN_FILES }, + { ERROR_WRITE_FAULT, SVSTREAM_WRITE_ERROR }, + { ERROR_WRITE_PROTECT, SVSTREAM_ACCESS_DENIED }, + { ERROR_DISK_FULL, SVSTREAM_DISK_FULL }, + + { DWORD(0xFFFFFFFF), SVSTREAM_GENERALERROR } + }; + + ErrCode nRetVal = SVSTREAM_GENERALERROR; // default error + int i=0; + do + { + if( errArr[i].wnt == nWntError ) + { + nRetVal = errArr[i].sv; + break; + } + i++; + } while( errArr[i].wnt != DWORD(0xFFFFFFFF) ); + return nRetVal; +} + +SvFileStream::SvFileStream( const OUString& rFileName, StreamMode nMode ) +{ + bIsOpen = false; + nLockCounter = 0; + m_isWritable = false; + pInstanceData.reset( new StreamData ); + + SetBufferSize( 8192 ); + // convert URL to SystemPath, if necessary + OUString aFileName; + + if ( FileBase::getSystemPathFromFileURL( rFileName, aFileName ) != FileBase::E_None ) + aFileName = rFileName; + Open( aFileName, nMode ); +} + +SvFileStream::SvFileStream() +{ + bIsOpen = false; + nLockCounter = 0; + m_isWritable = false; + pInstanceData.reset( new StreamData ); + + SetBufferSize( 8192 ); +} + +SvFileStream::~SvFileStream() +{ + Close(); +} + +/// Does not check for EOF, makes isEof callable +std::size_t SvFileStream::GetData( void* pData, std::size_t nSize ) +{ + DWORD nCount = 0; + if( IsOpen() ) + { + bool bResult = ReadFile(pInstanceData->hFile,pData,nSize,&nCount,nullptr); + if( !bResult ) + { + std::size_t nTestError = GetLastError(); + SetError(::GetSvError( nTestError ) ); + } + } + return nCount; +} + +std::size_t SvFileStream::PutData( const void* pData, std::size_t nSize ) +{ + DWORD nCount = 0; + if( IsOpen() ) + { + if(!WriteFile(pInstanceData->hFile,pData,nSize,&nCount,nullptr)) + SetError(::GetSvError( GetLastError() ) ); + } + return nCount; +} + +sal_uInt64 SvFileStream::SeekPos(sal_uInt64 const nPos) +{ + // check if a truncated STREAM_SEEK_TO_END was passed + assert(nPos != SAL_MAX_UINT32); + DWORD nNewPos = 0; + if( IsOpen() ) + { + if( nPos != STREAM_SEEK_TO_END ) + // 64-Bit files are not supported + nNewPos=SetFilePointer(pInstanceData->hFile,nPos,nullptr,FILE_BEGIN); + else + nNewPos=SetFilePointer(pInstanceData->hFile,0L,nullptr,FILE_END); + + if( nNewPos == 0xFFFFFFFF ) + { + SetError(::GetSvError( GetLastError() ) ); + nNewPos = 0; + } + } + else + SetError( SVSTREAM_GENERALERROR ); + return static_cast<sal_uInt64>(nNewPos); +} + +void SvFileStream::FlushData() +{ + if( IsOpen() ) + { + if( !FlushFileBuffers(pInstanceData->hFile) ) + SetError(::GetSvError(GetLastError())); + } +} + +bool SvFileStream::LockFile() +{ + bool bRetVal = false; + if( !nLockCounter ) + { + if( IsOpen() ) + { + bRetVal = ::LockFile(pInstanceData->hFile,0L,0L,LONG_MAX,0L ); + if( bRetVal ) + { + nLockCounter = 1; + } + else + SetError(::GetSvError(GetLastError())); + } + } + else + { + nLockCounter++; + bRetVal = true; + } + return bRetVal; +} + +void SvFileStream::UnlockFile() +{ + if( nLockCounter > 0) + { + if( nLockCounter == 1) + { + if( IsOpen() ) + { + if( ::UnlockFile(pInstanceData->hFile,0L,0L,LONG_MAX,0L ) ) + { + nLockCounter = 0; + } + else + SetError(::GetSvError(GetLastError())); + } + } + else + { + nLockCounter--; + } + } +} + +/* + NOCREATE TRUNC NT-Action + ---------------------------------------------- + 0 (Create) 0 OPEN_ALWAYS + 0 (Create) 1 CREATE_ALWAYS + 1 0 OPEN_EXISTING + 1 1 TRUNCATE_EXISTING +*/ +void SvFileStream::Open( const OUString& rFilename, StreamMode nMode ) +{ + OUString aParsedFilename(rFilename); + + SetLastError( ERROR_SUCCESS ); + Close(); + SvStream::ClearBuffer(); + + m_eStreamMode = nMode; + m_eStreamMode &= ~StreamMode::TRUNC; // don't truncate on reopen + + aFilename = aParsedFilename; + SetLastError( ERROR_SUCCESS ); // might be changed by Redirector + + DWORD nOpenAction; + DWORD nShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + DWORD nAccessMode = 0; + UINT nOldErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX ); + + if( nMode & StreamMode::SHARE_DENYREAD) + nShareMode &= ~FILE_SHARE_READ; + + if( nMode & StreamMode::SHARE_DENYWRITE) + nShareMode &= ~FILE_SHARE_WRITE; + + if( nMode & StreamMode::SHARE_DENYALL) + nShareMode = 0; + + if( nMode & StreamMode::READ ) + nAccessMode |= GENERIC_READ; + if( nMode & StreamMode::WRITE ) + nAccessMode |= GENERIC_WRITE; + + if( nAccessMode == GENERIC_READ ) // ReadOnly ? + nMode |= StreamMode::NOCREATE; // Don't create if readonly + + // Assignment based on true/false table above + if( !(nMode & StreamMode::NOCREATE) ) + { + if( nMode & StreamMode::TRUNC ) + nOpenAction = CREATE_ALWAYS; + else + nOpenAction = OPEN_ALWAYS; + } + else + { + if( nMode & StreamMode::TRUNC ) + nOpenAction = TRUNCATE_EXISTING; + else + nOpenAction = OPEN_EXISTING; + } + + DWORD nAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; + + if ( nMode & StreamMode::TEMPORARY ) + nAttributes |= FILE_ATTRIBUTE_TEMPORARY; + + pInstanceData->hFile = CreateFileW( + o3tl::toW(aFilename.getStr()), + nAccessMode, + nShareMode, + nullptr, + nOpenAction, + nAttributes, + nullptr + ); + + if( pInstanceData->hFile!=INVALID_HANDLE_VALUE && ( + // Did Create Always overwrite a file? + GetLastError() == ERROR_ALREADY_EXISTS || + // Did Create Always open a new file? + GetLastError() == ERROR_FILE_NOT_FOUND )) + { + // If so, no error + if( nOpenAction == OPEN_ALWAYS || nOpenAction == CREATE_ALWAYS ) + SetLastError( ERROR_SUCCESS ); + } + + // Otherwise, determine if we're allowed to read + if( (pInstanceData->hFile==INVALID_HANDLE_VALUE) && + (nAccessMode & GENERIC_WRITE)) + { + ErrCode nErr = ::GetSvError( GetLastError() ); + if(nErr==SVSTREAM_ACCESS_DENIED || nErr==SVSTREAM_SHARING_VIOLATION) + { + nMode &= ~StreamMode::WRITE; + nAccessMode = GENERIC_READ; + // OV, 28.1.97: Win32 sets file to length 0 + // if Openaction is CREATE_ALWAYS + nOpenAction = OPEN_EXISTING; + SetLastError( ERROR_SUCCESS ); + pInstanceData->hFile = CreateFileW( + o3tl::toW(aFilename.getStr()), + GENERIC_READ, + nShareMode, + nullptr, + nOpenAction, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, + nullptr + ); + if( GetLastError() == ERROR_ALREADY_EXISTS ) + SetLastError( ERROR_SUCCESS ); + } + } + + if( GetLastError() != ERROR_SUCCESS ) + { + bIsOpen = false; + SetError(::GetSvError( GetLastError() ) ); + } + else + { + bIsOpen = true; + // pInstanceData->bIsEof = false; + if( nAccessMode & GENERIC_WRITE ) + m_isWritable = true; + } + SetErrorMode( nOldErrorMode ); +} + +void SvFileStream::Close() +{ + if( IsOpen() ) + { + if( nLockCounter ) + { + nLockCounter = 1; + UnlockFile(); + } + Flush(); + CloseHandle( pInstanceData->hFile ); + } + bIsOpen = false; + nLockCounter= 0; + m_isWritable = false; + SvStream::ClearBuffer(); + SvStream::ClearError(); +} + +/// Reset filepointer to beginning of file +void SvFileStream::ResetError() +{ + SvStream::ClearError(); +} + +void SvFileStream::SetSize(sal_uInt64 const nSize) +{ + + if( IsOpen() ) + { + bool bError = false; + HANDLE hFile = pInstanceData->hFile; + DWORD const nOld = SetFilePointer( hFile, 0L, nullptr, FILE_CURRENT ); + if( nOld != 0xffffffff ) + { + if( SetFilePointer(hFile,nSize,nullptr,FILE_BEGIN ) != 0xffffffff) + { + bool bSucc = SetEndOfFile( hFile ); + if( !bSucc ) + bError = true; + } + if( SetFilePointer( hFile,nOld,nullptr,FILE_BEGIN ) == 0xffffffff) + bError = true; + } + if( bError ) + SetError(::GetSvError( GetLastError() ) ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/tools/source/stream/vcompat.cxx b/tools/source/stream/vcompat.cxx new file mode 100644 index 000000000..d77f866e0 --- /dev/null +++ b/tools/source/stream/vcompat.cxx @@ -0,0 +1,67 @@ +/* -*- 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 <tools/stream.hxx> +#include <tools/vcompat.hxx> + +VersionCompat::VersionCompat( SvStream& rStm, StreamMode nStreamMode, sal_uInt16 nVersion ) : + mpRWStm ( &rStm ), + mnCompatPos ( 0 ), + mnTotalSize ( 0 ), + mnStmMode ( nStreamMode ), + mnVersion ( nVersion ) +{ + if( !mpRWStm->GetError() ) + { + if( StreamMode::WRITE == mnStmMode ) + { + mpRWStm->WriteUInt16( mnVersion ); + mnCompatPos = mpRWStm->Tell(); + mnTotalSize = mnCompatPos + 4; + mpRWStm->SeekRel( 4 ); + } + else + { + mpRWStm->ReadUInt16( mnVersion ); + mpRWStm->ReadUInt32( mnTotalSize ); + mnCompatPos = mpRWStm->Tell(); + } + } +} + +VersionCompat::~VersionCompat() +{ + if( StreamMode::WRITE == mnStmMode ) + { + const sal_uInt32 nEndPos = mpRWStm->Tell(); + + mpRWStm->Seek( mnCompatPos ); + mpRWStm->WriteUInt32( nEndPos - mnTotalSize ); + mpRWStm->Seek( nEndPos ); + } + else + { + const sal_uInt32 nReadSize = mpRWStm->Tell() - mnCompatPos; + + if( mnTotalSize > nReadSize ) + mpRWStm->SeekRel( mnTotalSize - nReadSize ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |