diff options
Diffstat (limited to 'oox/source/dump/dumperbase.cxx')
-rw-r--r-- | oox/source/dump/dumperbase.cxx | 2580 |
1 files changed, 2580 insertions, 0 deletions
diff --git a/oox/source/dump/dumperbase.cxx b/oox/source/dump/dumperbase.cxx new file mode 100644 index 000000000..61d603228 --- /dev/null +++ b/oox/source/dump/dumperbase.cxx @@ -0,0 +1,2580 @@ +/* -*- 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 <oox/dump/dumperbase.hxx> + +#include <algorithm> +#include <string_view> + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/io/TextOutputStream.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <osl/file.hxx> +#include <rtl/math.hxx> +#include <rtl/tencinfo.h> +#include <oox/core/filterbase.hxx> +#include <oox/helper/binaryoutputstream.hxx> +#include <oox/helper/textinputstream.hxx> +#include <tools/time.hxx> +#include <o3tl/string_view.hxx> + +#ifdef DBG_UTIL + +namespace oox::dump { + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::uno; + +using ::oox::core::FilterBase; + +namespace { + +const sal_Unicode OOX_DUMP_BOM = 0xFEFF; +const sal_Int32 OOX_DUMP_MAXSTRLEN = 80; +const sal_Int32 OOX_DUMP_INDENT = 2; +const sal_Unicode OOX_DUMP_BINDOT = '.'; +const sal_Unicode OOX_DUMP_CFG_LISTSEP = ','; +const sal_Unicode OOX_DUMP_CFG_QUOTE = '\''; +const sal_Unicode OOX_DUMP_LF = '\n'; +const sal_Unicode OOX_DUMP_ITEMSEP = '='; +const sal_Int32 OOX_DUMP_BYTESPERLINE = 16; +const sal_Int64 OOX_DUMP_MAXARRAY = 16; + +} // namespace + +// file names ----------------------------------------------------------------- + +OUString InputOutputHelper::convertFileNameToUrl( const OUString& rFileName ) +{ + OUString aFileUrl; + if( ::osl::FileBase::getFileURLFromSystemPath( rFileName, aFileUrl ) == ::osl::FileBase::E_None ) + return aFileUrl; + return OUString(); +} + +sal_Int32 InputOutputHelper::getFileNamePos( std::u16string_view rFileUrl ) +{ + size_t nSepPos = rFileUrl.find( '/' ); + return (nSepPos == std::u16string_view::npos) ? 0 : (nSepPos + 1); +} + +std::u16string_view InputOutputHelper::getFileNameExtension( std::u16string_view rFileUrl ) +{ + sal_Int32 nNamePos = getFileNamePos( rFileUrl ); + size_t nExtPos = rFileUrl.rfind( '.' ); + if( nExtPos != std::u16string_view::npos && static_cast<sal_Int32>(nExtPos) >= nNamePos ) + return rFileUrl.substr( nExtPos + 1 ); + return std::u16string_view(); +} + +// input streams -------------------------------------------------------------- + +Reference< XInputStream > InputOutputHelper::openInputStream( + const Reference< XComponentContext >& rxContext, const OUString& rFileName ) +{ + Reference< XInputStream > xInStrm; + if( rxContext.is() ) try + { + Reference<XSimpleFileAccess3> xFileAccess(SimpleFileAccess::create(rxContext)); + xInStrm = xFileAccess->openFileRead( rFileName ); + } + catch( Exception& ) + { + } + return xInStrm; +} + +// output streams ------------------------------------------------------------- + +Reference< XOutputStream > InputOutputHelper::openOutputStream( + const Reference< XComponentContext >& rxContext, const OUString& rFileName ) +{ + Reference< XOutputStream > xOutStrm; + if( rxContext.is() ) try + { + Reference<XSimpleFileAccess3> xFileAccess(SimpleFileAccess::create(rxContext)); + xOutStrm = xFileAccess->openFileWrite( rFileName ); + } + catch( Exception& ) + { + } + return xOutStrm; +} + +Reference< XTextOutputStream2 > InputOutputHelper::openTextOutputStream( + const Reference< XComponentContext >& rxContext, const Reference< XOutputStream >& rxOutStrm, rtl_TextEncoding eTextEnc ) +{ + Reference< XTextOutputStream2 > xTextOutStrm; + const char* pcCharset = rtl_getMimeCharsetFromTextEncoding( eTextEnc ); + if( rxContext.is() && rxOutStrm.is() && pcCharset ) try + { + xTextOutStrm = TextOutputStream::create(rxContext); + xTextOutStrm->setOutputStream( rxOutStrm ); + xTextOutStrm->setEncoding( OUString::createFromAscii( pcCharset ) ); + } + catch( Exception& ) + { + } + return xTextOutStrm; +} + +Reference< XTextOutputStream2 > InputOutputHelper::openTextOutputStream( + const Reference< XComponentContext >& rxContext, const OUString& rFileName, rtl_TextEncoding eTextEnc ) +{ + return openTextOutputStream( rxContext, openOutputStream( rxContext, rFileName ), eTextEnc ); +} + +ItemFormat::ItemFormat() : + meDataType( DATATYPE_VOID ), + meFmtType( FORMATTYPE_NONE ) +{ +} + +void ItemFormat::set( DataType eDataType, FormatType eFmtType, const OUString& rItemName ) +{ + meDataType = eDataType; + meFmtType = eFmtType; + maItemName = rItemName; + maListName.clear(); +} + +OUStringVector::const_iterator ItemFormat::parse( const OUStringVector& rFormatVec ) +{ + set( DATATYPE_VOID, FORMATTYPE_NONE, OUString() ); + + OUStringVector::const_iterator aIt = rFormatVec.begin(), aEnd = rFormatVec.end(); + OUString aDataType, aFmtType; + if( aIt != aEnd ) aDataType = *aIt++; + if( aIt != aEnd ) aFmtType = *aIt++; + if( aIt != aEnd ) maItemName = *aIt++; + if( aIt != aEnd ) maListName = *aIt++; + + meDataType = StringHelper::convertToDataType( aDataType ); + meFmtType = StringHelper::convertToFormatType( aFmtType ); + + if( meFmtType == FORMATTYPE_NONE ) + { + if ( aFmtType == "unused" ) + set( meDataType, FORMATTYPE_HEX, OOX_DUMP_UNUSED ); + else if ( aFmtType == "unknown" ) + set( meDataType, FORMATTYPE_HEX, OOX_DUMP_UNKNOWN ); + } + + return aIt; +} + +OUStringVector ItemFormat::parse( const OUString& rFormatStr ) +{ + OUStringVector aFormatVec; + StringHelper::convertStringToStringList( aFormatVec, rFormatStr, false ); + OUStringVector::const_iterator aIt = parse( aFormatVec ); + return OUStringVector( aIt, const_cast< const OUStringVector& >( aFormatVec ).end() ); +} + +// append string to string ---------------------------------------------------- + +void StringHelper::appendChar( OUStringBuffer& rStr, sal_Unicode cChar, sal_Int32 nCount ) +{ + for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex ) + rStr.append( cChar ); +} + +void StringHelper::appendString( OUStringBuffer& rStr, const OUString& rData, sal_Int32 nWidth, sal_Unicode cFill ) +{ + appendChar( rStr, cFill, nWidth - rData.getLength() ); + rStr.append( rData ); +} + +// append decimal ------------------------------------------------------------- + +void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt8 nData, sal_Int32 nWidth, sal_Unicode cFill ) +{ + appendString( rStr, OUString::number( nData ), nWidth, cFill ); +} + +void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int8 nData, sal_Int32 nWidth, sal_Unicode cFill ) +{ + appendString( rStr, OUString::number( nData ), nWidth, cFill ); +} + +void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt16 nData, sal_Int32 nWidth, sal_Unicode cFill ) +{ + appendString( rStr, OUString::number( nData ), nWidth, cFill ); +} + +void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int16 nData, sal_Int32 nWidth, sal_Unicode cFill ) +{ + appendString( rStr, OUString::number( nData ), nWidth, cFill ); +} + +void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt32 nData, sal_Int32 nWidth, sal_Unicode cFill ) +{ + appendString( rStr, OUString::number( nData ), nWidth, cFill ); +} + +void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int32 nData, sal_Int32 nWidth, sal_Unicode cFill ) +{ + appendString( rStr, OUString::number( nData ), nWidth, cFill ); +} + +void StringHelper::appendDec( OUStringBuffer& rStr, sal_uInt64 nData, sal_Int32 nWidth, sal_Unicode cFill ) +{ + /* Values greater than biggest signed 64bit integer will change to + negative when converting to sal_Int64. Therefore, the trailing digit + will be written separately. */ + OUStringBuffer aBuffer; + if( nData > 9 ) + aBuffer.append( static_cast<sal_Int64>(nData / 10 ) ); + aBuffer.append( static_cast< sal_Unicode >( '0' + (nData % 10) ) ); + appendString( rStr, aBuffer.makeStringAndClear(), nWidth, cFill ); +} + +void StringHelper::appendDec( OUStringBuffer& rStr, sal_Int64 nData, sal_Int32 nWidth, sal_Unicode cFill ) +{ + appendString( rStr, OUString::number( nData ), nWidth, cFill ); +} + +void StringHelper::appendDec( OUStringBuffer& rStr, double fData, sal_Int32 nWidth, sal_Unicode cFill ) +{ + appendString( rStr, ::rtl::math::doubleToUString( fData, rtl_math_StringFormat_G, 15, '.', true ), nWidth, cFill ); +} + +// append hexadecimal --------------------------------------------------------- + +void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt8 nData, bool bPrefix ) +{ + static const sal_Unicode spcHexDigits[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; + if( bPrefix ) + rStr.append( "0x" ); + rStr.append( spcHexDigits[ (nData >> 4) & 0x0F ] ).append( spcHexDigits[ nData & 0x0F ] ); +} + +void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int8 nData, bool bPrefix ) +{ + appendHex( rStr, static_cast< sal_uInt8 >( nData ), bPrefix ); +} + +void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt16 nData, bool bPrefix ) +{ + appendHex( rStr, static_cast< sal_uInt8 >( nData >> 8 ), bPrefix ); + appendHex( rStr, static_cast< sal_uInt8 >( nData ), false ); +} + +void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int16 nData, bool bPrefix ) +{ + appendHex( rStr, static_cast< sal_uInt16 >( nData ), bPrefix ); +} + +void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt32 nData, bool bPrefix ) +{ + appendHex( rStr, static_cast< sal_uInt16 >( nData >> 16 ), bPrefix ); + appendHex( rStr, static_cast< sal_uInt16 >( nData ), false ); +} + +void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int32 nData, bool bPrefix ) +{ + appendHex( rStr, static_cast< sal_uInt32 >( nData ), bPrefix ); +} + +void StringHelper::appendHex( OUStringBuffer& rStr, sal_uInt64 nData, bool bPrefix ) +{ + appendHex( rStr, static_cast< sal_uInt32 >( nData >> 32 ), bPrefix ); + appendHex( rStr, static_cast< sal_uInt32 >( nData ), false ); +} + +void StringHelper::appendHex( OUStringBuffer& rStr, sal_Int64 nData, bool bPrefix ) +{ + appendHex( rStr, static_cast< sal_uInt64 >( nData ), bPrefix ); +} + +static sal_uInt64 +lcl_ConvertDouble(double const f) +{ + sal_uInt64 i = sal_uInt64(); + for (size_t j = 0; j < sizeof(double); ++j) + { // hopefully both endian independent and strict aliasing safe + reinterpret_cast<char *>(&i)[j] = reinterpret_cast<char const *>(&f)[j]; + } + return i; +} + +void StringHelper::appendHex( OUStringBuffer& rStr, double fData, bool bPrefix ) +{ + appendHex( rStr, lcl_ConvertDouble(fData), bPrefix ); +} + +// append shortened hexadecimal ----------------------------------------------- + +void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt8 nData, bool bPrefix ) +{ + appendHex( rStr, nData, bPrefix ); +} + +void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int8 nData, bool bPrefix ) +{ + appendHex( rStr, nData, bPrefix ); +} + +void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt16 nData, bool bPrefix ) +{ + if( nData > SAL_MAX_UINT8 ) + appendHex( rStr, nData, bPrefix ); + else + appendHex( rStr, static_cast< sal_uInt8 >( nData ), bPrefix ); +} + +void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int16 nData, bool bPrefix ) +{ + appendShortHex( rStr, static_cast< sal_uInt16 >( nData ), bPrefix ); +} + +void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt32 nData, bool bPrefix ) +{ + if( nData > SAL_MAX_UINT16 ) + appendHex( rStr, nData, bPrefix ); + else + appendShortHex( rStr, static_cast< sal_uInt16 >( nData ), bPrefix ); +} + +void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int32 nData, bool bPrefix ) +{ + appendShortHex( rStr, static_cast< sal_uInt32 >( nData ), bPrefix ); +} + +void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_uInt64 nData, bool bPrefix ) +{ + if( nData > SAL_MAX_UINT32 ) + appendHex( rStr, nData, bPrefix ); + else + appendShortHex( rStr, static_cast< sal_uInt32 >( nData ), bPrefix ); +} + +void StringHelper::appendShortHex( OUStringBuffer& rStr, sal_Int64 nData, bool bPrefix ) +{ + appendShortHex( rStr, static_cast< sal_uInt64 >( nData ), bPrefix ); +} + +void StringHelper::appendShortHex( OUStringBuffer& rStr, double fData, bool bPrefix ) +{ + appendHex( rStr, fData, bPrefix ); +} + +// append binary -------------------------------------------------------------- + +void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt8 nData, bool bDots ) +{ + for( sal_uInt8 nMask = 0x80; nMask != 0; (nMask >>= 1) &= 0x7F ) + { + rStr.append( static_cast< sal_Unicode >( (nData & nMask) ? '1' : '0' ) ); + if( bDots && (nMask == 0x10) ) + rStr.append( OOX_DUMP_BINDOT ); + } +} + +void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int8 nData, bool bDots ) +{ + appendBin( rStr, static_cast< sal_uInt8 >( nData ), bDots ); +} + +void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt16 nData, bool bDots ) +{ + appendBin( rStr, static_cast< sal_uInt8 >( nData >> 8 ), bDots ); + if( bDots ) + rStr.append( OOX_DUMP_BINDOT ); + appendBin( rStr, static_cast< sal_uInt8 >( nData ), bDots ); +} + +void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int16 nData, bool bDots ) +{ + appendBin( rStr, static_cast< sal_uInt16 >( nData ), bDots ); +} + +void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt32 nData, bool bDots ) +{ + appendBin( rStr, static_cast< sal_uInt16 >( nData >> 16 ), bDots ); + if( bDots ) + rStr.append( OOX_DUMP_BINDOT ); + appendBin( rStr, static_cast< sal_uInt16 >( nData ), bDots ); +} + +void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int32 nData, bool bDots ) +{ + appendBin( rStr, static_cast< sal_uInt32 >( nData ), bDots ); +} + +void StringHelper::appendBin( OUStringBuffer& rStr, sal_uInt64 nData, bool bDots ) +{ + appendBin( rStr, static_cast< sal_uInt32 >( nData >> 32 ), bDots ); + if( bDots ) + rStr.append( OOX_DUMP_BINDOT ); + appendBin( rStr, static_cast< sal_uInt32 >( nData ), bDots ); +} + +void StringHelper::appendBin( OUStringBuffer& rStr, sal_Int64 nData, bool bDots ) +{ + appendBin( rStr, static_cast< sal_uInt64 >( nData ), bDots ); +} + +void StringHelper::appendBin( OUStringBuffer& rStr, double fData, bool bDots ) +{ + appendBin( rStr, lcl_ConvertDouble(fData), bDots ); +} + +// append formatted value ----------------------------------------------------- + +void StringHelper::appendBool( OUStringBuffer& rStr, bool bData ) +{ + rStr.appendAscii( bData ? "true" : "false" ); +} + +// encoded text output -------------------------------------------------------- + +void StringHelper::appendCChar( OUStringBuffer& rStr, sal_Unicode cChar, bool bPrefix ) +{ + if( cChar > 0x00FF ) + { + if( bPrefix ) + rStr.append( "\\u" ); + appendHex( rStr, static_cast< sal_uInt16 >( cChar ), false ); + } + else + { + if( bPrefix ) + rStr.append( "\\x" ); + appendHex( rStr, static_cast< sal_uInt8 >( cChar ), false ); + } +} + +void StringHelper::appendEncChar( OUStringBuffer& rStr, sal_Unicode cChar, sal_Int32 nCount, bool bPrefix ) +{ + if( cChar < 0x0020 ) + { + // C-style hex code + OUStringBuffer aCode; + appendCChar( aCode, cChar, bPrefix ); + OUString aCodeStr = aCode.makeStringAndClear(); + for( sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx ) + rStr.append( aCodeStr ); + } + else + { + appendChar( rStr, cChar, nCount ); + } +} + +void StringHelper::appendEncString( OUStringBuffer& rStr, const OUString& rData, bool bPrefix ) +{ + sal_Int32 nBeg = 0; + sal_Int32 nIdx = 0; + sal_Int32 nEnd = rData.getLength(); + while( nIdx < nEnd ) + { + // find next character that needs encoding + while( (nIdx < nEnd) && (rData[ nIdx ] >= 0x20) ) ++nIdx; + // append portion + if( nBeg < nIdx ) + { + if( (nBeg == 0) && (nIdx == nEnd) ) + rStr.append( rData ); + else + rStr.append( rData.subView(nBeg, nIdx - nBeg) ); + } + // append characters to be encoded + while( (nIdx < nEnd) && (rData[ nIdx ] < 0x20) ) + { + appendCChar( rStr, rData[ nIdx ], bPrefix ); + ++nIdx; + } + // adjust limits + nBeg = nIdx; + } +} + +// token list ----------------------------------------------------------------- + +void StringHelper::appendToken( OUStringBuffer& rStr, std::u16string_view rToken, sal_Unicode cSep ) +{ + if( (rStr.getLength() > 0) && (!rToken.empty()) ) + rStr.append( cSep ); + rStr.append( rToken ); +} + +void StringHelper::appendIndex( OUStringBuffer& rStr, sal_Int64 nIdx ) +{ + OUStringBuffer aToken; + appendDec( aToken, nIdx ); + rStr.append( '[' ).append( aToken.makeStringAndClear() ).append( ']' ); +} + +std::u16string_view StringHelper::getToken( std::u16string_view rData, sal_Int32& rnPos, sal_Unicode cSep ) +{ + return trimSpaces( o3tl::getToken(rData, 0, cSep, rnPos ) ); +} + +void StringHelper::enclose( OUStringBuffer& rStr, sal_Unicode cOpen, sal_Unicode cClose ) +{ + rStr.insert( 0, cOpen ).append( cClose ? cClose : cOpen ); +} + +// string conversion ---------------------------------------------------------- + +namespace { + +sal_Int32 lclIndexOf( const OUString& rStr, sal_Unicode cChar, sal_Int32 nStartPos ) +{ + sal_Int32 nIndex = rStr.indexOf( cChar, nStartPos ); + return (nIndex < 0) ? rStr.getLength() : nIndex; +} + +OUString lclTrimQuotedStringList( const OUString& rStr ) +{ + OUStringBuffer aBuffer; + sal_Int32 nPos = 0; + sal_Int32 nLen = rStr.getLength(); + while( nPos < nLen ) + { + if( rStr[ nPos ] == OOX_DUMP_CFG_QUOTE ) + { + // quoted string, skip leading quote character + ++nPos; + // process quoted text and embedded literal quote characters + OUStringBuffer aToken; + do + { + // seek to next quote character and add text portion to token buffer + sal_Int32 nEnd = lclIndexOf( rStr, OOX_DUMP_CFG_QUOTE, nPos ); + aToken.append( rStr.subView(nPos, nEnd - nPos) ); + // process literal quotes + while( (nEnd + 1 < nLen) && (rStr[ nEnd ] == OOX_DUMP_CFG_QUOTE) && (rStr[ nEnd + 1 ] == OOX_DUMP_CFG_QUOTE) ) + { + aToken.append( OOX_DUMP_CFG_QUOTE ); + nEnd += 2; + } + // nEnd is start of possible next text portion + nPos = nEnd; + } + while( (nPos < nLen) && (rStr[ nPos ] != OOX_DUMP_CFG_QUOTE) ); + // add token, seek to list separator, ignore text following closing quote + aBuffer.append( aToken.makeStringAndClear() ); + nPos = lclIndexOf( rStr, OOX_DUMP_CFG_LISTSEP, nPos ); + if( nPos < nLen ) + aBuffer.append( OOX_DUMP_LF ); + // set current position behind list separator + ++nPos; + } + else + { + // find list separator, add token text to buffer + sal_Int32 nEnd = lclIndexOf( rStr, OOX_DUMP_CFG_LISTSEP, nPos ); + aBuffer.append( rStr.subView(nPos, nEnd - nPos) ); + if( nEnd < nLen ) + aBuffer.append( OOX_DUMP_LF ); + // set current position behind list separator + nPos = nEnd + 1; + } + } + + return aBuffer.makeStringAndClear(); +} + +} // namespace + +std::u16string_view StringHelper::trimSpaces( std::u16string_view rStr ) +{ + size_t nBeg = 0; + while( (nBeg < rStr.size()) && ((rStr[ nBeg ] == ' ') || (rStr[ nBeg ] == '\t')) ) + ++nBeg; + size_t nEnd = rStr.size(); + while( (nEnd > nBeg) && ((rStr[ nEnd - 1 ] == ' ') || (rStr[ nEnd - 1 ] == '\t')) ) + --nEnd; + return rStr.substr( nBeg, nEnd - nBeg ); +} + +OUString StringHelper::trimTrailingNul( const OUString& rStr ) +{ + sal_Int32 nLastPos = rStr.getLength() - 1; + if( (nLastPos >= 0) && (rStr[ nLastPos ] == 0) ) + return rStr.copy( 0, nLastPos ); + return rStr; +} + +OString StringHelper::convertToUtf8( std::u16string_view rStr ) +{ + return OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ); +} + +DataType StringHelper::convertToDataType( std::u16string_view rStr ) +{ + DataType eType = DATATYPE_VOID; + if ( rStr == u"int8" ) + eType = DATATYPE_INT8; + else if ( rStr == u"uint8" ) + eType = DATATYPE_UINT8; + else if ( rStr == u"int16" ) + eType = DATATYPE_INT16; + else if ( rStr == u"uint16" ) + eType = DATATYPE_UINT16; + else if ( rStr == u"int32" ) + eType = DATATYPE_INT32; + else if ( rStr == u"uint32" ) + eType = DATATYPE_UINT32; + else if ( rStr == u"int64" ) + eType = DATATYPE_INT64; + else if ( rStr == u"uint64" ) + eType = DATATYPE_UINT64; + else if ( rStr == u"float" ) + eType = DATATYPE_FLOAT; + else if ( rStr == u"double" ) + eType = DATATYPE_DOUBLE; + return eType; +} + +FormatType StringHelper::convertToFormatType( std::u16string_view rStr ) +{ + FormatType eType = FORMATTYPE_NONE; + if ( rStr == u"dec" ) + eType = FORMATTYPE_DEC; + else if ( rStr == u"hex" ) + eType = FORMATTYPE_HEX; + else if ( rStr == u"shorthex" ) + eType = FORMATTYPE_SHORTHEX; + else if ( rStr == u"bin" ) + eType = FORMATTYPE_BIN; + else if ( rStr == u"fix" ) + eType = FORMATTYPE_FIX; + else if ( rStr == u"bool" ) + eType = FORMATTYPE_BOOL; + return eType; +} + +bool StringHelper::convertFromDec( sal_Int64& ornData, std::u16string_view rData ) +{ + size_t nPos = 0; + size_t nLen = rData.size(); + bool bNeg = false; + if( (nLen > 0) && (rData[ 0 ] == '-') ) + { + bNeg = true; + ++nPos; + } + ornData = 0; + for( ; nPos < nLen; ++nPos ) + { + sal_Unicode cChar = rData[ nPos ]; + if( (cChar < '0') || (cChar > '9') ) + return false; + ornData = (ornData * 10) + (cChar - '0'); + } + if( bNeg ) + ornData *= -1; + return true; +} + +bool StringHelper::convertFromHex( sal_Int64& ornData, std::u16string_view rData ) +{ + ornData = 0; + for( size_t nPos = 0, nLen = rData.size(); nPos < nLen; ++nPos ) + { + sal_Unicode cChar = rData[ nPos ]; + if( ('0' <= cChar) && (cChar <= '9') ) + cChar -= '0'; + else if( ('A' <= cChar) && (cChar <= 'F') ) + cChar -= ('A' - 10); + else if( ('a' <= cChar) && (cChar <= 'f') ) + cChar -= ('a' - 10); + else + return false; + ornData = (ornData << 4) + cChar; + } + return true; +} + +bool StringHelper::convertStringToInt( sal_Int64& ornData, std::u16string_view rData ) +{ + if( (rData.size() > 2) && (rData[ 0 ] == '0') && ((rData[ 1 ] == 'X') || (rData[ 1 ] == 'x')) ) + return convertFromHex( ornData, rData.substr( 2 ) ); + return convertFromDec( ornData, rData ); +} + +bool StringHelper::convertStringToDouble( double& orfData, std::u16string_view rData ) +{ + rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok; + sal_Int32 nSize = 0; + sal_Unicode const * pBegin = rData.data(); + sal_Unicode const * pEnd; + orfData = rtl_math_uStringToDouble(pBegin, + pBegin + rData.size(), + '.', '\0', + &eStatus, &pEnd); + nSize = static_cast<sal_Int32>(pEnd - pBegin); + return (eStatus == rtl_math_ConversionStatus_Ok) && (nSize == static_cast<sal_Int32>(rData.size())); +} + +bool StringHelper::convertStringToBool( std::u16string_view rData ) +{ + if ( rData == u"true" ) + return true; + if ( rData == u"false" ) + return false; + sal_Int64 nData; + return convertStringToInt( nData, rData ) && (nData != 0); +} + +OUStringPair StringHelper::convertStringToPair( const OUString& rString, sal_Unicode cSep ) +{ + OUStringPair aPair; + if( !rString.isEmpty() ) + { + sal_Int32 nEqPos = rString.indexOf( cSep ); + if( nEqPos < 0 ) + { + aPair.first = rString; + } + else + { + aPair.first = StringHelper::trimSpaces( rString.subView( 0, nEqPos ) ); + aPair.second = StringHelper::trimSpaces( rString.subView( nEqPos + 1 ) ); + } + } + return aPair; +} + +void StringHelper::convertStringToStringList( OUStringVector& orVec, const OUString& rData, bool bIgnoreEmpty ) +{ + orVec.clear(); + OUString aUnquotedData = lclTrimQuotedStringList( rData ); + sal_Int32 nPos = 0; + sal_Int32 nLen = aUnquotedData.getLength(); + while( (0 <= nPos) && (nPos < nLen) ) + { + std::u16string_view aToken = getToken( aUnquotedData, nPos, OOX_DUMP_LF ); + if( !bIgnoreEmpty || !aToken.empty() ) + orVec.push_back( OUString(aToken) ); + } +} + +void StringHelper::convertStringToIntList( Int64Vector& orVec, const OUString& rData, bool bIgnoreEmpty ) +{ + orVec.clear(); + OUString aUnquotedData = lclTrimQuotedStringList( rData ); + sal_Int32 nPos = 0; + sal_Int32 nLen = aUnquotedData.getLength(); + sal_Int64 nData; + while( (0 <= nPos) && (nPos < nLen) ) + { + bool bOk = convertStringToInt( nData, getToken( aUnquotedData, nPos, OOX_DUMP_LF ) ); + if( !bIgnoreEmpty || bOk ) + orVec.push_back( bOk ? nData : 0 ); + } +} + +Base::~Base() +{ +} + +ConfigItemBase::~ConfigItemBase() +{ +} + +void ConfigItemBase::readConfigBlock( TextInputStream& rStrm ) +{ + readConfigBlockContents( rStrm ); +} + +void ConfigItemBase::implProcessConfigItemStr( + TextInputStream& /*rStrm*/, const OUString& /*rKey*/, const OUString& /*rData*/ ) +{ +} + +void ConfigItemBase::implProcessConfigItemInt( + TextInputStream& /*rStrm*/, sal_Int64 /*nKey*/, const OUString& /*rData*/ ) +{ +} + +void ConfigItemBase::readConfigBlockContents( TextInputStream& rStrm ) +{ + bool bLoop = true; + while( bLoop && !rStrm.isEof() ) + { + OUString aKey, aData; + switch( readConfigLine( rStrm, aKey, aData ) ) + { + case LINETYPE_DATA: + processConfigItem( rStrm, aKey, aData ); + break; + case LINETYPE_END: + bLoop = false; + break; + } + } +} + +ConfigItemBase::LineType ConfigItemBase::readConfigLine( + TextInputStream& rStrm, OUString& orKey, OUString& orData ) +{ + OUString aLine; + while( !rStrm.isEof() && aLine.isEmpty() ) + { + aLine = rStrm.readLine(); + if( !aLine.isEmpty() && (aLine[ 0 ] == OOX_DUMP_BOM) ) + aLine = aLine.copy( 1 ); + aLine = StringHelper::trimSpaces( aLine ); + if( !aLine.isEmpty() ) + { + // ignore comments (starting with hash or semicolon) + sal_Unicode cChar = aLine[ 0 ]; + if( (cChar == '#') || (cChar == ';') ) + aLine.clear(); + } + } + + OUStringPair aPair = StringHelper::convertStringToPair( aLine ); + orKey = aPair.first; + orData = aPair.second; + return ( !orKey.isEmpty() && (!orData.isEmpty() || orKey != "end" )) ? + LINETYPE_DATA : LINETYPE_END; +} + +void ConfigItemBase::processConfigItem( + TextInputStream& rStrm, const OUString& rKey, const OUString& rData ) +{ + sal_Int64 nKey; + if( StringHelper::convertStringToInt( nKey, rKey ) ) + implProcessConfigItemInt( rStrm, nKey, rData ); + else + implProcessConfigItemStr( rStrm, rKey, rData ); +} + +NameListBase::~NameListBase() +{ +} + +void NameListBase::setName( sal_Int64 nKey, const String& rName ) +{ + implSetName( nKey, rName ); +} + +void NameListBase::includeList( const NameListRef& rxList ) +{ + if( rxList ) + { + for (auto const& elem : *rxList) + maMap[ elem.first ] = elem.second; + implIncludeList( *rxList ); + } +} + +bool NameListBase::implIsValid() const +{ + return true; +} + +void NameListBase::implProcessConfigItemStr( + TextInputStream& rStrm, const OUString& rKey, const OUString& rData ) +{ + if ( rKey == "include" ) + include( rData ); + else if ( rKey == "exclude" ) + exclude( rData ); + else + ConfigItemBase::implProcessConfigItemStr( rStrm, rKey, rData ); +} + +void NameListBase::implProcessConfigItemInt( + TextInputStream& /*rStrm*/, sal_Int64 nKey, const OUString& rData ) +{ + implSetName( nKey, rData ); +} + +void NameListBase::insertRawName( sal_Int64 nKey, const OUString& rName ) +{ + maMap[ nKey ] = rName; +} + +const OUString* NameListBase::findRawName( sal_Int64 nKey ) const +{ + const_iterator aIt = maMap.find( nKey ); + return (aIt == end()) ? nullptr : &aIt->second; +} + +void NameListBase::include( const OUString& rListKeys ) +{ + OUStringVector aVec; + StringHelper::convertStringToStringList( aVec, rListKeys, true ); + for (auto const& elem : aVec) + includeList( mrCfgData.getNameList(elem) ); +} + +void NameListBase::exclude( const OUString& rKeys ) +{ + Int64Vector aVec; + StringHelper::convertStringToIntList( aVec, rKeys, true ); + for (auto const& elem : aVec) + maMap.erase(elem); +} + +void ItemFormatMap::insertFormats( const NameListRef& rxNameList ) +{ + if( Base::isValid( rxNameList ) ) + { + for (auto const& elemName : *rxNameList) + maMap[ elemName.first ].parse( elemName.second ); + } +} + +ConstList::ConstList( const SharedConfigData& rCfgData ) : + NameListBase( rCfgData ), + maDefName( OOX_DUMP_ERR_NONAME ), + mbQuoteNames( false ) +{ +} + +void ConstList::implProcessConfigItemStr( + TextInputStream& rStrm, const OUString& rKey, const OUString& rData ) +{ + if ( rKey == "default" ) + maDefName = rData; // Sets a default name for unknown keys. + else if ( rKey == "quote-names" ) + setQuoteNames( StringHelper::convertStringToBool( rData ) ); + else + NameListBase::implProcessConfigItemStr( rStrm, rKey, rData ); +} + +void ConstList::implSetName( sal_Int64 nKey, const OUString& rName ) +{ + insertRawName( nKey, rName ); +} + +OUString ConstList::implGetName( const Config& /*rCfg*/, sal_Int64 nKey ) const +{ + const OUString* pName = findRawName( nKey ); + OUString aName = pName ? *pName : maDefName; + if( mbQuoteNames ) + { + OUStringBuffer aBuffer( aName ); + StringHelper::enclose( aBuffer, OOX_DUMP_STRQUOTE ); + aName = aBuffer.makeStringAndClear(); + } + return aName; +} + +OUString ConstList::implGetNameDbl( const Config& /*rCfg*/, double /*fValue*/ ) const +{ + return OUString(); +} + +void ConstList::implIncludeList( const NameListBase& rList ) +{ + if( const ConstList* pConstList = dynamic_cast< const ConstList* >( &rList ) ) + { + maDefName = pConstList->maDefName; + mbQuoteNames = pConstList->mbQuoteNames; + } +} + +MultiList::MultiList( const SharedConfigData& rCfgData ) : + ConstList( rCfgData ), + mbIgnoreEmpty( true ) +{ +} + +void MultiList::setNamesFromVec( sal_Int64 nStartKey, const OUStringVector& rNames ) +{ + sal_Int64 nKey = nStartKey; + for (auto const& name : rNames) + { + if( !mbIgnoreEmpty || !name.isEmpty() ) + insertRawName( nKey, name); + ++nKey; + } +} + +void MultiList::implProcessConfigItemStr( + TextInputStream& rStrm, const OUString& rKey, const OUString& rData ) +{ + if ( rKey == "ignore-empty" ) + mbIgnoreEmpty = StringHelper::convertStringToBool( rData ); + else + ConstList::implProcessConfigItemStr( rStrm, rKey, rData ); +} + +void MultiList::implSetName( sal_Int64 nKey, const OUString& rName ) +{ + OUStringVector aNames; + StringHelper::convertStringToStringList( aNames, rName, false ); + setNamesFromVec( nKey, aNames ); +} + +FlagsList::FlagsList( const SharedConfigData& rCfgData ) : + NameListBase( rCfgData ), + mnIgnore( 0 ) +{ +} + +void FlagsList::implProcessConfigItemStr( + TextInputStream& rStrm, const OUString& rKey, const OUString& rData ) +{ + if ( rKey == "ignore" ) + { + sal_Int64 nIgnore; + if( StringHelper::convertStringToInt( nIgnore, rData ) ) + setIgnoreFlags( nIgnore ); + } + else + { + NameListBase::implProcessConfigItemStr( rStrm, rKey, rData ); + } +} + +void FlagsList::implSetName( sal_Int64 nKey, const OUString& rName ) +{ + if( (nKey != 0) && ((nKey & (nKey - 1)) == 0) ) // only a single bit set? + insertRawName( nKey, rName ); +} + +OUString FlagsList::implGetName( const Config& /*rCfg*/, sal_Int64 nKey ) const +{ + sal_Int64 nFound = mnIgnore; + OUStringBuffer aName; + // add known flags + for( const_iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + { + sal_Int64 nMask = aIt->first; + setFlag( nFound, nMask ); + if( !getFlag( mnIgnore, nMask ) ) + { + const OUString& rFlagName = aIt->second; + bool bOnOff = rFlagName.startsWith(":"); + bool bFlag = getFlag( nKey, nMask ); + if( bOnOff ) + { + StringHelper::appendToken( aName, rFlagName.subView( 1 ) ); + aName.appendAscii( bFlag ? ":on" : ":off" ); + } + else + { + bool bNegated = rFlagName.startsWith("!"); + sal_Int32 nBothSep = bNegated ? rFlagName.indexOf( '!', 1 ) : -1; + if( bFlag ) + { + if( !bNegated ) + StringHelper::appendToken( aName, rFlagName ); + else if( nBothSep > 0 ) + StringHelper::appendToken( aName, rFlagName.subView( nBothSep + 1 ) ); + } + else if( bNegated ) + { + if( nBothSep > 0 ) + StringHelper::appendToken( aName, rFlagName.subView( 1, nBothSep - 1 ) ); + else + StringHelper::appendToken( aName, rFlagName.subView( 1 ) ); + } + } + } + } + // add unknown flags + setFlag( nKey, nFound, false ); + if( nKey != 0 ) + { + OUStringBuffer aUnknown( OOX_DUMP_UNKNOWN ); + aUnknown.append( OOX_DUMP_ITEMSEP ); + StringHelper::appendShortHex( aUnknown, nKey ); + StringHelper::enclose( aUnknown, '(', ')' ); + StringHelper::appendToken( aName, aUnknown.makeStringAndClear() ); + } + return aName.makeStringAndClear(); +} + +OUString FlagsList::implGetNameDbl( const Config& /*rCfg*/, double /*fValue*/ ) const +{ + return OUString(); +} + +void FlagsList::implIncludeList( const NameListBase& rList ) +{ + if( const FlagsList* pFlagsList = dynamic_cast< const FlagsList* >( &rList ) ) + mnIgnore = pFlagsList->mnIgnore; +} + +bool CombiList::ExtItemFormatKey::operator<( const ExtItemFormatKey& rRight ) const +{ + return (mnKey < rRight.mnKey) || ((mnKey == rRight.mnKey) && (maFilter < rRight.maFilter)); +} + +CombiList::CombiList( const SharedConfigData& rCfgData ) : + FlagsList( rCfgData ) +{ +} + +void CombiList::implSetName( sal_Int64 nKey, const OUString& rName ) +{ + if( (nKey & (nKey - 1)) != 0 ) // more than a single bit set? + { + ::std::set< ExtItemFormatKey > aItemKeys; + ExtItemFormat aItemFmt; + OUStringVector aRemain = aItemFmt.parse( rName ); + for (auto const& elemRemain : aRemain) + { + OUStringPair aPair = StringHelper::convertStringToPair(elemRemain); + if ( aPair.first == "noshift" ) + { + aItemFmt.mbShiftValue = StringHelper::convertStringToBool( aPair.second ); + } + else if ( aPair.first == "filter" ) + { + OUStringPair aFilter = StringHelper::convertStringToPair( aPair.second, '~' ); + ExtItemFormatKey aKey( nKey ); + if( !aFilter.first.isEmpty() && StringHelper::convertStringToInt( aKey.maFilter.first, aFilter.first ) && + !aFilter.second.isEmpty() && StringHelper::convertStringToInt( aKey.maFilter.second, aFilter.second ) ) + { + if( aKey.maFilter.first == 0 ) + aKey.maFilter.second = 0; + aItemKeys.insert( aKey ); + } + } + } + if( aItemKeys.empty() ) + aItemKeys.insert( ExtItemFormatKey( nKey ) ); + for (auto const& itemKey : aItemKeys) + maFmtMap[itemKey] = aItemFmt; + } + else + { + FlagsList::implSetName( nKey, rName ); + } +} + +OUString CombiList::implGetName( const Config& rCfg, sal_Int64 nKey ) const +{ + sal_Int64 nFound = 0; + OUStringBuffer aName; + // add known flag fields + for (auto const& fmt : maFmtMap) + { + const ExtItemFormatKey& rMapKey = fmt.first; + sal_Int64 nMask = rMapKey.mnKey; + if( (nMask != 0) && ((nKey & rMapKey.maFilter.first) == rMapKey.maFilter.second) ) + { + const ExtItemFormat& rItemFmt = fmt.second; + + sal_uInt64 nUFlags = static_cast< sal_uInt64 >( nKey ); + sal_uInt64 nUMask = static_cast< sal_uInt64 >( nMask ); + if( rItemFmt.mbShiftValue ) + while( (nUMask & 1) == 0 ) { nUFlags >>= 1; nUMask >>= 1; } + + sal_uInt64 nUValue = nUFlags & nUMask; + sal_Int64 nSValue = static_cast< sal_Int64 >( nUValue ); + if( getFlag< sal_uInt64 >( nUValue, (nUMask + 1) >> 1 ) ) + setFlag( nSValue, static_cast< sal_Int64 >( ~nUMask ) ); + + OUStringBuffer aItem( rItemFmt.maItemName ); + OUStringBuffer aValue; + switch( rItemFmt.meDataType ) + { + case DATATYPE_INT8: StringHelper::appendValue( aValue, static_cast< sal_Int8 >( nSValue ), rItemFmt.meFmtType ); break; + case DATATYPE_UINT8: StringHelper::appendValue( aValue, static_cast< sal_uInt8 >( nUValue ), rItemFmt.meFmtType ); break; + case DATATYPE_INT16: StringHelper::appendValue( aValue, static_cast< sal_Int16 >( nSValue ), rItemFmt.meFmtType ); break; + case DATATYPE_UINT16: StringHelper::appendValue( aValue, static_cast< sal_uInt16 >( nUValue ), rItemFmt.meFmtType ); break; + case DATATYPE_INT32: StringHelper::appendValue( aValue, static_cast< sal_Int32 >( nSValue ), rItemFmt.meFmtType ); break; + case DATATYPE_UINT32: StringHelper::appendValue( aValue, static_cast< sal_uInt32 >( nUValue ), rItemFmt.meFmtType ); break; + case DATATYPE_INT64: StringHelper::appendValue( aValue, nSValue, rItemFmt.meFmtType ); break; + case DATATYPE_UINT64: StringHelper::appendValue( aValue, nUValue, rItemFmt.meFmtType ); break; + case DATATYPE_FLOAT: StringHelper::appendValue( aValue, static_cast< float >( nSValue ), rItemFmt.meFmtType ); break; + case DATATYPE_DOUBLE: StringHelper::appendValue( aValue, static_cast< double >( nSValue ), rItemFmt.meFmtType ); break; + default:; + } + StringHelper::appendToken( aItem, aValue.makeStringAndClear(), OOX_DUMP_ITEMSEP ); + if( !rItemFmt.maListName.isEmpty() ) + { + OUString aValueName = rCfg.getName( rItemFmt.maListName, static_cast< sal_Int64 >( nUValue ) ); + StringHelper::appendToken( aItem, aValueName, OOX_DUMP_ITEMSEP ); + } + StringHelper::enclose( aItem, '(', ')' ); + StringHelper::appendToken( aName, aItem.makeStringAndClear() ); + setFlag( nFound, nMask ); + } + } + setFlag( nKey, nFound, false ); + StringHelper::appendToken( aName, FlagsList::implGetName( rCfg, nKey ) ); + return aName.makeStringAndClear(); +} + +void CombiList::implIncludeList( const NameListBase& rList ) +{ + if( const CombiList* pCombiList = dynamic_cast< const CombiList* >( &rList ) ) + maFmtMap = pCombiList->maFmtMap; + FlagsList::implIncludeList( rList ); +} + +UnitConverter::UnitConverter( const SharedConfigData& rCfgData ) : + NameListBase( rCfgData ), + mfFactor( 1.0 ) +{ +} + +void UnitConverter::implSetName( sal_Int64 /*nKey*/, const OUString& /*rName*/ ) +{ + // nothing to do +} + +OUString UnitConverter::implGetName( const Config& rCfg, sal_Int64 nKey ) const +{ + return implGetNameDbl( rCfg, static_cast< double >( nKey ) ); +} + +OUString UnitConverter::implGetNameDbl( const Config& /*rCfg*/, double fValue ) const +{ + OUStringBuffer aValue; + StringHelper::appendDec( aValue, mfFactor * fValue ); + aValue.append( maUnitName ); + return aValue.makeStringAndClear(); +} + +void UnitConverter::implIncludeList( const NameListBase& /*rList*/ ) +{ +} + +const NameListRef & NameListWrapper::getNameList( const Config& rCfg ) const +{ + if (!mxList) + mxList = rCfg.getNameList( maName ); + return mxList; +} + +SharedConfigData::SharedConfigData( const OUString& rFileName, + const Reference< XComponentContext >& rxContext, const StorageRef& rxRootStrg, + const OUString& rSysFileName ) : + mxContext( rxContext ), + mxRootStrg( rxRootStrg ), + maSysFileName( rSysFileName ), + mbLoaded( false ) +{ + OUString aFileUrl = InputOutputHelper::convertFileNameToUrl( rFileName ); + if( !aFileUrl.isEmpty() ) + { + sal_Int32 nNamePos = InputOutputHelper::getFileNamePos( aFileUrl ); + maConfigPath = aFileUrl.copy( 0, nNamePos ); + mbLoaded = readConfigFile( aFileUrl ); + } +} + +SharedConfigData::~SharedConfigData() +{ +} + +const OUString* SharedConfigData::getOption( const OUString& rKey ) const +{ + ConfigDataMap::const_iterator aIt = maConfigData.find( rKey ); + return (aIt == maConfigData.end()) ? nullptr : &aIt->second; +} + +void SharedConfigData::setNameList( const OUString& rListName, const NameListRef& rxList ) +{ + if( !rListName.isEmpty() ) + maNameLists[ rListName ] = rxList; +} + +void SharedConfigData::eraseNameList( const OUString& rListName ) +{ + maNameLists.erase( rListName ); +} + +NameListRef SharedConfigData::getNameList( const OUString& rListName ) const +{ + NameListRef xList; + NameListMap::const_iterator aIt = maNameLists.find( rListName ); + if( aIt != maNameLists.end() ) + xList = aIt->second; + return xList; +} + +bool SharedConfigData::implIsValid() const +{ + return mbLoaded && mxContext.is() && mxRootStrg && !maSysFileName.isEmpty(); +} + +void SharedConfigData::implProcessConfigItemStr( + TextInputStream& rStrm, const OUString& rKey, const OUString& rData ) +{ + if ( rKey == "include-config-file" ) + readConfigFile( maConfigPath + rData ); + else if ( rKey == "constlist" ) + readNameList< ConstList >( rStrm, rData ); + else if ( rKey == "multilist" ) + readNameList< MultiList >( rStrm, rData ); + else if ( rKey == "flagslist" ) + readNameList< FlagsList >( rStrm, rData ); + else if ( rKey == "combilist" ) + readNameList< CombiList >( rStrm, rData ); + else if ( rKey == "shortlist" ) + createShortList( rData ); + else if ( rKey == "unitconverter" ) + createUnitConverter( rData ); + else + maConfigData[ rKey ] = rData; +} + +bool SharedConfigData::readConfigFile( const OUString& rFileUrl ) +{ + bool bLoaded = maConfigFiles.count( rFileUrl ) > 0; + if( !bLoaded ) + { + Reference< XInputStream > xInStrm = InputOutputHelper::openInputStream( mxContext, rFileUrl ); + TextInputStream aTxtStrm( mxContext, xInStrm, RTL_TEXTENCODING_UTF8 ); + if( !aTxtStrm.isEof() ) + { + maConfigFiles.insert( rFileUrl ); + readConfigBlockContents( aTxtStrm ); + bLoaded = true; + } + } + return bLoaded; +} + +void SharedConfigData::createShortList( const OUString& rData ) +{ + OUStringVector aDataVec; + StringHelper::convertStringToStringList( aDataVec, rData, false ); + if( aDataVec.size() < 3 ) + return; + + sal_Int64 nStartKey; + if( StringHelper::convertStringToInt( nStartKey, aDataVec[ 1 ] ) ) + { + std::shared_ptr< MultiList > xList = createNameList< MultiList >( aDataVec[ 0 ] ); + if( xList ) + { + aDataVec.erase( aDataVec.begin(), aDataVec.begin() + 2 ); + xList->setNamesFromVec( nStartKey, aDataVec ); + } + } +} + +void SharedConfigData::createUnitConverter( const OUString& rData ) +{ + OUStringVector aDataVec; + StringHelper::convertStringToStringList( aDataVec, rData, false ); + if( aDataVec.size() < 2 ) + return; + + OUString aFactor = aDataVec[ 1 ]; + bool bRecip = aFactor.startsWith("/"); + if( bRecip ) + aFactor = aFactor.copy( 1 ); + double fFactor; + if( StringHelper::convertStringToDouble( fFactor, aFactor ) && (fFactor != 0.0) ) + { + std::shared_ptr< UnitConverter > xList = createNameList< UnitConverter >( aDataVec[ 0 ] ); + if( xList ) + { + xList->setFactor( bRecip ? (1.0 / fFactor) : fFactor ); + if( aDataVec.size() >= 3 ) + xList->setUnitName( aDataVec[ 2 ] ); + } + } +} + +Config::Config( const char* pcEnvVar, const FilterBase& rFilter ) +{ + construct( pcEnvVar, rFilter ); +} + +Config::Config( const char* pcEnvVar, const Reference< XComponentContext >& rxContext, const StorageRef& rxRootStrg, const OUString& rSysFileName ) +{ + construct( pcEnvVar, rxContext, rxRootStrg, rSysFileName ); +} + +Config::~Config() +{ +} + +void Config::construct( const char* pcEnvVar, const FilterBase& rFilter ) +{ + if( !rFilter.getFileUrl().isEmpty() ) + construct( pcEnvVar, rFilter.getComponentContext(), rFilter.getStorage(), rFilter.getFileUrl() ); +} + +void Config::construct( const char* pcEnvVar, const Reference< XComponentContext >& rxContext, const StorageRef& rxRootStrg, const OUString& rSysFileName ) +{ + if( pcEnvVar && rxRootStrg && !rSysFileName.isEmpty() ) + if( const char* pcFileName = ::getenv( pcEnvVar ) ) + mxCfgData = std::make_shared<SharedConfigData>( OUString::createFromAscii( pcFileName ), rxContext, rxRootStrg, rSysFileName ); +} + +const OUString& Config::getStringOption( const String& rKey, const OUString& rDefault ) const +{ + const OUString* pData = implGetOption( rKey ); + return pData ? *pData : rDefault; +} + +bool Config::getBoolOption( const String& rKey, bool bDefault ) const +{ + const OUString* pData = implGetOption( rKey ); + return pData ? StringHelper::convertStringToBool( *pData ) : bDefault; +} + +bool Config::isDumperEnabled() const +{ + return getBoolOption( "enable-dumper", false ); +} + +bool Config::isImportEnabled() const +{ + return getBoolOption( "enable-import", true ); +} + +void Config::eraseNameList( const String& rListName ) +{ + mxCfgData->eraseNameList( rListName ); +} + +NameListRef Config::getNameList( const String& rListName ) const +{ + return mxCfgData->getNameList( rListName ); +} + +bool Config::implIsValid() const +{ + return isValid( mxCfgData ); +} + +const OUString* Config::implGetOption( const OUString& rKey ) const +{ + return mxCfgData->getOption( rKey ); +} + +Output::Output( const Reference< XComponentContext >& rxContext, const OUString& rFileName ) : + mxStrm( InputOutputHelper::openTextOutputStream( rxContext, rFileName, RTL_TEXTENCODING_UTF8 ) ), + mnCol( 0 ), + mnItemLevel( 0 ), + mnMultiLevel( 0 ), + mnItemIdx( 0 ), + mnLastItem( 0 ) +{ + if( mxStrm.is() ) + mxStrm->writeString( OUString( OOX_DUMP_BOM ) ); +} + +void Output::newLine() +{ + if( maLine.getLength() > 0 ) + { + mxStrm->writeString( maIndent ); + maLine.append( '\n' ); + mxStrm->writeString( maLine.makeStringAndClear() ); + mnCol = 0; + mnLastItem = 0; + } +} + +void Output::emptyLine( size_t nCount ) +{ + for( size_t nIdx = 0; nIdx < nCount; ++nIdx ) + mxStrm->writeString( OUString('\n') ); +} + +void Output::incIndent() +{ + OUStringBuffer aBuffer( maIndent ); + StringHelper::appendChar( aBuffer, ' ', OOX_DUMP_INDENT ); + maIndent = aBuffer.makeStringAndClear(); +} + +void Output::decIndent() +{ + if( maIndent.getLength() >= OOX_DUMP_INDENT ) + maIndent = maIndent.copy( OOX_DUMP_INDENT ); +} + +void Output::startTable( sal_Int32 nW1 ) +{ + startTable( 1, &nW1 ); +} + +void Output::startTable( sal_Int32 nW1, sal_Int32 nW2 ) +{ + sal_Int32 pnColWidths[ 2 ]; + pnColWidths[ 0 ] = nW1; + pnColWidths[ 1 ] = nW2; + startTable( 2, pnColWidths ); +} + +void Output::startTable( sal_Int32 nW1, sal_Int32 nW2, sal_Int32 nW3, sal_Int32 nW4 ) +{ + sal_Int32 pnColWidths[ 4 ]; + pnColWidths[ 0 ] = nW1; + pnColWidths[ 1 ] = nW2; + pnColWidths[ 2 ] = nW3; + pnColWidths[ 3 ] = nW4; + startTable( 4, pnColWidths ); +} + +void Output::startTable( size_t nColCount, const sal_Int32* pnColWidths ) +{ + maColPos.clear(); + maColPos.push_back( 0 ); + sal_Int32 nColPos = 0; + for( size_t nCol = 0; nCol < nColCount; ++nCol ) + { + nColPos = nColPos + pnColWidths[ nCol ]; + maColPos.push_back( nColPos ); + } +} + +void Output::tab() +{ + tab( mnCol + 1 ); +} + +void Output::tab( size_t nCol ) +{ + mnCol = nCol; + if( mnCol < maColPos.size() ) + { + sal_Int32 nColPos = maColPos[ mnCol ]; + if( maLine.getLength() >= nColPos ) + maLine.setLength( ::std::max< sal_Int32 >( nColPos - 1, 0 ) ); + StringHelper::appendChar( maLine, ' ', nColPos - maLine.getLength() ); + } + else + { + StringHelper::appendChar( maLine, ' ', 2 ); + } +} + +void Output::endTable() +{ + maColPos.clear(); +} + +void Output::resetItemIndex( sal_Int64 nIdx ) +{ + mnItemIdx = nIdx; +} + +void Output::startItem( const String& rItemName ) +{ + if( mnItemLevel == 0 ) + { + if( (mnMultiLevel > 0) && (maLine.getLength() > 0) ) + tab(); + if( rItemName.has() ) + { + writeItemName( rItemName ); + writeChar( OOX_DUMP_ITEMSEP ); + } + } + ++mnItemLevel; + mnLastItem = maLine.getLength(); +} + +void Output::contItem() +{ + if( mnItemLevel > 0 ) + { + if( (maLine.getLength() == 0) || (maLine[ maLine.getLength() - 1 ] != OOX_DUMP_ITEMSEP) ) + writeChar( OOX_DUMP_ITEMSEP ); + mnLastItem = maLine.getLength(); + } +} + +void Output::endItem() +{ + if( mnItemLevel > 0 ) + { + maLastItem = maLine.copy( mnLastItem ).makeStringAndClear(); + if( maLastItem.isEmpty() && mnLastItem > 0 && maLine[ mnLastItem - 1 ] == OOX_DUMP_ITEMSEP ) + maLine.setLength( mnLastItem - 1 ); + --mnItemLevel; + } + if( mnItemLevel == 0 ) + { + if( mnMultiLevel == 0 ) + newLine(); + } + else + contItem(); +} + +void Output::startMultiItems() +{ + ++mnMultiLevel; +} + +void Output::endMultiItems() +{ + if( mnMultiLevel > 0 ) + --mnMultiLevel; + if( mnMultiLevel == 0 ) + newLine(); +} + +void Output::writeChar( sal_Unicode cChar, sal_Int32 nCount ) +{ + StringHelper::appendEncChar( maLine, cChar, nCount ); +} + +void Output::writeAscii( const char* pcStr ) +{ + if( pcStr ) + maLine.appendAscii( pcStr ); +} + +void Output::writeString( const OUString& rStr ) +{ + StringHelper::appendEncString( maLine, rStr ); +} + +void Output::writeArray( const sal_uInt8* pnData, std::size_t nSize, sal_Unicode cSep ) +{ + const sal_uInt8* pnEnd = pnData ? (pnData + nSize) : nullptr; + for( const sal_uInt8* pnByte = pnData; pnByte < pnEnd; ++pnByte ) + { + if( pnByte > pnData ) + writeChar( cSep ); + writeHex( *pnByte, false ); + } +} + +void Output::writeBool( bool bData ) +{ + StringHelper::appendBool( maLine, bData ); +} + +void Output::writeDateTime( const util::DateTime& rDateTime ) +{ + writeDec( rDateTime.Year, 4, '0' ); + writeChar( '-' ); + writeDec( rDateTime.Month, 2, '0' ); + writeChar( '-' ); + writeDec( rDateTime.Day, 2, '0' ); + writeChar( 'T' ); + writeDec( rDateTime.Hours, 2, '0' ); + writeChar( ':' ); + writeDec( rDateTime.Minutes, 2, '0' ); + writeChar( ':' ); + writeDec( rDateTime.Seconds, 2, '0' ); +} + +bool Output::implIsValid() const +{ + return mxStrm.is(); +} + +void Output::writeItemName( const String& rItemName ) +{ + if( rItemName.has() && (rItemName[ 0 ] == '#') ) + { + writeString( rItemName.copy( 1 ) ); + StringHelper::appendIndex( maLine, mnItemIdx++ ); + } + else + writeString( rItemName ); +} + +StorageIterator::StorageIterator( const StorageRef& rxStrg ) : + mxStrg( rxStrg ) +{ + if( mxStrg ) + mxStrg->getElementNames( maNames ); + maIt = maNames.begin(); +} + +StorageIterator::~StorageIterator() +{ +} + +StorageIterator& StorageIterator::operator++() +{ + if( maIt != maNames.end() ) + ++maIt; + return *this; +} + +OUString StorageIterator::getName() const +{ + OUString aName; + if( maIt != maNames.end() ) + aName = *maIt; + return aName; +} + +bool StorageIterator::isStream() const +{ + return isValid() && mxStrg->openInputStream( *maIt ).is(); +} + +bool StorageIterator::isStorage() const +{ + if( !isValid() ) + return false; + StorageRef xStrg = mxStrg->openSubStorage( *maIt, false ); + return xStrg && xStrg->isStorage(); +} + +bool StorageIterator::implIsValid() const +{ + return mxStrg && mxStrg->isStorage() && (maIt != maNames.end()); +} + +ObjectBase::~ObjectBase() +{ +} + +void ObjectBase::construct( const ConfigRef& rxConfig ) +{ + mxConfig = rxConfig; +} + +void ObjectBase::construct( const ObjectBase& rParent ) +{ + *this = rParent; +} + +void ObjectBase::dump() +{ + if( isValid() ) + implDump(); +} + +bool ObjectBase::implIsValid() const +{ + return isValid( mxConfig ); +} + +void ObjectBase::implDump() +{ +} + +void StorageObjectBase::construct( const ObjectBase& rParent, const StorageRef& rxStrg, const OUString& rSysPath ) +{ + ObjectBase::construct( rParent ); + mxStrg = rxStrg; + maSysPath = rSysPath; +} + +void StorageObjectBase::construct( const ObjectBase& rParent ) +{ + ObjectBase::construct( rParent ); + if( ObjectBase::implIsValid() ) + { + mxStrg = cfg().getRootStorage(); + maSysPath = cfg().getSysFileName(); + } +} + +bool StorageObjectBase::implIsValid() const +{ + return mxStrg && !maSysPath.isEmpty() && ObjectBase::implIsValid(); +} + +void StorageObjectBase::implDump() +{ + bool bIsStrg = mxStrg->isStorage(); + bool bIsRoot = mxStrg->isRootStorage(); + Reference< XInputStream > xBaseStrm; + if( !bIsStrg ) + xBaseStrm = mxStrg->openInputStream( OUString() ); + + OUString aSysOutPath = maSysPath; + if( bIsRoot ) try + { + aSysOutPath += OOX_DUMP_DUMPEXT; + Reference<XSimpleFileAccess3> xFileAccess(SimpleFileAccess::create(getContext())); + xFileAccess->kill( aSysOutPath ); + } + catch( Exception& ) + { + } + + if( bIsStrg ) + { + extractStorage( mxStrg, OUString(), aSysOutPath ); + } + else if( xBaseStrm.is() ) + { + BinaryInputStreamRef xInStrm( std::make_shared<BinaryXInputStream>( xBaseStrm, false ) ); + xInStrm->seekToStart(); + implDumpBaseStream( xInStrm, aSysOutPath ); + } +} + +void StorageObjectBase::implDumpStream( const Reference< XInputStream >&, const OUString&, const OUString&, const OUString& ) +{ +} + +void StorageObjectBase::implDumpStorage( const StorageRef& rxStrg, const OUString& rStrgPath, const OUString& rSysPath ) +{ + extractStorage( rxStrg, rStrgPath, rSysPath ); +} + +void StorageObjectBase::implDumpBaseStream( const BinaryInputStreamRef&, const OUString& ) +{ +} + +void StorageObjectBase::addPreferredStream( const String& rStrmName ) +{ + if( rStrmName.has() ) + maPreferred.emplace_back( rStrmName, false ); +} + +void StorageObjectBase::addPreferredStorage( const String& rStrgPath ) +{ + if( rStrgPath.has() ) + maPreferred.emplace_back( rStrgPath, true ); +} + +OUString StorageObjectBase::getSysFileName( + const OUString& rStrmName, std::u16string_view rSysOutPath ) +{ + // encode all characters < 0x20 + OUStringBuffer aBuffer; + StringHelper::appendEncString( aBuffer, rStrmName, false ); + + // replace all characters reserved in file system + OUString aFileName = aBuffer.makeStringAndClear(); + static const sal_Unicode spcReserved[] = { '/', '\\', ':', '*', '?', '<', '>', '|' }; + for(const sal_Unicode cChar : spcReserved) + aFileName = aFileName.replace(cChar, '_'); + + // build full path + return OUString::Concat(rSysOutPath) + "/" + aFileName; +} + +void StorageObjectBase::extractStream( StorageBase& rStrg, const OUString& rStrgPath, const OUString& rStrmName, const OUString& rSysFileName ) +{ + BinaryXInputStream aInStrm( rStrg.openInputStream( rStrmName ), true ); + if( !aInStrm.isEof() ) + { + BinaryXOutputStream aOutStrm( InputOutputHelper::openOutputStream( getContext(), rSysFileName ), true ); + if( !aOutStrm.isEof() ) + aInStrm.copyToStream( aOutStrm ); + } + Reference< XInputStream > xDumpStrm = InputOutputHelper::openInputStream( getContext(), rSysFileName ); + if( xDumpStrm.is() ) + implDumpStream( xDumpStrm, rStrgPath, rStrmName, rSysFileName ); +} + +void StorageObjectBase::extractStorage( const StorageRef& rxStrg, const OUString& rStrgPath, const OUString& rSysPath ) +{ + // create directory in file system + ::osl::FileBase::RC eRes = ::osl::Directory::create( rSysPath ); + if( (eRes != ::osl::FileBase::E_None) && (eRes != ::osl::FileBase::E_EXIST) ) + return; + + // process preferred storages and streams in root storage first + if( rStrgPath.isEmpty() ) + { + for (auto const& elemPreferred : maPreferred) + extractItem( rxStrg, rStrgPath, elemPreferred.maName, rSysPath, elemPreferred.mbStorage, !elemPreferred.mbStorage ); + } + + // process children of the storage + for( StorageIterator aIt( rxStrg ); aIt.isValid(); ++aIt ) + { + // skip processed preferred items + OUString aItemName = aIt.getName(); + bool bFound = false; + if( rStrgPath.isEmpty() ) + { + for (auto const& elemPreferred : maPreferred) + { + bFound = elemPreferred.maName == aItemName; + if (bFound) + break; + } + } + if( !bFound ) + extractItem( rxStrg, rStrgPath, aItemName, rSysPath, aIt.isStorage(), aIt.isStream() ); + } +} + +void StorageObjectBase::extractItem( const StorageRef& rxStrg, const OUString& rStrgPath, const OUString& rItemName, std::u16string_view rSysPath, bool bIsStrg, bool bIsStrm ) +{ + OUString aSysFileName = getSysFileName( rItemName, rSysPath ); + if( bIsStrg ) + { + OUStringBuffer aStrgPath( rStrgPath ); + StringHelper::appendToken( aStrgPath, rItemName, '/' ); + implDumpStorage( rxStrg->openSubStorage( rItemName, false ), aStrgPath.makeStringAndClear(), aSysFileName ); + } + else if( bIsStrm ) + { + extractStream( *rxStrg, rStrgPath, rItemName, aSysFileName ); + } +} + +OutputObjectBase::~OutputObjectBase() +{ +} + +void OutputObjectBase::construct( const ObjectBase& rParent, const OUString& rSysFileName ) +{ + ObjectBase::construct( rParent ); + if( ObjectBase::implIsValid() ) + { + maSysFileName = rSysFileName; + mxOut = std::make_shared<Output>( getContext(), rSysFileName + OOX_DUMP_DUMPEXT ); + } +} + +void OutputObjectBase::construct( const OutputObjectBase& rParent ) +{ + *this = rParent; +} + +bool OutputObjectBase::implIsValid() const +{ + return isValid( mxOut ) && ObjectBase::implIsValid(); +} + +void OutputObjectBase::writeEmptyItem( const String& rName ) +{ + ItemGuard aItem( mxOut, rName ); +} + +void OutputObjectBase::writeInfoItem( const String& rName, const String& rData ) +{ + ItemGuard aItem( mxOut, rName ); + mxOut->writeString( rData ); +} + +void OutputObjectBase::writeCharItem( const String& rName, sal_Unicode cData ) +{ + ItemGuard aItem( mxOut, rName ); + mxOut->writeChar( OOX_DUMP_STRQUOTE ); + mxOut->writeChar( cData ); + mxOut->writeChar( OOX_DUMP_STRQUOTE ); +} + +void OutputObjectBase::writeStringItem( const String& rName, const OUString& rData ) +{ + ItemGuard aItem( mxOut, rName ); + mxOut->writeAscii( "(len=" ); + mxOut->writeDec( rData.getLength() ); + mxOut->writeAscii( ")," ); + OUStringBuffer aValue( rData.subView( 0, ::std::min( rData.getLength(), OOX_DUMP_MAXSTRLEN ) ) ); + StringHelper::enclose( aValue, OOX_DUMP_STRQUOTE ); + mxOut->writeString( aValue.makeStringAndClear() ); + if( rData.getLength() > OOX_DUMP_MAXSTRLEN ) + mxOut->writeAscii( ",cut" ); +} + +void OutputObjectBase::writeArrayItem( const String& rName, const sal_uInt8* pnData, std::size_t nSize, sal_Unicode cSep ) +{ + ItemGuard aItem( mxOut, rName ); + mxOut->writeArray( pnData, nSize, cSep ); +} + +void OutputObjectBase::writeDateTimeItem( const String& rName, const util::DateTime& rDateTime ) +{ + ItemGuard aItem( mxOut, rName ); + mxOut->writeDateTime( rDateTime ); +} + +void OutputObjectBase::writeGuidItem( const String& rName, const OUString& rGuid ) +{ + ItemGuard aItem( mxOut, rName ); + mxOut->writeString( rGuid ); + aItem.cont(); + mxOut->writeString( cfg().getStringOption( rGuid, OUString() ) ); +} + +InputObjectBase::~InputObjectBase() +{ +} + +void InputObjectBase::construct( const ObjectBase& rParent, const BinaryInputStreamRef& rxStrm, const OUString& rSysFileName ) +{ + OutputObjectBase::construct( rParent, rSysFileName ); + mxStrm = rxStrm; +} + +void InputObjectBase::construct( const OutputObjectBase& rParent, const BinaryInputStreamRef& rxStrm ) +{ + OutputObjectBase::construct( rParent ); + mxStrm = rxStrm; +} + +void InputObjectBase::construct( const InputObjectBase& rParent ) +{ + *this = rParent; +} + +bool InputObjectBase::implIsValid() const +{ + return mxStrm && OutputObjectBase::implIsValid(); +} + +void InputObjectBase::skipBlock( sal_Int64 nBytes, bool bShowSize ) +{ + sal_Int64 nEndPos = ::std::min< sal_Int64 >( mxStrm->tell() + nBytes, mxStrm->size() ); + if( mxStrm->tell() < nEndPos ) + { + if( bShowSize ) + writeDecItem( "skipped-data-size", static_cast< sal_uInt64 >( nEndPos - mxStrm->tell() ) ); + mxStrm->seek( nEndPos ); + } +} + +void InputObjectBase::dumpRawBinary( sal_Int64 nBytes, bool bShowOffset, bool bStream ) +{ + TableGuard aTabGuard( mxOut, + bShowOffset ? 12 : 0, + 3 * OOX_DUMP_BYTESPERLINE / 2 + 1, + 3 * OOX_DUMP_BYTESPERLINE / 2 + 1, + OOX_DUMP_BYTESPERLINE / 2 + 1 ); + + sal_Int64 nMaxShowSize = cfg().getIntOption< sal_Int64 >( + bStream ? "max-binary-stream-size" : "max-binary-data-size", SAL_MAX_INT64 ); + + bool bSeekable = mxStrm->size() >= 0; + sal_Int64 nEndPos = bSeekable ? ::std::min< sal_Int64 >( mxStrm->tell() + nBytes, mxStrm->size() ) : 0; + sal_Int64 nDumpEnd = bSeekable ? ::std::min< sal_Int64 >( mxStrm->tell() + nMaxShowSize, nEndPos ) : nMaxShowSize; + sal_Int64 nPos = bSeekable ? mxStrm->tell() : 0; + bool bLoop = true; + + while( bLoop && (nPos < nDumpEnd) ) + { + mxOut->writeHex( static_cast< sal_uInt32 >( nPos ) ); + mxOut->tab(); + + sal_uInt8 pnLineData[ OOX_DUMP_BYTESPERLINE ]; + sal_Int32 nLineSize = bSeekable ? ::std::min( static_cast< sal_Int32 >( nDumpEnd - mxStrm->tell() ), OOX_DUMP_BYTESPERLINE ) : OOX_DUMP_BYTESPERLINE; + sal_Int32 nReadSize = mxStrm->readMemory( pnLineData, nLineSize ); + bLoop = nReadSize == nLineSize; + nPos += nReadSize; + + if( nReadSize > 0 ) + { + const sal_uInt8* pnByte = nullptr; + const sal_uInt8* pnEnd = nullptr; + for( pnByte = pnLineData, pnEnd = pnLineData + nReadSize; pnByte != pnEnd; ++pnByte ) + { + if( (pnByte - pnLineData) == (OOX_DUMP_BYTESPERLINE / 2) ) mxOut->tab(); + mxOut->writeHex( *pnByte, false ); + mxOut->writeChar( ' ' ); + } + + aTabGuard.tab( 3 ); + for( pnByte = pnLineData, pnEnd = pnLineData + nReadSize; pnByte != pnEnd; ++pnByte ) + { + if( (pnByte - pnLineData) == (OOX_DUMP_BYTESPERLINE / 2) ) mxOut->tab(); + mxOut->writeChar( static_cast< sal_Unicode >( (*pnByte < 0x20) ? '.' : *pnByte ) ); + } + mxOut->newLine(); + } + } + + // skip undumped data + if( bSeekable ) + skipBlock( nEndPos - mxStrm->tell() ); +} + +void InputObjectBase::dumpBinary( const String& rName, sal_Int64 nBytes, bool bShowOffset ) +{ + { + MultiItemsGuard aMultiGuard( mxOut ); + writeEmptyItem( rName ); + writeDecItem( "size", nBytes ); + } + IndentGuard aIndGuard( mxOut ); + dumpRawBinary( nBytes, bShowOffset ); +} + +void InputObjectBase::dumpRemaining( sal_Int64 nBytes ) +{ + if( nBytes > 0 ) + { + if( cfg().getBoolOption( "show-trailing-unknown", true ) ) + dumpBinary( "remaining-data", nBytes, false ); + else + skipBlock( nBytes ); + } +} + +void InputObjectBase::dumpRemainingTo( sal_Int64 nPos ) +{ + if( mxStrm->isEof() || (mxStrm->tell() > nPos) ) + writeInfoItem( "stream-state", OOX_DUMP_ERR_STREAM ); + else + dumpRemaining( nPos - mxStrm->tell() ); + mxStrm->seek( nPos ); +} + +void InputObjectBase::dumpRemainingStream() +{ + dumpRemainingTo( mxStrm->size() ); +} + +void InputObjectBase::dumpArray( const String& rName, sal_Int32 nBytes, sal_Unicode cSep ) +{ + sal_Int32 nDumpSize = getLimitedValue< sal_Int32, sal_Int64 >( mxStrm->size() - mxStrm->tell(), 0, nBytes ); + if( nDumpSize > OOX_DUMP_MAXARRAY ) + { + dumpBinary( rName, nBytes, false ); + } + else if( nDumpSize > 1 ) + { + sal_uInt8 pnData[ OOX_DUMP_MAXARRAY ]; + mxStrm->readMemory( pnData, nDumpSize ); + writeArrayItem( rName, pnData, nDumpSize, cSep ); + } + else if( nDumpSize == 1 ) + dumpHex< sal_uInt8 >( rName ); +} + +sal_Unicode InputObjectBase::dumpUnicode( const String& rName ) +{ + sal_uInt16 nChar = mxStrm->readuInt16(); + sal_Unicode cChar = static_cast< sal_Unicode >( nChar ); + writeCharItem( rName( "char" ), cChar ); + return cChar; +} + +OUString InputObjectBase::dumpCharArray( const String& rName, sal_Int32 nLen, rtl_TextEncoding eTextEnc, bool bHideTrailingNul ) +{ + sal_Int32 nDumpSize = getLimitedValue< sal_Int32, sal_Int64 >( mxStrm->size() - mxStrm->tell(), 0, nLen ); + OUString aString; + if( nDumpSize > 0 ) + { + ::std::vector< char > aBuffer( static_cast< std::size_t >( nLen ) + 1 ); + sal_Int32 nCharsRead = mxStrm->readMemory(aBuffer.data(), nLen); + aBuffer[ nCharsRead ] = 0; + aString = OStringToOUString(std::string_view(aBuffer.data()), eTextEnc); + } + if( bHideTrailingNul ) + aString = StringHelper::trimTrailingNul( aString ); + writeStringItem( rName( "text" ), aString ); + return aString; +} + +OUString InputObjectBase::dumpUnicodeArray( const String& rName, sal_Int32 nLen, bool bHideTrailingNul ) +{ + OUStringBuffer aBuffer; + for( sal_Int32 nIndex = 0; !mxStrm->isEof() && (nIndex < nLen); ++nIndex ) + { + aBuffer.append( static_cast< sal_Unicode >( mxStrm->readuInt16() ) ); + } + OUString aString = aBuffer.makeStringAndClear(); + if( bHideTrailingNul ) + aString = StringHelper::trimTrailingNul( aString ); + writeStringItem( rName( "text" ), aString ); + return aString; +} + +util::DateTime InputObjectBase::dumpFileTime( const String& rName ) +{ + util::DateTime aDateTime; + + ItemGuard aItem( mxOut, rName( "file-time" ) ); + sal_Int64 nFileTime = dumpDec< sal_Int64 >( EMPTY_STRING ); + // file time is in 10^-7 seconds (100 nanoseconds), convert to nanoseconds + nFileTime *= 100; + // entire days + sal_Int64 nDays = nFileTime / sal_Int64( ::tools::Time::nanoSecPerDay ); + // number of entire years + sal_Int64 nYears = (nDays - (nDays / (4 * 365)) + (nDays / (100 * 365)) - (nDays / (400 * 365))) / 365; + // remaining days in the year + sal_Int64 nDaysInYear = nDays - (nYears * 365 + nYears / 4 - nYears / 100 + nYears / 400); + // the year (file dates start from 1601-01-01) + aDateTime.Year = static_cast< sal_uInt16 >( 1601 + nYears ); + // leap year? + bool bLeap = ((aDateTime.Year % 4 == 0) && (aDateTime.Year % 100 != 0)) || (aDateTime.Year % 400 == 0); + // static arrays with number of days in month + static const sal_Int64 spnDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + static const sal_Int64 spnDaysInMonthL[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + const sal_Int64* pnDaysInMonth = bLeap ? spnDaysInMonthL : spnDaysInMonth; + // the month + aDateTime.Month = 1; + while( nDaysInYear >= *pnDaysInMonth ) + { + nDaysInYear -= *pnDaysInMonth++; + ++aDateTime.Month; + } + // the day + aDateTime.Day = static_cast< sal_uInt16 >( nDaysInYear + 1 ); + // number of nanoseconds in the day + sal_Int64 nTimeInDay = nFileTime % sal_Int64( ::tools::Time::nanoSecPerDay ); + // nanoseconds + aDateTime.NanoSeconds = static_cast< sal_uInt32 >( nTimeInDay % ::tools::Time::nanoSecPerSec ); + nTimeInDay /= ::tools::Time::nanoSecPerSec; + // seconds + aDateTime.Seconds = static_cast< sal_uInt16 >( nTimeInDay % ::tools::Time::secondPerMinute ); + nTimeInDay /= ::tools::Time::secondPerMinute; + // minutes + aDateTime.Minutes = static_cast< sal_uInt16 >( nTimeInDay % ::tools::Time::minutePerHour ); + nTimeInDay /= ::tools::Time::minutePerHour; + // hours + aDateTime.Hours = static_cast< sal_uInt16 >( nTimeInDay ); + + writeDateTimeItem( EMPTY_STRING, aDateTime ); + return aDateTime; +} + +OUString InputObjectBase::dumpGuid( const String& rName ) +{ + OUStringBuffer aBuffer; + sal_uInt32 nData32; + sal_uInt16 nData16; + sal_uInt8 nData8; + + nData32 = mxStrm->readuInt32(); + StringHelper::appendHex( aBuffer, nData32, false ); + aBuffer.append( '-' ); + nData16 = mxStrm->readuInt16(); + StringHelper::appendHex( aBuffer, nData16, false ); + aBuffer.append( '-' ); + nData16 = mxStrm->readuInt16(); + StringHelper::appendHex( aBuffer, nData16, false ); + aBuffer.append( '-' ); + nData8 = mxStrm->readuChar(); + StringHelper::appendHex( aBuffer, nData8, false ); + nData8 = mxStrm->readuChar( ); + StringHelper::appendHex( aBuffer, nData8, false ); + aBuffer.append( '-' ); + for( int nIndex = 0; nIndex < 6; ++nIndex ) + { + nData8 = mxStrm->readuChar( ); + StringHelper::appendHex( aBuffer, nData8, false ); + } + StringHelper::enclose( aBuffer, '{', '}' ); + OUString aGuid = aBuffer.makeStringAndClear(); + writeGuidItem( rName( "guid" ), aGuid ); + return aGuid; +} + +void InputObjectBase::dumpItem( const ItemFormat& rItemFmt ) +{ + switch( rItemFmt.meDataType ) + { + case DATATYPE_VOID: break; + case DATATYPE_INT8: dumpValue< sal_Int8 >( rItemFmt ); break; + case DATATYPE_UINT8: dumpValue< sal_uInt8 >( rItemFmt ); break; + case DATATYPE_INT16: dumpValue< sal_Int16 >( rItemFmt ); break; + case DATATYPE_UINT16: dumpValue< sal_uInt16 >( rItemFmt ); break; + case DATATYPE_INT32: dumpValue< sal_Int32 >( rItemFmt ); break; + case DATATYPE_UINT32: dumpValue< sal_uInt32 >( rItemFmt ); break; + case DATATYPE_INT64: dumpValue< sal_Int64 >( rItemFmt ); break; + case DATATYPE_UINT64: dumpValue< sal_uInt64 >( rItemFmt ); break; + case DATATYPE_FLOAT: dumpValue< float >( rItemFmt ); break; + case DATATYPE_DOUBLE: dumpValue< double >( rItemFmt ); break; + default:; + } +} + +BinaryStreamObject::BinaryStreamObject( const ObjectBase& rParent, const BinaryInputStreamRef& rxStrm, const OUString& rSysFileName ) +{ + InputObjectBase::construct( rParent, rxStrm, rSysFileName ); +} + +void BinaryStreamObject::dumpBinaryStream( bool bShowOffset ) +{ + mxStrm->seekToStart(); + dumpRawBinary( mxStrm->size(), bShowOffset, true ); + mxOut->emptyLine(); +} + +void BinaryStreamObject::implDump() +{ + dumpBinaryStream(); +} + +void TextStreamObjectBase::construct( const ObjectBase& rParent, + const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc, const OUString& rSysFileName ) +{ + InputObjectBase::construct( rParent, rxStrm, rSysFileName ); + constructTextStrmObj( eTextEnc ); +} + +void TextStreamObjectBase::construct( const OutputObjectBase& rParent, + const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc ) +{ + InputObjectBase::construct( rParent, rxStrm ); + constructTextStrmObj( eTextEnc ); +} + +bool TextStreamObjectBase::implIsValid() const +{ + return InputObjectBase::implIsValid() && mxTextStrm; +} + +void TextStreamObjectBase::implDump() +{ + implDumpText( *mxTextStrm ); +} + +void TextStreamObjectBase::constructTextStrmObj( rtl_TextEncoding eTextEnc ) +{ + if( mxStrm ) + mxTextStrm = std::make_shared<TextInputStream>( getContext(), *mxStrm, eTextEnc ); +} + +TextLineStreamObject::TextLineStreamObject( const ObjectBase& rParent, + const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc, const OUString& rSysFileName ) +{ + TextStreamObjectBase::construct( rParent, rxStrm, eTextEnc, rSysFileName ); +} + +TextLineStreamObject::TextLineStreamObject( const OutputObjectBase& rParent, + const BinaryInputStreamRef& rxStrm, rtl_TextEncoding eTextEnc ) +{ + TextStreamObjectBase::construct( rParent, rxStrm, eTextEnc ); +} + +void TextLineStreamObject::implDumpText( TextInputStream& rTextStrm ) +{ + sal_uInt32 nLine = 0; + while( !rTextStrm.isEof() ) + { + OUString aLine = rTextStrm.readLine(); + if( !rTextStrm.isEof() || !aLine.isEmpty() ) + implDumpLine( aLine, ++nLine ); + } +} + +void TextLineStreamObject::implDumpLine( const OUString& rLine, sal_uInt32 nLine ) +{ + TableGuard aTabGuard( mxOut, 8 ); + mxOut->writeDec( nLine, 6 ); + mxOut->tab(); + mxOut->writeString( rLine ); + mxOut->newLine(); +} + +XmlStreamObject::XmlStreamObject( const ObjectBase& rParent, + const BinaryInputStreamRef& rxStrm, const OUString& rSysFileName ) +{ + TextStreamObjectBase::construct( rParent, rxStrm, RTL_TEXTENCODING_UTF8, rSysFileName ); +} + +void XmlStreamObject::implDumpText( TextInputStream& rTextStrm ) +{ + /* Buffers a start element and the following element text. Needed to dump + matching start/end elements and the element text on the same line. */ + OUStringBuffer aOldStartElem; + // special handling for VML + bool bIsVml = o3tl::equalsIgnoreAsciiCase(InputOutputHelper::getFileNameExtension( maSysFileName ), u"vml"); + + while( !rTextStrm.isEof() ) + { + // get the next element and the following element text from text stream + OUString aElem = rTextStrm.readToChar( '>', true ).trim(); + OUString aText = rTextStrm.readToChar( '<', false ); + + // remove multiple whitespace from element + sal_Int32 nPos = 0; + while( nPos < aElem.getLength() ) + { + while( (nPos < aElem.getLength()) && (aElem[ nPos ] >= 32) ) ++nPos; + if( nPos < aElem.getLength() ) + aElem = aElem.subView( 0, nPos ) + OUStringChar(' ') + o3tl::trim(aElem.subView( nPos )); + ++nPos; + } + + sal_Int32 nElemLen = aElem.getLength(); + if( (nElemLen >= 2) && (aElem[ 0 ] == '<') && (aElem[ nElemLen - 1 ] == '>') ) + { + // determine type of the element + bool bSimpleElem = (aElem[ 1 ] == '!') || (aElem[ 1 ] == '?') || (aElem[ nElemLen - 2 ] == '/') || + (bIsVml && (nElemLen == 4) && (aElem[ 1 ] == 'b') && (aElem[ 2 ] == 'r')); + bool bStartElem = !bSimpleElem && (aElem[ 1 ] != '/'); + bool bEndElem = !bSimpleElem && !bStartElem; + + /* Start element or simple element: flush old start element and + its text from previous iteration, and start a new indentation + level for the new element. Trim whitespace and line breaks from + the text of the old start element. */ + if( (bSimpleElem || bStartElem) && (aOldStartElem.getLength() > 0) ) + { + mxOut->writeString( aOldStartElem.makeStringAndClear().trim() ); + mxOut->newLine(); + mxOut->incIndent(); + } + + /* Start element: remember it and its text, to be able to print the + matching end element on the same line in the next iteration. */ + if( bStartElem ) + { + aOldStartElem.append( aElem + aText ); + } + else + { + /* End element: if a start element has been remembered in the + previous iteration, write it out here untrimmed, to show + all whitespace in the element text, and without trailing + line break. Code below will add the end element right after + it. Otherwise, return to previous indentation level. */ + if( bEndElem ) + { + if( aOldStartElem.getLength() == 0 ) + mxOut->decIndent(); + else + mxOut->writeString( aOldStartElem.makeStringAndClear() ); + } + + /* Write the element. Write following element text in a new + line, but only, if it does not contain of white space + entirely. */ + mxOut->writeString( aElem ); + mxOut->newLine(); + if( !aText.trim().isEmpty() ) + { + mxOut->writeString( aText ); + mxOut->newLine(); + } + } + } + } +} + +void RecordObjectBase::construct( const ObjectBase& rParent, + const BinaryInputStreamRef& rxBaseStrm, const OUString& rSysFileName, + const BinaryInputStreamRef& rxRecStrm, const String& rRecNames, const String& rSimpleRecs ) +{ + InputObjectBase::construct( rParent, rxRecStrm, rSysFileName ); + constructRecObjBase( rxBaseStrm, rRecNames, rSimpleRecs ); +} + +bool RecordObjectBase::implIsValid() const +{ + return mxBaseStrm && InputObjectBase::implIsValid(); +} + +void RecordObjectBase::implDump() +{ + NameListRef xRecNames = maRecNames.getNameList( cfg() ); + ItemFormatMap aSimpleRecs( maSimpleRecs.getNameList( cfg() ) ); + + while( implStartRecord( *mxBaseStrm, mnRecPos, mnRecId, mnRecSize ) ) + { + // record header + mxOut->emptyLine(); + writeHeader(); + implWriteExtHeader(); + IndentGuard aIndGuard( mxOut ); + sal_Int64 nRecPos = mxStrm->tell(); + + // record body + if( !mbBinaryOnly && cfg().hasName( xRecNames, mnRecId ) ) + { + ::std::map< sal_Int64, ItemFormat >::const_iterator aIt = aSimpleRecs.find( mnRecId ); + if( aIt != aSimpleRecs.end() ) + dumpItem( aIt->second ); + else + implDumpRecordBody(); + } + + // remaining undumped data + if( !mxStrm->isEof() && (mxStrm->tell() == nRecPos) ) + dumpRawBinary( mnRecSize, false ); + else + dumpRemainingTo( nRecPos + mnRecSize ); + } +} + +void RecordObjectBase::implWriteExtHeader() +{ +} + +void RecordObjectBase::implDumpRecordBody() +{ +} + +void RecordObjectBase::constructRecObjBase( const BinaryInputStreamRef& rxBaseStrm, const String& rRecNames, const String& rSimpleRecs ) +{ + mxBaseStrm = rxBaseStrm; + maRecNames = rRecNames; + maSimpleRecs = rSimpleRecs; + mnRecPos = mnRecId = mnRecSize = 0; + mbBinaryOnly = false; + if( InputObjectBase::implIsValid() ) + mbShowRecPos = cfg().getBoolOption( "show-record-position", true ); +} + +void RecordObjectBase::writeHeader() +{ + MultiItemsGuard aMultiGuard( mxOut ); + writeEmptyItem( "REC" ); + if( mbShowRecPos && mxBaseStrm->isSeekable() ) + writeShortHexItem( "pos", mnRecPos, "CONV-DEC" ); + writeShortHexItem( "size", mnRecSize, "CONV-DEC" ); + ItemGuard aItem( mxOut, "id" ); + mxOut->writeShortHex( mnRecId ); + addNameToItem( mnRecId, "CONV-DEC" ); + addNameToItem( mnRecId, maRecNames ); +} + +void SequenceRecordObjectBase::construct( const ObjectBase& rParent, + const BinaryInputStreamRef& rxBaseStrm, const OUString& rSysFileName, + const String& rRecNames, const String& rSimpleRecs ) +{ + BinaryInputStreamRef xRecStrm( std::make_shared<SequenceInputStream>( *mxRecData ) ); + RecordObjectBase::construct( rParent, rxBaseStrm, rSysFileName, xRecStrm, rRecNames, rSimpleRecs ); +} + +bool SequenceRecordObjectBase::implStartRecord( BinaryInputStream& rBaseStrm, sal_Int64& ornRecPos, sal_Int64& ornRecId, sal_Int64& ornRecSize ) +{ + bool bValid = true; + if( rBaseStrm.isSeekable() ) + { + ornRecPos = rBaseStrm.tell(); + // do not try to overread seekable streams, may cause assertions + bValid = ornRecPos < rBaseStrm.size(); + } + + // read the record header + if( bValid ) + bValid = implReadRecordHeader( rBaseStrm, ornRecId, ornRecSize ) && !rBaseStrm.isEof() && (0 <= ornRecSize) && (ornRecSize <= 0x00100000); + + // read record contents into data sequence + if( bValid ) + { + sal_Int32 nRecSize = static_cast< sal_Int32 >( ornRecSize ); + mxRecData->realloc( nRecSize ); + bValid = (nRecSize == 0) || (rBaseStrm.readData( *mxRecData, nRecSize ) == nRecSize); + mxStrm->seekToStart(); + } + return bValid; +} + +DumperBase::~DumperBase() +{ +} + +bool DumperBase::isImportEnabled() const +{ + return !isValid() || cfg().isImportEnabled(); +} + +void DumperBase::construct( const ConfigRef& rxConfig ) +{ + if( isValid( rxConfig ) && rxConfig->isDumperEnabled() ) + ObjectBase::construct( rxConfig ); +} + +} // namespace oox + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |