diff options
Diffstat (limited to 'tools/source')
87 files changed, 55391 insertions, 0 deletions
diff --git a/tools/source/communi/geninfo.cxx b/tools/source/communi/geninfo.cxx new file mode 100644 index 000000000000..43fb2a16b87e --- /dev/null +++ b/tools/source/communi/geninfo.cxx @@ -0,0 +1,411 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: geninfo.cxx,v $ + * $Revision: 1.12 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" +#include "tools/geninfo.hxx" +#include <stdio.h> + +// +// class GenericInformation +// + +/*****************************************************************************/ +GenericInformation::GenericInformation( const ByteString &rKey, + const ByteString &rValue, + GenericInformationList *pParentList, + GenericInformationList *pSubInfos ) +/*****************************************************************************/ + : ByteString( rKey ), + sValue( rValue ), + pInfoList( pSubInfos ), + pParent( pParentList ) +{ + // if a ParentList exists, insert this object into it + if ( pParent ) + pParent->InsertInfo( this ); + // make myself owner of pInfoList + if ( pInfoList ) + pInfoList->SetOwner( this ); +} + +/*****************************************************************************/ +GenericInformation::GenericInformation( const GenericInformation& rInf, + BOOL bCopySubs) +/*****************************************************************************/ + : ByteString( rInf ), + sValue( rInf.sValue ), + pInfoList( 0L ), + pParent(NULL) +{ + if(bCopySubs && rInf.pInfoList) + pInfoList = new GenericInformationList(*rInf.pInfoList, this); +} + +/*****************************************************************************/ +GenericInformation::~GenericInformation() +/*****************************************************************************/ +{ + // remove pInfoList and all childs out of memory + delete pInfoList; + pInfoList = 0; + + // remove this Info out of ParentList + if ( pParent ) + pParent->RemoveInfo( this ); +} + +/*****************************************************************************/ +BOOL GenericInformation::InsertSubInfo( GenericInformation *pInfo ) +/*****************************************************************************/ +{ + return ( pInfoList && pInfoList->InsertInfo( pInfo )); +} + +/*****************************************************************************/ +BOOL GenericInformation::InsertSubInfo( const ByteString &rPathKey, const ByteString &rValue, + BOOL bSearchByPath, BOOL bNewPath ) +/*****************************************************************************/ +{ + return (pInfoList && pInfoList->InsertInfo( rPathKey, rValue, bSearchByPath, bNewPath )); +} + +/*****************************************************************************/ +void GenericInformation::RemoveSubInfo( GenericInformation *pInfo, + BOOL bDelete ) +/*****************************************************************************/ +{ + pInfoList->RemoveInfo( pInfo, bDelete ); +} + +/*****************************************************************************/ +//void GenericInformation::RemoveSelf( BOOL bDelete ) +/*****************************************************************************/ +/*{ + if ( pParent ) + pParent->RemoveInfo( this, bDelete ); // loescht sich aus der Liste vom Parent und + // bei Bedarf auch mit obiger Methode alle Sublisten + + // loescht sich bei Bedarf auch selbst + if ( bDelete ) + delete this; +} +*/ + +/*****************************************************************************/ +GenericInformation *GenericInformation::GetSubInfo( ByteString &rKey, + BOOL bSearchByPath, + BOOL bCreatePath ) +/*****************************************************************************/ +{ + if ( !pInfoList && bCreatePath ) + pInfoList = new GenericInformationList( this ); + if ( pInfoList ) + return pInfoList->GetInfo( rKey, bSearchByPath, bCreatePath ); + return NULL; +} + + +// +// class GenericInformationList +// + +/*****************************************************************************/ +GenericInformationList::GenericInformationList( GenericInformation *pParent ) +/*****************************************************************************/ + : pOwner( pParent ) +{ +} + +/*****************************************************************************/ +GenericInformationList::GenericInformationList(const GenericInformationList& rList, + GenericInformation *pParent) +/*****************************************************************************/ + : GenericInformationList_Impl() +{ + USHORT i; + GenericInformation* pTemp,*pWork; + + pOwner = pParent; + + for(i=0;i<rList.Count();i++) + { + pTemp = rList.GetObject(i); + pWork = new GenericInformation(*pTemp,TRUE); + + Insert(pWork,LIST_APPEND); + } +} + +/*****************************************************************************/ +GenericInformationList::~GenericInformationList() +/*****************************************************************************/ +{ + // delete all Informations stored in this List + // ### GH: Hier werden dann wohl etwa die H�lfte der Eintr�ge gel�scht +/* for ( ULONG i = 0; i < Count(); i++ ) { + GetObject( i )->ListDeleted(); + delete GetObject( i ); + Remove( i );*/ + // Neue Variante: + while ( Count() ) { + GetObject( 0 )->ListDeleted(); + delete GetObject( 0 ); + Remove( (ULONG)0 ); + } +} + +/*****************************************************************************/ +GenericInformation *GenericInformationList::Search( ULONG &rPos, ByteString sKey, + ULONG nStart, ULONG nEnd ) +/*****************************************************************************/ +{ + if ( Count() == 0 ) { + rPos = 0; + return NULL; + } + + if ( nStart == nEnd ) { + rPos = nStart; + ByteString sCandidate = ByteString( *GetObject( nStart )); + if ( sCandidate.ToUpperAscii() == sKey.ToUpperAscii()) { + return GetObject( nStart ); // found !!! + } + else { + // requested key not found + return NULL; + } + } + + // search binary in existing list + ULONG nActPos = nStart + (( nEnd - nStart ) / 2 ); + rPos = nActPos; + ByteString sCandidate = ByteString( *GetObject( nActPos )); + + if ( sCandidate.ToUpperAscii() == sKey.ToUpperAscii()) + return GetObject( nActPos ); // found !!! + + // split the list at ActPos + if ( sCandidate < sKey ) + return Search( rPos, sKey, nActPos + 1, nEnd ); + else + return Search( rPos, sKey, nStart, nActPos ); +} + +/*****************************************************************************/ +GenericInformation *GenericInformationList::GetInfo( ByteString &rKey, + BOOL bSearchByPath, + BOOL bCreatePath ) +/*****************************************************************************/ +{ + + rKey.EraseLeadingChars( '/' ); + rKey.EraseTrailingChars( '/' ); + + ByteString sKey; + if ( bSearchByPath ) + sKey = rKey.GetToken( 0, '/' ); + else + sKey = rKey; + + ULONG nPos = 0; + GenericInformation *pReturnInfo = Search( nPos, sKey, 0, Count() - 1 ); + /* wenn kein Searchpath gesetzt und kein Returninfo vorhanden, + * gib NULL zurueck + * wenn Searchpath gesetzt und returninfo vorhanden, + * suche weiter nach unten + * wenn searchpath gesetzt kein returninfo vorhanden und newpath gesetzt, + * mache neues Verzeichniss + */ + USHORT nTokenCount = rKey.GetTokenCount('/'); + // search for next key of path in next level of tree + if ( bSearchByPath && (nTokenCount > 1)) { + ByteString sPath = ByteString(rKey.Copy( sKey.Len() + 1 )); + if ( !pReturnInfo ) { // wenn kein Return, dann muss man es anlegen + if ( !bCreatePath ) // wenn aber kein Create, dann nicht anlegen + return NULL; + pReturnInfo = new GenericInformation( sKey, "", this, NULL); + pReturnInfo->SetSubList( new GenericInformationList( pReturnInfo )); + } + return pReturnInfo->GetSubInfo( sPath, TRUE, bCreatePath ); + } + if ( !pReturnInfo && bCreatePath ) { + pReturnInfo = new GenericInformation ( sKey, "", this, NULL); + } + + return pReturnInfo; // kann durchaus NULL sein. +} + +/*****************************************************************************/ +ULONG GenericInformationList::InsertSorted( GenericInformation *pInfo, + BOOL bOverwrite, + ULONG nStart, ULONG nEnd ) +/*****************************************************************************/ +{ + if ( Count() == 0 ) { + // empty list, so insert at first pos + Insert( pInfo, LIST_APPEND ); + return 0; + } + + ByteString sKey( pInfo->GetBuffer()); + sKey.ToUpperAscii(); + + // Check to sppeed up reading a (partially) sorted list + if ( nStart == 0 && Count()-1 == nEnd ) + { + ByteString sCandidate( *GetObject( nEnd )); + if ( sCandidate.ToUpperAscii() < sKey ) + { + Insert( pInfo, LIST_APPEND ); + return nEnd+1; + } + } + +// ### GH: dieser Block schein �berfl�ssig zu sein + if ( Count() == 1 ) { + ByteString sCandidate( *GetObject( 0 )); + if ( sCandidate.ToUpperAscii() == sKey ) { + // key allready exists in list + if ( bOverwrite ) + Replace( pInfo, ULONG(0)); // ### Laut NF scheint hier ein Memory Leak zu sein + return 0; + } + else if ( sCandidate > sKey ) { + Insert( pInfo, ULONG(0)); + return 0; + } + else { + Insert( pInfo, LIST_APPEND ); + return 1; + } + } +// ### GH: /ENDE/ dieser Block schein �berfl�ssig zu sein + + ULONG nActPos = nStart + (( nEnd - nStart ) / 2 ); + ByteString sCandidate = ByteString( *GetObject( nActPos )); + + if ( sCandidate.ToUpperAscii() == sKey ) { + // key allready exists in list + if ( bOverwrite ) + Replace( pInfo, nActPos ); // ### Laut NF scheint hier ein Memory Leak zu sein + return nActPos; + } + + if ( nStart == nEnd ) { + // now more ways to search for key -> insert here + if ( sCandidate > sKey ) { + Insert( pInfo, nStart ); + return nStart; + } + else { + Insert( pInfo, nStart + 1 ); + return ( nStart + 1 ); + } + } + + if ( nActPos == Count() - 1 ) { + // reached end of list -> insert here + Insert( pInfo, LIST_APPEND ); + return ( nActPos + 1 ); + } + + ByteString sSecondCand = ByteString( *GetObject( nActPos + 1 )); + if (( sCandidate < sKey ) && ( sSecondCand.ToUpperAscii() > sKey )) { + // optimal position to insert object + Insert( pInfo, nActPos + 1 ); + return ( nActPos + 1 ); + } + + if ( sCandidate < sKey ) + return InsertSorted( pInfo, bOverwrite, nActPos + 1, nEnd ); + else + return InsertSorted( pInfo, bOverwrite, nStart, nActPos ); +} + +/*****************************************************************************/ +BOOL GenericInformationList::InsertInfo( GenericInformation *pInfo, + BOOL bOverwrite ) +/*****************************************************************************/ +{ + if ( !pInfo->Len()) + return FALSE; + + InsertSorted( pInfo, bOverwrite, 0, Count() - 1 ); + return TRUE; +} + + +/*****************************************************************************/ +BOOL GenericInformationList::InsertInfo( const ByteString &rPathKey, const ByteString &rValue, + BOOL bSearchByPath, BOOL bNewPath ) +/*****************************************************************************/ +{ + GenericInformation *pInfo; + ByteString sPathKey ( rPathKey ); + sPathKey.EraseLeadingChars( '/' ); + sPathKey.EraseTrailingChars( '/' ); + + pInfo = GetInfo( sPathKey, bSearchByPath, bNewPath ); + + if ( pInfo ) { + pInfo->SetValue( rValue ); + return TRUE; + } + return FALSE; +} + +/*****************************************************************************/ +void GenericInformationList::RemoveInfo( GenericInformation *pInfo, + BOOL bDelete ) +/*****************************************************************************/ +{ + Remove( pInfo ); + if ( bDelete ) + delete pInfo; +/* if ( Count() == 0 && pOwner ) // Leere Listen entfernen; + { + SetOwner( NULL ); + delete this; + } Rausgepatched by GH */ +} + +GenericInformation* GenericInformationList::SetOwner( GenericInformation *pNewOwner ) +{ + GenericInformation *pOldOwner = pOwner; + if ( pOwner ) // bei parent austragen; + pOwner->SetSubList( NULL ); + if ( pNewOwner ) + pNewOwner->SetSubList( this ); + pOwner = pNewOwner; + return pOldOwner; +} + + diff --git a/tools/source/communi/makefile.mk b/tools/source/communi/makefile.mk new file mode 100644 index 000000000000..9bfc52e0b2d5 --- /dev/null +++ b/tools/source/communi/makefile.mk @@ -0,0 +1,54 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.14 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=TOOLS +TARGET=communi + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +OBJFILES= \ + $(OBJ)$/parser.obj \ + $(OBJ)$/geninfo.obj \ + +SLOFILES= \ + $(SLO)$/parser.obj \ + $(SLO)$/geninfo.obj \ + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/tools/source/communi/parser.cxx b/tools/source/communi/parser.cxx new file mode 100644 index 000000000000..d2f87274f2f4 --- /dev/null +++ b/tools/source/communi/parser.cxx @@ -0,0 +1,472 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: parser.cxx,v $ + * $Revision: 1.19 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <stdio.h> +#include <tools/stream.hxx> +#include <tools/fsys.hxx> + +#include "tools/iparser.hxx" +#include "tools/geninfo.hxx" + + + +// +// class InformationParser +// + +#define cKeyLevelChar '\t' + +/*****************************************************************************/ +InformationParser::InformationParser( BOOL bReplace ) +/*****************************************************************************/ + : bRecover( FALSE ), + sOldLine( "" ), + bReplaceVariables( bReplace ), + nLevel( 0 ), + sUPD( "" ), + sVersion( "" ), + pActStream( NULL ), + nErrorCode( 0 ), + nErrorLine( 0 ), + sErrorText( "" ), + nActLine( 0 ) +{ +} + +/*****************************************************************************/ +InformationParser::~InformationParser() +/*****************************************************************************/ +{ +} + +/*****************************************************************************/ +ByteString &InformationParser::ReadLine() +/*****************************************************************************/ +{ + ByteString sLine; + + if ( bRecover ) { + bRecover = FALSE; + } + else { + if ( !pActStream->IsEof()) { + pActStream->ReadLine( sLine ); + xub_StrLen nStart = 0; + xub_StrLen nEnd = sLine.Len(); + BOOL bCopy = FALSE; + while ( nStart < nEnd && ( sLine.GetChar( nStart ) == ' ' || sLine.GetChar( nStart ) == 0x09 ) ) + { + nStart++; + bCopy = TRUE; + } + + while ( nStart < nEnd && ( sLine.GetChar( nEnd-1 ) == ' ' || sLine.GetChar( nEnd-1 ) == 0x09 ) ) + { + nEnd--; + bCopy = TRUE; + } + + if ( bCopy ) + sLine = sLine.Copy( nStart, nEnd - nStart ); + + if (( sLine.GetChar( 0 ) == '#' ) || ( !sLine.Len())) { + if ( sCurrentComment.Len()) + sCurrentComment += "\n"; + sCurrentComment += sLine; + return ReadLine(); + } + else { + if ( bReplaceVariables ) { + sLine.SearchAndReplaceAll( "%UPD", sUPD ); + sLine.SearchAndReplaceAll( "%VERSION", sVersion ); + } + } + } + else { + if ( nLevel ) { + sLine = "}"; + fprintf( stdout, "Reached EOF parsing %s. Suplying extra '}'\n",ByteString( sStreamName, gsl_getSystemTextEncoding()).GetBuffer() ); + // nErrorCode = IP_UNEXPECTED_EOF; + // nErrorLine = nActLine; + } + else + sLine = ""; + } + + sOldLine = sLine; + nActLine++; + } + + return sOldLine; +} + +/*****************************************************************************/ +GenericInformation *InformationParser::ReadKey( + GenericInformationList *pExistingList ) +/*****************************************************************************/ +{ + // this method has no error handling yet, but it works very fast. + // it is used to create whole informations and sub informations in + // a simple data format in memory, readed in a configuration file with + // following format: + + /* + + key [value] + { + key [value] + key [value] + { + key [value] + ... + ... + } + } + key [value] + ... + ... + + */ + + GenericInformation *pInfo = NULL; + + ByteString sLine( ReadLine()); + ByteString sKey; + ByteString sValue; + ByteString sComment( sCurrentComment ); + sCurrentComment = ""; + + // key separated from value by tab? + USHORT nWSPos = sLine.Search( ' ' ); + if ( sLine.Search( '\t' ) < nWSPos ) { + nWSPos = sLine.Search( '\t' ); + sLine.SearchAndReplace( "\t", " " ); + } + + if ( sLine.GetTokenCount( ' ' ) > 1 ) { + sKey = sLine.GetToken( 0, ' ' ); + sValue = sLine.Copy( sKey.Len() + 1 ); + while (( sValue.Search( ' ' ) == 0 ) || ( sValue.Search( '\t' ) == 0 )) { + sValue.Erase( 0, 1 ); + } + } + else + sKey=sLine; + + if ( bReplaceVariables && !nLevel ) { + sUPD = sKey.Copy( sKey.Len() - 3 ); + sVersion = sKey; + } + + if ( ReadLine() == "{" ) { + nLevel++; + GenericInformationList *pSubList = new GenericInformationList(); + while ( ReadLine() != "}" ) { + Recover(); + ReadKey( pSubList ); + } + nLevel--; + pInfo = new GenericInformation( sKey, sValue, + pExistingList, pSubList ); + pInfo->SetComment( sComment ); + } + else { + Recover(); + if ( !sKey.Equals( "}" ) && !sKey.Equals( "{" ) ) + { + pInfo = new GenericInformation( sKey, sValue, pExistingList ); + pInfo->SetComment( sComment ); + } + } + + return pInfo; +} + +/*****************************************************************************/ +void InformationParser::Recover() +/*****************************************************************************/ +{ + bRecover = TRUE; +} + +/*****************************************************************************/ +BOOL InformationParser::Save( SvStream &rOutStream, + const GenericInformationList *pSaveList, + USHORT level, BOOL bStripped ) +/*****************************************************************************/ +{ + USHORT i; + ULONG nInfoListCount; + ByteString sTmpStr; + GenericInformation *pGenericInfo; + GenericInformationList *pGenericInfoList; + + static ByteString aKeyLevel; + aKeyLevel.Expand( level, cKeyLevelChar ); + + for ( nInfoListCount = 0; nInfoListCount < pSaveList->Count(); nInfoListCount++) { + // Key-Value Paare schreiben + pGenericInfo = pSaveList->GetObject( nInfoListCount ); + sTmpStr = ""; + if ( !bStripped && level ) + sTmpStr.Append( aKeyLevel.GetBuffer(), level ); + + if ( !bStripped ) + for ( i = 0; i < pGenericInfo->GetComment().GetTokenCount( '\n' ); i++ ) { + sTmpStr += pGenericInfo->GetComment().GetToken( i, '\n' ); + sTmpStr += "\n"; + if ( level ) + sTmpStr.Append( aKeyLevel.GetBuffer(), level ); + } + + sTmpStr += pGenericInfo->GetBuffer(); + sTmpStr += ' '; + sTmpStr += pGenericInfo->GetValue(); + if ( !rOutStream.WriteLine( sTmpStr ) ) + return FALSE; + + // wenn vorhanden, bearbeite recursive die Sublisten + if (( pGenericInfoList = pGenericInfo->GetSubList() ) != NULL ) { + // oeffnende Klammer + sTmpStr = ""; + if ( !bStripped && level ) + sTmpStr.Append( aKeyLevel.GetBuffer(), level ); + sTmpStr += '{'; + if ( !rOutStream.WriteLine( sTmpStr ) ) + return FALSE; + // recursiv die sublist abarbeiten + if ( !Save( rOutStream, pGenericInfoList, level+1, bStripped ) ) + return FALSE; + // schliessende Klammer + sTmpStr = ""; + if ( !bStripped && level ) + sTmpStr.Append( aKeyLevel.GetBuffer(), level ); + sTmpStr += '}'; + if ( !rOutStream.WriteLine( sTmpStr ) ) + return FALSE; + } + } + return TRUE; +} + +/*****************************************************************************/ +GenericInformationList *InformationParser::Execute( + SvStream &rSourceStream, + GenericInformationList *pExistingList ) +/*****************************************************************************/ +{ + GenericInformationList *pList; + if ( pExistingList ) + pList = pExistingList; + else + pList = new GenericInformationList(); + + pActStream = &rSourceStream; + + // read all infos out of current file + while( !rSourceStream.IsEof()) { + nLevel = 0; + ReadKey( pList ); + } + + return pList; +} + +/*****************************************************************************/ +GenericInformationList *InformationParser::Execute( SvMemoryStream &rSourceStream, + GenericInformationList *pExistingList ) +/*****************************************************************************/ +{ + sStreamName = UniString( "Memory", gsl_getSystemTextEncoding()); + return Execute( (SvStream &)rSourceStream, pExistingList ); +} + +/*****************************************************************************/ +GenericInformationList *InformationParser::Execute( + SvFileStream &rSourceStream, + GenericInformationList *pExistingList ) +/*****************************************************************************/ +{ + if ( !rSourceStream.IsOpen()) + return NULL; + sStreamName = rSourceStream.GetFileName(); + return Execute( (SvStream &)rSourceStream, pExistingList ); +} + +/*****************************************************************************/ +GenericInformationList *InformationParser::Execute( UniString &rSourceFile, + GenericInformationList *pExistingList ) +/*****************************************************************************/ +{ + DirEntry aDirEntry( rSourceFile ); + if ( !aDirEntry.Exists()) + return NULL; + + GenericInformationList *pList; + if ( pExistingList ) + pList = pExistingList; + else + pList = new GenericInformationList(); + + // reset status + nErrorCode = 0; + nErrorLine = 0; + nActLine = 0; + + SvFileStream aActStream; + aActStream.Open( rSourceFile, STREAM_READ ); + if( aActStream.GetError()) + return NULL; + + pActStream = &aActStream; + if ( !Execute( aActStream, pList )) { + delete pList; + pList = NULL; + } + + // close the stream + aActStream.Close(); + pActStream = NULL; + + if ( !nErrorCode ) + return pList; + + return NULL; +} + +/*****************************************************************************/ +GenericInformationList *InformationParser::Execute( Dir &rDir, + GenericInformationList *pExistingList ) +/*****************************************************************************/ +{ + GenericInformationList *pList; + + if ( pExistingList ) + pList = pExistingList; + else + pList = new GenericInformationList(); + + for ( USHORT i = 0; i < rDir.Count(); i++ ) { + + // execute this dir + UniString sNextFile( rDir[i].GetFull()); + GenericInformationList *pSubList = Execute( sNextFile ); + + if ( !pSubList ) { + // any errors ? + delete pList; + return NULL; + } + + // create new info and insert it into list + ByteString sFileKey( rDir[i].GetName(), RTL_TEXTENCODING_UTF8 ); + new GenericInformation( + sFileKey, + ByteString( "" ), + pList, pSubList ); + } + + return pList; +} + +/*****************************************************************************/ +BOOL InformationParser::Save( SvFileStream &rSourceStream, + const GenericInformationList *pSaveList ) +/*****************************************************************************/ +{ + if ( !rSourceStream.IsOpen() || !Save( (SvStream &)rSourceStream, pSaveList, 0, FALSE )) + { + printf( "ERROR saving file \"%s\"\n",ByteString( rSourceStream.GetFileName(), gsl_getSystemTextEncoding()).GetBuffer() ); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ +BOOL InformationParser::Save( SvMemoryStream &rSourceStream, + const GenericInformationList *pSaveList ) +/*****************************************************************************/ +{ + Time a; + BOOL bRet = Save( (SvStream &)rSourceStream, pSaveList, 0, TRUE ); + Time b; + b = b - a; + return bRet; +} + +/*****************************************************************************/ +BOOL InformationParser::Save( const UniString &rSourceFile, + const GenericInformationList *pSaveList ) +/*****************************************************************************/ +{ + SvFileStream *pOutFile = new SvFileStream( rSourceFile, STREAM_STD_WRITE | STREAM_TRUNC ); + + if ( !Save( *pOutFile, pSaveList )) { + delete pOutFile; + return FALSE; + } + delete pOutFile; + return TRUE; +} + +/*****************************************************************************/ +USHORT InformationParser::GetErrorCode() +/*****************************************************************************/ +{ + return nErrorCode; +} + +/*****************************************************************************/ +ByteString &InformationParser::GetErrorText() +/*****************************************************************************/ +{ + // sErrorText = pActStream->GetFileName(); + sErrorText = ByteString( sStreamName, gsl_getSystemTextEncoding()); + sErrorText += ByteString( " (" ); + sErrorText += ByteString::CreateFromInt64(nErrorLine); + sErrorText += ByteString( "): " ); + + switch ( nErrorCode ) { + case IP_NO_ERROR: + sErrorText += ByteString( "Keine Fehler aufgetereten" ); + break; + case IP_UNEXPECTED_EOF: + sErrorText += ByteString( "Ungültiges Dateiende!" ); + break; + } + + return sErrorText; +} + + diff --git a/tools/source/datetime/datetime.cxx b/tools/source/datetime/datetime.cxx new file mode 100644 index 000000000000..94e49f4cde80 --- /dev/null +++ b/tools/source/datetime/datetime.cxx @@ -0,0 +1,445 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: datetime.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <tools/datetime.hxx> +#include <rtl/math.hxx> + +/************************************************************************* +|* +|* DateTime::IsBetween() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 18.05.92 +|* Letzte Aenderung TH 18.05.92 +|* +*************************************************************************/ + +BOOL DateTime::IsBetween( const DateTime& rFrom, + const DateTime& rTo ) const +{ + if ( (*this >= rFrom) && (*this <= rTo) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************* +|* +|* DateTime::operator >() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 18.05.92 +|* Letzte Aenderung TH 18.05.92 +|* +*************************************************************************/ + +BOOL DateTime::operator >( const DateTime& rDateTime ) const +{ + if ( (Date::operator>( rDateTime )) || + (Date::operator==( rDateTime ) && Time::operator>( rDateTime )) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************* +|* +|* DateTime::operator <() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 18.05.92 +|* Letzte Aenderung TH 18.05.92 +|* +*************************************************************************/ + +BOOL DateTime::operator <( const DateTime& rDateTime ) const +{ + if ( (Date::operator<( rDateTime )) || + (Date::operator==( rDateTime ) && Time::operator<( rDateTime )) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************* +|* +|* DateTime::operator >=() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 18.05.92 +|* Letzte Aenderung TH 18.05.92 +|* +*************************************************************************/ + +BOOL DateTime::operator >=( const DateTime& rDateTime ) const +{ + if ( (Date::operator>( rDateTime )) || + (Date::operator==( rDateTime ) && Time::operator>=( rDateTime )) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************* +|* +|* DateTime::operator <=() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 18.05.92 +|* Letzte Aenderung TH 18.05.92 +|* +*************************************************************************/ + +BOOL DateTime::operator <=( const DateTime& rDateTime ) const +{ + if ( (Date::operator<( rDateTime )) || + (Date::operator==( rDateTime ) && Time::operator<=( rDateTime )) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************* +|* +|* DateTime::GetSecFromDateTime() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 02.10.96 +|* Letzte Aenderung TH 02.10.96 +|* +*************************************************************************/ + +long DateTime::GetSecFromDateTime( const Date& rDate ) const +{ + if ( Date::operator<( rDate ) ) + return 0; + else + { + long nSec = Date( *this ) - rDate; + nSec *= 24UL*60*60; + long nHour = GetHour(); + long nMin = GetMin(); + nSec += (nHour*3600)+(nMin*60)+GetSec(); + return nSec; + } +} + +/************************************************************************* +|* +|* DateTime::GetSecFromDateTime() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 02.10.96 +|* Letzte Aenderung TH 02.10.96 +|* +*************************************************************************/ + +void DateTime::MakeDateTimeFromSec( const Date& rDate, ULONG nSec ) +{ + long nDays = nSec / (24UL*60*60); + ((Date*)this)->operator=( rDate ); + nSec -= nDays * (24UL*60*60); + USHORT nMin = (USHORT)(nSec / 60); + nSec -= nMin * 60; + ((Time*)this)->operator=( Time( 0, nMin, (USHORT)nSec ) ); + operator+=( nDays ); +} + +/************************************************************************* +|* +|* DateTime::operator +=() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 02.10.96 +|* Letzte Aenderung TH 02.10.96 +|* +*************************************************************************/ + +DateTime& DateTime::operator +=( const Time& rTime ) +{ + Time aTime = *this; + aTime += rTime; + USHORT nHours = aTime.GetHour(); + if ( aTime.GetTime() > 0 ) + { + while ( nHours >= 24 ) + { + Date::operator++(); + nHours -= 24; + } + aTime.SetHour( nHours ); + } + else if ( aTime.GetTime() != 0 ) + { + while ( nHours >= 24 ) + { + Date::operator--(); + nHours -= 24; + } + Date::operator--(); + aTime = Time( 24, 0, 0 )+aTime; + } + Time::operator=( aTime ); + + return *this; +} + +/************************************************************************* +|* +|* DateTime::operator -=() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 02.10.96 +|* Letzte Aenderung TH 02.10.96 +|* +*************************************************************************/ + +DateTime& DateTime::operator -=( const Time& rTime ) +{ + Time aTime = *this; + aTime -= rTime; + USHORT nHours = aTime.GetHour(); + if ( aTime.GetTime() > 0 ) + { + while ( nHours >= 24 ) + { + Date::operator++(); + nHours -= 24; + } + aTime.SetHour( nHours ); + } + else if ( aTime.GetTime() != 0 ) + { + while ( nHours >= 24 ) + { + Date::operator--(); + nHours -= 24; + } + Date::operator--(); + aTime = Time( 24, 0, 0 )+aTime; + } + Time::operator=( aTime ); + + return *this; +} + +/************************************************************************* +|* +|* DateTime::operator+() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 02.10.96 +|* Letzte Aenderung TH 02.10.96 +|* +*************************************************************************/ + +DateTime operator +( const DateTime& rDateTime, long nDays ) +{ + DateTime aDateTime( rDateTime ); + aDateTime += nDays; + return aDateTime; +} + +/************************************************************************* +|* +|* DateTime::operator-() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 02.10.96 +|* Letzte Aenderung TH 02.10.96 +|* +*************************************************************************/ + +DateTime operator -( const DateTime& rDateTime, long nDays ) +{ + DateTime aDateTime( rDateTime ); + aDateTime -= nDays; + return aDateTime; +} + +/************************************************************************* +|* +|* DateTime::operator+() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 02.10.96 +|* Letzte Aenderung TH 02.10.96 +|* +*************************************************************************/ + +DateTime operator +( const DateTime& rDateTime, const Time& rTime ) +{ + DateTime aDateTime( rDateTime ); + aDateTime += rTime; + return aDateTime; +} + +/************************************************************************* +|* +|* DateTime::operator-() +|* +|* Beschreibung DATETIME.SDW +|* Ersterstellung TH 02.10.96 +|* Letzte Aenderung TH 02.10.96 +|* +*************************************************************************/ + +DateTime operator -( const DateTime& rDateTime, const Time& rTime ) +{ + DateTime aDateTime( rDateTime ); + aDateTime -= rTime; + return aDateTime; +} + +/************************************************************************* +|* +|* DateTime::operator +=( double ) +|* +*************************************************************************/ + +DateTime& DateTime::operator +=( double fTimeInDays ) +{ + double fInt, fFrac; + if ( fTimeInDays < 0.0 ) + { + fInt = ::rtl::math::approxCeil( fTimeInDays ); + fFrac = fInt <= fTimeInDays ? 0.0 : fTimeInDays - fInt; + } + else + { + fInt = ::rtl::math::approxFloor( fTimeInDays ); + fFrac = fInt >= fTimeInDays ? 0.0 : fTimeInDays - fInt; + } + Date::operator+=( long(fInt) ); // full days + if ( fFrac ) + { + Time aTime(0); // default ctor calls system time, we don't need that + fFrac *= 24UL * 60 * 60 * 1000; // time expressed in milliseconds + aTime.MakeTimeFromMS( long(fFrac) ); // method handles negative ms + operator+=( aTime ); + } + return *this; +} + +/************************************************************************* +|* +|* DateTime::operator +( double ) +|* +*************************************************************************/ + +DateTime operator +( const DateTime& rDateTime, double fTimeInDays ) +{ + DateTime aDateTime( rDateTime ); + aDateTime += fTimeInDays; + return aDateTime; +} + +/************************************************************************* +|* +|* DateTime::operator -() +|* +*************************************************************************/ + +double operator -( const DateTime& rDateTime1, const DateTime& rDateTime2 ) +{ + long nDays = (const Date&) rDateTime1 - (const Date&) rDateTime2; + long nTime = rDateTime1.GetMSFromTime() - rDateTime2.GetMSFromTime(); + if ( nTime ) + { + double fTime = double(nTime); + fTime /= 24UL * 60 * 60 * 1000; // convert from milliseconds to fraction + if ( nDays < 0 && fTime > 0.0 ) + fTime = 1.0 - fTime; + return double(nDays) + fTime; + } + return double(nDays); +} + +void DateTime::GetWin32FileDateTime( sal_uInt32 & rLower, sal_uInt32 & rUpper ) +{ + const sal_Int64 a100nPerSecond = SAL_CONST_INT64( 10000000 ); + const sal_Int64 a100nPerDay = a100nPerSecond * sal_Int64( 60 * 60 * 24 ); + + sal_Int64 nYears = GetYear() - 1601; + sal_Int64 nDays = + nYears * 365 + + nYears / 4 - nYears / 100 + nYears / 400 + + GetDayOfYear() - 1; + + sal_Int64 aTime = + a100nPerDay * nDays + + a100nPerSecond * ( + sal_Int64( GetSec() ) + + 60 * sal_Int64( GetMin() ) + + 60 * 60 * sal_Int64( GetHour() ) ); + + rLower = sal_uInt32( aTime % SAL_CONST_UINT64( 0x100000000 ) ); + rUpper = sal_uInt32( aTime / SAL_CONST_UINT64( 0x100000000 ) ); +} + +DateTime DateTime::CreateFromWin32FileDateTime( const sal_uInt32 & rLower, const sal_uInt32 & rUpper ) +{ + const sal_Int64 a100nPerSecond = SAL_CONST_INT64( 10000000 ); + const sal_Int64 a100nPerDay = a100nPerSecond * sal_Int64( 60 * 60 * 24 ); + + sal_Int64 aTime = sal_Int64( + sal_uInt64( rUpper ) * SAL_CONST_UINT64( 0x100000000 ) + + sal_uInt64( rLower ) ); + + sal_Int64 nDays = aTime / a100nPerDay; + sal_Int64 nYears = + ( nDays - + ( nDays / ( 4 * 365 ) ) + + ( nDays / ( 100 * 365 ) ) - + ( nDays / ( 400 * 365 ) ) ) / 365; + nDays -= nYears * 365 + nYears / 4 - nYears / 100 + nYears / 400; + + USHORT nMonths = 0; + for( sal_Int64 nDaysCount = nDays; nDaysCount >= 0; ) + { + nDays = nDaysCount; + nMonths ++; + nDaysCount -= Date( + 1, nMonths, sal::static_int_cast< USHORT >(1601 + nYears) ). + GetDaysInMonth(); + } + + Date _aDate( + (USHORT)( nDays + 1 ), nMonths, + sal::static_int_cast< USHORT >(nYears + 1601) ); + Time _aTime( ULONG( ( aTime / ( a100nPerSecond * 60 * 60 ) ) % sal_Int64( 24 ) ), + ULONG( ( aTime / ( a100nPerSecond * 60 ) ) % sal_Int64( 60 ) ), + ULONG( ( aTime / ( a100nPerSecond ) ) % sal_Int64( 60 ) ) ); + + return DateTime( _aDate, _aTime ); +} diff --git a/tools/source/datetime/makefile.mk b/tools/source/datetime/makefile.mk new file mode 100644 index 000000000000..28973e70d0f4 --- /dev/null +++ b/tools/source/datetime/makefile.mk @@ -0,0 +1,54 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.6 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=datetime + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES= $(SLO)$/tdate.obj \ + $(SLO)$/ttime.obj \ + $(SLO)$/datetime.obj + +OBJFILES= $(OBJ)$/tdate.obj \ + $(OBJ)$/ttime.obj \ + $(OBJ)$/datetime.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/tools/source/datetime/tdate.cxx b/tools/source/datetime/tdate.cxx new file mode 100644 index 000000000000..7f204d5e46da --- /dev/null +++ b/tools/source/datetime/tdate.cxx @@ -0,0 +1,497 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: tdate.cxx,v $ + * $Revision: 1.9 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#if defined( OS2 ) +#define INCL_DOSDATETIME +#include <svpm.h> +#elif defined( WNT ) +#ifdef _MSC_VER +#pragma warning (push,1) +#endif +#include <tools/svwin.h> +#ifdef _MSC_VER +#pragma warning (pop) +#endif +#else +#include <time.h> +#endif + +#include <tools/debug.hxx> +#include <tools/date.hxx> +#ifdef MACOSX +extern "C" { +struct tm *localtime_r(const time_t *timep, struct tm *buffer); +} +#endif + +// ======================================================================= + +static USHORT aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 }; + +#define MAX_DAYS 3636532 + +// ======================================================================= + +inline BOOL ImpIsLeapYear( USHORT nYear ) +{ + return ( + ( ((nYear % 4) == 0) && ((nYear % 100) != 0) ) || + ( (nYear % 400) == 0 ) + ); +} + +// ----------------------------------------------------------------------- + +inline USHORT DaysInMonth( USHORT nMonth, USHORT nYear ) +{ + if ( nMonth != 2 ) + return aDaysInMonth[nMonth-1]; + else + { + if (ImpIsLeapYear(nYear)) + return aDaysInMonth[nMonth-1] + 1; + else + return aDaysInMonth[nMonth-1]; + } +} + +// ----------------------------------------------------------------------- + +static long DateToDays( USHORT nDay, USHORT nMonth, USHORT nYear ) +{ + long nDays; + + nDays = ((ULONG)nYear-1) * 365; + nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400); + for( USHORT i = 1; i < nMonth; i++ ) + nDays += DaysInMonth(i,nYear); + nDays += nDay; + return nDays; +} + +// ----------------------------------------------------------------------- + +static void DaysToDate( long nDays, + USHORT& rDay, USHORT& rMonth, USHORT& rYear ) +{ + long nTempDays; + long i = 0; + BOOL bCalc; + + do + { + nTempDays = (long)nDays; + rYear = (USHORT)((nTempDays / 365) - i); + nTempDays -= ((ULONG)rYear-1) * 365; + nTempDays -= ((rYear-1) / 4) - ((rYear-1) / 100) + ((rYear-1) / 400); + bCalc = FALSE; + if ( nTempDays < 1 ) + { + i++; + bCalc = TRUE; + } + else + { + if ( nTempDays > 365 ) + { + if ( (nTempDays != 366) || !ImpIsLeapYear( rYear ) ) + { + i--; + bCalc = TRUE; + } + } + } + } + while ( bCalc ); + + rMonth = 1; + while ( (ULONG)nTempDays > DaysInMonth( rMonth, rYear ) ) + { + nTempDays -= DaysInMonth( rMonth, rYear ); + rMonth++; + } + rDay = (USHORT)nTempDays; +} + +// ======================================================================= + +Date::Date() +{ +#if defined( OS2 ) + DATETIME aDateTime; + DosGetDateTime( &aDateTime ); + + // Datum zusammenbauen + nDate = ((ULONG)aDateTime.day) + + (((ULONG)aDateTime.month)*100) + + (((ULONG)aDateTime.year)*10000); +#elif defined WNT + SYSTEMTIME aDateTime; + GetLocalTime( &aDateTime ); + + // Datum zusammenbauen + nDate = ((ULONG)aDateTime.wDay) + + (((ULONG)aDateTime.wMonth)*100) + + (((ULONG)aDateTime.wYear)*10000); +#else + time_t nTmpTime; + struct tm aTime; + + // Zeit ermitteln + nTmpTime = time( 0 ); + + // Datum zusammenbauen + if ( localtime_r( &nTmpTime, &aTime ) ) + { + nDate = ((ULONG)aTime.tm_mday) + + (((ULONG)(aTime.tm_mon+1))*100) + + (((ULONG)(aTime.tm_year+1900))*10000); + } + else + nDate = 1 + 100 + (((ULONG)1900)*10000); +#endif +} + +// ----------------------------------------------------------------------- + +void Date::SetDay( USHORT nNewDay ) +{ + ULONG nMonth = GetMonth(); + ULONG nYear = GetYear(); + + nDate = ((ULONG)(nNewDay%100)) + (nMonth*100) + (nYear*10000); +} + +// ----------------------------------------------------------------------- + +void Date::SetMonth( USHORT nNewMonth ) +{ + ULONG nDay = GetDay(); + ULONG nYear = GetYear(); + + nDate = nDay + (((ULONG)(nNewMonth%100))*100) + (nYear*10000); +} + +// ----------------------------------------------------------------------- + +void Date::SetYear( USHORT nNewYear ) +{ + ULONG nDay = GetDay(); + ULONG nMonth = GetMonth(); + + nDate = nDay + (nMonth*100) + (((ULONG)(nNewYear%10000))*10000); +} + +// ----------------------------------------------------------------------- + +DayOfWeek Date::GetDayOfWeek() const +{ + return (DayOfWeek)((ULONG)(DateToDays( GetDay(), GetMonth(), GetYear() )-1) % 7); +} + +// ----------------------------------------------------------------------- + +USHORT Date::GetDayOfYear() const +{ + USHORT nDay = GetDay(); + for( USHORT i = 1; i < GetMonth(); i++ ) + nDay = nDay + ::DaysInMonth( i, GetYear() ); // += yields a warning on MSVC, so don't use it + return nDay; +} + +// ----------------------------------------------------------------------- + +USHORT Date::GetWeekOfYear( DayOfWeek eStartDay, + sal_Int16 nMinimumNumberOfDaysInWeek ) const +{ + short nWeek; + short n1WDay = (short)Date( 1, 1, GetYear() ).GetDayOfWeek(); + short nDayOfYear = (short)GetDayOfYear(); + + // Wochentage beginnen bei 0, deshalb einen abziehen + nDayOfYear--; + // StartDay beruecksichtigen + n1WDay = (n1WDay+(7-(short)eStartDay)) % 7; + + if (nMinimumNumberOfDaysInWeek < 1 || 7 < nMinimumNumberOfDaysInWeek) + { + DBG_ERRORFILE("Date::GetWeekOfYear: invalid nMinimumNumberOfDaysInWeek"); + nMinimumNumberOfDaysInWeek = 4; + } + + if ( nMinimumNumberOfDaysInWeek == 1 ) + { + nWeek = ((n1WDay+nDayOfYear)/7) + 1; + // 53te-Woche nur dann, wenn wir nicht schon in der ersten + // Woche des neuen Jahres liegen + if ( nWeek == 54 ) + nWeek = 1; + else if ( nWeek == 53 ) + { + short nDaysInYear = (short)GetDaysInYear(); + short nDaysNextYear = (short)Date( 1, 1, GetYear()+1 ).GetDayOfWeek(); + nDaysNextYear = (nDaysNextYear+(7-(short)eStartDay)) % 7; + if ( nDayOfYear > (nDaysInYear-nDaysNextYear-1) ) + nWeek = 1; + } + } + else if ( nMinimumNumberOfDaysInWeek == 7 ) + { + nWeek = ((n1WDay+nDayOfYear)/7); + // Erste Woche eines Jahres entspricht der letzen Woche des + // vorherigen Jahres + if ( nWeek == 0 ) + { + Date aLastDatePrevYear( 31, 12, GetYear()-1 ); + nWeek = aLastDatePrevYear.GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek ); + } + } + else // ( nMinimumNumberOfDaysInWeek == somehing_else, commentary examples for 4 ) + { + // x_monday - thursday + if ( n1WDay < nMinimumNumberOfDaysInWeek ) + nWeek = 1; + // friday + else if ( n1WDay == nMinimumNumberOfDaysInWeek ) + nWeek = 53; + // saturday + else if ( n1WDay == nMinimumNumberOfDaysInWeek + 1 ) + { + // Jahr nach Schaltjahr + if ( Date( 1, 1, GetYear()-1 ).IsLeapYear() ) + nWeek = 53; + else + nWeek = 52; + } + // sunday + else + nWeek = 52; + + if ( (nWeek == 1) || (nDayOfYear + n1WDay > 6) ) + { + if ( nWeek == 1 ) + nWeek += (nDayOfYear + n1WDay) / 7; + else + nWeek = (nDayOfYear + n1WDay) / 7; + if ( nWeek == 53 ) + { + // naechster x_Sonntag == erster x_Sonntag im neuen Jahr + // == noch gleiche Woche + long nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() ); + nTempDays += 6 - (GetDayOfWeek()+(7-(short)eStartDay)) % 7; + USHORT nDay; + USHORT nMonth; + USHORT nYear; + DaysToDate( nTempDays, nDay, nMonth, nYear ); + nWeek = Date( nDay, nMonth, nYear ).GetWeekOfYear( eStartDay, nMinimumNumberOfDaysInWeek ); + } + } + } + + return (USHORT)nWeek; +} + +// ----------------------------------------------------------------------- + +USHORT Date::GetDaysInMonth() const +{ + return DaysInMonth( GetMonth(), GetYear() ); +} + +// ----------------------------------------------------------------------- + +BOOL Date::IsLeapYear() const +{ + USHORT nYear = GetYear(); + return ImpIsLeapYear( nYear ); +} + +// ----------------------------------------------------------------------- + +BOOL Date::IsValid() const +{ + USHORT nDay = GetDay(); + USHORT nMonth = GetMonth(); + USHORT nYear = GetYear(); + + if ( !nMonth || (nMonth > 12) ) + return FALSE; + if ( !nDay || (nDay > DaysInMonth( nMonth, nYear )) ) + return FALSE; + else if ( nYear <= 1582 ) + { + if ( nYear < 1582 ) + return FALSE; + else if ( nMonth < 10 ) + return FALSE; + else if ( (nMonth == 10) && (nDay < 15) ) + return FALSE; + } + + return TRUE; +} + +// ----------------------------------------------------------------------- + +Date& Date::operator +=( long nDays ) +{ + USHORT nDay; + USHORT nMonth; + USHORT nYear; + long nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() ); + + nTempDays += nDays; + if ( nTempDays > MAX_DAYS ) + nDate = 31 + (12*100) + (((ULONG)9999)*10000); + else if ( nTempDays <= 0 ) + nDate = 1 + 100; + else + { + DaysToDate( nTempDays, nDay, nMonth, nYear ); + nDate = ((ULONG)nDay) + (((ULONG)nMonth)*100) + (((ULONG)nYear)*10000); + } + + return *this; +} + +// ----------------------------------------------------------------------- + +Date& Date::operator -=( long nDays ) +{ + USHORT nDay; + USHORT nMonth; + USHORT nYear; + long nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() ); + + nTempDays -= nDays; + if ( nTempDays > MAX_DAYS ) + nDate = 31 + (12*100) + (((ULONG)9999)*10000); + else if ( nTempDays <= 0 ) + nDate = 1 + 100; + else + { + DaysToDate( nTempDays, nDay, nMonth, nYear ); + nDate = ((ULONG)nDay) + (((ULONG)nMonth)*100) + (((ULONG)nYear)*10000); + } + + return *this; +} + +// ----------------------------------------------------------------------- + +Date& Date::operator ++() +{ + USHORT nDay; + USHORT nMonth; + USHORT nYear; + long nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() ); + + if ( nTempDays < MAX_DAYS ) + { + nTempDays++; + DaysToDate( nTempDays, nDay, nMonth, nYear ); + nDate = ((ULONG)nDay) + (((ULONG)nMonth)*100) + (((ULONG)nYear)*10000); + } + + return *this; +} + +// ----------------------------------------------------------------------- + +Date& Date::operator --() +{ + USHORT nDay; + USHORT nMonth; + USHORT nYear; + long nTempDays = DateToDays( GetDay(), GetMonth(), GetYear() ); + + if ( nTempDays > 1 ) + { + nTempDays--; + DaysToDate( nTempDays, nDay, nMonth, nYear ); + nDate = ((ULONG)nDay) + (((ULONG)nMonth)*100) + (((ULONG)nYear)*10000); + } + return *this; +} + +#ifndef MPW33 + +// ----------------------------------------------------------------------- + +Date Date::operator ++( int ) +{ + Date aOldDate = *this; + Date::operator++(); + return aOldDate; +} + +// ----------------------------------------------------------------------- + +Date Date::operator --( int ) +{ + Date aOldDate = *this; + Date::operator--(); + return aOldDate; +} + +#endif + +// ----------------------------------------------------------------------- + +Date operator +( const Date& rDate, long nDays ) +{ + Date aDate( rDate ); + aDate += nDays; + return aDate; +} + +// ----------------------------------------------------------------------- + +Date operator -( const Date& rDate, long nDays ) +{ + Date aDate( rDate ); + aDate -= nDays; + return aDate; +} + +// ----------------------------------------------------------------------- + +long operator -( const Date& rDate1, const Date& rDate2 ) +{ + ULONG nTempDays1 = DateToDays( rDate1.GetDay(), rDate1.GetMonth(), + rDate1.GetYear() ); + ULONG nTempDays2 = DateToDays( rDate2.GetDay(), rDate2.GetMonth(), + rDate2.GetYear() ); + return nTempDays1 - nTempDays2; +} diff --git a/tools/source/datetime/ttime.cxx b/tools/source/datetime/ttime.cxx new file mode 100644 index 000000000000..5417d0d3df31 --- /dev/null +++ b/tools/source/datetime/ttime.cxx @@ -0,0 +1,448 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ttime.cxx,v $ + * $Revision: 1.16 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _TOOLS_TIME_CXX + +#if defined( OS2 ) +#define INCL_DOSMISC +#define INCL_DOSDATETIME +#include <svpm.h> +#elif defined( WNT ) +#ifdef _MSC_VER +#pragma warning (push,1) +#endif +#include <tools/svwin.h> +#ifdef _MSC_VER +#pragma warning (pop) +#endif +#elif defined UNX +#include <unistd.h> +#include <limits.h> +#include <math.h> +#include <sys/time.h> +#endif + +#include <time.h> +#include <tools/time.hxx> + +#if defined(SOLARIS) && defined(__GNUC__) +extern long altzone; +#endif + +// ======================================================================= + +static sal_Int32 TimeToSec100( const Time& rTime ) +{ + short nSign = (rTime.GetTime() >= 0) ? +1 : -1; + sal_Int32 nHour = rTime.GetHour(); + sal_Int32 nMin = rTime.GetMin(); + sal_Int32 nSec = rTime.GetSec(); + sal_Int32 n100Sec = rTime.Get100Sec(); + +// Wegen Interal Compiler Error bei MSC, etwas komplizierter +// return (n100Sec + (nSec*100) + (nMin*60*100) + (nHour*60*60*100) * nSign); + + sal_Int32 nRet = n100Sec; + nRet += nSec*100; + nRet += nMin*60*100; + nRet += nHour*60*60*100; + + return (nRet * nSign); +} + +// ----------------------------------------------------------------------- + +static Time Sec100ToTime( sal_Int32 nSec100 ) +{ + short nSign; + if ( nSec100 < 0 ) + { + nSec100 *= -1; + nSign = -1; + } + else + nSign = 1; + + Time aTime( 0, 0, 0, nSec100 ); + aTime.SetTime( aTime.GetTime() * nSign ); + return aTime; +} + +// ======================================================================= + +Time::Time() +{ +#if defined( OS2 ) + DATETIME aDateTime; + DosGetDateTime( &aDateTime ); + + // Zeit zusammenbauen + nTime = (((sal_Int32)aDateTime.hours)*1000000) + + (((sal_Int32)aDateTime.minutes)*10000) + + (((sal_Int32)aDateTime.seconds)*100) + + ((sal_Int32)aDateTime.hundredths); +#elif defined( WNT ) + SYSTEMTIME aDateTime; + GetLocalTime( &aDateTime ); + + // Zeit zusammenbauen + nTime = (((sal_Int32)aDateTime.wHour)*1000000) + + (((sal_Int32)aDateTime.wMinute)*10000) + + (((sal_Int32)aDateTime.wSecond)*100) + + ((sal_Int32)aDateTime.wMilliseconds/10); +#else + time_t nTmpTime; + struct tm aTime; + + // Zeit ermitteln + nTmpTime = time( 0 ); + + // Zeit zusammenbauen + if ( localtime_r( &nTmpTime, &aTime ) ) + { + nTime = (((sal_Int32)aTime.tm_hour)*1000000) + + (((sal_Int32)aTime.tm_min)*10000) + + (((sal_Int32)aTime.tm_sec)*100); + } + else + nTime = 0; +#endif +} + +// ----------------------------------------------------------------------- + +Time::Time( const Time& rTime ) +{ + nTime = rTime.nTime; +} + +// ----------------------------------------------------------------------- + +Time::Time( ULONG nHour, ULONG nMin, ULONG nSec, ULONG n100Sec ) +{ + // Zeit normalisieren + nSec += n100Sec / 100; + n100Sec = n100Sec % 100; + nMin += nSec / 60; + nSec = nSec % 60; + nHour += nMin / 60; + nMin = nMin % 60; + + // Zeit zusammenbauen + nTime = (sal_Int32)(n100Sec + (nSec*100) + (nMin*10000) + (nHour*1000000)); +} + +// ----------------------------------------------------------------------- + +void Time::SetHour( USHORT nNewHour ) +{ + short nSign = (nTime >= 0) ? +1 : -1; + sal_Int32 nMin = GetMin(); + sal_Int32 nSec = GetSec(); + sal_Int32 n100Sec = Get100Sec(); + + nTime = (n100Sec + (nSec*100) + (nMin*10000) + + (((sal_Int32)nNewHour)*1000000)) * nSign; +} + +// ----------------------------------------------------------------------- + +void Time::SetMin( USHORT nNewMin ) +{ + short nSign = (nTime >= 0) ? +1 : -1; + sal_Int32 nHour = GetHour(); + sal_Int32 nSec = GetSec(); + sal_Int32 n100Sec = Get100Sec(); + + // kein Ueberlauf + nNewMin = nNewMin % 60; + + nTime = (n100Sec + (nSec*100) + (((sal_Int32)nNewMin)*10000) + + (nHour*1000000)) * nSign; +} + +// ----------------------------------------------------------------------- + +void Time::SetSec( USHORT nNewSec ) +{ + short nSign = (nTime >= 0) ? +1 : -1; + sal_Int32 nHour = GetHour(); + sal_Int32 nMin = GetMin(); + sal_Int32 n100Sec = Get100Sec(); + + // kein Ueberlauf + nNewSec = nNewSec % 60; + + nTime = (n100Sec + (((sal_Int32)nNewSec)*100) + (nMin*10000) + + (nHour*1000000)) * nSign; +} + +// ----------------------------------------------------------------------- + +void Time::Set100Sec( USHORT nNew100Sec ) +{ + short nSign = (nTime >= 0) ? +1 : -1; + sal_Int32 nHour = GetHour(); + sal_Int32 nMin = GetMin(); + sal_Int32 nSec = GetSec(); + + // kein Ueberlauf + nNew100Sec = nNew100Sec % 100; + + nTime = (((sal_Int32)nNew100Sec) + (nSec*100) + (nMin*10000) + + (nHour*1000000)) * nSign; +} + +// ----------------------------------------------------------------------- + +sal_Int32 Time::GetMSFromTime() const +{ + short nSign = (nTime >= 0) ? +1 : -1; + sal_Int32 nHour = GetHour(); + sal_Int32 nMin = GetMin(); + sal_Int32 nSec = GetSec(); + sal_Int32 n100Sec = Get100Sec(); + + return (((nHour*3600000)+(nMin*60000)+(nSec*1000)+(n100Sec*10))*nSign); +} + +// ----------------------------------------------------------------------- + +void Time::MakeTimeFromMS( sal_Int32 nMS ) +{ + short nSign; + if ( nMS < 0 ) + { + nMS *= -1; + nSign = -1; + } + else + nSign = 1; + + Time aTime( 0, 0, 0, nMS/10 ); + SetTime( aTime.GetTime() * nSign ); +} + +// ----------------------------------------------------------------------- + +double Time::GetTimeInDays() const +{ + short nSign = (nTime >= 0) ? +1 : -1; + double nHour = GetHour(); + double nMin = GetMin(); + double nSec = GetSec(); + double n100Sec = Get100Sec(); + + return (nHour+(nMin/60)+(nSec/(60*60))+(n100Sec/(60*60*100))) / 24 * nSign; +} + +// ----------------------------------------------------------------------- + +Time& Time::operator =( const Time& rTime ) +{ + nTime = rTime.nTime; + return *this; +} + +// ----------------------------------------------------------------------- + +Time& Time::operator +=( const Time& rTime ) +{ + nTime = Sec100ToTime( TimeToSec100( *this ) + + TimeToSec100( rTime ) ).GetTime(); + return *this; +} + +// ----------------------------------------------------------------------- + +Time& Time::operator -=( const Time& rTime ) +{ + nTime = Sec100ToTime( TimeToSec100( *this ) - + TimeToSec100( rTime ) ).GetTime(); + return *this; +} + +// ----------------------------------------------------------------------- + +Time operator +( const Time& rTime1, const Time& rTime2 ) +{ + return Sec100ToTime( TimeToSec100( rTime1 ) + + TimeToSec100( rTime2 ) ); +} + +// ----------------------------------------------------------------------- + +Time operator -( const Time& rTime1, const Time& rTime2 ) +{ + return Sec100ToTime( TimeToSec100( rTime1 ) - + TimeToSec100( rTime2 ) ); +} + +// ----------------------------------------------------------------------- + +BOOL Time::IsEqualIgnore100Sec( const Time& rTime ) const +{ + sal_Int32 n1 = (nTime < 0 ? -Get100Sec() : Get100Sec() ); + sal_Int32 n2 = (rTime.nTime < 0 ? -rTime.Get100Sec() : rTime.Get100Sec() ); + return (nTime - n1) == (rTime.nTime - n2); +} + +// ----------------------------------------------------------------------- + +Time Time::GetUTCOffset() +{ +#if defined( OS2 ) +#undef timezone + DATETIME aDateTime; + DosGetDateTime( &aDateTime ); + + // Zeit zusammenbauen + if ( aDateTime.timezone != -1 ) + { + short nTempTime = (short)Abs( aDateTime.timezone ); + Time aTime( 0, (USHORT)nTempTime ); + if ( aDateTime.timezone > 0 ) + aTime = -aTime; + return aTime; + } + else + return Time( 0 ); +#elif defined( WNT ) + TIME_ZONE_INFORMATION aTimeZone; + aTimeZone.Bias = 0; + DWORD nTimeZoneRet = GetTimeZoneInformation( &aTimeZone ); + sal_Int32 nTempTime = aTimeZone.Bias; + if ( nTimeZoneRet == TIME_ZONE_ID_STANDARD ) + nTempTime += aTimeZone.StandardBias; + else if ( nTimeZoneRet == TIME_ZONE_ID_DAYLIGHT ) + nTempTime += aTimeZone.DaylightBias; + Time aTime( 0, (USHORT)Abs( nTempTime ) ); + if ( nTempTime > 0 ) + aTime = -aTime; + return aTime; +#else + static ULONG nCacheTicks = 0; + static sal_Int32 nCacheSecOffset = -1; + ULONG nTicks = Time::GetSystemTicks(); + time_t nTime; + tm aTM; + sal_Int32 nLocalTime; + sal_Int32 nUTC; + short nTempTime; + + // Evt. Wert neu ermitteln + if ( (nCacheSecOffset == -1) || + ((nTicks - nCacheTicks) > 360000) || + ( nTicks < nCacheTicks ) // handle overflow + ) + { + nTime = time( 0 ); + localtime_r( &nTime, &aTM ); + nLocalTime = mktime( &aTM ); +#if defined( SOLARIS ) + // Solaris gmtime_r() seems not to handle daylight saving time + // flags correctly + nUTC = nLocalTime + ( aTM.tm_isdst == 0 ? timezone : altzone ); +#elif defined( LINUX ) + // Linux mktime() seems not to handle tm_isdst correctly + nUTC = nLocalTime - aTM.tm_gmtoff; +#else + gmtime_r( &nTime, &aTM ); + nUTC = mktime( &aTM ); +#endif + nCacheTicks = nTicks; + nCacheSecOffset = (nLocalTime-nUTC) / 60; + } + + nTempTime = (short)Abs( nCacheSecOffset ); + Time aTime( 0, (USHORT)nTempTime ); + if ( nCacheSecOffset < 0 ) + aTime = -aTime; + return aTime; +#endif +} + + +// ----------------------------------------------------------------------- + +ULONG Time::GetSystemTicks() +{ +#if defined WNT + return (ULONG)GetTickCount(); +#elif defined( OS2 ) + ULONG nClock; + DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &nClock, sizeof( nClock ) ); + return (ULONG)nClock; +#else + timeval tv; + gettimeofday (&tv, 0); + + double fTicks = tv.tv_sec; + fTicks *= 1000; + fTicks += ((tv.tv_usec + 500) / 1000); + + fTicks = fmod (fTicks, double(ULONG_MAX)); + return ULONG(fTicks); +#endif +} + +// ----------------------------------------------------------------------- + +ULONG Time::GetProcessTicks() +{ +#if defined WNT + return (ULONG)GetTickCount(); +#elif defined( OS2 ) + ULONG nClock; + DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &nClock, sizeof( nClock ) ); + return (ULONG)nClock; +#else + static ULONG nImplTicksPerSecond = 0; + static double dImplTicksPerSecond; + static double dImplTicksULONGMAX; + ULONG nTicks = (ULONG)clock(); + + if ( !nImplTicksPerSecond ) + { + nImplTicksPerSecond = CLOCKS_PER_SEC; + dImplTicksPerSecond = nImplTicksPerSecond; + dImplTicksULONGMAX = (double)(ULONG)ULONG_MAX; + } + + double fTicks = nTicks; + fTicks *= 1000; + fTicks /= dImplTicksPerSecond; + fTicks = fmod (fTicks, dImplTicksULONGMAX); + return (ULONG)fTicks; +#endif +} diff --git a/tools/source/debug/debug.cxx b/tools/source/debug/debug.cxx new file mode 100644 index 000000000000..119defa91ad0 --- /dev/null +++ b/tools/source/debug/debug.cxx @@ -0,0 +1,1813 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: debug.cxx,v $ + * $Revision: 1.17 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _TOOLS_DEBUG_CXX + +#if defined (UNX) || defined (GCC) +#include <unistd.h> +#else +#include <direct.h> +#endif + +#include <time.h> +#include <cstdarg> // combinations +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#ifdef OS2 +#define INCL_DOSSEMAPHORES +#define INCL_DOSMISC +#define INCL_WINDIALOGS +#define INCL_WINSHELLDATA +#include <svpm.h> +#endif + +#if defined ( WNT ) +#ifdef _MSC_VER +#pragma warning (push,1) +#endif +#include <tools/svwin.h> +#ifdef _MSC_VER +#pragma warning (pop) +#endif +#endif + +#include <tools/debug.hxx> +#include <rtl/string.h> + +#include <vector> + +#include <osl/diagnose.h> + +// ======================================================================= + +#ifdef DBG_UTIL + +// --- DbgErrors --- + +static sal_Char const DbgError_ProfEnd1[] = "DBG_PROF...() without DBG_PROFSTART(): "; +static sal_Char const DbgError_Xtor1[] = "DBG_DTOR() or DBG_CHKTHIS() without DBG_CTOR(): "; + +static sal_Char const DbgError_CtorDtor1[] = "this == NULL in class "; +static sal_Char const DbgError_CtorDtor2[] = "invalid this-Pointer %p in class "; +static sal_Char const DbgError_CtorDtor3[] = "Error-Msg from Object %p in class "; + +static sal_Char const DbgTrace_EnterCtor[] = "Enter Ctor from class "; +static sal_Char const DbgTrace_LeaveCtor[] = "Leave Ctor from class "; +static sal_Char const DbgTrace_EnterDtor[] = "Enter Dtor from class "; +static sal_Char const DbgTrace_LeaveDtor[] = "Leave Dtor from class "; +static sal_Char const DbgTrace_EnterMeth[] = "Enter method from class "; +static sal_Char const DbgTrace_LeaveMeth[] = "Leave method from class "; + +// --- PointerList --- + +#define PBLOCKCOUNT 1024 + +struct PBlock +{ + void* aData[PBLOCKCOUNT]; + USHORT nCount; + PBlock* pPrev; + PBlock* pNext; +}; + +class PointerList +{ +private: + PBlock* pFirst; + PBlock* pLast; + ULONG nCount; + +public: + PointerList() { pFirst = NULL; pLast = NULL; nCount = 0; } + ~PointerList(); + + void Add( const void* p ); + BOOL Remove( const void* p ); + + const void* Get( ULONG nPos ) const; + BOOL IsIn( const void* p ) const; + ULONG Count() const { return nCount; } +}; + +// --- Datentypen --- + +#define DBG_MAXNAME 28 + +struct ProfType +{ + ULONG nCount; + ULONG nTime; + ULONG nMinTime; + ULONG nMaxTime; + ULONG nStart; + ULONG nContinueTime; + ULONG nContinueStart; + sal_Char aName[DBG_MAXNAME+1]; +}; + +struct XtorType +{ + ULONG nCtorCalls; + ULONG nDtorCalls; + ULONG nMaxCount; + ULONG nStatics; + sal_Char aName[DBG_MAXNAME+1]; + BOOL bTest; + PointerList aThisList; +}; + +struct DebugData +{ + DbgData aDbgData; + USHORT bInit; + DbgPrintLine pDbgPrintMsgBox; + DbgPrintLine pDbgPrintWindow; + DbgPrintLine pDbgPrintShell; + DbgPrintLine pDbgPrintTestTool; + ::std::vector< DbgPrintLine > + aDbgPrintUserChannels; + PointerList* pProfList; + PointerList* pXtorList; + DbgTestSolarMutexProc pDbgTestSolarMutex; + pfunc_osl_printDetailedDebugMessage + pOldDebugMessageFunc; + bool bOslIsHooked; + + DebugData() + :bInit( FALSE ) + ,pDbgPrintMsgBox( FALSE ) + ,pDbgPrintWindow( NULL ) + ,pDbgPrintShell( NULL ) + ,pDbgPrintTestTool( NULL ) + ,pProfList( NULL ) + ,pXtorList( NULL ) + ,pDbgTestSolarMutex( NULL ) + ,pOldDebugMessageFunc( NULL ) + ,bOslIsHooked( false ) + { + aDbgData.nTestFlags = DBG_TEST_RESOURCE | DBG_TEST_MEM_INIT; + aDbgData.bOverwrite = TRUE; + aDbgData.nTraceOut = DBG_OUT_NULL; + aDbgData.nWarningOut = DBG_OUT_NULL; + aDbgData.nErrorOut = DBG_OUT_MSGBOX; + aDbgData.bMemInit = 0x77; + aDbgData.bMemBound = 0x55; + aDbgData.bMemFree = 0x33; + aDbgData.bHookOSLAssert = TRUE; + aDbgData.aDebugName[0] = 0; + aDbgData.aInclFilter[0] = 0; + aDbgData.aExclFilter[0] = 0; + aDbgData.aInclClassFilter[0] = 0; + aDbgData.aExclClassFilter[0] = 0; + aDbgData.aDbgWinState[0] = 0; + } +}; + +#define DBG_TEST_XTOR_EXTRA (DBG_TEST_XTOR_THIS | DBG_TEST_XTOR_FUNC | \ + DBG_TEST_XTOR_EXIT | DBG_TEST_XTOR_REPORT ) + +// ------------------------------ +// - statische Verwaltungsdaten - +// ------------------------------ + +static DebugData aDebugData; + +static sal_Char aCurPath[260]; + +static int bDbgImplInMain = FALSE; + +// ======================================================================= + +#if defined( WNT ) +static CRITICAL_SECTION aImplCritDbgSection; +#elif defined( OS2 ) +static HMTX hImplCritDbgSection = 0; +#endif +static BOOL bImplCritDbgSectionInit = FALSE; + +// ----------------------------------------------------------------------- + +void ImplDbgInitLock() +{ +#if defined( WNT ) + InitializeCriticalSection( &aImplCritDbgSection ); +#elif defined( OS2 ) + DosCreateMutexSem( NULL, &hImplCritDbgSection, 0, FALSE ); +#endif + bImplCritDbgSectionInit = TRUE; +} + +// ----------------------------------------------------------------------- + +void ImplDbgDeInitLock() +{ +#if defined( WNT ) + DeleteCriticalSection( &aImplCritDbgSection ); +#elif defined( OS2 ) + DosCloseMutexSem( hImplCritDbgSection ); +#endif + bImplCritDbgSectionInit = FALSE; +} + +// ----------------------------------------------------------------------- + +void ImplDbgLock() +{ + if ( !bImplCritDbgSectionInit ) + return; + +#if defined( WNT ) + EnterCriticalSection( &aImplCritDbgSection ); +#elif defined( OS2 ) + DosRequestMutexSem( hImplCritDbgSection, SEM_INDEFINITE_WAIT ); +#endif +} + +// ----------------------------------------------------------------------- + +void ImplDbgUnlock() +{ + if ( !bImplCritDbgSectionInit ) + return; + +#if defined( WNT ) + LeaveCriticalSection( &aImplCritDbgSection ); +#elif defined( OS2 ) + DosReleaseMutexSem( hImplCritDbgSection ); +#endif +} + +// ======================================================================= + +#if (defined WNT || defined OS2) && !defined SVX_LIGHT +//#define SV_MEMMGR // +#endif +#ifdef SV_MEMMGR +void DbgImpCheckMemory( void* p = NULL ); +void DbgImpCheckMemoryDeInit(); +void DbgImpMemoryInfo( sal_Char* pBuf ); +#endif + +#define FILE_LINEEND "\n" + +// ======================================================================= + +static BOOL ImplActivateDebugger( const sal_Char* pMsg ) +{ +#if defined( WNT ) + static sal_Char aImplDbgOutBuf[DBG_BUF_MAXLEN]; + strcpy( aImplDbgOutBuf, pMsg ); + strcat( aImplDbgOutBuf, "\r\n" ); + OutputDebugString( aImplDbgOutBuf ); + DebugBreak(); + return TRUE; +#else + (void) pMsg; // avoid warning about unused parameter + return FALSE; +#endif +} + +// ----------------------------------------------------------------------- + +static BOOL ImplCoreDump() +{ +#if defined( WNT ) + DebugBreak(); +#else + long* pTemp = 0; + *pTemp = 0xCCCC; +#endif + return TRUE; +} + +// ======================================================================= + +static ULONG ImplGetPerfTime() +{ +#if defined( WNT ) + return (ULONG)GetTickCount(); +#elif defined( OS2 ) + ULONG nClock; + DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &nClock, sizeof( nClock ) ); + return (ULONG)nClock; +#else + static ULONG nImplTicksPerSecond = 0; + static double dImplTicksPerSecond; + ULONG nTicks = (ULONG)clock(); + + if ( !nImplTicksPerSecond ) + { + nImplTicksPerSecond = CLOCKS_PER_SEC; + dImplTicksPerSecond = nImplTicksPerSecond; + } + + double fTicks = nTicks; + fTicks *= 1000; + fTicks /= dImplTicksPerSecond; + return (ULONG)fTicks; +#endif +} + +// ----------------------------------------------------------------------- + +typedef FILE* FILETYPE; +#define FileOpen fopen +#define FileRead fread +#define FileWrite fwrite +#define FilePrintF fprintf +#define FileClose fclose + +// ======================================================================= + +namespace +{ + enum ConfigSection + { + eOutput, + eMemory, + eGUI, + eObjects, + eTest, + + eUnknown + }; + + void lcl_lineFeed( FILETYPE _pFile ) + { + FilePrintF( _pFile, "%s", FILE_LINEEND ); + } + + const sal_Char* lcl_getSectionName( ConfigSection _eSection ) + { + const sal_Char* pSectionName = NULL; + switch ( _eSection ) + { + case eOutput : pSectionName = "output"; break; + case eMemory : pSectionName = "memory"; break; + case eGUI : pSectionName = "gui"; break; + case eObjects : pSectionName = "objects"; break; + case eTest : pSectionName = "test"; break; + case eUnknown: + OSL_ASSERT(false); + break; + } + return pSectionName; + } + + ConfigSection lcl_getSectionFromName( const sal_Char* _pSectionName, size_t _nSectionNameLength ) + { + if ( strncmp( _pSectionName, "output", _nSectionNameLength < 6 ? _nSectionNameLength : 6 ) == 0 ) + return eOutput; + if ( strncmp( _pSectionName, "memory", _nSectionNameLength < 6 ? _nSectionNameLength : 6 ) == 0 ) + return eMemory; + if ( strncmp( _pSectionName, "gui", _nSectionNameLength < 3 ? _nSectionNameLength : 3 ) == 0 ) + return eGUI; + if ( strncmp( _pSectionName, "objects", _nSectionNameLength < 7 ? _nSectionNameLength : 7 ) == 0 ) + return eObjects; + if ( strncmp( _pSectionName, "test", _nSectionNameLength < 4 ? _nSectionNameLength : 4 ) == 0 ) + return eTest; + return eUnknown; + } + + void lcl_startSection( FILETYPE _pFile, ConfigSection _eSection ) + { + FilePrintF( _pFile, "[%s]%s", lcl_getSectionName( _eSection ), FILE_LINEEND ); + } + + void lcl_writeConfigString( FILETYPE _pFile, const sal_Char* _pKeyName, const sal_Char* _pValue ) + { + FilePrintF( _pFile, "%s=%s%s", _pKeyName, _pValue, FILE_LINEEND ); + } + + void lcl_writeConfigBoolean( FILETYPE _pFile, const sal_Char* _pKeyName, bool _bValue ) + { + lcl_writeConfigString( _pFile, _pKeyName, _bValue ? "1" : "0" ); + } + + void lcl_writeConfigFlag( FILETYPE _pFile, const sal_Char* _pKeyName, ULONG _nAllFlags, ULONG _nCheckFlag ) + { + lcl_writeConfigBoolean( _pFile, _pKeyName, ( _nAllFlags & _nCheckFlag ) != 0 ); + } + + void lcl_writeConfigOutChannel( FILETYPE _pFile, const sal_Char* _pKeyName, ULONG _nValue ) + { + const sal_Char* names[ DBG_OUT_COUNT ] = + { + "dev/null", "file", "window", "shell", "messagebox", "testtool", "debugger", "coredump" + }; + lcl_writeConfigString( _pFile, _pKeyName, names[ _nValue ] ); + } + void lcl_writeHexByte( FILETYPE _pFile, const sal_Char* _pKeyName, BYTE _nValue ) + { + sal_Char buf[RTL_STR_MAX_VALUEOFINT32]; + rtl_String* stringData = NULL; + rtl_string_newFromStr_WithLength( &stringData, buf, rtl_str_valueOfInt32( buf, _nValue, 16 ) ); + + lcl_writeConfigString( _pFile, _pKeyName, stringData->buffer ); + + rtl_string_release( stringData ); + } + bool lcl_isConfigSection( const sal_Char* _pLine, size_t _nLineLen ) + { + if ( _nLineLen < 2 ) + // not even enough space for '[' and ']' + return false; + if ( ( _pLine[0] == '[' ) && ( _pLine[ _nLineLen - 1 ] == ']' ) ) + return true; + return false; + } + bool lcl_isConfigKey( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName ) + { + size_t nKeyLength = strlen( _pKeyName ); + if ( nKeyLength + 1 >= _nLineLen ) + // not even long enough for the key name plus "=" plus a one-character value + return false; + if ( ( strncmp( _pLine, _pKeyName, nKeyLength ) == 0 ) && ( _pLine[ nKeyLength ] == '=' ) ) + return true; + return false; + } + sal_Int32 lcl_tryReadConfigString( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName, sal_Char* _pValue, size_t _nValueLen ) + { + if ( !lcl_isConfigKey( _pLine, _nLineLen, _pKeyName ) ) + return 0; + size_t nValuePos = strlen( _pKeyName ) + 1; + size_t nValueLen = _nLineLen - nValuePos; + const sal_Char* pValue = _pLine + nValuePos; + strncpy( _pValue, pValue, ( _nValueLen > nValueLen ) ? nValueLen : _nValueLen ); + _pValue[ ( _nValueLen > nValueLen ) ? nValueLen : _nValueLen - 1 ] = 0; + return strlen( _pValue ); + } + void lcl_tryReadConfigBoolean( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName, ULONG* _out_pnValue ) + { + sal_Char aBuf[2]; + size_t nValueLen = lcl_tryReadConfigString( _pLine, _nLineLen, _pKeyName, aBuf, sizeof( aBuf ) ); + if ( nValueLen ) + *_out_pnValue = strcmp( aBuf, "1" ) == 0 ? TRUE : FALSE; + } + void lcl_tryReadOutputChannel( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName, ULONG* _out_pnValue ) + { + const sal_Char* names[ DBG_OUT_COUNT ] = + { + "dev/null", "file", "window", "shell", "messagebox", "testtool", "debugger", "coredump" + }; + sal_Char aBuf[20]; + size_t nValueLen = lcl_tryReadConfigString( _pLine, _nLineLen, _pKeyName, aBuf, sizeof( aBuf ) ); + if ( nValueLen ) + { + for ( ULONG name = 0; name < sizeof( names ) / sizeof( names[0] ); ++name ) + { + if ( strcmp( aBuf, names[ name ] ) == 0 ) + { + *_out_pnValue = name; + return; + } + } + } + } + void lcl_tryReadConfigFlag( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName, ULONG* _out_pnAllFlags, ULONG _nCheckFlag ) + { + sal_Char aBuf[2]; + size_t nValueLen = lcl_tryReadConfigString( _pLine, _nLineLen, _pKeyName, aBuf, sizeof( aBuf ) ); + if ( nValueLen ) + if ( strcmp( aBuf, "1" ) == 0 ) + *_out_pnAllFlags |= _nCheckFlag; + else + *_out_pnAllFlags &= ~_nCheckFlag; + } + void lcl_tryReadHexByte( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName, BYTE* _out_pnValue ) + { + sal_Char aBuf[3]; + size_t nValueLen = lcl_tryReadConfigString( _pLine, _nLineLen, _pKeyName, aBuf, sizeof( aBuf ) ); + if ( nValueLen ) + *_out_pnValue = (BYTE)rtl_str_toInt32( aBuf, 16 ); + } +} + +// ======================================================================= + +PointerList::~PointerList() +{ + PBlock* pBlock = pFirst; + while ( pBlock ) + { + PBlock* pNextBlock = pBlock->pNext; + delete pBlock; + pBlock = pNextBlock; + } +} + +// ----------------------------------------------------------------------- + +void PointerList::Add( const void* p ) +{ + if ( !pFirst ) + { + pFirst = new PBlock; + memset( pFirst->aData, 0, PBLOCKCOUNT * sizeof( void* ) ); + pFirst->nCount = 0; + pFirst->pPrev = NULL; + pFirst->pNext = NULL; + pLast = pFirst; + } + + PBlock* pBlock = pFirst; + while ( pBlock && (pBlock->nCount == PBLOCKCOUNT) ) + pBlock = pBlock->pNext; + + if ( !pBlock ) + { + pBlock = new PBlock; + memset( pBlock->aData, 0, PBLOCKCOUNT * sizeof( void* ) ); + pBlock->nCount = 0; + pBlock->pPrev = pLast; + pBlock->pNext = NULL; + pLast->pNext = pBlock; + pLast = pBlock; + } + + USHORT i = 0; + while ( pBlock->aData[i] ) + i++; + + pBlock->aData[i] = (void*)p; + pBlock->nCount++; + nCount++; +} + +// ----------------------------------------------------------------------- + +BOOL PointerList::Remove( const void* p ) +{ + if ( !p ) + return FALSE; + + PBlock* pBlock = pFirst; + while ( pBlock ) + { + USHORT i = 0; + while ( i < PBLOCKCOUNT ) + { + if ( ((ULONG)p) == ((ULONG)pBlock->aData[i]) ) + { + pBlock->aData[i] = NULL; + pBlock->nCount--; + nCount--; + + if ( !pBlock->nCount ) + { + if ( pBlock->pPrev ) + pBlock->pPrev->pNext = pBlock->pNext; + if ( pBlock->pNext ) + pBlock->pNext->pPrev = pBlock->pPrev; + if ( pBlock == pFirst ) + pFirst = pBlock->pNext; + if ( pBlock == pLast ) + pLast = pBlock->pPrev; + delete pBlock; + } + + return TRUE; + } + i++; + } + + pBlock = pBlock->pNext; + } + + return FALSE; +} + +// ----------------------------------------------------------------------- + +const void* PointerList::Get( ULONG nPos ) const +{ + if ( nCount <= nPos ) + return NULL; + + PBlock* pBlock = pFirst; + ULONG nStart = 0; + while ( pBlock ) + { + USHORT i = 0; + while ( i < PBLOCKCOUNT ) + { + if ( pBlock->aData[i] ) + { + nStart++; + if ( (nStart-1) == nPos ) + return pBlock->aData[i]; + } + + i++; + } + + pBlock = pBlock->pNext; + } + + return NULL; +} + +// ----------------------------------------------------------------------- + +BOOL PointerList::IsIn( const void* p ) const +{ + if ( !p ) + return FALSE; + + PBlock* pBlock = pFirst; + while ( pBlock ) + { + USHORT i = 0; + while ( i < PBLOCKCOUNT ) + { + if ( ((ULONG)p) == ((ULONG)pBlock->aData[i]) ) + return TRUE; + i++; + } + + pBlock = pBlock->pNext; + } + + return FALSE; +} + + +// ======================================================================= + +static void DbgGetDbgFileName( sal_Char* pStr, sal_Int32 nMaxLen ) +{ +#if defined( UNX ) + const sal_Char* pName = getenv("DBGSV_INIT"); + if ( !pName ) + pName = ".dbgsv.init"; + strncpy( pStr, pName, nMaxLen ); +#elif defined( WNT ) + const sal_Char* pName = getenv("DBGSV_INIT"); + if ( pName ) + strncpy( pStr, pName, nMaxLen ); + else + GetProfileStringA( "sv", "dbgsv", "dbgsv.ini", pStr, nMaxLen ); +#elif defined( OS2 ) + PrfQueryProfileString( HINI_PROFILE, (PSZ)"SV", (PSZ)"DBGSV", + "dbgsv.ini", (PSZ)pStr, nMaxLen ); +#else + strncpy( pStr, "dbgsv.ini", nMaxLen ); +#endif + pStr[ nMaxLen - 1 ] = 0; +} + +// ----------------------------------------------------------------------- + +static void DbgGetLogFileName( sal_Char* pStr ) +{ +#if defined( UNX ) + const sal_Char* pName = getenv("DBGSV_LOG"); + if ( !pName ) + pName = "dbgsv.log"; + strcpy( pStr, pName ); +#elif defined( WNT ) + const sal_Char* pName = getenv("DBGSV_LOG"); + if ( pName ) + strcpy( pStr, pName ); + else + GetProfileStringA( "sv", "dbgsvlog", "dbgsv.log", pStr, 200 ); +#elif defined( OS2 ) + PrfQueryProfileString( HINI_PROFILE, (PSZ)"SV", (PSZ)"DBGSVLOG", + "dbgsv.log", (PSZ)pStr, 200 ); +#else + strcpy( pStr, "dbgsv.log" ); +#endif +} + +// ----------------------------------------------------------------------- + +static void DbgDebugBeep() +{ +#if defined( WNT ) + MessageBeep( MB_ICONHAND ); +#elif defined( OS2 ) + WinAlarm( HWND_DESKTOP, WA_ERROR ); +#endif +} + +// ----------------------------------------------------------------------- + +static DebugData* GetDebugData() +{ + if ( !aDebugData.bInit ) + { + aDebugData.bInit = TRUE; + + // Default Debug-Namen setzen + DbgGetLogFileName( aDebugData.aDbgData.aDebugName ); + + // DEBUG.INI-File + sal_Char aBuf[ 4096 ]; + DbgGetDbgFileName( aBuf, sizeof( aBuf ) ); + FILETYPE pIniFile = FileOpen( aBuf, "r" ); + if ( pIniFile != NULL ) + { + ConfigSection eCurrentSection = eUnknown; + + // no sophisticated algorithm here, assume that the whole file fits into aBuf ... + ULONG nReallyRead = FileRead( aBuf, 1, sizeof( aBuf ) / sizeof( sal_Char ) - 1, pIniFile ); + aBuf[ nReallyRead ] = 0; + const sal_Char* pLine = aBuf; + while ( const sal_Char* pNextLine = strstr( pLine, FILE_LINEEND ) ) + { + size_t nLineLength = pNextLine - pLine; + + if ( lcl_isConfigSection( pLine, nLineLength ) ) + eCurrentSection = lcl_getSectionFromName( pLine + 1, nLineLength - 2 ); + + // elements of the [output] section + if ( eCurrentSection == eOutput ) + { + lcl_tryReadConfigString( pLine, nLineLength, "log_file", aDebugData.aDbgData.aDebugName, sizeof( aDebugData.aDbgData.aDebugName ) ); + lcl_tryReadConfigBoolean( pLine, nLineLength, "overwrite", &aDebugData.aDbgData.bOverwrite ); + lcl_tryReadConfigString( pLine, nLineLength, "include", aDebugData.aDbgData.aInclFilter, sizeof( aDebugData.aDbgData.aInclFilter ) ); + lcl_tryReadConfigString( pLine, nLineLength, "exclude", aDebugData.aDbgData.aExclFilter, sizeof( aDebugData.aDbgData.aExclFilter ) ); + lcl_tryReadConfigString( pLine, nLineLength, "include_class", aDebugData.aDbgData.aInclClassFilter, sizeof( aDebugData.aDbgData.aInclClassFilter ) ); + lcl_tryReadConfigString( pLine, nLineLength, "exclude_class", aDebugData.aDbgData.aExclClassFilter, sizeof( aDebugData.aDbgData.aExclClassFilter ) ); + lcl_tryReadOutputChannel( pLine, nLineLength, "trace", &aDebugData.aDbgData.nTraceOut ); + lcl_tryReadOutputChannel( pLine, nLineLength, "warning", &aDebugData.aDbgData.nWarningOut ); + lcl_tryReadOutputChannel( pLine, nLineLength, "error", &aDebugData.aDbgData.nErrorOut ); + lcl_tryReadConfigBoolean( pLine, nLineLength, "oslhook", &aDebugData.aDbgData.bHookOSLAssert ); + } + + // elements of the [memory] section + if ( eCurrentSection == eMemory ) + { + lcl_tryReadConfigFlag( pLine, nLineLength, "initialize", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_INIT ); + lcl_tryReadConfigFlag( pLine, nLineLength, "overwrite", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_OVERWRITE ); + lcl_tryReadConfigFlag( pLine, nLineLength, "overwrite_free", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_OVERWRITEFREE ); + lcl_tryReadConfigFlag( pLine, nLineLength, "pointer", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_POINTER ); + lcl_tryReadConfigFlag( pLine, nLineLength, "report", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_REPORT ); + lcl_tryReadConfigFlag( pLine, nLineLength, "trace", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_TRACE ); + lcl_tryReadConfigFlag( pLine, nLineLength, "new_and_delete", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_NEWDEL ); + lcl_tryReadConfigFlag( pLine, nLineLength, "object_test", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_XTOR ); + lcl_tryReadConfigFlag( pLine, nLineLength, "sys_alloc", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_SYSALLOC ); + lcl_tryReadConfigFlag( pLine, nLineLength, "leak_report", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_LEAKREPORT ); + + lcl_tryReadHexByte( pLine, nLineLength, "init_byte", &aDebugData.aDbgData.bMemInit ); + lcl_tryReadHexByte( pLine, nLineLength, "bound_byte", &aDebugData.aDbgData.bMemBound ); + lcl_tryReadHexByte( pLine, nLineLength, "free_byte", &aDebugData.aDbgData.bMemFree ); + } + + // elements of the [gui] section + if ( eCurrentSection == eGUI ) + { + lcl_tryReadConfigString( pLine, nLineLength, "debug_window_state", aDebugData.aDbgData.aDbgWinState, sizeof( aDebugData.aDbgData.aDbgWinState ) ); + } + + // elements of the [objects] section + if ( eCurrentSection == eObjects ) + { + lcl_tryReadConfigFlag( pLine, nLineLength, "check_this", &aDebugData.aDbgData.nTestFlags, DBG_TEST_XTOR_THIS ); + lcl_tryReadConfigFlag( pLine, nLineLength, "check_function", &aDebugData.aDbgData.nTestFlags, DBG_TEST_XTOR_FUNC ); + lcl_tryReadConfigFlag( pLine, nLineLength, "check_exit", &aDebugData.aDbgData.nTestFlags, DBG_TEST_XTOR_EXIT ); + lcl_tryReadConfigFlag( pLine, nLineLength, "generate_report", &aDebugData.aDbgData.nTestFlags, DBG_TEST_XTOR_REPORT ); + lcl_tryReadConfigFlag( pLine, nLineLength, "trace", &aDebugData.aDbgData.nTestFlags, DBG_TEST_XTOR_TRACE ); + } + + // elements of the [test] section + if ( eCurrentSection == eTest ) + { + lcl_tryReadConfigFlag( pLine, nLineLength, "profiling", &aDebugData.aDbgData.nTestFlags, DBG_TEST_PROFILING ); + lcl_tryReadConfigFlag( pLine, nLineLength, "resources", &aDebugData.aDbgData.nTestFlags, DBG_TEST_RESOURCE ); + lcl_tryReadConfigFlag( pLine, nLineLength, "dialog", &aDebugData.aDbgData.nTestFlags, DBG_TEST_DIALOG ); + lcl_tryReadConfigFlag( pLine, nLineLength, "bold_app_font", &aDebugData.aDbgData.nTestFlags, DBG_TEST_BOLDAPPFONT ); + } + + pLine = pNextLine + strlen( FILE_LINEEND ); + } + + FileClose( pIniFile ); + } + + getcwd( aCurPath, sizeof( aCurPath ) ); + + // Daten initialisieren + if ( aDebugData.aDbgData.nTestFlags & DBG_TEST_XTOR ) + aDebugData.pXtorList = new PointerList; + if ( aDebugData.aDbgData.nTestFlags & DBG_TEST_PROFILING ) + aDebugData.pProfList = new PointerList; + } + + return &aDebugData; +} + +// ----------------------------------------------------------------------- + +inline DebugData* ImplGetDebugData() +{ + if ( !aDebugData.bInit ) + return GetDebugData(); + else + return &aDebugData; +} + +// ----------------------------------------------------------------------- + +static FILETYPE ImplDbgInitFile() +{ + static BOOL bFileInit = FALSE; + + sal_Char aBuf[4096]; + getcwd( aBuf, sizeof( aBuf ) ); + chdir( aCurPath ); + + DebugData* pData = GetDebugData(); + FILETYPE pDebugFile; + + if ( !bFileInit ) + { + bFileInit = TRUE; + + if ( pData->aDbgData.bOverwrite ) + pDebugFile = FileOpen( pData->aDbgData.aDebugName, "w" ); + else + pDebugFile = FileOpen( pData->aDbgData.aDebugName, "a" ); + + if ( pDebugFile ) + { + time_t nTime = time( 0 ); + tm* pTime; +#ifdef UNX + tm aTime; + pTime = localtime_r( &nTime, &aTime ); +#else + pTime = localtime( &nTime ); +#endif + + // Header ausgeben + FilePrintF( pDebugFile, "******************************************************************************%s", FILE_LINEEND ); + FilePrintF( pDebugFile, "%s%s", pData->aDbgData.aDebugName, FILE_LINEEND ); + if ( pTime ) + FilePrintF( pDebugFile, "%s%s", asctime( pTime ), FILE_LINEEND ); + } + } + else + pDebugFile = FileOpen( pData->aDbgData.aDebugName, "a" ); + + chdir( aBuf ); + + return pDebugFile; +} + +// ----------------------------------------------------------------------- + +static void ImplDbgPrintFile( const sal_Char* pLine ) +{ + FILETYPE pDebugFile = ImplDbgInitFile(); + + if ( pDebugFile ) + { + FilePrintF( pDebugFile, "%s%s", pLine, FILE_LINEEND ); + FileClose( pDebugFile ); + } +} + +// ----------------------------------------------------------------------- + +static int ImplStrSearch( const sal_Char* pSearchStr, int nSearchLen, + const sal_Char* pStr, int nLen ) +{ + int nPos = 0; + while ( nPos+nSearchLen <= nLen ) + { + if ( strncmp( pStr+nPos, pSearchStr, nSearchLen ) == 0 ) + return 1; + nPos++; + } + + return 0; +} + +// ----------------------------------------------------------------------- + +static int ImplDbgFilter( const sal_Char* pFilter, const sal_Char* pMsg, + int bEmpty ) +{ + int nStrLen = strlen( pFilter ); + if ( !nStrLen ) + return bEmpty; + + int nMsgLen = strlen( pMsg ); + const sal_Char* pTok = pFilter; + int nTok = 0; + while ( pTok[nTok] ) + { + if ( pTok[nTok] == ';' ) + { + if ( nTok && ImplStrSearch( pTok, nTok, pMsg, nMsgLen ) ) + return TRUE; + + pTok += nTok+1; + nTok = 0; + } + + nTok++; + } + + if ( nTok && ImplStrSearch( pTok, nTok, pMsg, nMsgLen ) ) + return TRUE; + else + return FALSE; +} + +// ----------------------------------------------------------------------- + +extern "C" +void SAL_CALL dbg_printOslDebugMessage( const sal_Char * pszFileName, sal_Int32 nLine, const sal_Char * pszMessage ) +{ + DbgOut( pszMessage ? pszMessage : "assertion failed!", DBG_OUT_ERROR, pszFileName, (USHORT)nLine ); +} + +// ----------------------------------------------------------------------- + +static void DebugInit() +{ + bDbgImplInMain = TRUE; + ImplDbgInitLock(); + + DebugData* pData = GetDebugData(); + if( pData->aDbgData.bHookOSLAssert && ! pData->bOslIsHooked ) + { + pData->pOldDebugMessageFunc = osl_setDetailedDebugMessageFunc( &dbg_printOslDebugMessage ); + pData->bOslIsHooked = true; + } +} + +// ----------------------------------------------------------------------- + +static void DebugDeInit() +{ + DebugData* pData = GetDebugData(); + ULONG i; + ULONG nCount; + ULONG nOldOut; + + if( pData->bOslIsHooked ) + { + osl_setDetailedDebugMessageFunc( pData->pOldDebugMessageFunc ); + pData->bOslIsHooked = FALSE; + } + + // Statistik-Ausgaben immer in File + nOldOut = pData->aDbgData.nTraceOut; + pData->aDbgData.nTraceOut = DBG_OUT_FILE; + + // Xtor-Liste ausgeben + if ( pData->pXtorList && pData->pXtorList->Count() && + (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_REPORT) ) + { + DbgOutf( "------------------------------------------------------------------------------" ); + DbgOutf( "Object Report" ); + DbgOutf( "------------------------------------------------------------------------------" ); + DbgOutf( "%-27s : %-9s : %-9s : %-7s : %-3s : %-6s :", + "XTor-List", "Ctor", "Dtor", "MaxInst", "St.", "Diff." ); + DbgOutf( "----------------------------:-----------:-----------:---------:----:---------:" ); + for( i = 0, nCount = pData->pXtorList->Count(); i < nCount; i++ ) + { + XtorType* pXtorData = (XtorType*)pData->pXtorList->Get( i ); + if ( pXtorData->bTest ) + { + // Static-Objekte dazurechnen + pXtorData->nDtorCalls += pXtorData->nStatics; + if ( pXtorData->nStatics && (pXtorData->nDtorCalls > pXtorData->nCtorCalls) ) + pXtorData->nDtorCalls = pXtorData->nCtorCalls; + DbgOutf( "%-27s : %9lu : %9lu : %7lu : %3lu : %4lu %-1s :", + pXtorData->aName, pXtorData->nCtorCalls, pXtorData->nDtorCalls, + pXtorData->nMaxCount, pXtorData->nStatics, + pXtorData->nCtorCalls - pXtorData->nDtorCalls, + (pXtorData->nCtorCalls - pXtorData->nDtorCalls) ? "!" : " " ); + } + } + DbgOutf( "==============================================================================" ); + } + + // Aufraeumen + if ( pData->pXtorList ) + { + for( i = 0, nCount = pData->pXtorList->Count(); i < nCount; i++ ) + { + XtorType* pXtorData = (XtorType*)pData->pXtorList->Get( i ); + delete pXtorData; + } + delete pData->pXtorList; + pData->pXtorList = NULL; + } + + // Alles auf FALSE setzen, damit globale Variablen nicht das + // System zum Abstuerzen bringt. Dabei muessen aber die + // Memory-Flags erhalten bleiben, da sonst new/delete in globalen + // Variablen abstuerzen, da die Pointeranpassung dann nicht mehr richtig + // funktioniert + pData->aDbgData.nTraceOut = nOldOut; + pData->aDbgData.nTestFlags &= (DBG_TEST_MEM | DBG_TEST_PROFILING); + pData->aDbgPrintUserChannels.clear(); + pData->pDbgPrintTestTool = NULL; + pData->pDbgPrintShell = NULL; + pData->pDbgPrintWindow = NULL; + pData->pDbgPrintShell = NULL; + pData->pOldDebugMessageFunc = NULL; + ImplDbgDeInitLock(); +} + +// ----------------------------------------------------------------------- + +static void DebugGlobalDeInit() +{ + DebugData* pData = GetDebugData(); + ULONG i; + ULONG nCount; + ULONG nOldOut; + + // Statistik-Ausgaben immer in File + nOldOut = pData->aDbgData.nTraceOut; + pData->aDbgData.nTraceOut = DBG_OUT_FILE; + + // Profileliste ausgeben + if ( pData->pProfList && pData->pProfList->Count() ) + { + DbgOutf( "------------------------------------------------------------------------------" ); + DbgOutf( "Profiling Report" ); + DbgOutf( "------------------------------------------------------------------------------" ); + DbgOutf( "%-25s : %-9s : %-6s : %-6s : %-6s : %-9s :", + "Prof-List (ms)", "Time", "Min", "Max", "Ave", "Count" ); + DbgOutf( "--------------------------:-----------:--------:--------:--------:-----------:" ); + for( i = 0, nCount = pData->pProfList->Count(); i < nCount; i++ ) + { + ProfType* pProfData = (ProfType*)pData->pProfList->Get( i ); + ULONG nAve = pProfData->nTime / pProfData->nCount; + DbgOutf( "%-25s : %9lu : %6lu : %6lu : %6lu : %9lu :", + pProfData->aName, pProfData->nTime, + pProfData->nMinTime, pProfData->nMaxTime, nAve, + pProfData->nCount ); + } + DbgOutf( "==============================================================================" ); + } + + // Aufraeumen + if ( pData->pProfList ) + { + for( i = 0, nCount = pData->pProfList->Count(); i < nCount; i++ ) + { + ProfType* pProfData = (ProfType*)pData->pProfList->Get( i ); + delete pProfData; + } + delete pData->pProfList; + pData->pProfList = NULL; + } + +#ifdef SV_MEMMGR + DbgImpCheckMemoryDeInit(); +#endif + + // Profiling-Flags ausschalten + pData->aDbgData.nTraceOut = nOldOut; + pData->aDbgData.nTestFlags &= ~DBG_TEST_PROFILING; +} + +// ----------------------------------------------------------------------- + +void ImpDbgOutfBuf( sal_Char* pBuf, const sal_Char* pFStr, ... ) +{ + va_list pList; + + va_start( pList, pFStr ); + sal_Char aBuf[DBG_BUF_MAXLEN]; + vsprintf( aBuf, pFStr, pList ); + va_end( pList ); + + strcat( pBuf, aBuf ); + strcat( pBuf, "\n" ); +} + +// ----------------------------------------------------------------------- + +static void DebugXTorInfo( sal_Char* pBuf ) +{ + DebugData* pData = GetDebugData(); + ULONG i; + ULONG nCount; + + // Xtor-Liste ausgeben + if ( pData->pXtorList && pData->pXtorList->Count() && + (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_REPORT) ) + { + ImpDbgOutfBuf( pBuf, "------------------------------------------------------------------------------" ); + ImpDbgOutfBuf( pBuf, "Object Report" ); + ImpDbgOutfBuf( pBuf, "------------------------------------------------------------------------------" ); + ImpDbgOutfBuf( pBuf, "%-27s : %-9s : %-9s : %-7s : %-3s : %-6s :", + "XTor-List", "Ctor", "Dtor", "MaxInst", "St.", "Diff." ); + ImpDbgOutfBuf( pBuf, "----------------------------:-----------:-----------:---------:----:---------:" ); + for( i = 0, nCount = pData->pXtorList->Count(); i < nCount; i++ ) + { + XtorType* pXtorData = (XtorType*)pData->pXtorList->Get( i ); + if ( pXtorData->bTest ) + { + ImpDbgOutfBuf( pBuf, "%-27s : %9lu : %9lu : %7lu : %3lu : %6lu :", + pXtorData->aName, pXtorData->nCtorCalls, pXtorData->nDtorCalls, + pXtorData->nMaxCount, pXtorData->nStatics, + pXtorData->nCtorCalls - pXtorData->nDtorCalls ); + } + } + ImpDbgOutfBuf( pBuf, "==============================================================================" ); + ImpDbgOutfBuf( pBuf, "" ); + } +} + +// ----------------------------------------------------------------------- +BOOL ImplDbgFilterMessage( const sal_Char* pMsg ) +{ + DebugData* pData = GetDebugData(); + if ( !ImplDbgFilter( pData->aDbgData.aInclFilter, pMsg, TRUE ) ) + return TRUE; + if ( ImplDbgFilter( pData->aDbgData.aExclFilter, pMsg, FALSE ) ) + return TRUE; + return FALSE; +} + +// ----------------------------------------------------------------------- + +void* DbgFunc( USHORT nAction, void* pParam ) +{ + DebugData* pDebugData = ImplGetDebugData(); + + if ( nAction == DBG_FUNC_GETDATA ) + return (void*)&(pDebugData->aDbgData); + else if ( nAction == DBG_FUNC_GETPRINTMSGBOX ) + return (void*)(long)(pDebugData->pDbgPrintMsgBox); + else if ( nAction == DBG_FUNC_FILTERMESSAGE ) + if ( ImplDbgFilterMessage( (const sal_Char*) pParam ) ) + return (void*) -1; + else + return (void*) 0; // aka NULL + else + + { + switch ( nAction ) + { + case DBG_FUNC_DEBUGSTART: + DebugInit(); + break; + + case DBG_FUNC_DEBUGEND: + DebugDeInit(); + break; + + case DBG_FUNC_GLOBALDEBUGEND: + DebugGlobalDeInit(); + break; + + case DBG_FUNC_SETPRINTMSGBOX: + pDebugData->pDbgPrintMsgBox = (DbgPrintLine)(long)pParam; + break; + + case DBG_FUNC_SETPRINTWINDOW: + pDebugData->pDbgPrintWindow = (DbgPrintLine)(long)pParam; + break; + + case DBG_FUNC_SETPRINTSHELL: + pDebugData->pDbgPrintShell = (DbgPrintLine)(long)pParam; + break; + + case DBG_FUNC_SETPRINTTESTTOOL: + pDebugData->pDbgPrintTestTool = (DbgPrintLine)(long)pParam; + break; + + case DBG_FUNC_SAVEDATA: + { + const DbgData* pData = static_cast< const DbgData* >( pParam ); + + sal_Char aBuf[ 4096 ]; + DbgGetDbgFileName( aBuf, sizeof( aBuf ) ); + FILETYPE pIniFile = FileOpen( aBuf, "w" ); + if ( pIniFile == NULL ) + break; + + lcl_startSection( pIniFile, eOutput ); + lcl_writeConfigString( pIniFile, "log_file", pData->aDebugName ); + lcl_writeConfigBoolean( pIniFile, "overwrite", pData->bOverwrite ); + lcl_writeConfigString( pIniFile, "include", pData->aInclFilter ); + lcl_writeConfigString( pIniFile, "exclude", pData->aExclFilter ); + lcl_writeConfigString( pIniFile, "include_class", pData->aInclClassFilter ); + lcl_writeConfigString( pIniFile, "exclude_class", pData->aExclClassFilter ); + lcl_writeConfigOutChannel( pIniFile, "trace", pData->nTraceOut ); + lcl_writeConfigOutChannel( pIniFile, "warning", pData->nWarningOut ); + lcl_writeConfigOutChannel( pIniFile, "error", pData->nErrorOut ); + lcl_writeConfigBoolean( pIniFile, "oslhook", pData->bHookOSLAssert ); + + lcl_lineFeed( pIniFile ); + lcl_startSection( pIniFile, eMemory ); + lcl_writeConfigFlag( pIniFile, "initialize", pData->nTestFlags, DBG_TEST_MEM_INIT ); + lcl_writeConfigFlag( pIniFile, "overwrite", pData->nTestFlags, DBG_TEST_MEM_OVERWRITE ); + lcl_writeConfigFlag( pIniFile, "overwrite_free", pData->nTestFlags, DBG_TEST_MEM_OVERWRITEFREE ); + lcl_writeConfigFlag( pIniFile, "pointer", pData->nTestFlags, DBG_TEST_MEM_POINTER ); + lcl_writeConfigFlag( pIniFile, "report", pData->nTestFlags, DBG_TEST_MEM_REPORT ); + lcl_writeConfigFlag( pIniFile, "trace", pData->nTestFlags, DBG_TEST_MEM_TRACE ); + lcl_writeConfigFlag( pIniFile, "new_and_delete", pData->nTestFlags, DBG_TEST_MEM_NEWDEL ); + lcl_writeConfigFlag( pIniFile, "object_test", pData->nTestFlags, DBG_TEST_MEM_XTOR ); + lcl_writeConfigFlag( pIniFile, "sys_alloc", pData->nTestFlags, DBG_TEST_MEM_SYSALLOC ); + lcl_writeConfigFlag( pIniFile, "leak_report", pData->nTestFlags, DBG_TEST_MEM_LEAKREPORT ); + + lcl_lineFeed( pIniFile ); + lcl_writeHexByte( pIniFile, "init_byte", pData->bMemInit ); + lcl_writeHexByte( pIniFile, "bound_byte", pData->bMemBound ); + lcl_writeHexByte( pIniFile, "free_byte", pData->bMemFree ); + + lcl_lineFeed( pIniFile ); + lcl_startSection( pIniFile, eGUI ); + lcl_writeConfigString( pIniFile, "debug_window_state", pData->aDbgWinState ); + + lcl_lineFeed( pIniFile ); + lcl_startSection( pIniFile, eObjects ); + lcl_writeConfigFlag( pIniFile, "check_this", pData->nTestFlags, DBG_TEST_XTOR_THIS ); + lcl_writeConfigFlag( pIniFile, "check_function", pData->nTestFlags, DBG_TEST_XTOR_FUNC ); + lcl_writeConfigFlag( pIniFile, "check_exit", pData->nTestFlags, DBG_TEST_XTOR_EXIT ); + lcl_writeConfigFlag( pIniFile, "generate_report", pData->nTestFlags, DBG_TEST_XTOR_REPORT ); + lcl_writeConfigFlag( pIniFile, "trace", pData->nTestFlags, DBG_TEST_XTOR_TRACE ); + + lcl_lineFeed( pIniFile ); + lcl_startSection( pIniFile, eTest ); + lcl_writeConfigFlag( pIniFile, "profiling", pData->nTestFlags, DBG_TEST_PROFILING ); + lcl_writeConfigFlag( pIniFile, "resources", pData->nTestFlags, DBG_TEST_RESOURCE ); + lcl_writeConfigFlag( pIniFile, "dialog", pData->nTestFlags, DBG_TEST_DIALOG ); + lcl_writeConfigFlag( pIniFile, "bold_app_font", pData->nTestFlags, DBG_TEST_BOLDAPPFONT ); + + FileClose( pIniFile ); + } + break; + + case DBG_FUNC_MEMTEST: +#ifdef SV_MEMMGR + DbgImpCheckMemory( pParam ); +#endif + break; + + case DBG_FUNC_XTORINFO: + DebugXTorInfo( (sal_Char*)pParam ); + break; + + case DBG_FUNC_MEMINFO: +#ifdef SV_MEMMGR + DbgImpMemoryInfo( (sal_Char*)pParam ); +#endif + break; + + case DBG_FUNC_COREDUMP: + ImplCoreDump(); + break; + + case DBG_FUNC_ALLERROROUT: + return (void*)(ULONG)TRUE; + + case DBG_FUNC_SETTESTSOLARMUTEX: + pDebugData->pDbgTestSolarMutex = (DbgTestSolarMutexProc)(long)pParam; + break; + + case DBG_FUNC_TESTSOLARMUTEX: + if ( pDebugData->pDbgTestSolarMutex ) + pDebugData->pDbgTestSolarMutex(); + break; + + case DBG_FUNC_PRINTFILE: + ImplDbgPrintFile( (const sal_Char*)pParam ); + break; + case DBG_FUNC_UPDATEOSLHOOK: + { + const DbgData* pData = static_cast< const DbgData* >( pParam ); + pDebugData->aDbgData.bHookOSLAssert = pData->bHookOSLAssert; + if( pDebugData->bOslIsHooked && ! pData->bHookOSLAssert ) + { + osl_setDetailedDebugMessageFunc( pDebugData->pOldDebugMessageFunc ); + pDebugData->bOslIsHooked = FALSE; + } + else if( ! pDebugData->bOslIsHooked && pData->bHookOSLAssert ) + { + pDebugData->pOldDebugMessageFunc = osl_setDetailedDebugMessageFunc( &dbg_printOslDebugMessage ); + pDebugData->bOslIsHooked = TRUE; + } + } + break; + } + + return NULL; + } +} + +// ----------------------------------------------------------------------- + +DbgChannelId DbgRegisterUserChannel( DbgPrintLine pProc ) +{ + DebugData* pData = ImplGetDebugData(); + pData->aDbgPrintUserChannels.push_back( pProc ); + return (DbgChannelId)( pData->aDbgPrintUserChannels.size() - 1 + DBG_OUT_USER_CHANNEL_0 ); +} + +// ----------------------------------------------------------------------- + +void DbgProf( USHORT nAction, DbgDataType* pDbgData ) +{ + // Ueberhaupt Profiling-Test an + DebugData* pData = ImplGetDebugData(); + + if ( !(pData->aDbgData.nTestFlags & DBG_TEST_PROFILING) ) + return; + + sal_Char aBuf[DBG_BUF_MAXLEN]; + ProfType* pProfData = (ProfType*)pDbgData->pData; + ULONG nTime; + if ( (nAction != DBG_PROF_START) && !pProfData ) + { + strcpy( aBuf, DbgError_ProfEnd1 ); + strcat( aBuf, pDbgData->pName ); + DbgError( aBuf ); + return; + } + + switch ( nAction ) + { + case DBG_PROF_START: + if ( !pDbgData->pData ) + { + pDbgData->pData = (void*)new ProfType; + pProfData = (ProfType*)pDbgData->pData; + strncpy( pProfData->aName, pDbgData->pName, DBG_MAXNAME ); + pProfData->aName[DBG_MAXNAME] = '\0'; + pProfData->nCount = 0; + pProfData->nTime = 0; + pProfData->nMinTime = 0xFFFFFFFF; + pProfData->nMaxTime = 0; + pProfData->nStart = 0xFFFFFFFF; + pProfData->nContinueTime = 0; + pProfData->nContinueStart = 0xFFFFFFFF; + pData->pProfList->Add( (void*)pProfData ); + } + + if ( pProfData->nStart == 0xFFFFFFFF ) + { + pProfData->nStart = ImplGetPerfTime(); + pProfData->nCount++; + } + break; + + case DBG_PROF_STOP: + nTime = ImplGetPerfTime(); + + if ( pProfData->nStart == 0xFFFFFFFF ) + { + DbgError( DbgError_ProfEnd1 ); + return; + } + + if ( pProfData->nContinueStart != 0xFFFFFFFF ) + { + pProfData->nContinueTime += ImplGetPerfTime() - pProfData->nContinueStart; + pProfData->nContinueStart = 0xFFFFFFFF; + } + + nTime -= pProfData->nStart; + nTime -= pProfData->nContinueTime; + + if ( nTime < pProfData->nMinTime ) + pProfData->nMinTime = nTime; + + if ( nTime > pProfData->nMaxTime ) + pProfData->nMaxTime = nTime; + + pProfData->nTime += nTime; + + pProfData->nStart = 0xFFFFFFFF; + pProfData->nContinueTime = 0; + pProfData->nContinueStart = 0xFFFFFFFF; + break; + + case DBG_PROF_CONTINUE: + if ( pProfData->nContinueStart != 0xFFFFFFFF ) + { + pProfData->nContinueTime += ImplGetPerfTime() - pProfData->nContinueStart; + pProfData->nContinueStart = 0xFFFFFFFF; + } + break; + + case DBG_PROF_PAUSE: + if ( pProfData->nContinueStart == 0xFFFFFFFF ) + pProfData->nContinueStart = ImplGetPerfTime(); + break; + } +} + +// ----------------------------------------------------------------------- + +void DbgXtor( DbgDataType* pDbgData, USHORT nAction, const void* pThis, + DbgUsr fDbgUsr ) +{ + DebugData* pData = ImplGetDebugData(); + + // Verbindung zu Debug-Memory-Manager testen +#ifdef SV_MEMMGR + if ( pData->aDbgData.nTestFlags & DBG_TEST_MEM_XTOR ) + DbgImpCheckMemory(); +#endif + + // Schnell-Test + if ( !(pData->aDbgData.nTestFlags & DBG_TEST_XTOR) ) + return; + + XtorType* pXtorData = (XtorType*)pDbgData->pData; + if ( !pXtorData ) + { + pDbgData->pData = (void*)new XtorType; + pXtorData = (XtorType*)pDbgData->pData; + strncpy( pXtorData->aName, pDbgData->pName, DBG_MAXNAME ); + pXtorData->aName[DBG_MAXNAME] = '\0'; + pXtorData->nCtorCalls = 0; + pXtorData->nDtorCalls = 0; + pXtorData->nMaxCount = 0; + pXtorData->nStatics = 0; + pXtorData->bTest = TRUE; + pData->pXtorList->Add( (void*)pXtorData ); + + if ( !ImplDbgFilter( pData->aDbgData.aInclClassFilter, pXtorData->aName, TRUE ) ) + pXtorData->bTest = FALSE; + if ( ImplDbgFilter( pData->aDbgData.aExclClassFilter, pXtorData->aName, FALSE ) ) + pXtorData->bTest = FALSE; + } + if ( !pXtorData->bTest ) + return; + + sal_Char aBuf[DBG_BUF_MAXLEN]; + USHORT nAct = nAction & ~DBG_XTOR_DTOROBJ; + + // Trace (Enter) + if ( (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_TRACE) && + !(nAction & DBG_XTOR_DTOROBJ) ) + { + if ( nAct != DBG_XTOR_CHKOBJ ) + { + if ( nAct == DBG_XTOR_CTOR ) + strcpy( aBuf, DbgTrace_EnterCtor ); + else if ( nAct == DBG_XTOR_DTOR ) + strcpy( aBuf, DbgTrace_EnterDtor ); + else + strcpy( aBuf, DbgTrace_EnterMeth ); + strcat( aBuf, pDbgData->pName ); + DbgTrace( aBuf ); + } + } + + // Sind noch Xtor-Tests als Trace an + if ( pData->aDbgData.nTestFlags & DBG_TEST_XTOR_EXTRA ) + { + // DBG_CTOR-Aufruf vor allen anderen DBG_XTOR-Aufrufen + if ( ((nAction & ~DBG_XTOR_DTOROBJ) != DBG_XTOR_CTOR) && !pDbgData->pData ) + { + strcpy( aBuf, DbgError_Xtor1 ); + strcat( aBuf, pDbgData->pName ); + DbgError( aBuf ); + return; + } + + // Testen, ob This-Pointer gueltig + if ( pData->aDbgData.nTestFlags & DBG_TEST_XTOR_THIS ) + { + if ( (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_EXIT) || + !(nAction & DBG_XTOR_DTOROBJ) ) + { + // This-Pointer == NULL + if ( !pThis ) + { + strcpy( aBuf, DbgError_CtorDtor1 ); + strcat( aBuf, pDbgData->pName ); + DbgError( aBuf ); + return; + } + + if ( (nAction & ~DBG_XTOR_DTOROBJ) != DBG_XTOR_CTOR ) + { + if ( !pXtorData->aThisList.IsIn( pThis ) ) + { + sprintf( aBuf, DbgError_CtorDtor2, pThis ); + strcat( aBuf, pDbgData->pName ); + DbgError( aBuf ); + } + } + } + } + + // Function-Test durchfuehren und Verwaltungsdaten updaten + const sal_Char* pMsg = NULL; + switch ( nAction & ~DBG_XTOR_DTOROBJ ) + { + case DBG_XTOR_CTOR: + if ( nAction & DBG_XTOR_DTOROBJ ) + { + if ( fDbgUsr && + (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_EXIT) && + (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_FUNC) ) + pMsg = fDbgUsr( pThis ); + } + else + { + pXtorData->nCtorCalls++; + if ( !bDbgImplInMain ) + pXtorData->nStatics++; + if ( (pXtorData->nCtorCalls-pXtorData->nDtorCalls) > pXtorData->nMaxCount ) + pXtorData->nMaxCount = pXtorData->nCtorCalls - pXtorData->nDtorCalls; + + if ( pData->aDbgData.nTestFlags & DBG_TEST_XTOR_THIS ) + pXtorData->aThisList.Add( pThis ); + } + break; + + case DBG_XTOR_DTOR: + if ( nAction & DBG_XTOR_DTOROBJ ) + { + pXtorData->nDtorCalls++; + if ( pData->aDbgData.nTestFlags & DBG_TEST_XTOR_THIS ) + pXtorData->aThisList.Remove( pThis ); + } + else + { + if ( fDbgUsr && + (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_FUNC) ) + pMsg = fDbgUsr( pThis ); + } + break; + + case DBG_XTOR_CHKTHIS: + case DBG_XTOR_CHKOBJ: + if ( nAction & DBG_XTOR_DTOROBJ ) + { + if ( fDbgUsr && + (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_EXIT) && + (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_FUNC) ) + pMsg = fDbgUsr( pThis ); + } + else + { + if ( fDbgUsr && + (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_FUNC) ) + pMsg = fDbgUsr( pThis ); + } + break; + } + + // Gegebenenfalls Fehlermeldung ausgeben + if ( pMsg ) + { + sprintf( aBuf, DbgError_CtorDtor3, pThis ); + strcat( aBuf, pDbgData->pName ); + strcat( aBuf, ": \n" ); + strcat( aBuf, pMsg ); + DbgError( aBuf ); + } + } + + // Trace (Leave) + if ( (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_TRACE) && + (nAction & DBG_XTOR_DTOROBJ) ) + { + if ( nAct != DBG_XTOR_CHKOBJ ) + { + if ( nAct == DBG_XTOR_CTOR ) + strcpy( aBuf, DbgTrace_LeaveCtor ); + else if ( nAct == DBG_XTOR_DTOR ) + strcpy( aBuf, DbgTrace_LeaveDtor ); + else + strcpy( aBuf, DbgTrace_LeaveMeth ); + strcat( aBuf, pDbgData->pName ); + DbgTrace( aBuf ); + } + } +} + +// ----------------------------------------------------------------------- + +void DbgOut( const sal_Char* pMsg, USHORT nDbgOut, const sal_Char* pFile, USHORT nLine ) +{ + static BOOL bIn = FALSE; + if ( bIn ) + return; + bIn = TRUE; + + DebugData* pData = GetDebugData(); + sal_Char const * pStr; + ULONG nOut; + int nBufLen = 0; + + if ( nDbgOut == DBG_OUT_ERROR ) + { + nOut = pData->aDbgData.nErrorOut; + pStr = "Error: "; + if ( pData->aDbgData.nErrorOut == DBG_OUT_FILE ) + DbgDebugBeep(); + } + else if ( nDbgOut == DBG_OUT_WARNING ) + { + nOut = pData->aDbgData.nWarningOut; + pStr = "Warning: "; + } + else + { + nOut = pData->aDbgData.nTraceOut; + pStr = NULL; + } + + if ( nOut == DBG_OUT_NULL ) + { + bIn = FALSE; + return; + } + + if ( ImplDbgFilterMessage( pMsg ) ) + { + bIn = FALSE; + return; + } + + ImplDbgLock(); + + sal_Char aBufOut[DBG_BUF_MAXLEN]; + if ( pStr ) + { + strcpy( aBufOut, pStr ); + nBufLen = strlen( pStr ); + } + else + aBufOut[0] = '\0'; + + int nMsgLen = strlen( pMsg ); + if ( nBufLen+nMsgLen > DBG_BUF_MAXLEN ) + { + int nCopyLen = DBG_BUF_MAXLEN-nBufLen-3; + strncpy( &(aBufOut[nBufLen]), pMsg, nCopyLen ); + strcpy( &(aBufOut[nBufLen+nCopyLen]), "..." ); + } + else + strcpy( &(aBufOut[nBufLen]), pMsg ); + + if ( pFile && nLine && (nBufLen+nMsgLen < DBG_BUF_MAXLEN) ) + { + if ( nOut == DBG_OUT_MSGBOX ) + strcat( aBufOut, "\n" ); + else + strcat( aBufOut, " " ); + strcat( aBufOut, "From File " ); + strcat( aBufOut, pFile ); + strcat( aBufOut, " at Line " ); + + // Line in String umwandeln und dranhaengen + sal_Char aLine[9]; + sal_Char* pLine = &aLine[7]; + USHORT i; + memset( aLine, 0, sizeof( aLine ) ); + do + { + i = nLine % 10; + pLine--; + *(pLine) = (sal_Char)i + 48; + nLine /= 10; + } + while ( nLine ); + strcat( aBufOut, pLine ); + } + + if ( ( nOut >= DBG_OUT_USER_CHANNEL_0 ) && ( nOut - DBG_OUT_USER_CHANNEL_0 < pData->aDbgPrintUserChannels.size() ) ) + { + DbgPrintLine pPrinter = pData->aDbgPrintUserChannels[ nOut - DBG_OUT_USER_CHANNEL_0 ]; + if ( pPrinter ) + pPrinter( aBufOut ); + else + nOut = DBG_OUT_DEBUGGER; + } + + if ( nOut == DBG_OUT_COREDUMP ) + { + if ( !ImplCoreDump() ) + nOut = DBG_OUT_DEBUGGER; + } + + if ( nOut == DBG_OUT_DEBUGGER ) + { + if ( !ImplActivateDebugger( aBufOut ) ) + nOut = DBG_OUT_TESTTOOL; + } + + if ( nOut == DBG_OUT_TESTTOOL ) + { + if ( pData->pDbgPrintTestTool ) + pData->pDbgPrintTestTool( aBufOut ); + else + nOut = DBG_OUT_MSGBOX; + } + + if ( nOut == DBG_OUT_MSGBOX ) + { + if ( pData->pDbgPrintMsgBox ) + pData->pDbgPrintMsgBox( aBufOut ); + else + nOut = DBG_OUT_SHELL; + } + + if ( nOut == DBG_OUT_SHELL ) + { + if ( pData->pDbgPrintShell ) + pData->pDbgPrintShell( aBufOut ); + else + nOut = DBG_OUT_WINDOW; + } + + if ( nOut == DBG_OUT_WINDOW ) + { + if ( pData->pDbgPrintWindow ) + pData->pDbgPrintWindow( aBufOut ); + else + nOut = DBG_OUT_FILE; + } + + if ( nOut == DBG_OUT_FILE ) + ImplDbgPrintFile( aBufOut ); + + ImplDbgUnlock(); + + bIn = FALSE; +} + +// ----------------------------------------------------------------------- + +void DbgOutTypef( USHORT nDbgOut, const sal_Char* pFStr, ... ) +{ + va_list pList; + + va_start( pList, pFStr ); + sal_Char aBuf[DBG_BUF_MAXLEN]; + vsprintf( aBuf, pFStr, pList ); + va_end( pList ); + + DbgOut( aBuf, nDbgOut ); +} + +// ----------------------------------------------------------------------- + +void DbgOutf( const sal_Char* pFStr, ... ) +{ + va_list pList; + + va_start( pList, pFStr ); + sal_Char aBuf[DBG_BUF_MAXLEN]; + vsprintf( aBuf, pFStr, pList ); + va_end( pList ); + + DbgOut( aBuf ); +} + +// ======================================================================= + +#else + +void* DbgFunc( USHORT, void* ) { return NULL; } + +void DbgProf( USHORT, DbgDataType* ) {} +void DbgXtor( DbgDataType*, USHORT, const void*, DbgUsr ) {} + +void DbgOut( const sal_Char*, USHORT, const sal_Char*, USHORT ) {} +void DbgOutTypef( USHORT, const sal_Char*, ... ) {} +void DbgOutf( const sal_Char*, ... ) {} + +#endif diff --git a/tools/source/debug/makefile.mk b/tools/source/debug/makefile.mk new file mode 100644 index 000000000000..a818ba3d9168 --- /dev/null +++ b/tools/source/debug/makefile.mk @@ -0,0 +1,57 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.7 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=debug + +ENABLE_EXCEPTIONS := TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +CXXFILES= debug.cxx \ + stcktree.cxx + +SLOFILES= $(SLO)$/debug.obj \ + $(SLO)$/stcktree.obj + +OBJFILES= $(OBJ)$/debug.obj \ + $(OBJ)$/stcktree.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/tools/source/debug/stcktree.cxx b/tools/source/debug/stcktree.cxx new file mode 100644 index 000000000000..7d18c2b5b9f7 --- /dev/null +++ b/tools/source/debug/stcktree.cxx @@ -0,0 +1,323 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: stcktree.cxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <string.h> + +#include <tools/debug.hxx> + +// ----------------------------------------------------------------------- + +#if defined( DBG_UTIL ) && defined( WNT ) && defined( INTEL ) + +struct ImpDbgStackTree +{ + ImpDbgStackTree* pLeft_; + ImpDbgStackTree* pRight_; + ImpDbgStackTree* pCaller_; + ImpDbgStackTree* pSub_; + ULONG nIP_; + ULONG nBytesLeak_; + ULONG nBytesPeak_; + ULONG nBytes_; + ULONG nCountLeak_; + ULONG nCountPeak_; + ULONG nCount_; + ULONG nMax_; + ULONG nMin_; + + ImpDbgStackTree( ImpDbgStackTree* pSub, ULONG nIP ); + ~ImpDbgStackTree(); + + ImpDbgStackTree* Add( ULONG nAlloc, ULONG* pBP, ULONG nIP ); + void Print( int nLevel, ULONG nCount, ULONG nCountLeak ); + void Print( int nLevel ); +}; + +static ImpDbgStackTree* pImpDbgStackTreeRoot = NULL; +static ULONG* pImpDbgStackTreeBP = NULL; +static ULONG nImpDbgStackTreeMain = 0; +static int nImpDbgStackTreeSem = 0; + +// ----------------------------------------------------------------------- + +ImpDbgStackTree::ImpDbgStackTree( ImpDbgStackTree* pSub, ULONG nIP ) +{ + pSub_ = pSub; + nIP_ = nIP; + pLeft_ = pRight_ = pCaller_ = NULL; + nBytesLeak_ = nBytesPeak_ = nBytes_ = 0; + nCountLeak_ = nCountPeak_ = nCount_ = 0; +} + +// ----------------------------------------------------------------------- + +ImpDbgStackTree::~ImpDbgStackTree() +{ + if ( pLeft_ ) + delete pLeft_; + if ( pRight_ ) + delete pRight_; + if ( pCaller_ ) + delete pCaller_; +} + +// ----------------------------------------------------------------------- + +void ImpDbgStackTree::Print( int nLevel, ULONG nCount, ULONG nCountLeak ) +{ + if ( pLeft_ ) + pLeft_->Print( nLevel, nCount, nCountLeak ); + + if ( nCount_ >= nCount && nCountLeak_ >= nCountLeak ) + { + if ( nMax_ == nMin_ ) + { + ULONG nTemp = nCountLeak_ * nMin_; + DbgOutf( "%*c%08lx Count=%lu/%lu/%lu Bytes=%lu/%lu/%lu Size=%lu", + nLevel, ' ', nIP_, + nCount_, nCountPeak_, nCountLeak_, + nBytes_, nBytesPeak_, nTemp, + nMin_ ); + } + else + { + DbgOutf( "%*c%08lx Count=%lu/%lu/%lu Bytes=%lu/%lu/%lu Size=%lu-%lu", + nLevel, ' ', nIP_, + nCount_, nCountPeak_, nCountLeak_, + nBytes_, nBytesPeak_, nBytesLeak_, + nMin_, nMax_ ); + } + + if ( pCaller_ ) + if( nLevel > 3 && nCountLeak ) + pCaller_->Print( nLevel + 1, nCount, 1 ); + else + pCaller_->Print( nLevel + 1, nCount, nCountLeak ); + } + + if ( pRight_ ) + pRight_->Print( nLevel, nCount, nCountLeak ); +} + +// ----------------------------------------------------------------------- + +void ImpDbgStackTree::Print( int nLevel ) +{ + if ( pSub_ ) + pSub_->Print( nLevel + 1 ); + DbgOutf( "%*c%08lx", nLevel, ' ',nIP_ ); +} + +// ----------------------------------------------------------------------- + +ImpDbgStackTree* ImpDbgStackTree::Add( ULONG nAlloc, ULONG *pBP, ULONG nIP ) +{ + if ( nIP < nIP_ ) + { + if ( !pLeft_ ) + pLeft_ = new ImpDbgStackTree( pSub_, nIP ); + return pLeft_->Add( nAlloc, pBP, nIP ); + } + if ( nIP > nIP_ ) + { + if ( !pRight_ ) + pRight_ = new ImpDbgStackTree( pSub_, nIP ); + return pRight_->Add( nAlloc, pBP, nIP ); + } + + nCount_++; + nCountLeak_++; + if ( nCountLeak_ > nCountPeak_ ) + nCountPeak_ = nCountLeak_; + nBytes_ += nAlloc; + nBytesLeak_ += nAlloc; + if ( nBytesLeak_ > nBytesPeak_ ) + nBytesPeak_ = nBytesLeak_; + if ( nCount_ == 1 ) + nMax_ = nMin_ = nAlloc; + else if ( nMax_ < nAlloc ) + nMax_ = nAlloc; + else if ( nMin_ > nAlloc ) + nMin_ = nAlloc; + + if ( !(pBP[0] & 3) && (ULONG)pBP < pBP[0] && pBP[0] < (ULONG)pImpDbgStackTreeBP ) + { + pBP = (ULONG*)pBP[0]; + nIP = pBP[1]; + if ( 0x01100000 <= nIP && nIP < 0x20000000 && nIP != nImpDbgStackTreeMain ) + { + if ( !pCaller_ ) + pCaller_ = new ImpDbgStackTree( this, nIP ); + return pCaller_->Add( nAlloc, pBP, nIP ); + } + else + return this; + } + + return this; +} + +// ----------------------------------------------------------------------- + +void DbgStartStackTree() +{ + if ( !nImpDbgStackTreeMain ) + { + ULONG* pBP; + __asm mov pBP, ebp; + + pImpDbgStackTreeBP = (ULONG*)pBP[0]; + nImpDbgStackTreeMain = pImpDbgStackTreeBP[1]; + } +} + +// ----------------------------------------------------------------------- + +void DbgEndStackTree() +{ + if ( nImpDbgStackTreeMain ) + { + nImpDbgStackTreeMain = 0; + if ( pImpDbgStackTreeRoot ) + { + // Ausgaben ins File umleiten + DbgData* pData = DbgGetData(); + ULONG nOldOut = pData->nTraceOut; + pData->nTraceOut = DBG_OUT_FILE; + + DbgOutf( "Leak-Report" ); + DbgOutf( "===========" ); + DbgOutf( "Mem-StackTree:" ); + DbgOutf( "{" ); + pImpDbgStackTreeRoot->Print( 1, 1, 2 ); + DbgOutf( "}" ); + + DbgOutf( "Alloc-Report" ); + DbgOutf( "===========" ); + DbgOutf( "Mem-StackTree:" ); + DbgOutf( "{" ); + pImpDbgStackTreeRoot->Print( 1, 1000, 0 ); // ??? + DbgOutf( "}" ); + + pData->nTraceOut = nOldOut; + + nImpDbgStackTreeSem++; + delete pImpDbgStackTreeRoot; + pImpDbgStackTreeRoot = NULL; + nImpDbgStackTreeSem--; + } + } +} + +// ----------------------------------------------------------------------- + +void* DbgGetStackTree( ULONG nAlloc ) +{ + ImpDbgStackTree* pReturn = NULL; + + if ( nImpDbgStackTreeMain && !nImpDbgStackTreeSem ) + { + nImpDbgStackTreeSem++; + + ULONG* pBP; + __asm mov pBP, ebp; + + ULONG nIP = pBP[1]; + if ( !pImpDbgStackTreeRoot ) + pImpDbgStackTreeRoot = new ImpDbgStackTree( NULL, nIP ); + pReturn = pImpDbgStackTreeRoot->Add( nAlloc, pBP, nIP ); + nImpDbgStackTreeSem--; + } + + return pReturn; +} + +// ----------------------------------------------------------------------- + +void DbgFreeStackTree( void* pVoid, ULONG nAlloc ) +{ + ImpDbgStackTree* p = (ImpDbgStackTree*)pVoid; + + if ( p && nImpDbgStackTreeMain && !nImpDbgStackTreeSem ) + { + if ( nAlloc < p->nMin_ ) + nAlloc = p->nMin_; + + p->nCountLeak_--; + p->nBytesLeak_ -= nAlloc; + + if ( p->nMax_ && 0xFFFFFFFF / p->nMax_ > p->nCountLeak_ ) + { + if ( p->nBytesLeak_ > p->nMax_ * p->nCountLeak_ ) + { + nAlloc += p->nBytesLeak_ - p->nMax_ * p->nCountLeak_; + p->nBytesLeak_ = p->nMax_ * p->nCountLeak_; + } + } + + if ( p->pSub_ ) + DbgFreeStackTree( (void*)(p->pSub_), nAlloc ); + } +} + +// ----------------------------------------------------------------------- + +void DbgPrintStackTree( void* pVoid ) +{ + ImpDbgStackTree* p = (ImpDbgStackTree*)pVoid; + + if ( p && nImpDbgStackTreeMain && !nImpDbgStackTreeSem ) + { + // Ausgaben ins File umleiten + DbgData* pData = DbgGetData(); + ULONG nOldOut = pData->nTraceOut; + pData->nTraceOut = DBG_OUT_FILE; + + DbgOutf( "Mem-StackTree:" ); + DbgOutf( "{" ); + p->Print( 1 ); + DbgOutf( "}" ); + + pData->nTraceOut = nOldOut; + } +} + +#else + +void DbgStartStackTree() {} +void DbgEndStackTree() {} +void* DbgGetStackTree( ULONG ) { return NULL; } +void DbgFreeStackTree( void*, ULONG ) {} +void DbgPrintStackTree( void* ) {} + +#endif diff --git a/tools/source/fsys/comdep.cxx b/tools/source/fsys/comdep.cxx new file mode 100644 index 000000000000..3975b386cf52 --- /dev/null +++ b/tools/source/fsys/comdep.cxx @@ -0,0 +1,47 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: comdep.cxx,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include "comdep.hxx" +#include <tools/debug.hxx> +#include <tools/list.hxx> +#include <tools/fsys.hxx> + +DBG_NAMEEX( DirEntry ) + +#if defined UNX +#include "unx.cxx" +#elif defined WNT +#include "wntmsc.cxx" +#elif defined OS2 +#include "os2.cxx" +#endif diff --git a/tools/source/fsys/comdep.hxx b/tools/source/fsys/comdep.hxx new file mode 100644 index 000000000000..2cfea44d7392 --- /dev/null +++ b/tools/source/fsys/comdep.hxx @@ -0,0 +1,159 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: comdep.hxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _COMDEP_HXX +#define _COMDEP_HXX + +#include <tools/fsys.hxx> + +#define ACCESSDELIM(e) ( (e == FSYS_STYLE_MAC) ? ":" : \ + ( ( e == FSYS_STYLE_VFAT || e == FSYS_STYLE_HPFS || \ + e == FSYS_STYLE_FAT ) || e == FSYS_STYLE_NTFS ) \ + ? "\\" : "/" ) +#define ACCESSDELIM_C(e)(char)\ + ( (e == FSYS_STYLE_MAC) ? ':' : \ + ( ( e == FSYS_STYLE_VFAT || e == FSYS_STYLE_HPFS || \ + e == FSYS_STYLE_FAT ) || e == FSYS_STYLE_NTFS ) \ + ? '\\' : '/' ) +#define SEARCHDELIM(e) ( (e == FSYS_STYLE_SYSV || e == FSYS_STYLE_BSD) ? ":" \ + : ";" ) +#define SEARCHDELIM_C(e)(char)\ + ( (e == FSYS_STYLE_SYSV || e == FSYS_STYLE_BSD) ? ':' \ + : ';' ) +#define ACTPARENT(e) ( (e == FSYS_STYLE_MAC) ? ":" : ".." ) +#define ACTCURRENT(e) ( (e == FSYS_STYLE_MAC) ? "" : "." ) + +#if defined UNX +#include "unx.hxx" +#elif defined WNT +#include "wntmsc.hxx" +#elif defined OS2 +#include "os2.hxx" +#endif + +//-------------------------------------------------------------------- + +#ifndef LINUX +DIR *opendir( const char* pPfad ); +dirent *readdir( DIR *pDir ); +int closedir( DIR *pDir ); +char *volumeid( const char* pPfad ); +#endif + +//-------------------------------------------------------------------- + +struct DirReader_Impl +{ + Dir* pDir; + DIR* pDosDir; + dirent* pDosEntry; + DirEntry* pParent; + String aPath; + ByteString aBypass; + BOOL bReady; + BOOL bInUse; + + DirReader_Impl( Dir &rDir ) + : pDir( &rDir ), + pDosEntry( 0 ), + pParent( 0 ), + aPath( GUI2FSYS(rDir.GetFull()) ), + bReady ( FALSE ), + bInUse( FALSE ) + { +#ifndef BOOTSTRAP + // Redirection + FSysRedirector::DoRedirect( aPath ); +#endif + + // nur den String der Memer-Var nehmen! + +#if defined(UNX) || defined(OS2) //for further exlpanation see DirReader_Impl::Read() in unx.cxx + pDosDir = NULL; +#else + aBypass = ByteString(aPath, osl_getThreadTextEncoding()); + pDosDir = opendir( (char*) aBypass.GetBuffer() ); +#endif + + // Parent f"ur die neuen DirEntries ermitteln + pParent = pDir->GetFlag() == FSYS_FLAG_NORMAL || + pDir->GetFlag() == FSYS_FLAG_ABSROOT + ? pDir + : pDir->GetParent(); + + } + + ~DirReader_Impl() + { if( pDosDir ) closedir( pDosDir ); } + + // die folgenden sind systemabh"angig implementiert + USHORT Init(); // initialisiert, liest ggf. devices + USHORT Read(); // liest 1 Eintrag, F2ugt ein falls ok +}; + +//-------------------------------------------------------------------- + +struct FileCopier_Impl +{ + FSysAction nActions; // was zu tun ist (Copy/Move/recur) + Link aErrorLink; // bei Fehlern zu rufen + ErrCode eErr; // aktueller Fehlercode im Error-Handler + const DirEntry* pErrSource; // fuer Error-Handler falls Source-Fehler + const DirEntry* pErrTarget; // fuer Error-Handler falls Target-Fehler + + FileCopier_Impl() + : nActions( 0 ), eErr( 0 ), + pErrSource( 0 ), pErrTarget( 0 ) + {} + FileCopier_Impl( const FileCopier_Impl &rOrig ) + : nActions( rOrig.nActions ), eErr( 0 ), + pErrSource( 0 ), pErrTarget( 0 ) + {} + + FileCopier_Impl& operator=( const FileCopier_Impl &rOrig ) + { + nActions = rOrig.nActions; + eErr = 0; pErrSource = 0; pErrTarget = 0; + return *this; + } +}; + +//-------------------------------------------------------------------- + +#if defined WNT || defined OS2 +BOOL IsRedirectable_Impl( const ByteString &rPath ); +#else +#define IsRedirectable_Impl( rPath ) TRUE +#endif + +//-------------------------------------------------------------------- + + +#endif diff --git a/tools/source/fsys/dirent.cxx b/tools/source/fsys/dirent.cxx new file mode 100644 index 000000000000..3a87ecc35250 --- /dev/null +++ b/tools/source/fsys/dirent.cxx @@ -0,0 +1,3216 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dirent.cxx,v $ + * $Revision: 1.27 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + + +#if !defined UNX +#include <io.h> +#include <process.h> +#endif + +#if defined(UNX) || defined(OS2) +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#endif + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <tools/debug.hxx> +#include <tools/list.hxx> +#include "comdep.hxx" +#include <tools/fsys.hxx> +#define _TOOLS_HXX +#include <tools/urlobj.hxx> + +#ifdef UNX +#define _MAX_PATH 260 +#endif +#include <tools/stream.hxx> + +#ifndef _VOS_MUTEX_HXX +#include <vos/mutex.hxx> +#endif + +#include <osl/file.hxx> +#include <rtl/instance.hxx> + + +using namespace osl; +using namespace rtl; + +int ApiRet2ToSolarError_Impl( int nApiRet ); + +//-------------------------------------------------------------------- +int Sys2SolarError_Impl( int nSysErr ) +{ + switch ( nSysErr ) + { +#ifdef WNT + case NO_ERROR: return ERRCODE_NONE; + case ERROR_INVALID_FUNCTION: return ERRCODE_IO_GENERAL; + case ERROR_FILE_NOT_FOUND: return ERRCODE_IO_NOTEXISTS; + case ERROR_PATH_NOT_FOUND: return ERRCODE_IO_NOTEXISTSPATH; + case ERROR_TOO_MANY_OPEN_FILES: return ERRCODE_IO_TOOMANYOPENFILES; + case ERROR_ACCESS_DENIED: return ERRCODE_IO_ACCESSDENIED; + case ERROR_INVALID_HANDLE: return ERRCODE_IO_GENERAL; + case ERROR_NOT_ENOUGH_MEMORY: return ERRCODE_IO_OUTOFMEMORY; + case ERROR_INVALID_BLOCK: return ERRCODE_IO_GENERAL; +// case ERROR_BAD_ENVIRONMENT: return ERRCODE_IO_; + case ERROR_BAD_FORMAT: return ERRCODE_IO_WRONGFORMAT; + case ERROR_INVALID_ACCESS: return ERRCODE_IO_ACCESSDENIED; +// case ERROR_INVALID_DATA: return ERRCODE_IO_; + case ERROR_INVALID_DRIVE: return ERRCODE_IO_INVALIDDEVICE; + case ERROR_CURRENT_DIRECTORY: return ERRCODE_IO_CURRENTDIR; + case ERROR_NOT_SAME_DEVICE: return ERRCODE_IO_NOTSAMEDEVICE; +// case ERROR_NO_MORE_FILES: return ERRCODE_IO_; + case ERROR_WRITE_PROTECT: return ERRCODE_IO_CANTWRITE; + case ERROR_BAD_UNIT: return ERRCODE_IO_INVALIDDEVICE; + case ERROR_NOT_READY: return ERRCODE_IO_DEVICENOTREADY; + case ERROR_BAD_COMMAND: return ERRCODE_IO_GENERAL; + case ERROR_CRC: return ERRCODE_IO_BADCRC; + case ERROR_BAD_LENGTH: return ERRCODE_IO_INVALIDLENGTH; + case ERROR_SEEK: return ERRCODE_IO_CANTSEEK; + case ERROR_NOT_DOS_DISK: return ERRCODE_IO_WRONGFORMAT; + case ERROR_SECTOR_NOT_FOUND: return ERRCODE_IO_GENERAL; + case ERROR_WRITE_FAULT: return ERRCODE_IO_CANTWRITE; + case ERROR_READ_FAULT: return ERRCODE_IO_CANTREAD; + case ERROR_GEN_FAILURE: return ERRCODE_IO_GENERAL; + case ERROR_SHARING_VIOLATION: return ERRCODE_IO_LOCKVIOLATION; + case ERROR_LOCK_VIOLATION: return ERRCODE_IO_LOCKVIOLATION; + case ERROR_WRONG_DISK: return ERRCODE_IO_INVALIDDEVICE; + case ERROR_NOT_SUPPORTED: return ERRCODE_IO_NOTSUPPORTED; +#else + case 0: return ERRCODE_NONE; + case ENOENT: return ERRCODE_IO_NOTEXISTS; + case EACCES: return ERRCODE_IO_ACCESSDENIED; + case EEXIST: return ERRCODE_IO_ALREADYEXISTS; + case EINVAL: return ERRCODE_IO_INVALIDPARAMETER; + case EMFILE: return ERRCODE_IO_TOOMANYOPENFILES; + case ENOMEM: return ERRCODE_IO_OUTOFMEMORY; + case ENOSPC: return ERRCODE_IO_OUTOFSPACE; +#endif + } + + DBG_TRACE1( "FSys: unknown system error %d occured", nSysErr ); + return FSYS_ERR_UNKNOWN; +} + +//-------------------------------------------------------------------- + +#ifndef BOOTSTRAP + +FSysRedirector* FSysRedirector::_pRedirector = 0; +BOOL FSysRedirector::_bEnabled = TRUE; +#ifdef UNX +BOOL bInRedirection = TRUE; +#else +BOOL bInRedirection = FALSE; +#endif +static NAMESPACE_VOS( OMutex )* pRedirectMutex = 0; + +//------------------------------------------------------------------------ +void FSysRedirector::Register( FSysRedirector *pRedirector ) +{ + if ( pRedirector ) + pRedirectMutex = new NAMESPACE_VOS( OMutex ); + else + DELETEZ( pRedirectMutex ); + _pRedirector = pRedirector; +} + +//------------------------------------------------------------------------ + +void FSysRedirector::DoRedirect( String &rPath ) +{ + String aURL(rPath); + + // if redirection is disabled or not even registered do nothing + if ( !_bEnabled || !pRedirectMutex ) + return; + + // redirect only removable or remote volumes + if ( !IsRedirectable_Impl( ByteString( aURL, osl_getThreadTextEncoding() ) ) ) + return; + + // Redirection is acessible only by one thread per time + // dont move the guard behind the bInRedirection check!!! + // think of nested calls (when called from callback) + NAMESPACE_VOS( OGuard ) aGuard( pRedirectMutex ); + + // if already in redirection, dont redirect + if ( bInRedirection ) + return; + + // dont redirect on nested calls + bInRedirection = TRUE; + + // convert to URL +#ifndef UNX + for ( sal_Unicode *p = (sal_Unicode*)aURL.GetBuffer(); *p; ++p ) + if ( '\\' == *p ) *p = '/'; + else if ( ':' == *p ) *p = '|'; +#endif + + aURL.Insert( String("file:///", osl_getThreadTextEncoding()), 0 ); + + // do redirection + Redirector(); + + bInRedirection = FALSE; + return; +} + +//------------------------------------------------------------------------ + +FSysRedirector* FSysRedirector::Redirector() +{ + if ( !_pRedirector ) + Register( new FSysRedirector ); + return _pRedirector; +} + +#endif // BOOTSTRAP + +//-------------------------------------------------------------------- + +class DirEntryStack: public List +{ +public: + DirEntryStack() {}; + ~DirEntryStack(); + + inline void Push( DirEntry *pEntry ); + inline DirEntry* Pop(); + inline DirEntry* Top(); + inline DirEntry* Bottom(); +}; + +inline void DirEntryStack::Push( DirEntry *pEntry ) +{ + List::Insert( pEntry, LIST_APPEND ); +} + +inline DirEntry* DirEntryStack::Pop() +{ + return (DirEntry*) List::Remove( Count() - 1 ); +} + +inline DirEntry* DirEntryStack::Top() +{ + return (DirEntry*) List::GetObject( Count() - 1 ); +} + +inline DirEntry* DirEntryStack::Bottom() +{ + return (DirEntry*) List::GetObject( 0 ); +} + +//-------------------------------------------------------------------- + +DBG_NAME( DirEntry ); + +/************************************************************************* +|* +|* DirEntry::~DirEntryStack() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MI 04.07.91 +|* +*************************************************************************/ + +DirEntryStack::~DirEntryStack() +{ + while ( Count() ) + delete Pop(); +} + +/************************************************************************* +|* +|* ImpCheckDirEntry() +|* +|* Beschreibung Pruefung eines DirEntry fuer DBG_UTIL +|* Parameter void* p Zeiger auf den DirEntry +|* Return-Wert char* Fehlermeldungs-TExtension oder NULL +|* Ersterstellung MI 16.07.91 +|* Letzte Aenderung MI 26.05.93 +|* +*************************************************************************/ + +#ifdef DBG_UTIL +const char* ImpCheckDirEntry( const void* p ) +{ + DirEntry* p0 = (DirEntry*)p; + + if ( p0->pParent ) + DBG_CHKOBJ( p0->pParent, DirEntry, ImpCheckDirEntry ); + + return NULL; +} +#endif + +/************************************************************************* +|* +|* ImplCutPath() +|* +|* Beschreibung Fuegt ... ein, damit maximal nMaxChars lang +|* Ersterstellung MI 06.04.94 +|* Letzte Aenderung DV 24.06.96 +|* +*************************************************************************/ + +ByteString ImplCutPath( const ByteString& rStr, USHORT nMax, char cAccDel ) +{ + USHORT nMaxPathLen = nMax; + ByteString aCutPath( rStr ); + BOOL bInsertPrefix = FALSE; + USHORT nBegin = aCutPath.Search( cAccDel ); + + if( nBegin == STRING_NOTFOUND ) + nBegin = 0; + else + nMaxPathLen += 2; // fuer Prefix <Laufwerk>: + + while( aCutPath.Len() > nMaxPathLen ) + { + USHORT nEnd = aCutPath.Search( cAccDel, nBegin + 1 ); + USHORT nCount; + + if ( nEnd != STRING_NOTFOUND ) + { + nCount = nEnd - nBegin; + aCutPath.Erase( nBegin, nCount ); + bInsertPrefix = TRUE; + } + else + break; + } + + if ( aCutPath.Len() > nMaxPathLen ) + { + for ( USHORT n = nMaxPathLen; n > nMaxPathLen/2; --n ) + if ( !ByteString(aCutPath.GetChar(n)).IsAlphaNumericAscii() ) + { + aCutPath.Erase( n ); + aCutPath += "..."; + break; + } + } + + if ( bInsertPrefix ) + { + ByteString aIns( cAccDel ); + aIns += "..."; + aCutPath.Insert( aIns, nBegin ); + } + + return aCutPath; +} + +/************************************************************************* +|* +|* DirEntry::ImpParseOs2Name() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MI 23.06.95 +|* +*************************************************************************/ + +FSysError DirEntry::ImpParseOs2Name( const ByteString& rPfad, FSysPathStyle eStyle ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + // die einzelnen Namen auf einen Stack packen + String aPfad( rPfad, osl_getThreadTextEncoding() ); + DirEntryStack aStack; + + do + { + // den Namen vor dem ersten "\\" abspalten, + // falls '\\' am Anfang, ist der Name '\\', + // der Rest immer ohne die fuehrenden '\\'. + // ein ":" trennt ebenfalls, gehoert aber zum Namen + // den ersten '\\', '/' oder ':' suchen + USHORT nPos; + for ( nPos = 0; + nPos < aPfad.Len() && //?O + aPfad.GetChar(nPos) != '\\' && aPfad.GetChar(nPos) != '/' && //?O + aPfad.GetChar(nPos) != ':'; //?O + nPos++ ) + /* do nothing */; + + // ist der Name ein UNC Pathname? + if ( nPos == 0 && aPfad.Len() > 1 && + ( ( aPfad.GetChar(0) == '\\' && aPfad.GetChar(1) == '\\' ) || + ( aPfad.GetChar(0) == '/' && aPfad.GetChar(1) == '/' ) ) ) + { + for ( nPos = 2; aPfad.Len() > nPos; ++nPos ) + if ( aPfad.GetChar(nPos) == '\\' || aPfad.GetChar(nPos) == '/' ) + break; + aName = ByteString( aPfad.Copy( 2, nPos-2 ), osl_getThreadTextEncoding() ); + aStack.Push( new DirEntry( aName, FSYS_FLAG_ABSROOT, eStyle ) ); + } + // ist der Name die Root des aktuellen Drives? + else if ( nPos == 0 && aPfad.Len() > 0 && + ( aPfad.GetChar(0) == '\\' || aPfad.GetChar(0) == '/' ) ) + { + // Root-Directory des aktuellen Drives + aStack.Push( new DirEntry( FSYS_FLAG_ABSROOT ) ); + } + else + { + // ist der Name ein Drive? + if ( nPos < aPfad.Len() && aPfad.GetChar(nPos) == ':' ) + { + aName = ByteString( aPfad.Copy( 0, nPos + 1 ), osl_getThreadTextEncoding() ); + + // ist der Name die Root des Drives + if ( (nPos + 1) < aPfad.Len() && + ( aPfad.GetChar(nPos+1) == '\\' || aPfad.GetChar(nPos+1) == '/' ) ) + { + // schon was auf dem Stack? + // oder Novell-Format? (not supported wegen URLs) + if ( aStack.Count() || aName.Len() > 2 ) + { + aName = rPfad; + return FSYS_ERR_MISPLACEDCHAR; + } + // Root-Directory des Drive + aStack.Push( new DirEntry( aName, FSYS_FLAG_ABSROOT, eStyle ) ); + } + else + { + // liegt ein anderes Drive auf dem Stack? + if ( aStack.Count() && + COMPARE_EQUAL != aStack.Bottom()->aName.CompareIgnoreCaseToAscii(aName) ) + aStack.Clear(); + + // liegt jetzt nichts mehr auf dem Stack? + if ( !aStack.Count() ) + aStack.Push( new DirEntry( aName, FSYS_FLAG_RELROOT, eStyle ) ); + } + } + + // es ist kein Drive + else + { + // den Namen ohne Trenner abspalten + aName = ByteString( aPfad.Copy( 0, nPos ), osl_getThreadTextEncoding() ); + + // stellt der Name die aktuelle Directory dar? + if ( aName == "." ) + /* do nothing */; + + // stellt der Name die Parent-Directory dar? + else if ( aName == ".." ) + { + // ist nichts, ein Parent oder eine relative Root + // auf dem Stack? + if ( ( aStack.Count() == 0 ) || + ( aStack.Top()->eFlag == FSYS_FLAG_PARENT ) || + ( aStack.Top()->eFlag == FSYS_FLAG_RELROOT ) ) + // fuehrende Parents kommen auf den Stack + aStack.Push( new DirEntry( FSYS_FLAG_PARENT ) ); + + // ist es eine absolute Root + else if ( aStack.Top()->eFlag == FSYS_FLAG_ABSROOT ) + { + // die hat keine Parent-Directory + aName = rPfad; + return FSYS_ERR_NOTEXISTS; + } + else + // sonst hebt der Parent den TOS auf + delete aStack.Pop(); + } + + else + { + if ( eStyle == FSYS_STYLE_FAT ) + { + // ist der Name grundsaetzlich ungueltig? + int nPunkte = 0; + const char *pChar; + for ( pChar = aName.GetBuffer(); + nPunkte < 2 && *pChar != 0; + pChar++ ) + { + if ( *pChar == ';' ) + nPunkte = 0; + else + nPunkte += ( *pChar == '.' ) ? 1 : 0; + } + if ( nPunkte > 1 ) + { + aName = rPfad; + return FSYS_ERR_MISPLACEDCHAR; + } + } + + // normalen Entries kommen auf den Stack + DirEntry *pNew = new DirEntry( aName, FSYS_FLAG_NORMAL, eStyle ); + if ( !pNew->IsValid() ) + { + aName = rPfad; + ErrCode eErr = pNew->GetError(); + delete pNew; + return eErr; + } + aStack.Push( pNew ); + } + } + } + + // den Restpfad bestimmen + aPfad.Erase( 0, nPos + 1 ); + while ( aPfad.Len() && ( aPfad.GetChar(0) == '\\' || aPfad.GetChar(0) == '/' ) ) + aPfad.Erase( 0, 1 ); + } + while ( aPfad.Len() ); + + ULONG nErr = ERRCODE_NONE; + // Haupt-Entry (selbst) zuweisen + if ( aStack.Count() == 0 ) + { + eFlag = FSYS_FLAG_CURRENT; + aName.Erase(); + } + else + { + eFlag = aStack.Top()->eFlag; + aName = aStack.Top()->aName; + nErr = aStack.Top()->nError; + delete aStack.Pop(); + } + + // die Parent-Entries vom Stack holen + DirEntry** pTemp = &pParent; // Zeiger auf den Member pParent setzen + while ( aStack.Count() ) + { + *pTemp = aStack.Pop(); + + // Zeiger auf den Member pParent des eigenen Parent setzen + pTemp = &( (*pTemp)->pParent ); + } + + // wird damit ein Volume beschrieben? + if ( !pParent && eFlag == FSYS_FLAG_RELROOT && aName.Len() ) + eFlag = FSYS_FLAG_VOLUME; + + // bei gesetztem ErrorCode den Namen komplett "ubernehmen + if ( nErr ) + aName = rPfad; + return nErr; +} + +/************************************************************************* +|* +|* DirEntry::ImpParseName() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.08.91 +|* Letzte Aenderung MI 26.05.93 +|* +*************************************************************************/ + +FSysError DirEntry::ImpParseName( const ByteString& rbInitName, + FSysPathStyle eStyle ) +{ + String rInitName( rbInitName, osl_getThreadTextEncoding() ); + if ( eStyle == FSYS_STYLE_HOST ) + eStyle = DEFSTYLE; + + // KI-Division of FSys + if ( eStyle == FSYS_STYLE_DETECT ) + { + sal_Unicode cFirst = rInitName.GetChar(0); + if ( rInitName.Len() == 2 && rInitName.GetChar(1) == ':' && + ((cFirst >= 'A' && cFirst <= 'Z') || + (cFirst >= 'a' && cFirst <= 'z'))) + eStyle = FSYS_STYLE_HPFS; + else if ( rInitName.Len() > 2 && rInitName.GetChar(1) == ':' ) + { + if ( rInitName.Search( ':', 2 ) == STRING_NOTFOUND ) + eStyle = FSYS_STYLE_HPFS; + else + eStyle = FSYS_STYLE_MAC; + } + else if ( rInitName.Search( '/' ) != STRING_NOTFOUND ) + eStyle = FSYS_STYLE_BSD; + else if ( rInitName.Search( '\\' ) != STRING_NOTFOUND ) + eStyle = FSYS_STYLE_HPFS; + else if ( rInitName.Search( ':' ) != STRING_NOTFOUND ) + eStyle = FSYS_STYLE_MAC; + else + eStyle = FSYS_STYLE_HPFS; + } + + switch ( eStyle ) + { + case FSYS_STYLE_FAT: + case FSYS_STYLE_VFAT: + case FSYS_STYLE_HPFS: + case FSYS_STYLE_NTFS: + case FSYS_STYLE_NWFS: + return ImpParseOs2Name( rbInitName, eStyle ); + + case FSYS_STYLE_BSD: + case FSYS_STYLE_SYSV: + return ImpParseUnixName( rbInitName, eStyle ); + + case FSYS_STYLE_MAC: + return FSYS_ERR_OK; + + default: + return FSYS_ERR_UNKNOWN; + } +} + +/************************************************************************* +|* +|* GetStyle() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 15.11.91 +|* Letzte Aenderung MI 15.11.91 +|* +*************************************************************************/ + +static FSysPathStyle GetStyle( FSysPathStyle eStyle ) +{ + if ( eStyle == FSYS_STYLE_HOST || eStyle == FSYS_STYLE_DETECT ) + return DEFSTYLE; + else + return eStyle; +} + +/************************************************************************* +|* +|* DirEntry::ImpTrim() +|* +|* Beschreibung bringt den Namen auf Betriebssystem-Norm +|* z.B. 8.3 lower beim MS-DOS Formatter +|* wirkt nicht rekursiv +|* Ersterstellung MI 12.08.91 +|* Letzte Aenderung MI 21.05.92 +|* +*************************************************************************/ + +void DirEntry::ImpTrim( FSysPathStyle eStyle ) +{ + // Wildcards werden nicht geclipt + if ( ( aName.Search( '*' ) != STRING_NOTFOUND ) || + ( aName.Search( '?' ) != STRING_NOTFOUND ) || + ( aName.Search( ';' ) != STRING_NOTFOUND ) ) + return; + + switch ( eStyle ) + { + case FSYS_STYLE_FAT: + { + USHORT nPunktPos = aName.Search( '.' ); + if ( nPunktPos == STRING_NOTFOUND ) + { + if ( aName.Len() > 8 ) + { + nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK; + aName.Erase( 8 ); + } + } + else + { + if ( nPunktPos > 8 ) + { + nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK; + aName.Erase( 8, nPunktPos - 8 ); + nPunktPos = 8; + } + if ( aName.Len() > nPunktPos + 3 ) + { + if ( aName.Len() - nPunktPos > 4 ) + { + nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK; + aName.Erase( nPunktPos + 4 ); + } + } + } + aName.ToLowerAscii(); + break; + } + + case FSYS_STYLE_VFAT: + case FSYS_STYLE_HPFS: + case FSYS_STYLE_NTFS: + case FSYS_STYLE_NWFS: + if ( aName.Len() > 254 ) + { + nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK; + aName.Erase( 254 ); + } + + if ( eStyle == FSYS_STYLE_HPFS && + ( eFlag == FSYS_FLAG_ABSROOT || eFlag == FSYS_FLAG_RELROOT ) ) + aName.ToUpperAscii(); + break; + + case FSYS_STYLE_SYSV: + if ( aName.Len() > 14 ) + { + nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK; + aName.Erase( 14 ); + } + break; + + case FSYS_STYLE_BSD: + if ( aName.Len() > 250 ) + { + nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK; + aName.Erase( 250 ); + } + break; + + case FSYS_STYLE_MAC: + if ( eFlag & ( FSYS_FLAG_ABSROOT | FSYS_FLAG_VOLUME ) ) + { + if ( aName.Len() > 27 ) + { + nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK; + aName.Erase( 27 ); + } + } + else + { + if ( aName.Len() > 31 ) + { + nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK; + aName.Erase( 31 ); + } + } + break; + + default: + /* kann nicht sein */; + } +} + +/************************************************************************* +|* +|* DirEntry::DirEntry() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +DirEntry::DirEntry( const ByteString& rName, DirEntryFlag eDirFlag, + FSysPathStyle eStyle ) : +#ifdef FEAT_FSYS_DOUBLESPEED + pStat( 0 ), +#endif + aName( rName ) +{ + DBG_CTOR( DirEntry, ImpCheckDirEntry ); + + pParent = NULL; + eFlag = eDirFlag; + nError = FSYS_ERR_OK; + + ImpTrim( eStyle ); +} + +/************************************************************************* +|* +|* DirEntry::DirEntry() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +DirEntry::DirEntry( const DirEntry& rOrig ) : +#ifdef FEAT_FSYS_DOUBLESPEED + pStat( rOrig.pStat ? new FileStat(*rOrig.pStat) : 0 ), +#endif + aName( rOrig.aName ) +{ + DBG_CTOR( DirEntry, ImpCheckDirEntry ); + + eFlag = rOrig.eFlag; + nError = rOrig.nError; + + if ( rOrig.pParent ) + { + pParent = new DirEntry( *rOrig.pParent ); + } + else + { + pParent = NULL; + } +} + +/************************************************************************* +|* +|* DirEntry::DirEntry() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +DirEntry::DirEntry( const String& rInitName, FSysPathStyle eStyle ) +#ifdef FEAT_FSYS_DOUBLESPEED + : pStat( 0 ) +#endif +{ + DBG_CTOR( DirEntry, ImpCheckDirEntry ); + + pParent = NULL; + + // schnelle Loesung fuer Leerstring + if ( !rInitName.Len()) + { + eFlag = FSYS_FLAG_CURRENT; + nError = FSYS_ERR_OK; + return; + } + + ByteString aTmpName(rInitName, osl_getThreadTextEncoding()); + if( eStyle == FSYS_STYLE_URL || aTmpName.CompareIgnoreCaseToAscii("file:",5 ) == COMPARE_EQUAL ) + { +#ifndef BOOTSTRAP + DBG_WARNING( "File URLs are not permitted but accepted" ); + aTmpName = ByteString(String(INetURLObject( rInitName ).PathToFileName()), osl_getThreadTextEncoding()); + eStyle = FSYS_STYLE_HOST; +#endif // BOOTSTRAP + } + else + { + ::rtl::OUString aTmp; + ::rtl::OUString aOInitName; + if ( FileBase::getFileURLFromSystemPath( OUString( rInitName ), aTmp ) == FileBase::E_None ) + { + aOInitName = OUString( rInitName ); + aTmpName = ByteString( String(aOInitName), osl_getThreadTextEncoding() ); + } + +#ifdef DBG_UTIL + // ASF nur bei Default eStyle, nicht z.B. aus MakeShortName() + if( eStyle == FSYS_STYLE_HOST && + aTmpName.Search( "://" ) != STRING_NOTFOUND ) + { + ByteString aErr = "DirEntries akzeptieren nur File URLS: "; + aErr += aTmpName; + DBG_WARNING( aErr.GetBuffer() ); + } +#endif + } + + nError = ImpParseName( aTmpName, eStyle ); + + if ( nError != FSYS_ERR_OK ) + eFlag = FSYS_FLAG_INVALID; +} + +/*************************************************************************/ + +DirEntry::DirEntry( const ByteString& rInitName, FSysPathStyle eStyle ) +#ifdef FEAT_FSYS_DOUBLESPEED + : pStat( 0 ) +#endif +{ + DBG_CTOR( DirEntry, ImpCheckDirEntry ); + + pParent = NULL; + + // schnelle Loesung fuer Leerstring + if ( !rInitName.Len() ) + { + eFlag = FSYS_FLAG_CURRENT; + nError = FSYS_ERR_OK; + return; + } + + ByteString aTmpName( rInitName ); + if( eStyle == FSYS_STYLE_URL || rInitName.CompareIgnoreCaseToAscii("file:",5 ) == COMPARE_EQUAL ) + { +#ifndef BOOTSTRAP + DBG_WARNING( "File URLs are not permitted but accepted" ); + aTmpName = ByteString(String(INetURLObject( rInitName ).PathToFileName()), osl_getThreadTextEncoding()); + eStyle = FSYS_STYLE_HOST; +#endif + } +#ifdef DBG_UTIL + else + // ASF nur bei Default eStyle, nicht z.B. aus MakeShortName() + if( eStyle == FSYS_STYLE_HOST && + rInitName.Search( "://" ) != STRING_NOTFOUND ) + { + ByteString aErr = "DirEntries akzeptieren nur File URLS: "; + aErr += rInitName; + DBG_WARNING( aErr.GetBuffer() ); + } +#endif + + nError = ImpParseName( aTmpName, eStyle ); + + if ( nError != FSYS_ERR_OK ) + eFlag = FSYS_FLAG_INVALID; +} + +/************************************************************************* +|* +|* DirEntry::DirEntry() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +DirEntry::DirEntry( DirEntryFlag eDirFlag ) +#ifdef FEAT_FSYS_DOUBLESPEED + : pStat( 0 ) +#endif +{ + DBG_CTOR( DirEntry, ImpCheckDirEntry ); + + eFlag = eDirFlag; + nError = ( eFlag == FSYS_FLAG_INVALID ) ? FSYS_ERR_UNKNOWN : FSYS_ERR_OK; + pParent = NULL; +} + +/************************************************************************* +|* +|* DirEntry::~DirEntry() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +DirEntry::~DirEntry() +{ + DBG_DTOR( DirEntry, ImpCheckDirEntry ); + + delete pParent; +#ifdef FEAT_FSYS_DOUBLESPEED + delete pStat; +#endif + +} + +/************************************************************************* +|* +|* DirEntry::ImpGetTopPtr() const +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +const DirEntry* DirEntry::ImpGetTopPtr() const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + const DirEntry *pTemp = this; + while ( pTemp->pParent ) + pTemp = pTemp->pParent; + + return pTemp; +} + +/************************************************************************* +|* +|* DirEntry::ImpGetTopPtr() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 13.11.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +DirEntry* DirEntry::ImpGetTopPtr() +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + DirEntry *pTemp = this; + while ( pTemp->pParent ) + pTemp = pTemp->pParent; + + return pTemp; +} + +/************************************************************************* +|* +|* DirEntry::ImpGetPreTopPtr() +|* +|* Beschreibung liefert einen Pointer auf den vorletzten Entry +|* Ersterstellung MI 01.11.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +DirEntry* DirEntry::ImpGetPreTopPtr() +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + DirEntry *pTemp = this; + if ( pTemp->pParent ) + { + while ( pTemp->pParent->pParent ) + pTemp = pTemp->pParent; + } + + return pTemp; +} + +/************************************************************************* +|* +|* DirEntry::ImpChangeParent() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MI 21.05.92 +|* +*************************************************************************/ + +DirEntry* DirEntry::ImpChangeParent( DirEntry* pNewParent, BOOL bNormalize ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + DirEntry *pTemp = pParent; + if ( bNormalize && pNewParent && + pNewParent->eFlag == FSYS_FLAG_RELROOT && !pNewParent->aName.Len() ) + { + pParent = 0; + delete pNewParent; + } + else + pParent = pNewParent; + + return pTemp; +} + +/************************************************************************* +|* +|* DirEntry::Exists() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MI 24.09.91 +|* +*************************************************************************/ + +BOOL DirEntry::Exists( FSysAccess nAccess ) const +{ +#ifndef BOOTSTRAP + static NAMESPACE_VOS(OMutex) aLocalMutex; + NAMESPACE_VOS(OGuard) aGuard( aLocalMutex ); +#endif + if ( !IsValid() ) + return FALSE; + +#if defined WNT || defined OS2 + // spezielle Filenamen sind vom System da + if ( ( aName.CompareIgnoreCaseToAscii("CLOCK$") == COMPARE_EQUAL || + aName.CompareIgnoreCaseToAscii("CON") == COMPARE_EQUAL || + aName.CompareIgnoreCaseToAscii("AUX") == COMPARE_EQUAL || + aName.CompareIgnoreCaseToAscii("COM1") == COMPARE_EQUAL || + aName.CompareIgnoreCaseToAscii("COM2") == COMPARE_EQUAL || + aName.CompareIgnoreCaseToAscii("COM3") == COMPARE_EQUAL || + aName.CompareIgnoreCaseToAscii("COM4") == COMPARE_EQUAL || + aName.CompareIgnoreCaseToAscii("LPT1") == COMPARE_EQUAL || + aName.CompareIgnoreCaseToAscii("LPT2") == COMPARE_EQUAL || + aName.CompareIgnoreCaseToAscii("LPT3") == COMPARE_EQUAL || + aName.CompareIgnoreCaseToAscii("NUL") == COMPARE_EQUAL || + aName.CompareIgnoreCaseToAscii("PRN") == COMPARE_EQUAL ) ) + return TRUE; +#endif + + FSysFailOnErrorImpl(); + DirEntryKind eKind = FileStat( *this, nAccess ).GetKind(); + if ( eKind & ( FSYS_KIND_FILE | FSYS_KIND_DIR ) ) + { + return TRUE; + } + +#if defined WNT || defined OS2 + if ( 0 != ( eKind & FSYS_KIND_DEV ) ) + { + return DRIVE_EXISTS( ImpGetTopPtr()->aName.GetChar(0) ); + } +#endif + + return 0 != ( eKind & ( FSYS_KIND_FILE | FSYS_KIND_DIR ) ); +} + +/************************************************************************* +|* +|* DirEntry::First() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 15.01.92 +|* +*************************************************************************/ + +BOOL DirEntry::First() +{ + FSysFailOnErrorImpl(); + + String aUniPathName( GetPath().GetFull() ); +#ifndef BOOTSTRAP + FSysRedirector::DoRedirect( aUniPathName ); + ByteString aPathName(aUniPathName, osl_getThreadTextEncoding()); +#else + ByteString aPathName(aUniPathName, gsl_getSystemTextEncoding()); +#endif + aPathName = GUI2FSYS( aPathName ); + + DIR *pDir = opendir( (char*) aPathName.GetBuffer() ); + if ( pDir ) + { +#ifndef BOOTSTRAP + WildCard aWildeKarte( String(CMP_LOWER( aName ), osl_getThreadTextEncoding()) ); +#else + WildCard aWildeKarte( String(CMP_LOWER( aName ), gsl_getSystemTextEncoding()) ); +#endif + for ( dirent* pEntry = readdir( pDir ); + pEntry; + pEntry = readdir( pDir ) ) + { + ByteString aFound( FSYS2GUI( ByteString( pEntry->d_name ) ) ); + if ( aWildeKarte.Matches( String(CMP_LOWER( aFound ), osl_getThreadTextEncoding()))) + { + aName = aFound; + closedir( pDir ); + return TRUE; + } + } + closedir( pDir ); + } + return FALSE; +} + +/************************************************************************* +|* +|* DirEntry::GetFull() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +String DirEntry::GetFull( FSysPathStyle eStyle, BOOL bWithDelimiter, + USHORT nMaxChars ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + ByteString aRet; + eStyle = GetStyle( eStyle ); + if ( pParent ) + { + if ( ( pParent->eFlag == FSYS_FLAG_ABSROOT || + pParent->eFlag == FSYS_FLAG_RELROOT || + pParent->eFlag == FSYS_FLAG_VOLUME ) ) + { + aRet = ByteString(pParent->GetName( eStyle ), osl_getThreadTextEncoding()); + aRet += ByteString(GetName( eStyle ), osl_getThreadTextEncoding()); + } + else + { + aRet = ByteString(pParent->GetFull( eStyle ), osl_getThreadTextEncoding()); + aRet += ACCESSDELIM_C(eStyle); + aRet += ByteString(GetName( eStyle ), osl_getThreadTextEncoding()); + } + } + else + { + aRet = ByteString(GetName( eStyle ), osl_getThreadTextEncoding()); + } + + if ( ( eStyle == FSYS_STYLE_MAC ) && + ( ImpGetTopPtr()->eFlag != FSYS_FLAG_VOLUME ) && + ( ImpGetTopPtr()->eFlag != FSYS_FLAG_ABSROOT ) && + ( aRet.GetChar(0) != ':' ) ) + aRet.Insert( ACCESSDELIM_C(eStyle), 0 ); + + //! Hack + if ( bWithDelimiter ) + if ( aRet.GetChar( aRet.Len()-1 ) != ACCESSDELIM_C(eStyle) ) + aRet += ACCESSDELIM_C(eStyle); + + //! noch ein Hack + if ( nMaxChars < STRING_MAXLEN ) + aRet = ImplCutPath( aRet, nMaxChars, ACCESSDELIM_C(eStyle) ); + + return String(aRet, osl_getThreadTextEncoding()); +} + +/************************************************************************* +|* +|* DirEntry::GetPath() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +DirEntry DirEntry::GetPath() const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + if ( pParent ) + return DirEntry( *pParent ); + + return DirEntry(); +} + +/************************************************************************* +|* +|* DirEntry::GetExtension() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +String DirEntry::GetExtension( char cSep ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + const char *p0 = ( aName.GetBuffer() ); + const char *p1 = p0 + aName.Len() - 1; + while ( p1 >= p0 && *p1 != cSep ) + p1--; + + if ( p1 >= p0 ) + // es wurde ein cSep an der Position p1 gefunden + return String( + aName.Copy( static_cast< xub_StrLen >(p1 - p0 + 1) ), + osl_getThreadTextEncoding()); + return String(); +} + +/************************************************************************* +|* +|* DirEntry::GetBase() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +String DirEntry::GetBase( char cSep ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + const char *p0 = ( aName.GetBuffer() ); + const char *p1 = p0 + aName.Len() - 1; + while ( p1 >= p0 && *p1 != cSep ) + p1--; + + if ( p1 >= p0 ) + // es wurde ein cSep an der Position p1 gefunden + return String( + aName.Copy( 0, static_cast< xub_StrLen >(p1 - p0) ), + osl_getThreadTextEncoding()); + + else + // es wurde kein cSep gefunden + return String(aName, osl_getThreadTextEncoding()); +} + +/************************************************************************* +|* +|* DirEntry::GetName() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 13:47 +|* +*************************************************************************/ + +String DirEntry::GetName( FSysPathStyle eStyle ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + ByteString aRet; + eStyle = GetStyle( eStyle ); + + switch( eFlag ) + { + case FSYS_FLAG_PARENT: + aRet = ACTPARENT(eStyle); + break; + + case FSYS_FLAG_ABSROOT: + { + if ( eStyle == FSYS_STYLE_URL ) + { + aRet = "file:///"; + aRet += aName; + +#ifndef UNX + if ( aName.Len()) + { + if ( aName.GetChar(aName.Len()-1) == ':' ) + { + aRet.SetChar(aRet.Len()-1, '|'); + } + else + { + aRet.Insert( '/', 5 ); + } + aRet += "/"; + } +#endif + } + else if ( eStyle != FSYS_STYLE_MAC && + aName.Len() > 1 && aName.GetChar( 1 ) != ':' ) + { + // UNC-Pathname + aRet = ACCESSDELIM_C(eStyle); + aRet += ACCESSDELIM_C(eStyle); + aRet += aName ; + aRet += ACCESSDELIM_C(eStyle); + } + else + { + aRet = aName; + aRet += ACCESSDELIM_C(eStyle); + } + break; + } + + case FSYS_FLAG_INVALID: + case FSYS_FLAG_VOLUME: + { + if ( eStyle == FSYS_STYLE_URL ) + { + aRet = "file:///"; + aRet += aName; +#ifndef UNX + if ( aName.Len() && aName.GetChar(aName.Len()-1) == ':' ) + { + aRet.SetChar(aRet.Len()-1, '|'); + } +#endif + } + else + { + aRet = aName; + } + + break; + } + + case FSYS_FLAG_RELROOT: + if ( !aName.Len() ) + { + aRet = ACTCURRENT(eStyle); + break; + } + + default: + aRet = aName; + } + + return String(aRet, osl_getThreadTextEncoding()); +} + +/************************************************************************* +|* +|* DirEntry::IsAbs() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +bool DirEntry::IsAbs() const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + +#ifdef UNX + return ( pParent ? pParent->IsAbs() : eFlag == FSYS_FLAG_ABSROOT ); +#else + return ( pParent ? pParent->IsAbs() : eFlag == FSYS_FLAG_ABSROOT && aName.Len() > 0 ); +#endif +} + +/************************************************************************* +|* +|* DirEntry::CutName() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +String DirEntry::CutName( FSysPathStyle eStyle ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + eStyle = GetStyle( eStyle ); + + String aOldName( GetName( eStyle ) ); + + if ( pParent ) + { + DirEntry *pOldParent = pParent; + if ( pOldParent ) + { + pParent = pOldParent->pParent; + eFlag = pOldParent->eFlag; + aName = pOldParent->aName; + pOldParent->pParent = NULL; + delete pOldParent; + } + else + { + eFlag = FSYS_FLAG_CURRENT; + aName.Erase(); + } + } + else + { + eFlag = FSYS_FLAG_CURRENT; + aName.Erase(); + delete pParent; + pParent = NULL; + } + + return aOldName; +} + +/************************************************************************* +|* +|* DirEntry::NameCompare +|* +|* Beschreibung Vergleich nur die Namen (ohne Pfad, aber mit Gross/Klein) +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +StringCompare DirEntry::NameCompare( const DirEntry &rWith ) const +{ + ByteString aThisName; + ByteString aParameterName; + +#ifdef UNX + aThisName = aName; + aParameterName = rWith.aName; +#else + aThisName = ByteString(aName).ToLowerAscii(); + aParameterName = ByteString(rWith.aName).ToLowerAscii(); +#endif + + return aThisName.CompareTo( aParameterName ); +} + + +/************************************************************************* +|* +|* DirEntry::operator==() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +BOOL DirEntry::operator==( const DirEntry& rEntry ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + // test wheather the contents are textual the same + + if ( nError && ( nError == rEntry.nError ) ) + return TRUE; + if ( nError || rEntry.nError || + ( eFlag == FSYS_FLAG_INVALID ) || + ( rEntry.eFlag == FSYS_FLAG_INVALID ) ) + return FALSE; + +#ifndef OS2 + const +#endif + DirEntry *pThis = (DirEntry *)this; +#ifndef OS2 + const +#endif + DirEntry *pWith = (DirEntry *)&rEntry; + while( pThis && pWith && (pThis->eFlag == pWith->eFlag) ) + { + if ( CMP_LOWER(pThis->aName) != CMP_LOWER(pWith->aName) ) + break; + pThis = pThis->pParent; + pWith = pWith->pParent; + } + + return ( !pThis && !pWith ); +} + +/************************************************************************* +|* +|* DirEntry::operator=() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +DirEntry& DirEntry::operator=( const DirEntry& rEntry ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + if ( this == &rEntry ) + return *this; + if ( rEntry.nError != FSYS_ERR_OK ) { + DBG_ERROR("Zuweisung mit invalidem DirEntry"); + nError = rEntry.nError; + return *this; + } + + // Name und Typ uebernehmen, Refs beibehalten + aName = rEntry.aName; + eFlag = rEntry.eFlag; + nError = FSYS_ERR_OK; + + DirEntry *pOldParent = pParent; + if ( rEntry.pParent ) + pParent = new DirEntry( *rEntry.pParent ); + else + pParent = NULL; + + if ( pOldParent ) + delete pOldParent; + return *this; +} + +/************************************************************************* +|* +|* DirEntry::operator+() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +DirEntry DirEntry::operator+( const DirEntry& rEntry ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); +#ifdef DBG_UTIL + static BOOL bTested = FALSE; + if ( !bTested ) + { + bTested = TRUE; + FSysTest(); + } +#endif + + const DirEntry *pEntryTop = rEntry.ImpGetTopPtr(); + const DirEntry *pThisTop = ImpGetTopPtr(); + + // "." + irgendwas oder irgendwas + "d:irgendwas" +/* TPF:org + if ( ( eFlag == FSYS_FLAG_RELROOT && !aName ) || + ( pEntryTop->aName.Len() && + ( pEntryTop->eFlag == FSYS_FLAG_ABSROOT || + pEntryTop->eFlag == FSYS_FLAG_RELROOT || + pEntryTop->eFlag == FSYS_FLAG_VOLUME ) ) ) + return rEntry; +*/ + + if ( + (eFlag == FSYS_FLAG_RELROOT && !aName.Len()) || + ( + (pEntryTop->aName.Len() || + ((rEntry.Level()>1)?(rEntry[rEntry.Level()-2].aName.CompareIgnoreCaseToAscii(RFS_IDENTIFIER)==COMPARE_EQUAL):FALSE)) + && + (pEntryTop->eFlag == FSYS_FLAG_ABSROOT || + pEntryTop->eFlag == FSYS_FLAG_RELROOT || + pEntryTop->eFlag == FSYS_FLAG_VOLUME) + ) + ) + { + return rEntry; + } + + // irgendwas + "." (=> pEntryTop == &rEntry) + if ( pEntryTop->eFlag == FSYS_FLAG_RELROOT && !pEntryTop->aName.Len() ) + { + DBG_ASSERT( pEntryTop == &rEntry, "DirEntry::op+ buggy" ); + return *this; + } + + // root += ".." (=> unmoeglich) + if ( pEntryTop->eFlag == FSYS_FLAG_PARENT && pThisTop == this && + ( eFlag == FSYS_FLAG_ABSROOT ) ) + return DirEntry( FSYS_FLAG_INVALID ); + + // irgendwas += abs (=> nur Device uebernehmen falls vorhanden) + if ( pEntryTop->eFlag == FSYS_FLAG_ABSROOT ) + { + ByteString aDevice; + if ( pThisTop->eFlag == FSYS_FLAG_ABSROOT ) + aDevice = pThisTop->aName; + DirEntry aRet = rEntry; + if ( aDevice.Len() ) + aRet.ImpGetTopPtr()->aName = aDevice; + return aRet; + } + + // irgendwas += ".." (=> aufloesen) + if ( eFlag == FSYS_FLAG_NORMAL && pEntryTop->eFlag == FSYS_FLAG_PARENT ) + { + String aConcated( GetFull() ); + aConcated += ACCESSDELIM_C(FSYS_STYLE_HOST); + aConcated += rEntry.GetFull(); + return DirEntry( aConcated ); + } + + // sonst einfach hintereinander haengen + DirEntry aRet( rEntry ); + DirEntry *pTop = aRet.ImpGetTopPtr(); + pTop->pParent = new DirEntry( *this ); + + return aRet; +} + +/************************************************************************* +|* +|* DirEntry::operator+=() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +DirEntry &DirEntry::operator+=( const DirEntry& rEntry ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + return *this = *this + rEntry; +} + +/************************************************************************* +|* +|* DirEntry::GetAccessDelimiter() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 27.05.93 +|* Letzte Aenderung MI 10.06.93 +|* +*************************************************************************/ + +String DirEntry::GetAccessDelimiter( FSysPathStyle eFormatter ) +{ + return String( ACCESSDELIM_C( GetStyle( eFormatter ) ) ); +} + +/************************************************************************* +|* +|* DirEntry::SetExtension() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 02.08.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +void DirEntry::SetExtension( const String& rExtension, char cSep ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + // do not set extensions for drives + if(eFlag == FSYS_FLAG_ABSROOT) + { + nError = FSYS_ERR_NOTSUPPORTED; + return; + } + + // cSep im Namen suchen + const char *p0 = ( aName.GetBuffer() ); + const char *p1 = p0 + aName.Len() - 1; + while ( p1 >= p0 && *p1 != cSep ) + p1--; + if ( p1 >= p0 ) + { + // es wurde ein cSep an der Position p1 gefunden + aName.Erase( + static_cast< xub_StrLen >( + p1 - p0 + 1 - ( rExtension.Len() ? 0 : 1 )) ); + aName += ByteString(rExtension, osl_getThreadTextEncoding()); + } + else if ( rExtension.Len() ) + { + // es wurde kein cSep gefunden + aName += cSep; + aName += ByteString(rExtension, osl_getThreadTextEncoding()); + } +} + +/************************************************************************* +|* +|* DirEntry::CutExtension() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 23.07.93 +|* Letzte Aenderung MI 23.07.93 +|* +*************************************************************************/ + +String DirEntry::CutExtension( char cSep ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + const char *p0 = ( aName.GetBuffer() ); + const char *p1 = p0 + aName.Len() - 1; + while ( p1 >= p0 && *p1 != cSep ) + p1--; + + if ( p1 >= p0 ) + { + // es wurde ein cSep an der Position p1 gefunden + aName.Erase( static_cast< xub_StrLen >(p1-p0) ); + return String(p1 + 1, osl_getThreadTextEncoding()); + } + + return String(); +} + +/************************************************************************* +|* +|* DirEntry::SetName() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 04.09.93 +|* Letzte Aenderung MI 04.09.93 +|* +*************************************************************************/ + +void DirEntry::SetName( const String& rName, FSysPathStyle eFormatter ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + if ( eFormatter == FSYS_STYLE_HOST || eFormatter == FSYS_STYLE_DETECT ) + eFormatter = DEFSTYLE; + ByteString aAccDelim( ACCESSDELIM_C( eFormatter ) ); + + if ( (eFlag != FSYS_FLAG_NORMAL) || + (aName.Search( ':' ) != STRING_NOTFOUND) || + (aName.Search( aAccDelim ) != STRING_NOTFOUND) || + (eFormatter == FSYS_STYLE_FAT && (aName.GetTokenCount( '.' ) > 2) ) ) + { + eFlag = FSYS_FLAG_INVALID; + } + else + { + aName = ByteString(rName, osl_getThreadTextEncoding()); + } +} + +/************************************************************************* +|* +|* DirEntry::Find() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ +BOOL DirEntry::Find( const String& rPfad, char cDelim ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + if ( ImpGetTopPtr()->eFlag == FSYS_FLAG_ABSROOT ) + return TRUE; + + BOOL bWild = aName.Search( '*' ) != STRING_NOTFOUND || + aName.Search( '?' ) != STRING_NOTFOUND; + if ( !cDelim ) + cDelim = SEARCHDELIM(DEFSTYLE)[0]; + + USHORT nTokenCount = rPfad.GetTokenCount( cDelim ); + USHORT nIndex = 0; + ByteString aThis = ACCESSDELIM(DEFSTYLE); + aThis += ByteString(GetFull(), osl_getThreadTextEncoding()); + for ( USHORT nToken = 0; nToken < nTokenCount; ++nToken ) + { + ByteString aPath = ByteString(rPfad, osl_getThreadTextEncoding()).GetToken( 0, cDelim, nIndex ); + + if ( aPath.Len() ) + { + if (aPath.GetChar(aPath.Len()-1)== ACCESSDELIM(DEFSTYLE)[0]) + aPath.Erase(aPath.Len()-1); + aPath += aThis; + DirEntry aEntry( String(aPath, osl_getThreadTextEncoding())); + if ( aEntry.ToAbs() && + ( ( !bWild && aEntry.Exists() ) || ( bWild && aEntry.First() ) ) ) + { + (*this) = aEntry; + return TRUE; + } + } + } + return FALSE; +} + +/************************************************************************* +|* +|* DirEntry::ImpToRel() +|* +|* Beschreibung +|* Ersterstellung MI 17.06.93 +|* Letzte Aenderung MI 17.06.93 +|* +*************************************************************************/ + +BOOL DirEntry::ImpToRel( String aCurStr ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + DirEntry aThis(*this); + aThis.ToAbs(); + String aThisStr( aThis.GetFull( FSYS_STYLE_HPFS ) ); + + // #109512 preserve case of path even if caseinsensitive + String aThisCompareStr( aThisStr ), aCurCompareStr( aCurStr ); + if ( ! IsCaseSensitive() ) + { + aThisCompareStr.ToLowerAscii(); + aCurCompareStr.ToLowerAscii(); + } + + // "Ubereinstimmung pr"ufen + USHORT nPos = aThisCompareStr.Match( aCurCompareStr ); + if ( nPos == STRING_MATCH && aThisStr.Len() != aCurStr.Len() ) + nPos = Min( aThisStr.Len(), aCurStr.Len() ); + + // Sonderfall, die DirEntries sind identisch + if ( nPos == STRING_MATCH ) + { + // dann ist der relative Pfad das aktuelle Verzeichnis + *this = DirEntry(); + return TRUE; + } + + // Sonderfall, die DirEntries sind total verschieden + if ( nPos == 0 ) + { + // dann ist der relativste Pfad absolut + *this = aThis; + return FALSE; + } + + // sonst nehmen wir die identischen Einzelteile vorne weg + while ( nPos > 0 && aThisStr.GetChar(nPos) != '\\' ) + --nPos; + aThisStr.Erase( 0, nPos + ( ( aThisStr.GetChar(nPos) == '\\' ) ? 1 : 0 ) ); + aCurStr.Erase( 0, nPos + ( ( aCurStr.GetChar(nPos) == '\\' ) ? 1 : 0 ) ); + + // und fuellen mit dem Level der Directories auf + for ( nPos = 0; nPos < aCurStr.Len(); ++nPos ) + if ( aCurStr.GetChar(nPos) == '\\' ) + aThisStr.Insert( String( "..\\", osl_getThreadTextEncoding() ), 0 ); + + // das ist dann unser relativer Pfad + *this = DirEntry( aThisStr, FSYS_STYLE_HPFS ); + return TRUE; +} + +/************************************************************************* +|* +|* DirEntry::CutRelParents() +|* +|* Beschreibung +|* Ersterstellung MI 01.08.95 +|* Letzte Aenderung MI 01.08.95 +|* +*************************************************************************/ + +USHORT DirEntry::CutRelParents() +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + // erstes '..' finden + DirEntry *pDir = 0; + DirEntry *pPar; + + for ( pPar = this; + pPar && pPar->eFlag != FSYS_FLAG_PARENT; + pPar = pPar->pParent ) + pDir = pPar; + + // '..' zaehlen + USHORT nParCount = 0; + while ( pPar && pPar->eFlag == FSYS_FLAG_PARENT ) + { + ++nParCount; + pPar = pPar->pParent; + } + + // cutten + if ( pDir ) + DELETEZ(pDir->pParent); + else + eFlag = FSYS_FLAG_CURRENT; + + return nParCount; +} + +/************************************************************************* +|* +|* DirEntry::ToRel() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.06.93 +|* Letzte Aenderung MI 17.06.93 +|* +*************************************************************************/ + +BOOL DirEntry::ToRel() +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + DirEntry aCur; + aCur.ToAbs(); + return ImpToRel( aCur.GetFull( FSYS_STYLE_HPFS ) ); +} + +/************************************************************************* +|* +|* DirEntry::ToRel() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +BOOL DirEntry::ToRel( const DirEntry& rStart ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + DirEntry aStart( rStart ); + aStart.ToAbs(); + return ImpToRel( aStart.GetFull( FSYS_STYLE_HPFS ) ); +} + +/************************************************************************* +|* +|* DirEntry::GetDevice() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +#ifndef UNX + +DirEntry DirEntry::GetDevice() const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + const DirEntry *pTop = ImpGetTopPtr(); + + if ( ( pTop->eFlag == FSYS_FLAG_ABSROOT || pTop->eFlag == FSYS_FLAG_RELROOT ) && + pTop->aName.Len() ) + return DirEntry( pTop->aName, FSYS_FLAG_VOLUME, FSYS_STYLE_HOST ); + else + return DirEntry( ByteString(), FSYS_FLAG_INVALID, FSYS_STYLE_HOST ); +} + +#endif + +/************************************************************************* +|* +|* DirEntry::SetBase() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 23.10.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +void DirEntry::SetBase( const String& rBase, char cSep ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + const char *p0 = ( aName.GetBuffer() ); + const char *p1 = p0 + aName.Len() - 1; + while ( p1 >= p0 && *p1 != cSep ) + p1--; + + if ( p1 >= p0 ) + { + // es wurde ein cSep an der Position p1 gefunden + aName.Erase( 0, static_cast< xub_StrLen >(p1 - p0) ); + aName.Insert( ByteString(rBase, osl_getThreadTextEncoding()), 0 ); + } + else + aName = ByteString(rBase, osl_getThreadTextEncoding()); +} + +/************************************************************************* +|* +|* DirEntry::GetSearchDelimiter() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 10.06.93 +|* Letzte Aenderung MI 10.06.93 +|* +*************************************************************************/ + +String DirEntry::GetSearchDelimiter( FSysPathStyle eFormatter ) +{ + return String( ByteString(SEARCHDELIM( GetStyle( eFormatter ) ) ), osl_getThreadTextEncoding()); +} + +/************************************************************************* +|* +|* DirEntry::GetMaxNameLen() +|* +|* Beschreibung Liefert die maximale Anzahl von Zeichen in +|* einzelnen Namensteile. Bei FileSystmen mit +|* fester Extension (FAT) zaehlt diese nicht mit. +|* Bei unbekannten FileSytemen und FSYS_STYLE_URL +|* wird USHRT_MAX zurueckgegeben. +|* Ersterstellung MI 17.06.97 +|* Letzte Aenderung MI 17.06.97 +|* +*************************************************************************/ + +USHORT DirEntry::GetMaxNameLen( FSysPathStyle eFormatter ) +{ + eFormatter = GetStyle( eFormatter ); + switch ( eFormatter ) + { + case FSYS_STYLE_MAC: return 31; + + case FSYS_STYLE_FAT: return 8; + + case FSYS_STYLE_VFAT: + case FSYS_STYLE_NTFS: + case FSYS_STYLE_NWFS: + case FSYS_STYLE_HPFS: return 255; + + + case FSYS_STYLE_SYSV: return 14; + + case FSYS_STYLE_BSD: return 250; + + default: + return USHRT_MAX; + } +} + +/************************************************************************* +|* +|* DirEntry::TempName() +|* +|* Beschreibung FSYS.SDW - Aha, wo? +|* Ersterstellung VB 06.09.93 (im SWG) +|* Letzte Aenderung MI 06.02.98 +|* +*************************************************************************/ +namespace { struct TempNameBase_Impl : public rtl::Static< DirEntry, TempNameBase_Impl > {}; } + +const DirEntry& DirEntry::SetTempNameBase( const String &rBase ) +{ + DirEntry aTempDir = DirEntry().TempName().GetPath(); + aTempDir += DirEntry( rBase ); +#ifdef UNX + ByteString aName( aTempDir.GetFull(), osl_getThreadTextEncoding()); + if ( access( aName.GetBuffer(), W_OK | X_OK | R_OK ) ) + { + // Create the directory and only on success give all rights to + // everyone. Use mkdir instead of DirEntry::MakeDir because + // this returns TRUE even if directory already exists. + + if ( !mkdir( aName.GetBuffer(), S_IRWXU | S_IRWXG | S_IRWXO ) ) + chmod( aName.GetBuffer(), S_IRWXU | S_IRWXG | S_IRWXO ); + + // This will not create a directory but perhaps FileStat called + // there modifies the DirEntry + + aTempDir.MakeDir(); + } +#else + aTempDir.MakeDir(); +#endif + DirEntry &rEntry = TempNameBase_Impl::get(); + rEntry = aTempDir.TempName( FSYS_KIND_DIR ); + return rEntry; +} + +DirEntry DirEntry::TempName( DirEntryKind eKind ) const +{ + // ggf. Base-Temp-Dir verwenden (macht Remote keinen Sinn => vorher) + const DirEntry &rEntry = TempNameBase_Impl::get(); + if ( !pParent && FSYS_FLAG_CURRENT != rEntry.eFlag && FSYS_FLAG_ABSROOT != eFlag ) + + { + DirEntry aFactory( rEntry ); + aFactory += GetName(); + return aFactory.TempName(); + } + + ByteString aDirName; // hiermit hatte MPW C++ Probleme - immmer noch?? + char *ret_val; + size_t i; + + // dertermine Directory, Prefix and Extension + char pfx[6]; + char ext[5]; + const char *dir; + const char *pWild = strchr( aName.GetBuffer(), '*' ); + if ( !pWild ) + pWild = strchr( aName.GetBuffer(), '?' ); + + if ( pWild ) + { + if ( pParent ) + aDirName = ByteString(pParent->GetFull(), osl_getThreadTextEncoding()); + strncpy( pfx, aName.GetBuffer(), Min( (int)5, (int)(pWild-aName.GetBuffer()) ) ); + pfx[ pWild-aName.GetBuffer() ] = 0; + const char *pExt = strchr( pWild, '.' ); + if ( pExt ) + { + strncpy( ext, pExt, 4 ); + ext[4] = 0; + } + else + strcpy( ext, ".tmp" ); + } + else + { + aDirName = ByteString(GetFull(), osl_getThreadTextEncoding()); + strcpy( pfx, "sv" ); + strcpy( ext, ".tmp" ); + } + dir = aDirName.GetBuffer(); + + // wurde kein Dir angegeben, dann nehmen wir ein passendes TEMP-Verz. + char sBuf[_MAX_PATH]; + if ( eFlag == FSYS_FLAG_CURRENT || ( !pParent && pWild ) ) + dir = TempDirImpl(sBuf); + + // ab hier leicht modifizierter Code von VB + DirEntry aRet(FSYS_FLAG_INVALID); + i = strlen(dir); + // need to add ?\\? + prefix + number + pid + .ext + '\0' +# define TMPNAME_SIZE ( 1 + 5 + 5 + 10 + 4 + 1 ) + ret_val = new char[i + TMPNAME_SIZE ]; + if (ret_val) + { + strcpy(ret_val,dir); + + /* Make sure directory ends with a separator */ +#if defined(WNT) || defined(OS2) + if ( i>0 && ret_val[i-1] != '\\' && ret_val[i-1] != '/' && + ret_val[i-1] != ':') + ret_val[i++] = '\\'; +#elif defined UNX + if (i>0 && ret_val[i-1] != '/') + ret_val[i++] = '/'; +#else +#error unknown operating system +#endif + + strncpy(ret_val + i, pfx, 5); + ret_val[i + 5] = '\0'; /* strncpy doesn't put a 0 if more */ + i = strlen(ret_val); /* than 'n' chars. */ + + /* Prefix can have 5 chars, leaving 3 for numbers. 26 ** 3 == 17576 + * Welcome to the 21st century, we can have longer filenames now ;) + * New format: pfx + "5 char milli/micro second res" + "current pid" + ".tmp" + */ +#if (defined MSC || defined __MINGW32__) && defined WNT + /* Milliseconds !! */ + static unsigned long u = GetTickCount(); + unsigned long mypid = static_cast<unsigned long>(_getpid()); +#else + /* Microseconds !! */ + static unsigned long u = clock(); + unsigned long mypid = static_cast<unsigned long>(getpid()); +#endif + for ( unsigned long nOld = u; ++u != nOld; ) /* Hae??? */ + { + u %= 100000; /* on *NIX repeats every 100ms, maybe less if CLOCKS_PER_SEC > 10^6 */ + snprintf(ret_val+i, TMPNAME_SIZE, "%05lu%lu", u, mypid); + + strcat(ret_val,ext); + + if ( FSYS_KIND_FILE == eKind ) + { + SvFileStream aStream( String( ret_val, osl_getThreadTextEncoding()), + STREAM_WRITE|STREAM_SHARE_DENYALL ); + if ( aStream.IsOpen() ) + { + aStream.Seek( STREAM_SEEK_TO_END ); + if ( 0 == aStream.Tell() ) + { + aRet = DirEntry( String( ret_val, osl_getThreadTextEncoding())); + break; + } + aStream.Close(); + } + } + else + { + // Redirect + String aRetVal(ret_val, osl_getThreadTextEncoding()); + String aRedirected (aRetVal); +#ifndef BOOTSTRAP + FSysRedirector::DoRedirect( aRedirected ); +#endif + if ( FSYS_KIND_DIR == eKind ) + { + if ( 0 == _mkdir( ByteString(aRedirected.GetBuffer(), osl_getThreadTextEncoding()).GetBuffer() ) ) + { + aRet = DirEntry( aRetVal ); + break; + } + } + else + { +#if defined(UNX) || defined(OS2) + if( access( ByteString(aRedirected, osl_getThreadTextEncoding()).GetBuffer(), F_OK ) ) + { + aRet = DirEntry( aRetVal ); + break; + } +#else + struct stat aStat; + if ( stat( ByteString(aRedirected, osl_getThreadTextEncoding()).GetBuffer(), &aStat ) ) + { + aRet = DirEntry( aRetVal ); + break; + } +#endif + } + } + } + + delete[] ret_val; + ret_val = 0; + } + + return aRet; +} + +/************************************************************************* +|* +|* DirEntry::operator[]() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 03.03.92 +|* Letzte Aenderung MI 03.03.92 +|* +*************************************************************************/ + +const DirEntry &DirEntry::operator[]( USHORT nParentLevel ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + //TPF: maybe to be implemented (FastFSys) + + const DirEntry *pRes = this; + while ( pRes && nParentLevel-- ) + pRes = pRes->pParent; + + return *pRes; +} + +/************************************************************************* +|* +|* DirEntry::ImpParseUnixName() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MI 26.05.93 +|* +*************************************************************************/ + +FSysError DirEntry::ImpParseUnixName( const ByteString& rPfad, FSysPathStyle eStyle ) +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + // die einzelnen Namen auf einen Stack packen + DirEntryStack aStack; + ByteString aPfad( rPfad ); + do + { + // den Namen vor dem ersten "/" abspalten, + // falls '/' am Anfang, ist der Name '/', + // der Rest immer ohne die fuehrenden '/'. + // den ersten '/' suchen + USHORT nPos; + for ( nPos = 0; + nPos < aPfad.Len() && aPfad.GetChar(nPos) != '/'; + nPos++ ) + /* do nothing */; + + // ist der Name die Root des aktuellen Drives? + if ( nPos == 0 && aPfad.Len() > 0 && ( aPfad.GetChar(0) == '/' ) ) + { + // Root-Directory des aktuellen Drives + aStack.Push( new DirEntry( FSYS_FLAG_ABSROOT ) ); + } + else + { + // den Namen ohne Trenner abspalten + aName = aPfad.Copy( 0, nPos ); + + // stellt der Name die aktuelle Directory dar? + if ( aName == "." ) + /* do nothing */; + +#ifdef UNX + // stellt der Name das User-Dir dar? + else if ( aName == "~" ) + { + DirEntry aHome( String( (const char *) getenv( "HOME" ), osl_getThreadTextEncoding()) ); + for ( USHORT n = aHome.Level(); n; --n ) + aStack.Push( new DirEntry( aHome[ (USHORT) n-1 ] ) ); + } +#endif + + // stellt der Name die Parent-Directory dar? + else if ( aName == ".." ) + { + // ist nichts, ein Parent oder eine relative Root + // auf dem Stack? + if ( ( aStack.Count() == 0 ) || + ( aStack.Top()->eFlag == FSYS_FLAG_PARENT ) ) + // fuehrende Parents kommen auf den Stack + aStack.Push( new DirEntry( ByteString(), FSYS_FLAG_PARENT, eStyle ) ); + + // ist es eine absolute Root + else if ( aStack.Top()->eFlag == FSYS_FLAG_ABSROOT ) { + // die hat keine Parent-Directory + return FSYS_ERR_NOTEXISTS; + } + else + // sonst hebt der Parent den TOS auf + delete aStack.Pop(); + } + else + { + DirEntry *pNew = NULL; + // normalen Entries kommen auf den Stack + pNew = new DirEntry( aName, FSYS_FLAG_NORMAL, eStyle ); + if ( !pNew->IsValid() ) + { + aName = rPfad; + ErrCode eErr = pNew->GetError(); + delete pNew; + return eErr; + } + aStack.Push( pNew ); + } + } + + // den Restpfad bestimmen + aPfad.Erase( 0, nPos + 1 ); + while ( aPfad.Len() && ( aPfad.GetChar(0) == '/' ) ) + aPfad.Erase( 0, 1 ); + } + while ( aPfad.Len() ); + + // Haupt-Entry (selbst) zuweisen + if ( aStack.Count() == 0 ) + { + eFlag = FSYS_FLAG_CURRENT; + aName.Erase(); + } + else + { + eFlag = aStack.Top()->eFlag; + aName = aStack.Top()->aName; + delete aStack.Pop(); + } + + // die Parent-Entries vom Stack holen + DirEntry** pTemp = &pParent; + while ( aStack.Count() ) + { + *pTemp = aStack.Pop(); + pTemp = &( (*pTemp)->pParent ); + } + + return FSYS_ERR_OK; +} + +/************************************************************************* +|* +|* DirEntry::MakeShortName() +|* +|* Beschreibung +|* Ersterstellung TLX +|* Letzte Aenderung PB 21.08.97 (in CreateEntry_Impl()) +|* +*************************************************************************/ + +ErrCode CreateEntry_Impl( const DirEntry &rPath, DirEntryKind eKind ) +{ + // versuchen, anzulegen (ausser bei FSYS_KIND_ALL) + ErrCode eErr = ERRCODE_NONE; + if ( FSYS_KIND_FILE == eKind ) + { + SvFileStream aStream( rPath.GetFull(), STREAM_STD_WRITE ); + aStream.WriteLine( "" ); + eErr = aStream.GetError(); + } + else if ( FSYS_KIND_ALL != eKind ) + eErr = rPath.MakeDir() ? ERRCODE_NONE : ERRCODE_IO_UNKNOWN; + + // erfolgreich? + if ( !rPath.Exists() ) + eErr = ERRCODE_IO_UNKNOWN; // Doch was schiefgegangen ? + + // ggf. wieder l"oschen + if ( FSYS_KIND_NONE == eKind ) + rPath.Kill(); + + // Fehlercode zur?ckliefern + return eErr; +} + +BOOL IsValidEntry_Impl( const DirEntry &rPath, + const String &rLongName, + DirEntryKind eKind, + BOOL bIsShortened, + BOOL bUseDelim ) +{ + // Parameter-Pr"uefung + DBG_ASSERT( eKind == FSYS_KIND_NONE || eKind == FSYS_KIND_ALL || + eKind == FSYS_KIND_FILE || eKind == FSYS_KIND_DIR, + "invalid entry-kind" ); + + // Alle von MSDOS erreichbaren FSYS_STYLES muessen den + // MSDOS Filenamenanforderungen genuegen. Sonst wird probiert, + // ob sich eine Datei des gewuenschten Names anlegen laesst. + FSysPathStyle eStyle = DirEntry::GetPathStyle( rPath.GetDevice().GetName() ); + DirEntry aPath(rPath); + DirEntry aName(rLongName, eStyle); + if ( !aName.IsValid() || aName.Level() != 1 ) + return FALSE; + aPath += aName; + if ( 1 == aPath.Level() ) + return FALSE; + if ( eStyle == FSYS_STYLE_FAT || eStyle == FSYS_STYLE_NWFS || + eStyle == FSYS_STYLE_UNKNOWN ) + { + DirEntry aDosEntry( rLongName, FSYS_STYLE_FAT ); + if ( !aDosEntry.IsValid() ) + return FALSE; + } + + // Pfad-Trenner sind nicht erlaubt (bei ungek"urzten auch nicht FSYS_SHORTNAME_DELIMITER) + char cDelim = bUseDelim == 2 ? FSYS_SHORTNAME_DELIMITER : char(0); + if ( + rLongName.Search(DirEntry::GetAccessDelimiter()) != STRING_NOTFOUND || + (!bIsShortened && rLongName.Search(cDelim) != STRING_NOTFOUND) + ) + { + return FALSE; + } + + // MI: Abfrage nach 'CON:' etc. wird jetzt in Exists() mitgemacht + if ( aPath.Exists() ) + return FALSE; + + return (ERRCODE_NONE == CreateEntry_Impl( aPath, eKind )); +} + +//------------------------------------------------------------------------- + +#define MAX_EXT_FAT 3 +#define MAX_LEN_FAT 8 +#define INVALID_CHARS_FAT "\\/\"':|^<>[]?* " + +#define MAX_EXT_MAC 16 // nur wegen sinnvoller Namensk"rzung +#define MAX_LEN_MAC 31 +#define INVALID_CHARS_MAC "\":" + +#define MAX_EXT_MAX 250 +#define MAX_LEN_MAX 255 +#define INVALID_CHARS_DEF "\\/\"':|^<>?*" + +BOOL DirEntry::MakeShortName( const String& rLongName, DirEntryKind eKind, + BOOL bUseDelim, FSysPathStyle eStyle ) +{ + String aLongName(rLongName); + + // Alle '#' aus den Dateinamen entfernen, weil das INetURLObject + // damit Probleme hat. Siehe auch #51246# + aLongName.EraseAllChars( '#' ); + ByteString bLongName(aLongName, osl_getThreadTextEncoding()); + + // Auf Novell-Servern (wegen der rottigen Clients) nur 7bit ASCII + + // HRO: #69627# Weg mit dem Scheiss. Wenn es Client gibt, die so einen + // BUG haben, dann muss halt der Client ersetzt werden, aber doch nicht das + // Office kastrieren !!! + +#if 0 + if ( FSYS_STYLE_NWFS == GetPathStyle( ImpGetTopPtr()->GetName() ) ) + { + for ( USHORT n = aLongName.Len(); n; --n ) + { + short nChar = aLongName(n-1); + if ( nChar < 32 || nChar >= 127 ) + aLongName.Erase( n-1, 1 ); + } + } +#endif + + // bei FSYS_KIND_ALL den alten Namen merken und abh"angen (rename) + ByteString aOldName; + if ( FSYS_KIND_ALL == eKind ) + { + aOldName = ByteString(CutName(), osl_getThreadTextEncoding()); + aOldName = CMP_LOWER(aOldName); + } + + // ist der Langname direkt verwendbar? + if ( IsValidEntry_Impl( *this, aLongName, eKind, FALSE, bUseDelim ) ) + { + operator+=( DirEntry(aLongName) ); + return TRUE; + } + + // max L"angen feststellen + USHORT nMaxExt, nMaxLen; + if ( FSYS_STYLE_DETECT == eStyle ) + eStyle = DirEntry::GetPathStyle( GetDevice().GetName() ); + ByteString aInvalidChars; + switch ( eStyle ) + { + case FSYS_STYLE_FAT: + nMaxExt = MAX_EXT_FAT; + nMaxLen = MAX_LEN_FAT; + aInvalidChars = INVALID_CHARS_FAT; + break; + + case FSYS_STYLE_MAC: + nMaxExt = MAX_EXT_MAC; + nMaxLen = MAX_LEN_MAC; + aInvalidChars = INVALID_CHARS_MAC; + break; + + default: + nMaxExt = MAX_EXT_MAX; + nMaxLen = MAX_LEN_MAX; + aInvalidChars = INVALID_CHARS_DEF; + } + + // Extension abschneiden und kuerzen + ByteString aExt; + ByteString aFName = bLongName; + if ( FSYS_STYLE_MAC != eStyle ) + { + DirEntry aUnparsed; + aUnparsed.aName = bLongName; + aExt = ByteString(aUnparsed.CutExtension(), osl_getThreadTextEncoding()); + aFName = aUnparsed.aName; + if ( aExt.Len() > nMaxExt ) + { + char c = aExt.GetChar( aExt.Len() - 1 ); + aExt.Erase(nMaxExt-1); + aExt += c; + } + } + + if ( FSYS_STYLE_FAT != eStyle ) + { + // ausser auf einem FAT-System geh"ort die Extension zur + // Maxl"ange. Muss also vorher mit dem Punkt abgezogen werden. + nMaxLen -= ( aExt.Len() + 1 ); + } + + // Name k"urzen + ByteString aSName; + for ( const char *pc = aFName.GetBuffer(); aSName.Len() < nMaxLen && *pc; ++pc ) + { + if ( STRING_NOTFOUND == aInvalidChars.Search( *pc ) && + (unsigned char) *pc >= (unsigned char) 32 && + ( !aSName.Len() || *pc != ' ' || aSName.GetChar(aSName.Len()-1) != ' ' ) ) + aSName += *pc; + } + aSName.EraseTrailingChars(); + + // HRO: #74246# Also cut leading spaces + aSName.EraseLeadingChars(); + + if ( !aSName.Len() ) + aSName = "noname"; + + // kommt dabei der alte Name raus? + ByteString aNewName = aSName; + if ( aExt.Len() ) + ( aNewName += '.' ) += aExt; + operator+=( DirEntry(String(aNewName, osl_getThreadTextEncoding())) ); + if ( FSYS_KIND_ALL == eKind && CMP_LOWER(aName) == aOldName ) + if ( FSYS_KIND_ALL == eKind && CMP_LOWER(ByteString(GetName(), osl_getThreadTextEncoding())) == aOldName ) + return TRUE; + + // kann der gek"urzte Name direkt verwendet werden? + if ( !Exists() && (ERRCODE_NONE == CreateEntry_Impl( *this, eKind )) ) + return TRUE; + + // darf '?##' verwendet werden, um eindeutigen Name zu erzeugen? + if ( bUseDelim ) + { + // eindeutigen Namen per '?##' erzeugen + aSName.Erase( nMaxLen-3 ); + if ( bUseDelim != 2 ) + aSName += FSYS_SHORTNAME_DELIMITER; + for ( int n = 1; n < 99; ++n ) + { + // Name zusammensetzen + ByteString aTmpStr( aSName ); + aTmpStr += ByteString::CreateFromInt32(n); + if ( aExt.Len() ) + ( aTmpStr += '.' ) += aExt; + + // noch nicht vorhanden? + SetName( String(aTmpStr, osl_getThreadTextEncoding()) ); + + if ( !Exists() ) + { + // Fehler setzen !!! + nError = CreateEntry_Impl( *this, eKind ); + return (ERRCODE_NONE == nError); + } + } + } + + // keine ## mehr frei / ?## soll nicht verwendet werden + nError = ERRCODE_IO_ALREADYEXISTS; + return FALSE; +} + +/************************************************************************* +|* +|* DirEntry::CreatePath() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +BOOL DirEntry::MakeDir( BOOL bSloppy ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + // Schnellpruefung, ob vorhanden + if ( FileStat( *this ).IsKind( FSYS_KIND_DIR ) ) + return TRUE; + if ( bSloppy && pParent ) + if ( FileStat( *pParent ).IsKind( FSYS_KIND_DIR ) ) + return TRUE; + + const DirEntry *pNewDir = bSloppy ? pParent : this; + if ( pNewDir ) + { + // den Path zum Dir erzeugen + if ( pNewDir->pParent && !pNewDir->pParent->MakeDir(FALSE) ) + return FALSE; + + // das Dir selbst erzeugen + if ( pNewDir->eFlag == FSYS_FLAG_ABSROOT || + pNewDir->eFlag == FSYS_FLAG_ABSROOT || + pNewDir->eFlag == FSYS_FLAG_VOLUME ) + return TRUE; + else + { + //? nError = ??? + if ( FileStat( *pNewDir ).IsKind( FSYS_KIND_DIR ) ) + return TRUE; + else + { + FSysFailOnErrorImpl(); + String aDirName(pNewDir->GetFull()); +#ifndef BOOTSTRAP + FSysRedirector::DoRedirect( aDirName ); +#endif + ByteString bDirName( aDirName, osl_getThreadTextEncoding() ); + bDirName = GUI2FSYS( bDirName ); + +#ifdef WIN32 + SetLastError(0); +#endif + BOOL bResult = (0 == _mkdir( (char*) bDirName.GetBuffer() )); + if ( !bResult ) + { + // Wer hat diese Methode const gemacht ? +#ifdef WIN32 + ((DirEntry *)this)->SetError( Sys2SolarError_Impl( GetLastError() ) ); +#else + ((DirEntry *)this)->SetError( Sys2SolarError_Impl( errno ) ); +#endif + } + + return bResult; + } + } + } + return TRUE; +} + +/************************************************************************* +|* +|* DirEntry::CopyTo() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MI 07.08.96 +|* +*************************************************************************/ + +FSysError DirEntry::CopyTo( const DirEntry& rDest, FSysAction nActions ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + if ( FSYS_ACTION_COPYFILE != (nActions & FSYS_ACTION_COPYFILE) ) +#ifdef UNX + { + // Hardlink anlegen + HACK(redirection missing) + ByteString aThis(GUI2FSYS(GetFull()), osl_getThreadTextEncoding()); + ByteString aDest(GUI2FSYS(rDest.GetFull()), osl_getThreadTextEncoding()); + if (link( aThis.GetBuffer(), aDest.GetBuffer() ) == -1) + return Sys2SolarError_Impl( errno ); + else + return FSYS_ERR_OK; + } +#else + return FSYS_ERR_NOTSUPPORTED; +#endif + + FileCopier fc(*this, rDest); + return fc.Execute(nActions); +} + +/************************************************************************* +|* +|* DirEntry::MoveTo() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung HRO 24.03.99 +|* +*************************************************************************/ + +#if defined WNT || defined UNX || defined OS2 + +FSysError DirEntry::MoveTo( const DirEntry& rNewName ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + +/* + FileStat aSourceStat(*this); + if ( !aSourceStat.IsKind(FSYS_KIND_FILE) ) + return FSYS_ERR_NOTAFILE; +*/ + + DirEntry aDest(rNewName); + FileStat aDestStat(rNewName); + if ( aDestStat.IsKind(FSYS_KIND_DIR ) ) + { + aDest += String(aName, osl_getThreadTextEncoding()); + } + if ( aDest.Exists() ) + { + return FSYS_ERR_ALREADYEXISTS; + } + +#if defined(OS2) + if ( FileStat(*this).IsKind(FSYS_KIND_DIR) && aDest.GetPath() != GetPath() ) + { + return FSYS_ERR_NOTSUPPORTED; + } +#endif + + FSysFailOnErrorImpl(); + String aFrom( GetFull() ); + +#ifndef BOOTSTRAP + FSysRedirector::DoRedirect(aFrom); +#endif + + String aTo( aDest.GetFull() ); + +#ifndef BOOTSTRAP + FSysRedirector::DoRedirect(aTo); +#endif + + ByteString bFrom(aFrom, osl_getThreadTextEncoding()); + ByteString bTo(aTo, osl_getThreadTextEncoding()); + bFrom = GUI2FSYS(bFrom); + bTo = GUI2FSYS(bTo); + +#ifdef WNT + // MoveTo nun atomar + SetLastError(0); + + DirEntry aFromDevice(String(bFrom, osl_getThreadTextEncoding())); + DirEntry aToDevice(String(bTo,osl_getThreadTextEncoding())); + aFromDevice.ToAbs(); + aToDevice.ToAbs(); + aFromDevice=aFromDevice.GetDevice(); + aToDevice=aToDevice.GetDevice(); + + //Quelle und Ziel auf gleichem device? + if (aFromDevice==aToDevice) + { + // ja, also intra-device-move mit MoveFile + MoveFile( bFrom.GetBuffer(), bTo.GetBuffer() ); + // MoveFile ist buggy bei cross-device operationen. + // Der R?ckgabewert ist auch dann TRUE, wenn nur ein Teil der Operation geklappt hat. + // Zudem zeigt MoveFile unterschiedliches Verhalten bei unterschiedlichen NT-Versionen. + return Sys2SolarError_Impl( GetLastError() ); + } + else + { + //nein, also inter-device-move mit copy/delete + FSysError nCopyError = CopyTo(rNewName, FSYS_ACTION_COPYFILE); + + DirEntry aKill(String(bTo, osl_getThreadTextEncoding())); + FileStat aKillStat(String(bTo, osl_getThreadTextEncoding())); + if ( aKillStat.IsKind(FSYS_KIND_DIR ) ) + { + aKill += String(aName, osl_getThreadTextEncoding()); + } + + if (nCopyError==FSYS_ERR_OK) + { + if (Kill()==FSYS_ERR_OK) + { + return FSYS_ERR_OK; + } + else + { + aKill.Kill(); + return FSYS_ERR_ACCESSDENIED; + } + } + else + { + aKill.Kill(); + return nCopyError; + } + } +#else + // #68639# + // on some nfs connections rename with from == to + // leads to destruction of file + if ( ( aFrom != aTo ) && ( 0 != rename( bFrom.GetBuffer(), bTo.GetBuffer() ) ) ) +#if !defined(UNX) && !defined(OS2) + return Sys2SolarError_Impl( GetLastError() ); +#else + { + if( errno == EXDEV ) +// cross device geht latuernich nicht mit rename + { + FILE *fpIN = fopen( bFrom.GetBuffer(), "r" ); + FILE *fpOUT = fopen( bTo.GetBuffer(), "w" ); + if( fpIN && fpOUT ) + { + char pBuf[ 16384 ]; + int nBytes, nWritten, nErr = 0; + errno = 0; + while( ( nBytes = fread( pBuf, 1, sizeof(pBuf), fpIN ) ) && ! nErr ) + { + nWritten = fwrite( pBuf, 1, nBytes, fpOUT ); + // Fehler im fwrite ? + if( nWritten < nBytes ) + { + nErr = errno; + break; + } + } + fclose( fpIN ); + fclose( fpOUT ); + if ( nErr ) + { + unlink( bTo.GetBuffer() ); + return Sys2SolarError_Impl( nErr ); + } + else + { + unlink( bFrom.GetBuffer() ); + } + } + else + { + return Sys2SolarError_Impl( EXDEV ); + } + } + else + { + return Sys2SolarError_Impl( errno ); + } + } +#endif +#endif + return ERRCODE_NONE; +} + +#endif + +/************************************************************************* +|* +|* DirEntry::Kill() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MI 07.08.96 +|* +*************************************************************************/ + +FSysError DirEntry::Kill( FSysAction nActions ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + FSysError eError = FSYS_ERR_OK; + FSysFailOnErrorImpl(); + + // Name als doppelt 0-terminierter String + String aTmpName( GetFull() ); +#ifndef BOOTSTRAP + FSysRedirector::DoRedirect( aTmpName ); +#endif + ByteString bTmpName( aTmpName, osl_getThreadTextEncoding()); + bTmpName = GUI2FSYS(bTmpName); + + char *pName = new char[bTmpName.Len()+2]; + strcpy( pName, bTmpName.GetBuffer() ); + pName[bTmpName.Len()+1] = (char) 0; + + //read-only files sollen auch geloescht werden koennen + BOOL isReadOnly = FileStat::GetReadOnlyFlag(*this); + if (isReadOnly) + { + FileStat::SetReadOnlyFlag(*this, FALSE); + } + + // directory? + if ( FileStat( *this ).IsKind(FSYS_KIND_DIR) ) + { + // Inhalte recursiv loeschen? + if ( FSYS_ACTION_RECURSIVE == (nActions & FSYS_ACTION_RECURSIVE) ) + { + Dir aDir( *this, FSYS_KIND_DIR|FSYS_KIND_FILE ); + for ( USHORT n = 0; eError == FSYS_ERR_OK && n < aDir.Count(); ++n ) + { + const DirEntry &rSubDir = aDir[n]; + DirEntryFlag flag = rSubDir.GetFlag(); + if ( flag != FSYS_FLAG_CURRENT && flag != FSYS_FLAG_PARENT ) + eError = rSubDir.Kill(nActions); + } + } + + // das Dir selbst loeschen +#ifdef WIN32 + SetLastError(0); +#endif + if ( eError == FSYS_ERR_OK && 0 != _rmdir( (char*) pName ) ) + // + { + // falls L"oschen nicht ging, CWD umsetzen +#ifdef WIN32 + eError = Sys2SolarError_Impl( GetLastError() ); +#else + eError = Sys2SolarError_Impl( errno ); +#endif + if ( eError ) + { + GetPath().SetCWD(); +#ifdef WIN32 + SetLastError(0); +#endif + if (_rmdir( (char*) pName) != 0) + { +#ifdef WIN32 + eError = Sys2SolarError_Impl( GetLastError() ); +#else + eError = Sys2SolarError_Impl( errno ); +#endif + } + else + { + eError = FSYS_ERR_OK; + } + } + } + } + else + { + if ( FSYS_ACTION_USERECYCLEBIN == (nActions & FSYS_ACTION_USERECYCLEBIN) ) + { +#ifdef OS2 + eError = ApiRet2ToSolarError_Impl( DosDelete( (PSZ) pName ) ); +#elif defined(WNT) + SHFILEOPSTRUCT aOp; + aOp.hwnd = 0; + aOp.wFunc = FO_DELETE; + aOp.pFrom = pName; + aOp.pTo = 0; + aOp.fFlags = FOF_ALLOWUNDO|FOF_SILENT|FOF_NOCONFIRMATION; + aOp.hNameMappings = 0; + aOp.lpszProgressTitle = 0; + eError = Sys2SolarError_Impl( SHFileOperation( &aOp ) ); +#else + eError = ERRCODE_IO_NOTSUPPORTED; +#endif + } + else + { +#ifdef WIN32 + SetLastError(0); +#endif + if ( 0 != _unlink( (char*) pName ) ) + { +#ifdef WIN32 + eError = Sys2SolarError_Impl( GetLastError() ); +#else + eError = Sys2SolarError_Impl( errno ); +#endif + } + else + { + eError = ERRCODE_NONE; + } + } + } + + //falls Fehler, originales read-only flag wieder herstellen + if ( isReadOnly && (eError!=ERRCODE_NONE) ) + { + FileStat::SetReadOnlyFlag(*this, isReadOnly); + } + + delete[] pName; + return eError; +} + +/************************************************************************* +|* +|* DirEntry::Contains() +|* +|* Beschreibung ob rSubEntry direkt oder indirect in *this liegt +|* Ersterstellung MI 20.03.97 +|* Letzte Aenderung MI 20.03.97 +|* +*************************************************************************/ + +BOOL DirEntry::Contains( const DirEntry &rSubEntry ) const +{ + DBG_ASSERT( IsAbs() && rSubEntry.IsAbs(), "must be absolute entries" ); + + USHORT nThisLevel = Level(); + USHORT nSubLevel = rSubEntry.Level(); + if ( nThisLevel < nSubLevel ) + { + for ( ; nThisLevel; --nThisLevel, --nSubLevel ) + if ( (*this)[nThisLevel-1] != rSubEntry[nSubLevel-1] ) + return FALSE; + return TRUE; + } + return FALSE; +} + +/************************************************************************* +|* +|* DirEntry::Level() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 03.03.92 +|* Letzte Aenderung MI 03.03.92 +|* +*************************************************************************/ + +USHORT DirEntry::Level() const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + USHORT nLevel = 0; + const DirEntry *pRes = this; + while ( pRes ) + { + pRes = pRes->pParent; + nLevel++; + } + + return nLevel; +} + +/************************************************************************* +|* +|* DirEntry::ConvertNameToSystem() +|* +|* Beschreibung +|* Ersterstellung DV 29.03.96 +|* Letzte Aenderung DV 29.03.96 +|* +*************************************************************************/ + +String DirEntry::ConvertNameToSystem( const String &rName ) +{ + return rName; +} + +/************************************************************************* +|* +|* DirEntry::ConvertSystemToName() +|* +|* Beschreibung +|* Ersterstellung DV 29.03.96 +|* Letzte Aenderung DV 29.03.96 +|* +*************************************************************************/ + +String DirEntry::ConvertSystemToName( const String &rName ) +{ + return rName; +} + +/************************************************************************* +|* +|* DirEntry::IsValid() +|* +|* Beschreibung +|* Ersterstellung MI 18.09.93 +|* Letzte Aenderung TPF 18.09.98 +|* +*************************************************************************/ + +BOOL DirEntry::IsValid() const +{ + return (nError == FSYS_ERR_OK); +} + +/************************************************************************* +|* +|* DirEntry::IsRFSAvailable() +|* +|* Beschreibung +|* Ersterstellung TPF 21.10.98 +|* Letzte Aenderung TPF 21.10.98 +|* +*************************************************************************/ + +BOOL DirEntry::IsRFSAvailable() +{ + return FALSE; +} + +/************************************************************************* +|* +|* IsLongNameOnFAT() +|* +|* Beschreibung ?berpr?ft , ob das DirEntry einen langen +|* Filenamen auf einer FAT-Partition enth?lt (EAs). +|* (eigentlich nur f?r OS2 interessant) +|* Ersterstellung TPF 02.10.98 +|* Letzte Aenderung TPF 01.03.1999 +|* +*************************************************************************/ + +BOOL DirEntry::IsLongNameOnFAT() const +{ + // FAT-System? + DirEntry aTempDirEntry(*this); + aTempDirEntry.ToAbs(); + if (DirEntry::GetPathStyle(aTempDirEntry.GetDevice().GetName().GetChar(0)) != FSYS_STYLE_FAT) + { + return FALSE; // nein, also false + } + + // DirEntry-Kette auf lange Dateinamen pr?fen + for( USHORT iLevel = this->Level(); iLevel > 0; iLevel-- ) + { + const DirEntry& rEntry = (const DirEntry&) (*this)[iLevel-1]; + String aBase( rEntry.GetBase() ); + String aExtension( rEntry.GetExtension() ); + + if (aBase.Len()>8) // Name > 8? + { + return TRUE; + } + + if (aExtension.Len()>3) // Extension > 3? + { + return TRUE; + } + } + return FALSE; +} + +//======================================================================== + +#if defined(DBG_UTIL) + +void FSysTest() +{ +} + +#endif + diff --git a/tools/source/fsys/filecopy.cxx b/tools/source/fsys/filecopy.cxx new file mode 100644 index 000000000000..5b98002fd999 --- /dev/null +++ b/tools/source/fsys/filecopy.cxx @@ -0,0 +1,489 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: filecopy.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#if defined WNT +#ifndef _SVWIN_H +#include <io.h> +#include <tools/svwin.h> +#endif + +#elif defined(OS2) +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <share.h> +#include <io.h> + +#elif defined UNX +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> + +#endif + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <stdio.h> +#include "comdep.hxx" +#include <tools/fsys.hxx> +#include <tools/stream.hxx> +#include <osl/file.hxx> + +using namespace ::osl; + +/************************************************************************* +|* +|* FileCopier::FileCopier() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 13.04.94 +|* Letzte Aenderung MI 13.04.94 +|* +*************************************************************************/ + +FileCopier::FileCopier() : + + nBytesTotal ( 0 ), + nBytesCopied( 0 ), + nBlockSize ( 4096 ), + pImp ( new FileCopier_Impl ) + +{ +} + +// ----------------------------------------------------------------------- + +FileCopier::FileCopier( const DirEntry& rSource, const DirEntry& rTarget ) : + + aSource ( rSource ), + aTarget ( rTarget ), + nBytesTotal ( 0 ), + nBytesCopied( 0 ), + nBlockSize ( 4096 ), + pImp ( new FileCopier_Impl ) + +{ +} + +// ----------------------------------------------------------------------- + +FileCopier::FileCopier( const FileCopier& rCopier ) : + + aSource ( rCopier.aSource ), + aTarget ( rCopier.aTarget ), + nBytesTotal ( 0 ), + nBytesCopied ( 0 ), + aProgressLink ( rCopier.aProgressLink ), + nBlockSize ( 4096 ), + pImp ( new FileCopier_Impl ) + +{ +} + +/************************************************************************* +|* +|* FileCopier::~FileCopier() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 13.04.94 +|* Letzte Aenderung MI 13.04.94 +|* +*************************************************************************/ + +FileCopier::~FileCopier() +{ + delete pImp; +} + +/************************************************************************* +|* +|* FileCopier::operator =() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 13.04.94 +|* Letzte Aenderung MI 13.04.94 +|* +*************************************************************************/ + +FileCopier& FileCopier::operator = ( const FileCopier &rCopier ) +{ + aSource = rCopier.aSource; + aTarget = rCopier.aTarget; + nBytesTotal = rCopier.nBytesTotal; + nBytesCopied = rCopier.nBytesCopied; + nBytesCopied = rCopier.nBytesCopied; + nBlockSize = rCopier.nBlockSize; + aProgressLink = rCopier.aProgressLink; + *pImp = *(rCopier.pImp); + return *this; +} + +/************************************************************************* +|* +|* FileCopier::Progress() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 13.04.94 +|* Letzte Aenderung MI 13.04.94 +|* +*************************************************************************/ + +BOOL FileCopier::Progress() +{ + if ( !aProgressLink ) + return TRUE; + else + { + if ( aProgressLink.Call( this ) ) + return TRUE; + return ( 0 == Error( ERRCODE_ABORT, 0, 0 ) ); + } +} + +//--------------------------------------------------------------------------- + +ErrCode FileCopier::Error( ErrCode eErr, const DirEntry* pSource, const DirEntry* pTarget ) +{ + // kein Fehler oder kein ErrorHandler? + if ( !eErr || !pImp->aErrorLink ) + // => Error beibehalten + return eErr; + + // sonst gesetzten ErrorHandler fragen + pImp->pErrSource = pSource; + pImp->pErrTarget = pTarget; + pImp->eErr = eErr; + ErrCode eRet = (ErrCode) pImp->aErrorLink.Call( this ); + pImp->pErrSource = 0; + pImp->pErrTarget = 0; + return eRet; +} + +//--------------------------------------------------------------------------- + +const DirEntry* FileCopier::GetErrorSource() const +{ + return pImp->pErrSource; +} + +//--------------------------------------------------------------------------- + +const DirEntry* FileCopier::GetErrorTarget() const +{ + return pImp->pErrTarget; +} + +//--------------------------------------------------------------------------- + +ErrCode FileCopier::GetError() const +{ + return pImp->eErr; +} + +//--------------------------------------------------------------------------- + +void FileCopier::SetErrorHdl( const Link &rLink ) +{ + pImp->aErrorLink = rLink; +} + +//--------------------------------------------------------------------------- + +const Link& FileCopier::GetErrorHdl() const +{ + return pImp->aErrorLink ; +} + +/************************************************************************* +|* +|* FileCopier::Execute() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 13.04.94 +|* Letzte Aenderung PB 16.06.00 +|* +*************************************************************************/ + +FSysError FileCopier::DoCopy_Impl( + const DirEntry &rSource, const DirEntry &rTarget ) +{ + FSysError eRet = FSYS_ERR_OK; + ErrCode eWarn = FSYS_ERR_OK; + + // HPFS->FAT? + FSysPathStyle eSourceStyle = DirEntry::GetPathStyle( rSource.ImpGetTopPtr()->GetName() ); + FSysPathStyle eTargetStyle = DirEntry::GetPathStyle( rTarget.ImpGetTopPtr()->GetName() ); + BOOL bMakeShortNames = ( eSourceStyle == FSYS_STYLE_HPFS && eTargetStyle == FSYS_STYLE_FAT ); + + // Zieldateiname ggf. kuerzen + DirEntry aTgt; + if ( bMakeShortNames ) + { + aTgt = rTarget.GetPath(); + aTgt.MakeShortName( rTarget.GetName() ); + } + else + aTgt = rTarget; + + // kein Move wenn Namen gekuerzt werden muessten + if ( bMakeShortNames && FSYS_ACTION_MOVE == ( pImp->nActions & FSYS_ACTION_MOVE ) && aTgt != rTarget ) + return ERRCODE_IO_NAMETOOLONG; + + // source is directory? + FileStat aSourceFileStat( rSource ); + if ( aSourceFileStat.IsKind( FSYS_KIND_DIR ) ) + { +#ifdef OS2 + CHAR szSource[CCHMAXPATHCOMP]; + HOBJECT hSourceObject; + + strcpy(szSource, ByteString(rSource.GetFull(), osl_getThreadTextEncoding()).GetBuffer()); + hSourceObject = WinQueryObject(szSource); + + if ( hSourceObject ) + { + PSZ pszSourceName; + PSZ pszTargetName; + CHAR szTarget[CCHMAXPATHCOMP]; + HOBJECT hTargetObject; + HOBJECT hReturn = NULLHANDLE; + + strcpy(szTarget, ByteString(rTarget.GetFull(), osl_getThreadTextEncoding()).GetBuffer()); + pszTargetName = strrchr(szTarget, '\\'); + pszSourceName = strrchr(szSource, '\\'); + + hTargetObject = WinQueryObject(szTarget); + + if ( hTargetObject ) + WinDestroyObject(hTargetObject); + + if ( pszTargetName && pszSourceName ) + { + *pszTargetName = '\0'; + pszSourceName++; + pszTargetName++; + + if(strcmp(pszSourceName, pszTargetName) == 0) + { + hTargetObject = WinQueryObject(szTarget); + + if(pImp->nActions & FSYS_ACTION_MOVE) + { + hReturn = WinMoveObject(hSourceObject, hTargetObject, 0); + } + else + { + hReturn = WinCopyObject(hSourceObject, hTargetObject, 0); + } + if ( bMakeShortNames && aTarget.Exists() ) + aTarget.Kill(); + return hReturn ? FSYS_ERR_OK : FSYS_ERR_UNKNOWN; + } + } + } +#endif + // recursive copy + eRet = Error( aTgt.MakeDir() ? FSYS_ERR_OK : FSYS_ERR_UNKNOWN, 0, &aTgt ); + Dir aSourceDir( rSource, FSYS_KIND_DIR|FSYS_KIND_FILE ); + for ( USHORT n = 0; ERRCODE_TOERROR(eRet) == FSYS_ERR_OK && n < aSourceDir.Count(); ++n ) + { + const DirEntry &rSubSource = aSourceDir[n]; + DirEntryFlag eFlag = rSubSource.GetFlag(); + if ( eFlag != FSYS_FLAG_CURRENT && eFlag != FSYS_FLAG_PARENT ) + { + DirEntry aSubTarget( aTgt ); + aSubTarget += rSubSource.GetName(); + eRet = DoCopy_Impl( rSubSource, aSubTarget ); + if ( eRet && !eWarn ) + eWarn = eRet; + } + } + } + else if ( aSourceFileStat.IsKind(FSYS_KIND_FILE) ) + { + if ( ( FSYS_ACTION_KEEP_EXISTING == ( pImp->nActions & FSYS_ACTION_KEEP_EXISTING ) ) && + aTgt.Exists() ) + { + // Do not overwrite existing file in target folder. + return ERRCODE_NONE; + } + + // copy file + nBytesCopied = 0; + nBytesTotal = FileStat( rSource ).GetSize(); + + ::rtl::OUString aFileName; + FileBase::getFileURLFromSystemPath( ::rtl::OUString(rSource.GetFull()), aFileName ); + SvFileStream aSrc( aFileName, STREAM_READ|STREAM_NOCREATE|STREAM_SHARE_DENYNONE ); + + if ( !aSrc.GetError() ) + { +#ifdef UNX + struct stat buf; + if ( fstat( aSrc.GetFileHandle(), &buf ) == -1 ) + eRet = Error( FSYS_ERR_ACCESSDENIED, 0, &aTgt ); +#endif + ::rtl::OUString aTargetFileName; + FileBase::getFileURLFromSystemPath( ::rtl::OUString(aTgt.GetFull()), aTargetFileName ); + + SvFileStream aTargetStream( aTargetFileName, STREAM_WRITE | STREAM_TRUNC | STREAM_SHARE_DENYWRITE ); + if ( !aTargetStream.GetError() ) + { +#ifdef UNX + if ( fchmod( aTargetStream.GetFileHandle(), buf.st_mode ) == -1 ) + eRet = Error( FSYS_ERR_ACCESSDENIED, 0, &aTgt ); +#endif + size_t nAllocSize = 0, nSize = 0; + char *pBuf = 0; + while ( Progress() && nSize == nAllocSize && eRet == FSYS_ERR_OK ) + { + // adjust the block-size + if ( nBlockSize > nAllocSize ) + { + delete[] pBuf; + nAllocSize = nBlockSize; + pBuf = new char[nAllocSize]; + } + + // copy one block + nSize = aSrc.Read( pBuf, nBlockSize ); + aTargetStream.Write( pBuf, nSize ); + if ( aTargetStream.GetError() ) + eRet = Error( aTargetStream.GetError(), 0, &aTgt ); + + // adjust counters + nBytesCopied += nSize; + if ( nBytesCopied > nBytesTotal ) + nBytesTotal = nBytesCopied; + } + delete[] pBuf; + } + else + eRet = Error( aTargetStream.GetError(), 0, &aTgt ); + + // unvollstaendiges File wieder loeschen + aTargetStream.Close(); + + if ( nBytesCopied != nBytesTotal ) + { + aTgt.Kill(); + } + } + else + eRet = Error( aSrc.GetError(), &rSource, 0 ); + } + else if ( aSourceFileStat.IsKind(FSYS_KIND_NONE) ) + eRet = Error( ERRCODE_IO_NOTEXISTS, &rSource, 0 ); + else + eRet = Error( ERRCODE_IO_NOTSUPPORTED, &rSource, 0 ); + +#ifdef WNT + // Set LastWriteTime and Attributes of the target identical with the source + + if ( FSYS_ERR_OK == ERRCODE_TOERROR(eRet) ) + { + WIN32_FIND_DATA fdSource; + ByteString aFullSource(aSource.GetFull(), osl_getThreadTextEncoding()); + ByteString aFullTarget(aTgt.GetFull(), osl_getThreadTextEncoding()); + HANDLE hFind = FindFirstFile( aFullSource.GetBuffer() , &fdSource ); + if ( hFind != INVALID_HANDLE_VALUE ) + { + FindClose( hFind ); + + HANDLE hFile = CreateFile( aFullTarget.GetBuffer(), GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + + if ( hFile != INVALID_HANDLE_VALUE ) + { + SetFileTime( hFile, NULL, NULL, &fdSource.ftLastWriteTime ); + CloseHandle( hFile ); + } + + SetFileAttributes( aFullTarget.GetBuffer(), fdSource.dwFileAttributes ); + } + } +#endif + // bei Move ggf. das File/Dir loeschen + if ( FSYS_ERR_OK == ERRCODE_TOERROR(eRet) && ( pImp->nActions & FSYS_ACTION_MOVE ) ) + { + ErrCode eKillErr = Error( rSource.Kill() | ERRCODE_WARNING_MASK, &rSource, 0 ); + if ( eKillErr != ERRCODE_WARNING_MASK ) + { + if ( rSource.Exists() ) + // loeschen ging nicht => dann die Kopie wieder loeschen + aTgt.Kill( pImp->nActions ); + if ( !eWarn ) + eWarn = eKillErr; + } + } + + return !eRet ? eWarn : eRet; +} + +// ----------------------------------------------------------------------- + +FSysError FileCopier::Execute( FSysAction nActions ) +{ + return ExecuteExact( nActions ); +} + +// ----------------------------------------------------------------------- + +FSysError FileCopier::ExecuteExact( FSysAction nActions, FSysExact eExact ) +{ + DirEntry aAbsSource = DirEntry( aSource); + DirEntry aAbsTarget = DirEntry( aTarget ); + pImp->nActions = nActions; + + // check if both pathes are accessible and source and target are different + if ( !aAbsTarget.ToAbs() || !aAbsSource.ToAbs() || aAbsTarget == aAbsSource ) + return FSYS_ERR_ACCESSDENIED; + + // check if copy would be endless recursive into itself + if ( FSYS_ACTION_RECURSIVE == ( nActions & FSYS_ACTION_RECURSIVE ) && + aAbsSource.Contains( aAbsTarget ) ) + return ERRCODE_IO_RECURSIVE; + + // target is directory? + if ( eExact == FSYS_NOTEXACT && + FileStat( aAbsTarget ).IsKind(FSYS_KIND_DIR) && FileStat( aAbsSource ).IsKind(FSYS_KIND_FILE) ) + // append name of source + aAbsTarget += aSource.GetName(); + + // recursive copy + return DoCopy_Impl( aAbsSource, aAbsTarget ); +} diff --git a/tools/source/fsys/fstat.cxx b/tools/source/fsys/fstat.cxx new file mode 100644 index 000000000000..1ad1a47566cb --- /dev/null +++ b/tools/source/fsys/fstat.cxx @@ -0,0 +1,422 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: fstat.cxx,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#if defined( WIN) +#include <stdio.h> +#include <dos.h> +#endif + +#ifdef UNX +#include <errno.h> +#endif + +#include <limits.h> +#include <string.h> + +#include "comdep.hxx" +#include <tools/fsys.hxx> + +/************************************************************************* +|* +|* FileStat::FileStat() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 11.06.91 +|* Letzte Aenderung MI 11.06.91 +|* +*************************************************************************/ + +FileStat::FileStat() +: // don't use Default-Ctors! + aDateCreated( ULONG(0) ), + aTimeCreated( ULONG(0) ), + aDateModified( ULONG(0) ), + aTimeModified( ULONG(0) ), + aDateAccessed( ULONG(0) ), + aTimeAccessed( ULONG(0) ) +{ + nSize = 0; + nKindFlags = FSYS_KIND_UNKNOWN; + nError = FSYS_ERR_OK; +} + +/************************************************************************* +|* +|* FileStat::FileStat() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 11.06.91 +|* Letzte Aenderung MI 11.06.91 +|* +*************************************************************************/ + +FileStat::FileStat( const DirEntry& rDirEntry, FSysAccess nAccess ) +: // don't use Default-Ctors! + aDateCreated( ULONG(0) ), + aTimeCreated( ULONG(0) ), + aDateModified( ULONG(0) ), + aTimeModified( ULONG(0) ), + aDateAccessed( ULONG(0) ), + aTimeAccessed( ULONG(0) ) +{ + BOOL bCached = FSYS_ACCESS_CACHED == (nAccess & FSYS_ACCESS_CACHED); + BOOL bFloppy = FSYS_ACCESS_FLOPPY == (nAccess & FSYS_ACCESS_FLOPPY); + +#ifdef FEAT_FSYS_DOUBLESPEED + const FileStat *pStatFromDir = bCached ? rDirEntry.ImpGetStat() : 0; + if ( pStatFromDir ) + { + nError = pStatFromDir->nError; + nKindFlags = pStatFromDir->nKindFlags; + nSize = pStatFromDir->nSize; + aCreator = pStatFromDir->aCreator; + aType = pStatFromDir->aType; + aDateCreated = pStatFromDir->aDateCreated; + aTimeCreated = pStatFromDir->aTimeCreated; + aDateModified = pStatFromDir->aDateModified; + aTimeModified = pStatFromDir->aTimeModified; + aDateAccessed = pStatFromDir->aDateAccessed; + aTimeAccessed = pStatFromDir->aTimeAccessed; + } + else +#endif + Update( rDirEntry, bFloppy ); +} + +/************************************************************************* +|* +|* FileStat::IsYounger() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MA 11.11.91 +|* Letzte Aenderung MA 11.11.91 +|* +*************************************************************************/ + +// TRUE wenn die Instanz j"unger als rIsOlder ist. +// FALSE wenn die Instanz "alter oder gleich alt wie rIsOlder ist. + +BOOL FileStat::IsYounger( const FileStat& rIsOlder ) const +{ + if ( aDateModified > rIsOlder.aDateModified ) + return TRUE; + if ( ( aDateModified == rIsOlder.aDateModified ) && + ( aTimeModified > rIsOlder.aTimeModified ) ) + return TRUE; + + return FALSE; +} + +/************************************************************************* +|* +|* FileStat::IsKind() +|* +|* Ersterstellung MA 11.11.91 (?) +|* Letzte Aenderung KH 16.01.95 +|* +*************************************************************************/ + +BOOL FileStat::IsKind( DirEntryKind nKind ) const +{ + BOOL bRet = ( ( nKind == FSYS_KIND_UNKNOWN ) && + ( nKindFlags == FSYS_KIND_UNKNOWN ) ) || + ( ( nKindFlags & nKind ) == nKind ); + return bRet; +} + +/************************************************************************* +|* +|* FileStat::HasReadOnlyFlag() +|* +|* Ersterstellung MI 06.03.97 +|* Letzte Aenderung UT 01.07.98 +|* +*************************************************************************/ + +BOOL FileStat::HasReadOnlyFlag() +{ +#if defined WNT || defined UNX || defined OS2 + return TRUE; +#else + return FALSE; +#endif +} + +/************************************************************************* +|* +|* FileStat::GetReadOnlyFlag() +|* +|* Ersterstellung MI 06.03.97 +|* Letzte Aenderung UT 02.07.98 +|* +*************************************************************************/ + +BOOL FileStat::GetReadOnlyFlag( const DirEntry &rEntry ) +{ + + ByteString aFPath(rEntry.GetFull(), osl_getThreadTextEncoding()); +#if defined WNT + DWORD nRes = GetFileAttributes( (LPCTSTR) aFPath.GetBuffer() ); + return ULONG_MAX != nRes && + ( FILE_ATTRIBUTE_READONLY & nRes ) == FILE_ATTRIBUTE_READONLY; +#elif defined OS2 + FILESTATUS3 aFileStat; + APIRET nRet = DosQueryPathInfo( (PSZ)aFPath.GetBuffer(), 1, &aFileStat, sizeof(aFileStat) ); + switch ( nRet ) + { + case NO_ERROR: + return FILE_READONLY == ( aFileStat.attrFile & FILE_READONLY ); + default: + return FALSE; + } +#elif defined UNX + /* could we stat the object? */ + struct stat aBuf; + if (stat(aFPath.GetBuffer(), &aBuf)) + return FALSE; + /* jupp, is writable for user? */ + return((aBuf.st_mode & S_IWUSR) != S_IWUSR); +#else + return FALSE; +#endif +} + +/************************************************************************* +|* +|* FileStat::SetReadOnlyFlag() +|* +|* Ersterstellung MI 06.03.97 +|* Letzte Aenderung UT 01.07.98 +|* +*************************************************************************/ + +ULONG FileStat::SetReadOnlyFlag( const DirEntry &rEntry, BOOL bRO ) +{ + + ByteString aFPath(rEntry.GetFull(), osl_getThreadTextEncoding()); + +#if defined WNT + DWORD nRes = GetFileAttributes( (LPCTSTR) aFPath.GetBuffer() ); + if ( ULONG_MAX != nRes ) + nRes = SetFileAttributes( (LPCTSTR) aFPath.GetBuffer(), + ( nRes & ~FILE_ATTRIBUTE_READONLY ) | + ( bRO ? FILE_ATTRIBUTE_READONLY : 0 ) ); + return ( ULONG_MAX == nRes ) ? ERRCODE_IO_UNKNOWN : 0; +#elif defined OS2 + FILESTATUS3 aFileStat; + APIRET nRet = DosQueryPathInfo( (PSZ)aFPath.GetBuffer(), 1, &aFileStat, sizeof(aFileStat) ); + if ( !nRet ) + { + aFileStat.attrFile = ( aFileStat.attrFile & ~FILE_READONLY ) | + ( bRO ? FILE_READONLY : 0 ); + nRet = DosSetPathInfo( (PSZ)aFPath.GetBuffer(), 1, &aFileStat, sizeof(aFileStat), 0 ); + } + switch ( nRet ) + { + case NO_ERROR: + return ERRCODE_NONE; + + case ERROR_SHARING_VIOLATION: + return ERRCODE_IO_LOCKVIOLATION; + + default: + return ERRCODE_IO_NOTEXISTS; + } +#elif defined UNX + /* first, stat the object to get permissions */ + struct stat aBuf; + if (stat(aFPath.GetBuffer(), &aBuf)) + return ERRCODE_IO_NOTEXISTS; + /* set or clear write bit for user */ + mode_t nMode; + if (bRO) + { + nMode = aBuf.st_mode & ~S_IWUSR; + nMode = aBuf.st_mode & ~S_IWGRP; + nMode = aBuf.st_mode & ~S_IWOTH; + } + else + nMode = aBuf.st_mode | S_IWUSR; + /* change it on fs */ + if (chmod(aFPath.GetBuffer(), nMode)) + { + switch (errno) + { + case EPERM : + case EROFS : + return ERRCODE_IO_ACCESSDENIED; + default : + return ERRCODE_IO_NOTEXISTS; + } + } + else + return ERRCODE_NONE; +#else + return ERRCODE_IO_NOTSUPPORTED; +#endif +} + +/************************************************************************* +|* +|* FileStat::SetDateTime +|* +|* Ersterstellung PB 27.06.97 +|* Letzte Aenderung +|* +*************************************************************************/ +#if defined WNT || defined OS2 + +void FileStat::SetDateTime( const String& rFileName, + const DateTime& rNewDateTime ) +{ + ByteString aFileName(rFileName, osl_getThreadTextEncoding()); + + Date aNewDate = rNewDateTime; + Time aNewTime = rNewDateTime; + +#if defined WNT + TIME_ZONE_INFORMATION aTZI; + DWORD dwTZI = GetTimeZoneInformation( &aTZI ); + + if ( dwTZI != (DWORD)-1 && dwTZI != TIME_ZONE_ID_UNKNOWN ) + { + // 1. Korrektur der Zeitzone + LONG nDiff = aTZI.Bias; + Time aOldTime = aNewTime; // alte Zeit merken + + // 2. evt. Korrektur Sommer-/Winterzeit + if ( dwTZI == TIME_ZONE_ID_DAYLIGHT ) + nDiff += aTZI.DaylightBias; + + Time aDiff( abs( nDiff / 60 /*Min -> Std*/ ), 0 ); + + if ( nDiff > 0 ) + { + aNewTime += aDiff; // Stundenkorrektur + + // bei "Uberlauf korrigieren + if ( aNewTime >= Time( 24, 0 ) ) + aNewTime -= Time( 24, 0 ); + + // Tages"uberlauf? + if ( aOldTime == Time( 0, 0 ) || // 00:00 -> 01:00 + aNewTime < aOldTime ) // 23:00 -> 00:00 | 01:00 ... + aNewDate++; + } + else if ( nDiff < 0 ) + { + aNewTime -= aDiff; // Stundenkorrektur + + // negative Zeit (-1:00) korrigieren: 23:00 + if (aNewTime < Time( 0, 0 ) ) + aNewTime += Time( 24, 0 ); + + // Tagesunterlauf ? + if ( aOldTime == Time( 0, 0 ) || // 00:00 -> 23:00 + aNewTime > aOldTime ) // 01:00 -> 23:00 | 22:00 ... + aNewDate--; + } + } + + + SYSTEMTIME aTime; + aTime.wYear = aNewDate.GetYear(); + aTime.wMonth = aNewDate.GetMonth(); + aTime.wDayOfWeek = 0; + aTime.wDay = aNewDate.GetDay(); + aTime.wHour = aNewTime.GetHour(); + aTime.wMinute = aNewTime.GetMin(); + aTime.wSecond = aNewTime.GetSec(); + aTime.wMilliseconds = 0; + FILETIME aFileTime; + SystemTimeToFileTime( &aTime, &aFileTime ); + + HANDLE hFile = CreateFile( aFileName.GetBuffer(), GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ); + + if ( hFile != INVALID_HANDLE_VALUE ) + { + SetFileTime( hFile, &aFileTime, &aFileTime, &aFileTime ); + CloseHandle( hFile ); + } +#elif defined OS2 + + // open file + ULONG nAction = FILE_EXISTED; + HFILE hFile = 0; + ULONG nFlags = OPEN_FLAGS_WRITE_THROUGH | + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_CACHE | + OPEN_FLAGS_RANDOM | OPEN_FLAGS_NOINHERIT | + OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE; + + APIRET nRet = DosOpen((PSZ)aFileName.GetBuffer(), &hFile, (PULONG)&nAction, + 0/*size*/, FILE_NORMAL, + OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS, + nFlags, 0/*ea*/); + + if ( nRet == 0 ) + { + FILESTATUS3 FileInfoBuffer; + + nRet = DosQueryFileInfo( + hFile, 1, &FileInfoBuffer, sizeof(FileInfoBuffer)); + + if ( nRet == 0 ) + { + FDATE aNewDate; + FTIME aNewTime; + + // create date and time words + aNewDate.day = rNewDateTime.GetDay(); + aNewDate.month = rNewDateTime.GetMonth(); + aNewDate.year = rNewDateTime.GetYear() - 1980; + aNewTime.twosecs = rNewDateTime.GetSec() / 2; + aNewTime.minutes = rNewDateTime.GetMin(); + aNewTime.hours = rNewDateTime.GetHour(); + + // set file date and time + FileInfoBuffer.fdateCreation = aNewDate; + FileInfoBuffer.ftimeCreation = aNewTime; + FileInfoBuffer.fdateLastAccess = aNewDate; + FileInfoBuffer.ftimeLastAccess = aNewTime; + FileInfoBuffer.fdateLastWrite = aNewDate; + FileInfoBuffer.ftimeLastWrite = aNewTime; + + DosSetFileInfo(hFile, 1, &FileInfoBuffer, sizeof(FileInfoBuffer)); + } + DosClose(hFile); + } +#endif + +} +#endif diff --git a/tools/source/fsys/makefile.mk b/tools/source/fsys/makefile.mk new file mode 100644 index 000000000000..bd9ebdb82f1b --- /dev/null +++ b/tools/source/fsys/makefile.mk @@ -0,0 +1,71 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.12 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=fsys +ENABLE_EXCEPTIONS=true + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +.IF "$(COM)"=="GCC" +CFLAGSCXX+=-fexceptions +.ENDIF + + +# --- Files -------------------------------------------------------- + +SLOFILES= \ + $(SLO)$/tempfile.obj \ + $(SLO)$/wldcrd.obj \ + $(SLO)$/fstat.obj \ + $(SLO)$/comdep.obj \ + $(SLO)$/filecopy.obj \ + $(SLO)$/dirent.obj \ + $(SLO)$/tdir.obj \ + $(SLO)$/urlobj.obj + +OBJFILES= $(OBJ)$/wldcrd.obj \ + $(OBJ)$/fstat.obj \ + $(OBJ)$/comdep.obj \ + $(OBJ)$/filecopy.obj \ + $(OBJ)$/dirent.obj \ + $(OBJ)$/tdir.obj \ + $(OBJ)$/urlobj.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + diff --git a/tools/source/fsys/os2.cxx b/tools/source/fsys/os2.cxx new file mode 100644 index 000000000000..fd5ffa9291a3 --- /dev/null +++ b/tools/source/fsys/os2.cxx @@ -0,0 +1,1017 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: os2.cxx,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#define INCL_DOSEXCEPTIONS + +#include <stdlib.h> + +#ifdef __BORLANDC__ +#include <alloc.h> +#else +#include <malloc.h> +#endif +#include <tools/debug.hxx> +#include <tools/list.hxx> +#include <tools/bigint.hxx> +#include <tools/fsys.hxx> +#include "comdep.hxx" + +#ifdef OS2 +#ifndef _VOS_MUTEX_HXX //autogen +#include <vos/mutex.hxx> +#endif +#endif + +int Sys2SolarError_Impl( int nSysErr ); + +DECLARE_LIST( DirEntryList, DirEntry* ); +DECLARE_LIST( FSysSortList, FSysSort* ); +DECLARE_LIST( FileStatList, FileStat* ); + +static char sCaseMap[256]; +static BOOL bCaseMap = FALSE; +static BOOL bDriveMap = FALSE; + +struct DriveMapItem +{ + DirEntryKind nKind; + char cName; + FSysPathStyle nStyle; +}; + +void CreateCaseMapImpl(); +void CreateDriveMapImpl(); + +static DriveMapItem aDriveMap[26]; + +static BOOL bLastCaseSensitive = FALSE; + +//==================================================================== + +int ApiRet2ToSolarError_Impl( int nApiRet ) +{ + switch ( nApiRet ) + { + case NO_ERROR: return ERRCODE_NONE; + case ERROR_FILE_NOT_FOUND: return ERRCODE_IO_NOTEXISTS; + case ERROR_PATH_NOT_FOUND: return ERRCODE_IO_NOTEXISTSPATH; + case ERROR_TOO_MANY_OPEN_FILES: return ERRCODE_IO_TOOMANYOPENFILES; + case ERROR_ACCESS_DENIED: return ERRCODE_IO_ACCESSDENIED; + case ERROR_NOT_ENOUGH_MEMORY: return ERRCODE_IO_OUTOFMEMORY; + case ERROR_BAD_FORMAT: return ERRCODE_IO_WRONGFORMAT; + case ERROR_NOT_SAME_DEVICE: return ERRCODE_IO_INVALIDDEVICE; + case ERROR_WRITE_PROTECT: return ERRCODE_IO_INVALIDDEVICE; + case ERROR_BAD_UNIT: return ERRCODE_IO_INVALIDDEVICE; + case ERROR_CRC: return ERRCODE_IO_INVALIDDEVICE; + case ERROR_NOT_DOS_DISK: return ERRCODE_IO_INVALIDDEVICE; + case ERROR_WRITE_FAULT: return ERRCODE_IO_CANTWRITE; + case ERROR_READ_FAULT: return ERRCODE_IO_CANTREAD; + case ERROR_SHARING_VIOLATION: return ERRCODE_IO_LOCKVIOLATION; + case ERROR_LOCK_VIOLATION: return ERRCODE_IO_LOCKVIOLATION; + case ERROR_WRONG_DISK: return ERRCODE_IO_LOCKVIOLATION; + case ERROR_HANDLE_DISK_FULL: return ERRCODE_IO_OUTOFSPACE; + case ERROR_NOT_SUPPORTED: return ERRCODE_IO_NOTSUPPORTED; + case ERROR_DUP_NAME: return ERRCODE_IO_ALREADYEXISTS; + case ERROR_BAD_NETPATH: return ERRCODE_IO_NOTEXISTSPATH; + case ERROR_DEV_NOT_EXIST: return ERRCODE_IO_NOTEXISTS; + case ERROR_NETWORK_ACCESS_DENIED: return ERRCODE_IO_ACCESSDENIED; + case ERROR_INVALID_PARAMETER: return ERRCODE_IO_INVALIDPARAMETER; + case ERROR_NET_WRITE_FAULT: return ERRCODE_IO_CANTWRITE; + case ERROR_DEVICE_IN_USE: return ERRCODE_IO_INVALIDPARAMETER; + case ERROR_DISK_FULL: return ERRCODE_IO_OUTOFSPACE; + case ERROR_BAD_ARGUMENTS: return ERRCODE_IO_INVALIDPARAMETER; + case ERROR_BAD_PATHNAME: return ERRCODE_IO_NOTEXISTSPATH; + case ERROR_LOCK_FAILED: return ERRCODE_IO_LOCKVIOLATION; + case ERROR_LOCKED: return ERRCODE_IO_LOCKVIOLATION; + case ERROR_DUPLICATE_NAME: return ERRCODE_IO_ALREADYEXISTS; + case ERROR_DIRECTORY_IN_CDS: return ERRCODE_IO_LOCKVIOLATION; + case ERROR_CURRENT_DIRECTORY: return ERRCODE_IO_LOCKVIOLATION; + case ERROR_FILENAME_EXCED_RANGE: return ERRCODE_IO_NAMETOOLONG; + } + + DBG_TRACE1( "FSys: unknown apiret error %d occured", nApiRet ); + return FSYS_ERR_UNKNOWN; +} + +//-------------------------------------------------------------------- + +char* volumeid( const char* pPfad ) +{ + static FSINFO aFSInfoBuf; + ULONG ulFSInfoLevel = FSIL_VOLSER; + ULONG nDriveNumber; + + nDriveNumber = toupper(*pPfad) - 'A' + 1; + + if ( nDriveNumber >= 3 ) + { + APIRET rc = DosQueryFSInfo( + nDriveNumber, ulFSInfoLevel, &aFSInfoBuf, sizeof(FSINFO) ); + if ( rc ) + return 0; + return (char*) aFSInfoBuf.vol.szVolLabel; + } + return 0; +} + +//-------------------------------------------------------------------- + +/************************************************************************* +|* +|* DirEntry::ToAbs() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 13:30 +|* +*************************************************************************/ + +BOOL DirEntry::ToAbs() +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + if ( FSYS_FLAG_VOLUME == eFlag ) + { + eFlag = FSYS_FLAG_ABSROOT; + return TRUE; + } + + if ( IsAbs() ) + return TRUE; + + char sBuf[_MAX_PATH + 1]; + *this = DirEntry( String( getcwd( sBuf, _MAX_PATH ), osl_getThreadTextEncoding() ) ) + *this; + + return IsAbs(); +} + +/************************************************************************* +|* +|* DirEntry::GetVolume() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 04.03.92 +|* Letzte Aenderung MI 04.03.92 +|* +*************************************************************************/ + +String DirEntry::GetVolume() const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + String aRet; + const DirEntry *pTop = ImpGetTopPtr(); + ByteString aName = ByteString( pTop->aName ).ToLowerAscii(); + + if ( ( pTop->eFlag == FSYS_FLAG_ABSROOT || + pTop->eFlag == FSYS_FLAG_RELROOT || + pTop->eFlag == FSYS_FLAG_VOLUME ) + && aName != "a:" && aName != "b:" && Exists() ) + { + const char *pVol; + pVol = volumeid( (char*) pTop->aName.GetBuffer() ); + if (pVol) + aRet = String( pVol, osl_getThreadTextEncoding()); + } + + return aRet; +} + +/************************************************************************* +|* +|* DirEntry::SetCWD() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MI 21.05.92 +|* +*************************************************************************/ + +BOOL DirEntry::SetCWD( BOOL bSloppy ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + if ( eFlag == FSYS_FLAG_CURRENT && !aName.Len() ) + return TRUE; + + if ( !chdir(ByteString(GetFull(), osl_getThreadTextEncoding()).GetBuffer()) ) + { + //nError = FSYS_ERR_OK; + return TRUE; + } + + if ( bSloppy && pParent && + !chdir(ByteString(pParent->GetFull(), osl_getThreadTextEncoding()).GetBuffer()) ) + { + //nError = FSYS_ERR_OK; + return TRUE; + } + + //nError = FSYS_ERR_NOTADIRECTORY; + return FALSE; +} + +/************************************************************************* +|* +|* DirEntry::MoveTo() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 14:07 +|* +*************************************************************************/ + +#if 0 // YD see dirent.cxx +BOOL createLongNameEA( const PCSZ pszPath, ULONG ulAttributes, const String& aLongName ); + +FSysError DirEntry::MoveTo( const DirEntry& rDest ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + DirEntry aTmpDest(rDest); + FileStat aTmpStat(aTmpDest); + if ( aTmpStat.IsKind(FSYS_KIND_DIR) ) + aTmpDest += DirEntry( GetName() ); + + String aSource( GetFull() ); + String aDest( aTmpDest.GetFull() ); + String aShortSource(""); + String aShortDest(""); + + if (Folder::IsAvailable()) + { + if (IsLongNameOnFAT()) + { + // in kurzen Pfad wandeln + ItemIDPath aItemIDPath(aSource); + aShortSource = aItemIDPath.GetHostNotationPath(); + } + if (rDest.IsLongNameOnFAT()) + { + // in kurzen Pfad wandeln + ItemIDPath aItemIDPath(aDest); + aShortDest = aItemIDPath.GetHostNotationPath(); + } + } + + APIRET nRet = DosMove( aShortSource.Len()>0?(PSZ)aShortSource.GetStr():(PSZ)aSource.GetStr(), + aShortDest.Len()>0?(PSZ)aShortDest.GetStr():(PSZ)aDest.GetStr()); + + if ( nRet == ERROR_DIRECTORY_IN_CDS || + nRet == ERROR_CURRENT_DIRECTORY ) + { + // 2nd chance with modified CWD + DosSetCurrentDir( (PSZ) "\\" ); + nRet = DosMove( aShortSource.Len()>0?(PSZ)aShortSource.GetStr():(PSZ)aSource.GetStr(), + aShortDest.Len()>0?(PSZ)aShortDest.GetStr():(PSZ)aDest.GetStr()); + } + else if ( nRet == ERROR_NOT_SAME_DEVICE ) + { + // other volume => copy+delete + FileCopier aMover( *this, rDest ); + nRet = aMover.Execute( FSYS_ACTION_MOVE|FSYS_ACTION_RECURSIVE ); + return nRet; + } + + if ( (nRet==NO_ERROR) && aShortDest.Len()>0) + { + createLongNameEA((const char*)aShortDest, FILE_NORMAL, rDest.GetName()); + } + + return ApiRet2ToSolarError_Impl( nRet ); +} +#endif // 0 + +//------------------------------------------------------------------------- + +USHORT DirReader_Impl::Init() +{ + // Block-Devices auflisten? + if ( pDir->eAttrMask & FSYS_KIND_BLOCK ) + { + CreateDriveMapImpl(); + // CWD merken + DirEntry aCurrentDir; + aCurrentDir.ToAbs(); + + // einzeln auf Existenz und Masken-konformit"at pr"ufen + USHORT nRead = 0; + char sDrive[3] = { '?', ':', 0 }; + char sRoot[4] = { '?', ':', '\\', 0 }; + for ( char c = 'a'; c <= 'z'; c++ ) + { + sDrive[0] = c; + sRoot[0] = c; + DirEntry* pDrive = new DirEntry( sDrive, FSYS_FLAG_VOLUME, FSYS_STYLE_HOST ); + if ( pDir->aNameMask.Matches( String( ByteString(sDrive), osl_getThreadTextEncoding())) + && aDriveMap[c-'a'].nKind != FSYS_KIND_UNKNOWN ) + { + if ( pDir->pStatLst ) //Status fuer Sort gewuenscht? + { + FileStat *pNewStat = new FileStat( *pDrive ); + pDir->ImpSortedInsert( pDrive, pNewStat ); + } + else + pDir->ImpSortedInsert( pDrive, NULL ); + ++nRead; + } + else + delete pDrive; + } + + // CWD restaurieren + aCurrentDir.SetCWD(); + return nRead; + } + + return 0; +} + +//------------------------------------------------------------------------- + +USHORT DirReader_Impl::Read() +{ + if (!pDosDir) + { + pDosDir = opendir( (char*) ByteString(aPath, osl_getThreadTextEncoding()).GetBuffer() ); + } + + if (!pDosDir) + { + bReady = TRUE; + return 0; + } + + // Directories und Files auflisten? + if ( ( pDir->eAttrMask & FSYS_KIND_DIR || pDir->eAttrMask & FSYS_KIND_FILE ) && + ( ( pDosEntry = readdir( pDosDir ) ) != NULL ) ) + { + String aD_Name(pDosEntry->d_name, osl_getThreadTextEncoding()); + if ( pDir->aNameMask.Matches( aD_Name ) ) + { + DirEntryFlag eFlag = + 0 == strcmp( pDosEntry->d_name, "." ) ? FSYS_FLAG_CURRENT + : 0 == strcmp( pDosEntry->d_name, ".." ) ? FSYS_FLAG_PARENT + : FSYS_FLAG_NORMAL; + DirEntry *pTemp = new DirEntry( ByteString(pDosEntry->d_name), eFlag, FSYS_STYLE_UNX ); + if ( pParent ) + pTemp->ImpChangeParent( new DirEntry( *pParent ), FALSE); + FileStat aStat( *pTemp ); + if ( ( ( ( pDir->eAttrMask & FSYS_KIND_DIR ) && + ( aStat.IsKind( FSYS_KIND_DIR ) ) ) || + ( ( pDir->eAttrMask & FSYS_KIND_FILE ) && + !( aStat.IsKind( FSYS_KIND_DIR ) ) ) ) && + !( pDir->eAttrMask & FSYS_KIND_VISIBLE && + pDosEntry->d_name[0] == '.' ) ) + { + if ( pDir->pStatLst ) //Status fuer Sort gewuenscht? + pDir->ImpSortedInsert( pTemp, new FileStat( aStat ) ); + else + pDir->ImpSortedInsert( pTemp, NULL );; + return 1; + } + else + delete pTemp; + } + } + else + bReady = TRUE; + return 0; +} + +/************************************************************************* +|* +|* FileStat::FileStat() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MA 05.11.91 +|* Letzte Aenderung MA 07.11.91 +|* +*************************************************************************/ + +FileStat::FileStat( const void *pInfo, // struct dirent + const void * ): // dummy + aDateCreated(0), + aTimeCreated(0), + aDateModified(0), + aTimeModified(0), + aDateAccessed(0), + aTimeAccessed(0) +{ + struct dirent *pDirent = (struct dirent*) pInfo; + + nSize = pDirent->d_size; + + aDateCreated = MsDos2Date( (FDATE*) &pDirent->d_date ); + aTimeCreated = MsDos2Time( (FTIME*) &pDirent->d_time ); + aDateModified = aDateModified; + aTimeModified = aTimeModified; + aDateAccessed = aDateModified; + aTimeAccessed = aTimeModified; + + nKindFlags = FSYS_KIND_FILE; + if ( pDirent->d_type & DOS_DIRECT ) + nKindFlags = FSYS_KIND_DIR; +} + +/************************************************************************* +|* +|* FileStat::Update() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 11.06.91 +|* Letzte Aenderung MA 07.11.91 +|* +*************************************************************************/ + +struct _FSYS_FSQBUFFER +{ + FSQBUFFER2 aBuf; + UCHAR sBuf[256]; +}; + +BOOL FileStat::Update( const DirEntry& rDirEntry, BOOL bAccessRemovableDevice ) +{ + nSize = 0; + FSysPathStyle eStyle = FSYS_STYLE_UNKNOWN; + aCreator.Erase(); + aType.Erase(); + aDateCreated = Date(0); + aTimeCreated = Time(0); + aDateModified = Date(0); + aTimeModified = Time(0); + aDateAccessed = Date(0); + aTimeAccessed = Time(0); + + if ( !rDirEntry.IsValid() ) + { + nError = FSYS_ERR_NOTEXISTS; + return FALSE; + } + + // Sonderbehandlung falls es sich um eine Root ohne Laufwerk handelt + if ( !rDirEntry.aName.Len() && rDirEntry.eFlag == FSYS_FLAG_ABSROOT ) + { + nKindFlags = FSYS_KIND_DIR; + nError = FSYS_ERR_OK; + return TRUE; + } + + // Sonderbehandlung falls es sich um eine Wildcard handelt + ByteString aTempName( rDirEntry.GetName(), osl_getThreadTextEncoding() ); + if ( strchr( aTempName.GetBuffer(), '?' ) || + strchr( aTempName.GetBuffer(), '*' ) || + strchr( aTempName.GetBuffer(), ';' ) ) + { + nKindFlags = FSYS_KIND_WILD; + nError = FSYS_ERR_OK; + return TRUE; + } + + // Sonderbehandlung falls es sich um eine Root handelt + if ( rDirEntry.eFlag == FSYS_FLAG_VOLUME || + rDirEntry.eFlag == FSYS_FLAG_ABSROOT ) + { + if ( rDirEntry.eFlag == FSYS_FLAG_VOLUME ) + nKindFlags = FSYS_KIND_DEV; + else + nKindFlags = FSYS_KIND_DIR; + + if ( rDirEntry.aName.Len() == 2 ) + { + if ( !bDriveMap ) + CreateDriveMapImpl(); + + ByteString rDirEntryUpperCase = ByteString( rDirEntry.aName ).ToUpperAscii(); + DriveMapItem &rItem = aDriveMap[rDirEntryUpperCase.GetChar(0) - 'A']; + if ( !rItem.nKind ) + { + nError = ERRCODE_IO_INVALIDDEVICE; + nKindFlags = FSYS_KIND_UNKNOWN; + return FALSE; + } + else + { + if ( rDirEntry.eFlag == FSYS_FLAG_VOLUME ) + nKindFlags |= FSYS_KIND_BLOCK | rItem.nKind; + eStyle = rItem.nStyle; + } + } + + nError = FSYS_ERR_OK; + return TRUE; + } + + // disable error-boxes for hard-errors + DosError(FERR_DISABLEHARDERR); + + // Statusinformation vom Betriebssystem holen + DirEntry aTempDirEntry( rDirEntry ); + char* p; + + aTempDirEntry.ToAbs(); + ByteString aFullName( aTempDirEntry.GetFull(), osl_getThreadTextEncoding() ); + +#if 0 // YD + if (Folder::IsAvailable() && aTempDirEntry.IsLongNameOnFAT()) + { + // in String mit kurzem Pfad wandeln + ItemIDPath aItemIDPath(aTempDirEntry.GetFull()); + aFullName = ByteString( aItemIDPath.GetHostNotationPath(), osl_getThreadTextEncoding() ); + } +#endif + + p = (char *) aFullName.GetBuffer(); + + FILESTATUS3 filestat; + memset( &filestat, 0, sizeof( filestat ) ); + if( DosQueryPathInfo( (PSZ)p, 1, &filestat, sizeof( filestat ) ) ) + { + nError = FSYS_ERR_NOTEXISTS; + nKindFlags = FSYS_KIND_UNKNOWN; + return FALSE; + } + + nError = FSYS_ERR_OK; + nSize = filestat.cbFile; + + nKindFlags = FSYS_KIND_UNKNOWN; + if( filestat.attrFile & FILE_DIRECTORY ) + nKindFlags |= FSYS_KIND_DIR; + if ( nKindFlags == FSYS_KIND_UNKNOWN ) + nKindFlags = nKindFlags | FSYS_KIND_FILE; + + aDateModified = Date( filestat.fdateLastWrite.day, + filestat.fdateLastWrite.month, + filestat.fdateLastWrite.year + 1980 ); + + aTimeModified = Time( filestat.ftimeLastWrite.hours, + filestat.ftimeLastWrite.minutes, + filestat.ftimeLastWrite.twosecs*2 ); + + if ( filestat.fdateCreation.day ) + { + aDateCreated = Date( filestat.fdateCreation.day, + filestat.fdateCreation.month, + filestat.fdateCreation.year + 1980 ); + + aTimeCreated = Time( filestat.ftimeCreation.hours, + filestat.ftimeCreation.minutes, + filestat.ftimeCreation.twosecs*2 ); + } + else + { + aDateCreated = aDateModified; + aTimeCreated = aTimeModified; + } + + if ( filestat.fdateLastAccess.day > 0 ) + { + aDateAccessed = Date( filestat.fdateLastAccess.day, + filestat.fdateLastAccess.month, + filestat.fdateLastAccess.year + 1980 ); + + aTimeAccessed = Time( filestat.ftimeLastAccess.hours, + filestat.ftimeLastAccess.minutes, + filestat.ftimeLastAccess.twosecs*2 ); + } + else + { + aDateAccessed = aDateModified; + aTimeAccessed = aTimeModified; + } + + return TRUE; +} + +BOOL IsRedirectable_Impl( const ByteString &rPath ) +{ + if ( rPath.Len() >= 3 && ':' == rPath.GetBuffer()[1] ) + { + ByteString aVolume = rPath.Copy( 0, 3 ); + DriveMapItem &rItem = aDriveMap[toupper(aVolume.GetChar(0)) - 'A']; + return FSYS_KIND_FIXED != rItem.nKind; + } + return FALSE; +} + +#if 0 +BOOL IsRedirectable_Impl( const String &rPath ) +{ + if ( rPath.Len() >= 3 && ':' == rPath.GetStr()[1] ) + { + DriveMapItem &rItem = aDriveMap[toupper(rPath[0]) - 'A']; + return FSYS_KIND_FIXED != rItem.nKind; + } + return FALSE; +} +#endif + + +/************************************************************************* +|* +|* TempDirImpl() +|* +|* Beschreibung liefert den Namens des Directories fuer temporaere +|* Dateien +|* Ersterstellung MI 16.03.94 +|* Letzte Aenderung MI 16.03.94 +|* +*************************************************************************/ + +const char* TempDirImpl( char *pBuf ) +{ + PSZ pVar; + USHORT nRet; + BOOL bAppendTemp = FALSE; // mu\s noch \\temp angeh"angt werden + + // Erstmal sehen, ob TEMP oder TMP gesetzt sind + nRet = DosScanEnv( (PSZ)"TEMP", &pVar ); + if( nRet ) + nRet = DosScanEnv( (PSZ)"temp", &pVar ); + if( nRet ) + nRet = DosScanEnv( (PSZ)"TMP", &pVar ); + if( nRet ) + nRet = DosScanEnv( (PSZ)"tmp", &pVar ); + if( nRet ) + nRet = DosScanEnv( (PSZ)"TMPDIR", &pVar ); + + // falls das geklappt hat, und ein Backslash dranhaengt, + // oder falls es bisher nicht geklappt hat, + // muessen wir nachher einen Backslash entfernen + BOOL bRemoveBS = nRet || *(pVar+strlen(pVar)-1) == '\\'; + + // Keine temp-Variable gefunden, dann gehen wir mal auf die Suche + // nach dem System-Laufwerk + if( nRet ) + { + nRet = DosScanEnv( (PSZ)"USER_INI",&pVar ); + bAppendTemp = (0 == nRet); + } + if( nRet ) + { + nRet = DosScanEnv( (PSZ)"SYSTEM_INI", &pVar ); + bAppendTemp = (0 == nRet); + } + if( nRet ) + // Wenn das immer noch nicht reicht nehmen wir eben die Root von C: +#ifdef __BORLANDC__ + pVar = (PSZ)"c:\\temp\\"; +#else + pVar = (PCSZ)"c:\\temp\\"; +#endif + strcpy( pBuf, (const char*)pVar ); + + // jetzt haengt ggf. ein Backlash dran, den wir abschneiden, + // ggf. inklusive dahinter haengendem Dateinamen + if ( bRemoveBS ) + { + char *pTail = pBuf + strlen(pBuf) - 1; + for ( char cLast = *pTail; cLast != '\\'; cLast = *(--pTail) ) + *pTail = 0; + } + + if ( bAppendTemp ) + strcat( pBuf, "\\temp" ); + DirEntry( pBuf ).MakeDir(); + + return pBuf; +} + +#define CURRENT_COUNTRY 0 +#define NLS_CODEPAGE 850 + +/*==================================================================== + * CreateCaseMapImpl() + * creates a map of each character to convert to lower + *--------------------------------------------------------------------*/ + +#if 0 +void CreateCaseMapImpl() +{ + // build a string starting with code 0 as first character upto 255 + char sTemp[256]; + USHORT n; + + for ( n = 0; n < 256; ++n ) + sTemp[n] = (char) n; + + // convert string to upper case + COUNTRYCODE aCountry; + aCountry.country = CURRENT_COUNTRY; /* Country code */ + aCountry.codepage = NLS_CODEPAGE; /* Code page */ + DosMapCase( 255, &aCountry, sTemp+1 ); + + // fill a global buffer starting with code 0 as first character upto 255 + for ( n = 0; n < 256; ++n ) + sCaseMap[n] = (char) n; + + // reorder by upper-code and store in a global buffer + for ( n = 255; n > 0; --n ) + // was this character converted? + if ( sTemp[n] != (char) n ) + // we found a convertion from upper to lower + sCaseMap[ (unsigned char) sTemp[n] ] = (char) n; + + bCaseMap = TRUE; +} + +String ToLowerImpl( const String& rSource ) +{ + if ( !bCaseMap ) + CreateCaseMapImpl(); + + // TH sagt: International ist zu langsam, also mit einer eigenen Map + ByteString aLower( rSource ); + for ( USHORT n = 0; n < aLower.Len(); ++n ) + aLower[n] = sCaseMap[ (unsigned char) aLower[n] ]; + return aLower; +} +#endif // 0 + +/*==================================================================== + * CreateDriveMapImpl() + * creates a map of drive-infos like FileSystem (style) and Kind (remote) + *--------------------------------------------------------------------*/ +typedef struct _FSQBUFFER_ +{ + FSQBUFFER2 aBuf; + UCHAR sBuf[64]; +} FSQBUFFER_; + +void CreateDriveMapImpl() +{ +#ifdef POWERPC + // !!!!! Hack, da der untere Teil mit Beta 2 noch abstuertzt !!!!! + BYTE nFloppies = 1; + for ( USHORT nDrive = 0; nDrive < 26; ++nDrive ) + { + if ( nDrive < nFloppies ) + { + aDriveMap[nDrive].nKind = FSYS_KIND_REMOVEABLE; + aDriveMap[nDrive].nStyle = FSYS_STYLE_FAT; + } + else + { + aDriveMap[nDrive].nKind = FSYS_KIND_UNKNOWN; + aDriveMap[nDrive].nStyle = FSYS_STYLE_UNKNOWN; + } + } + + aDriveMap[2].nKind = FSYS_KIND_FIXED; + aDriveMap[2].nStyle = FSYS_STYLE_FAT; +#else + FSQBUFFER_ aBuf; + ULONG nBufLen; + APIRET nRet; + USHORT nDrive; + + // disable error-boxes for hard-errors + DosError(FERR_DISABLEHARDERR); + + // determine number of floppy-drives + BYTE nFloppies; + nRet = DosDevConfig( (void*) &nFloppies, DEVINFO_FLOPPY ); + + // reset the map + for ( nDrive = 0; nDrive < 26; ++nDrive ) + { + if ( nDrive < nFloppies ) + { + aDriveMap[nDrive].nKind = FSYS_KIND_REMOVEABLE; + aDriveMap[nDrive].nStyle = FSYS_STYLE_FAT; + } + else + { + aDriveMap[nDrive].nKind = FSYS_KIND_UNKNOWN; + aDriveMap[nDrive].nStyle = FSYS_STYLE_UNKNOWN; + } + } + + // determine file-system via DosOpen/DocDevIOCtrl + for ( nDrive = 2; nDrive < 26; ++nDrive ) + { + // open drive + BOOL bFixed; + HFILE nDevHandle; + char pDriveName[3] = "#:"; + pDriveName[0] = nDrive+'a'; + ULONG nAction; + nRet = DosOpen( (PSZ) pDriveName, &nDevHandle, + &nAction, 0, 0, OPEN_ACTION_OPEN_IF_EXISTS, + OPEN_FLAGS_DASD|OPEN_SHARE_DENYNONE|OPEN_ACCESS_READONLY, + 0 ); + + // exists? + if ( !nRet ) + { + // removeable? + BYTE nDriveId = nDrive; + ULONG nParaOutLen, nDataOutLen; + nRet = DosDevIOCtl(nDevHandle, 8, 0x20, + &nDriveId, sizeof(nDriveId), &nParaOutLen, + &bFixed, sizeof(bFixed), &nDataOutLen ); + + // prepare the drive-map + if ( !nRet && !bFixed ) + aDriveMap[nDrive].nKind = FSYS_KIND_REMOVEABLE; + + // close drive + DosClose(nDevHandle); + } + else if ( nRet == ERROR_NOT_READY ) + aDriveMap[nDrive].nKind = FSYS_KIND_REMOVEABLE | FSYS_KIND_CDROM; + } + + // determine file-system via FSAttach + nRet = 0; + for ( USHORT n = 3; nRet != ERROR_NO_MORE_ITEMS; ++n ) + { + nBufLen = sizeof( aBuf ); + nRet = DosQueryFSAttach( 0, n, FSAIL_DRVNUMBER, + (_FSQBUFFER2*) &aBuf, &nBufLen ); + if ( !nRet ) + { + nDrive = toupper(aBuf.aBuf.szName[0]) - 'A'; + + if ( aDriveMap[nDrive].nKind == FSYS_KIND_UNKNOWN ) + aDriveMap[nDrive].nKind = + aBuf.aBuf.iType == 3 ? FSYS_KIND_FIXED : + aBuf.aBuf.iType == 4 ? FSYS_KIND_REMOTE : + FSYS_KIND_UNKNOWN; + + char *pType = (char*)(aBuf.aBuf.szName + aBuf.aBuf.cbName + 1); + aDriveMap[nDrive].nStyle = + strcmp( pType, "FAT" ) == 0 ? FSYS_STYLE_FAT : + strcmp( pType, "FAT32" ) == 0 ? FSYS_STYLE_VFAT : + strcmp( pType, "NTFS" ) == 0 ? FSYS_STYLE_NTFS : + strcmp( pType, "HPFS" ) == 0 ? FSYS_STYLE_HPFS : + strcmp( pType, "JFS" ) == 0 ? FSYS_STYLE_HPFS : + strcmp( pType, "RAMFS" ) == 0 ? FSYS_STYLE_HPFS : + strcmp( pType, "NDFS32" ) == 0 ? FSYS_STYLE_HPFS : + strcmp( pType, "NWFS" ) == 0 ? FSYS_STYLE_NWFS : + strcmp( pType, "EXT2" ) == 0 ? FSYS_STYLE_UNX : + strcmp( pType, "NFS" ) == 0 ? FSYS_STYLE_UNX : + FSYS_STYLE_UNKNOWN; + if ( strcmp( pType, "CDFS" ) == 0 ) + aDriveMap[nDrive].nKind = FSYS_KIND_CDROM|FSYS_KIND_REMOVEABLE; + } + } +#endif + + bDriveMap = TRUE; +} + +Time MsDos2Time( const time_t *pTimeT ) +{ + tm *pTm = localtime( pTimeT ); + if ( pTm ) + return Time( pTm->tm_hour, pTm->tm_min, pTm->tm_sec ); + else + return Time(0); +} + +Date MsDos2Date( const time_t *pTimeT ) +{ + tm *pTm = localtime( pTimeT ); + if ( pTm ) + return Date( pTm->tm_mday, pTm->tm_mon + 1, pTm->tm_year ); + else + return Date(0); +} + +/************************************************************************* +|* +|* DirEntry::GetPathStyle() const +|* +|* Beschreibung +|* Ersterstellung MI 11.05.95 +|* Letzte Aenderung MI 11.05.95 +|* +*************************************************************************/ + +FSysPathStyle DirEntry::GetPathStyle( const String &rDevice ) +{ + ByteString aRootDir(rDevice, osl_getThreadTextEncoding()); + // UNC-Pathname? + if ( aRootDir.Len()==0 || ( aRootDir.Len() > 1 && aRootDir.GetChar(1) != ':' ) ) + return FSYS_STYLE_UNKNOWN; + + if ( !bDriveMap ) + CreateDriveMapImpl(); + return aDriveMap[toupper(aRootDir.GetChar(0)) - 'A'].nStyle; +} + +/************************************************************************* +|* +|* DirEntry::IsCaseSensitive() const +|* +|* Beschreibung +|* Ersterstellung TPF 26.02.1999 +|* Letzte Aenderung +|* +*************************************************************************/ + +BOOL DirEntry::IsCaseSensitive( FSysPathStyle eFormatter ) const +{ + if (eFormatter==FSYS_STYLE_HOST) + { + if (GetPathStyle(GetDevice().GetName()) == FSYS_STYLE_UNX) + { + return TRUE; + } + else + { + return FALSE; + } + } + else + { + BOOL isCaseSensitive = FALSE; // ich bin unter OS2, also ist der default im Zweifelsfall case insensitiv + switch ( eFormatter ) + { + case FSYS_STYLE_MAC: + case FSYS_STYLE_FAT: + case FSYS_STYLE_VFAT: + case FSYS_STYLE_NTFS: + case FSYS_STYLE_NWFS: + case FSYS_STYLE_HPFS: + case FSYS_STYLE_DETECT: + { + isCaseSensitive = FALSE; + break; + } + case FSYS_STYLE_SYSV: + case FSYS_STYLE_BSD: + { + isCaseSensitive = TRUE; + break; + } + default: + { + isCaseSensitive = FALSE; // ich bin unter OS2, also ist der default im Zweifelsfall case insensitiv + break; + } + } + return isCaseSensitive; + } +} + + + + +//========================================================================= + +ErrCode FileStat::QueryDiskSpace( const String &rPath, + BigInt &rFreeBytes, BigInt &rTotalBytes ) +{ + FSALLOCATE aFSInfoBuf; + ByteString aVol( DirEntry(rPath).ImpGetTopPtr()->GetName(), osl_getThreadTextEncoding()); + ULONG nDriveNumber = toupper( aVol.GetChar(0) ) - 'A' + 1; + + APIRET rc = DosQueryFSInfo( nDriveNumber, FSIL_ALLOC, + &aFSInfoBuf, sizeof(aFSInfoBuf) ); + if ( rc ) + return Sys2SolarError_Impl( rc ); + + BigInt aBytesPerCluster( BigInt(aFSInfoBuf.cbSector) * + BigInt(aFSInfoBuf.cSectorUnit) ); + rFreeBytes = aBytesPerCluster * BigInt(aFSInfoBuf.cUnitAvail); + rTotalBytes = aBytesPerCluster * BigInt(aFSInfoBuf.cUnit); + return 0; +} + +//========================================================================= + +void FSysEnableSysErrorBox( BOOL bEnable ) +{ + DosError( bEnable ? 0 : FERR_DISABLEHARDERR ); +} + diff --git a/tools/source/fsys/os2.hxx b/tools/source/fsys/os2.hxx new file mode 100644 index 000000000000..073505b07d6d --- /dev/null +++ b/tools/source/fsys/os2.hxx @@ -0,0 +1,96 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: os2.hxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _os2_hxx +#define _os2_hxx + + +#define INCL_DOSEXCEPTIONS +#define INCL_DOSFILEMGR +#define INCL_DOSPROCESS +#define INCL_DOSDEVICES +#define INCL_DOSERRORS +#define INCL_DOSMISC +#define INCL_DOSNLS /* National Language Support values */ +#include <svpm.h> + +#include <dirent.h> +#include <string.h> + +#include <sys\types.h> +#include <sys\stat.h> +#include <stdio.h> +#include <ctype.h> +#include <emx/syscalls.h> + +#define FSYS_UNIX FALSE + +#define DOS_DIRECT _A_SUBDIR +#define DOS_VOLUMEID _A_VOLID + +#define _mkdir(p) mkdir(p, 0777) + +const char* TempDirImpl( char *pBuf ); +String ToLowerImpl( const String& ); + +#define DEFSTYLE FSYS_STYLE_OS2 +#define MKDIR( p ) mkdir( (unsigned char*) p ) +#define CMP_LOWER(s) ( s.ToLowerAscii() ) + +#define START_DRV 'a' + +inline BOOL DRIVE_EXISTS( char c ) +{ + ULONG nCur, nMap; + APIRET nRet = DosQueryCurrentDisk( &nCur, &nMap ); + return ( nMap & 1 << (c - 'a') ) != 0; +} + +#include <time.h> +//#include <datetime.hxx> + +inline Time MsDos2Time( FTIME* aTime ) +{ + return Time( aTime->hours, aTime->minutes, 2*aTime->twosecs ); +} + +inline Date MsDos2Date( FDATE* aDate ) +{ + return Date( aDate->day, aDate->month, aDate->year ); +} + +Time MsDos2Time( const time_t *pTimeT ); + +Date MsDos2Date( const time_t *pTimeT ); + +#define FSysFailOnErrorImpl() + +#endif + diff --git a/tools/source/fsys/tdir.cxx b/tools/source/fsys/tdir.cxx new file mode 100644 index 000000000000..30b1db33904d --- /dev/null +++ b/tools/source/fsys/tdir.cxx @@ -0,0 +1,771 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: tdir.cxx,v $ + * $Revision: 1.13 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _DIR_CXX + +#include <stdlib.h> +#include <cstdarg> +#include <limits.h> +#include <tools/debug.hxx> +#include <tools/list.hxx> + +#ifndef _COMPED_HXX +#include "comdep.hxx" +#endif +#include <tools/fsys.hxx> + + +DBG_NAME( Dir ) + +DECLARE_LIST( DirEntryList, DirEntry* ) +DECLARE_LIST( FSysSortList, FSysSort* ) +DECLARE_LIST( FileStatList, FileStat* ) + +#define APPEND (USHORT) 65535 + +/************************************************************************* +|* +|* Dir::InsertPointReached() +|* +|* Beschreibung stellt fest, ob eingefuegt werden musz +|* Ersterstellung MA 05.11.91 +|* Letzte Aenderung MI 05.02.92 +|* +*************************************************************************/ + +BOOL Dir::ImpInsertPointReached( const DirEntry& rNewEntry, + const FileStat& rNewStat, + ULONG nCurPos, ULONG nSortIndex ) const +{ +#define VALUE( nKindFlags ) \ + ( ( FSYS_KIND_FILE | FSYS_KIND_DIR | FSYS_KIND_DEV | \ + FSYS_KIND_CHAR | FSYS_KIND_BLOCK ) & nKindFlags ) + + // einfache Dinge erfordern einfache Loesungen + if ( !pLst->Count() ) + return TRUE; + + FSysSort nSort = *( pSortLst->GetObject( nSortIndex ) ); + FileStat *pOldStat = NULL; + DirEntry *pCurLstObj = pLst->GetObject( nCurPos ); + if ( pStatLst ) + pOldStat = pStatLst->GetObject( nCurPos ); + + switch( nSort ) + { + case FSYS_SORT_NAME: + case (FSYS_SORT_NAME | FSYS_SORT_ASCENDING): + if ( pCurLstObj->aName > rNewEntry.aName ) + return TRUE; + if ( !(pCurLstObj->aName == rNewEntry.aName) ) + return FALSE; + break; + case (FSYS_SORT_NAME | FSYS_SORT_DESCENDING): + if ( pCurLstObj->aName < rNewEntry.aName ) + return TRUE; + if ( !(pCurLstObj->aName == rNewEntry.aName) ) + return FALSE; + break; + + case FSYS_SORT_EXT: + case (FSYS_SORT_EXT | FSYS_SORT_ASCENDING): + { + if ( pCurLstObj->GetExtension() > rNewEntry.GetExtension() ) + return TRUE; + if ( !(pCurLstObj->GetExtension() == rNewEntry.GetExtension()) ) + return FALSE; + break; + } + case (FSYS_SORT_EXT | FSYS_SORT_DESCENDING): + { + if ( pCurLstObj->GetExtension() < rNewEntry.GetExtension() ) + return TRUE; + if ( !(pCurLstObj->GetExtension() == rNewEntry.GetExtension()) ) + return FALSE; + break; + } + + case FSYS_SORT_KIND: + case (FSYS_SORT_KIND | FSYS_SORT_ASCENDING ): + if ( VALUE(pOldStat->nKindFlags) > VALUE(rNewStat.nKindFlags) ) + return TRUE; + if ( !(VALUE(pOldStat->nKindFlags) == VALUE(rNewStat.nKindFlags)) ) + return FALSE; + break; + case (FSYS_SORT_KIND | FSYS_SORT_DESCENDING): + if ( VALUE(pOldStat->nKindFlags) < VALUE(rNewStat.nKindFlags) ) + return TRUE; + if ( !(VALUE(pOldStat->nKindFlags) == VALUE(rNewStat.nKindFlags)) ) + return FALSE; + break; + + case FSYS_SORT_SIZE: + case (FSYS_SORT_SIZE | FSYS_SORT_ASCENDING): + if ( pOldStat->nSize > rNewStat.nSize ) + return TRUE; + if ( !(pOldStat->nSize == rNewStat.nSize) ) + return FALSE; + break; + case (FSYS_SORT_SIZE | FSYS_SORT_DESCENDING): + if ( pOldStat->nSize < rNewStat.nSize ) + return TRUE; + if ( !(pOldStat->nSize == rNewStat.nSize) ) + return FALSE; + break; + + case FSYS_SORT_MODIFYED: + case (FSYS_SORT_MODIFYED | FSYS_SORT_ASCENDING): + if ( (pOldStat->aDateModified >= rNewStat.aDateModified) && + (pOldStat->aTimeModified > rNewStat.aTimeModified) ) + return TRUE; + if ( !((pOldStat->aDateModified == rNewStat.aDateModified) && + (pOldStat->aTimeModified == rNewStat.aTimeModified)) ) + return FALSE; + break; + case (FSYS_SORT_MODIFYED | FSYS_SORT_DESCENDING): + if ( (pOldStat->aDateModified <= rNewStat.aDateModified) && + (pOldStat->aTimeModified < rNewStat.aTimeModified) ) + return TRUE; + if ( !((pOldStat->aDateModified == rNewStat.aDateModified) && + (pOldStat->aTimeModified == rNewStat.aTimeModified)) ) + return FALSE; + break; + + case FSYS_SORT_CREATED: + case (FSYS_SORT_CREATED | FSYS_SORT_ASCENDING): + if ( (pOldStat->aDateCreated >= rNewStat.aDateCreated) && + (pOldStat->aTimeCreated > rNewStat.aTimeCreated) ) + return TRUE; + if ( !((pOldStat->aDateCreated == rNewStat.aDateCreated) && + (pOldStat->aTimeCreated == rNewStat.aTimeCreated)) ) + return FALSE; + break; + case (FSYS_SORT_CREATED | FSYS_SORT_DESCENDING): + if ( (pOldStat->aDateCreated <= rNewStat.aDateCreated) && + (pOldStat->aTimeCreated < rNewStat.aTimeCreated) ) + return TRUE; + if ( !((pOldStat->aDateCreated == rNewStat.aDateCreated) && + (pOldStat->aTimeCreated == rNewStat.aTimeCreated)) ) + return FALSE; + break; + + case FSYS_SORT_ACCESSED: + case (FSYS_SORT_ACCESSED | FSYS_SORT_ASCENDING): + if ( (pOldStat->aDateAccessed >= rNewStat.aDateAccessed) && + (pOldStat->aTimeAccessed > rNewStat.aTimeAccessed) ) + return TRUE; + if ( !((pOldStat->aDateAccessed == rNewStat.aDateAccessed) && + (pOldStat->aTimeAccessed == rNewStat.aTimeAccessed)) ) + return FALSE; + break; + case (FSYS_SORT_ACCESSED | FSYS_SORT_DESCENDING): + if ( (pOldStat->aDateAccessed <= rNewStat.aDateAccessed) && + (pOldStat->aTimeAccessed < rNewStat.aTimeAccessed) ) + return TRUE; + if ( !((pOldStat->aDateAccessed == rNewStat.aDateAccessed) && + (pOldStat->aTimeAccessed == rNewStat.aTimeAccessed)) ) + return FALSE; + break; + default: /* Kann nicht sein */; + } + + if ( nSortIndex == ( pSortLst->Count() - 1 ) ) + return TRUE; + else + //Rekursion + return ImpInsertPointReached( rNewEntry, rNewStat, + nCurPos, nSortIndex + 1 ); +#undef VALUE +} + +/************************************************************************* +|* +|* Dir::ImpSortedInsert() +|* +|* Beschreibung fuegt sortiert ein +|* Ersterstellung MA 05.11.91 +|* Letzte Aenderung MA 03.12.91 +|* +*************************************************************************/ + +void Dir::ImpSortedInsert( const DirEntry *pNewEntry, const FileStat *pNewStat ) +{ + //Sonderfall, keine Sortierung gewuenscht. + if ( !pSortLst ) { + pLst->Insert( (DirEntry*)pNewEntry, APPEND ); + return; + } + + pLst->First(); + do { + if ( ImpInsertPointReached( *pNewEntry, *pNewStat, pLst->GetCurPos(), + (ULONG)0 ) ) + { + if ( pStatLst ) + pStatLst->Insert( (FileStat*)pNewStat, pLst->GetCurPos() ); + pLst->Insert( (DirEntry*)pNewEntry ); + return; + } + } while( pLst->Next() ); + + if ( pStatLst ) + pStatLst->Insert( (FileStat*)pNewStat, APPEND ); + pLst->Insert( (DirEntry*)pNewEntry, APPEND ); +} + +/************************************************************************* +|* +|* Dir::Construct() +|* +|* Beschreibung gemeinsame Implementation der Ctoren +|* Ersterstellung MI 02.06.93 +|* Letzte Aenderung MI 02.06.93 +|* +*************************************************************************/ + +void Dir::Construct( DirEntryKind nKindFlags ) +{ + pLst = NULL; + pSortLst = NULL; + pStatLst = NULL; + eAttrMask = nKindFlags; + ByteString aTempName( GetName(), osl_getThreadTextEncoding() ); + if ( aTempName.Search( "*" ) != STRING_NOTFOUND || + aTempName.Search( "?" ) != STRING_NOTFOUND ) +#if defined( WNT ) && !defined( WTC ) + { + ByteString aTStr(CutName(), osl_getThreadTextEncoding()); + char* pBuffer = new char[aTStr.Len()+1]; + strcpy( pBuffer, aTStr.GetBuffer() ); + CharLowerBuff( pBuffer, aTStr.Len() ); + aNameMask = WildCard( String(pBuffer, osl_getThreadTextEncoding()), ';' ); + delete pBuffer; + } +#else + aNameMask = WildCard( CutName(), ';' ); +#endif + else + aNameMask = String("*", osl_getThreadTextEncoding()); +} + +/************************************************************************* +|* +|* Dir::Update() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 16.05.91 +|* Letzte Aenderung MI 19.09.96 +|* +*************************************************************************/ + +BOOL Dir::Update() +{ + Reset(); + return Scan( USHRT_MAX ) > 0; +} + +/************************************************************************* +|* +|* Dir::Reset() +|* +|* Beschreibung +|* Ersterstellung MI 22.10.96 +|* Letzte Aenderung MI 22.10.96 +|* +*************************************************************************/ + +void Dir::Reset() +{ + // ggf. alten Reader l"oschen + if ( pReader && pReader->bInUse ) + DELETEZ(pReader); + + // alle DirEntries aus der Liste entfernen und deren Speicher freigeben + if ( pLst ) + { + DirEntry* pEntry = pLst->First(); + while (pEntry) + { + DirEntry* pNext = pLst->Next(); + delete pEntry; + pEntry = pNext; + } + pLst->Clear(); + } + else + pLst = new DirEntryList(); + + // Alte File-Stat's Loeschen + if ( pStatLst ) + { + //Erstmal die alten Loeschen + FileStat* pEntry = pStatLst->First(); + while (pEntry) + { + FileStat* pNext = pStatLst->Next(); + delete pEntry; + pEntry = pNext; + } + pStatLst->Clear(); + delete pStatLst; + } + + // Verlangen die Sortierkriterien FileStat's? + if ( pSortLst ) + { + pSortLst->First(); + do + { + if ( *( pSortLst->GetCurObject() ) & + ( FSYS_SORT_KIND | FSYS_SORT_SIZE | + FSYS_SORT_CREATED | FSYS_SORT_MODIFYED | FSYS_SORT_ACCESSED ) ) + pStatLst = new FileStatList(); + } while ( !pStatLst && pSortLst->Next() ); + } + +#ifndef BOOTSTRAP + // ggf. einen neuen Reader aufsetzen + if ( !pReader ) + pReader = new DirReader_Impl( *this ); +#endif + + // gibt es das zu oeffnende Verzeichnis ueberhaupt? +#if !defined(UNX) && !defined(OS2) //explanation: see DirReader_Impl::Read() in unx.cxx + if( !pReader->pDosDir ) + { + nError = FSYS_ERR_NOTADIRECTORY; + DELETEZ( pReader ); + return; + } +#endif +} + +/************************************************************************* +|* +|* Dir::Scan() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 18.09.96 +|* Letzte Aenderung MI 19.09.96 +|* +*************************************************************************/ + +USHORT Dir::Scan( USHORT nCount ) +{ + + USHORT nRead = 0; // Anzahl in dieser Runde gelesener Eintr"age + FSysFailOnErrorImpl(); + + // noch nicht fertig gewesen + if ( pReader ) + { + // frischer Reader? + if ( !pLst->Count() ) + { + // dann ggf. Laufwerke scannen + pReader->bInUse = TRUE; + nRead = pReader->Init(); + } + + // weiterlesen... + while ( nRead <= nCount && !pReader->bReady ) + nRead = nRead + pReader->Read(); + + // fertig? + if ( pReader && pReader->bReady ) + DELETEZ( pReader ); + } + + // Anzahl der gelesenen zur"uckgeben + return nRead; +} + +/************************************************************************* +|* +|* Dir::Dir() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 16.05.91 +|* Letzte Aenderung MI 04.03.92 +|* +*************************************************************************/ + +Dir::Dir( const DirEntry& rDirEntry, DirEntryKind nKindFlags, FSysSort nSort, ... ): + DirEntry( rDirEntry ), + pReader( 0 ) +{ + DBG_CTOR( Dir, NULL ); + + Construct( nKindFlags ); + + std::va_list pArgs; + va_start( pArgs, nSort ); + ImpSetSort( pArgs, nSort ); + + Reset(); +} + +/************************************************************************* +|* +|* Dir::Dir() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 02.06.93 +|* Letzte Aenderung MI 02.06.93 +|* +*************************************************************************/ + +Dir::Dir( const DirEntry& rDirEntry, DirEntryKind nKindFlags ): + DirEntry( rDirEntry ), + pReader( 0 ) +{ + DBG_CTOR( Dir, NULL ); + + Construct( nKindFlags ); + Reset(); +} + +/************************************************************************* +|* +|* Dir::Dir() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 16.05.91 +|* Letzte Aenderung MA 04.11.91 +|* +*************************************************************************/ + +Dir::Dir(): + pReader( 0 ) +{ + DBG_CTOR( Dir, NULL ); + + pLst = NULL; + pSortLst = NULL; + pStatLst = NULL; + eAttrMask = FSYS_KIND_ALL; + aNameMask = String("*", osl_getThreadTextEncoding()); +} + +/************************************************************************* +|* +|* Dir::~Dir() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 16.05.91 +|* Letzte Aenderung MA 04.11.91 +|* +*************************************************************************/ + +Dir::~Dir() +{ + DBG_DTOR( Dir, NULL ); + + // alle DirEntries aus der Liste entfernen und deren Speicher freigeben + if ( pLst ) + { + DirEntry* pEntry = pLst->First(); + while (pEntry) + { + DirEntry* pNext = pLst->Next(); + delete pEntry; + pEntry = pNext; + } + pLst->Clear(); + + delete pLst; + } + + // alle Sorts aus der Liste entfernen und deren Speicher freigeben + if ( pSortLst ) + { + FSysSort* pEntry = pSortLst->First(); + while (pEntry) + { + FSysSort* pNext = pSortLst->Next(); + delete pEntry; + pEntry = pNext; + } + pSortLst->Clear(); + + delete pSortLst; + } + + // alle FileStat's aus der Liste entfernen und deren Speicher freigeben + if ( pStatLst ) + { + FileStat* pEntry = pStatLst->First(); + while (pEntry) + { + FileStat* pNext = pStatLst->Next(); + delete pEntry; + pEntry = pNext; + } + pStatLst->Clear(); + delete pStatLst; + } + + // ggf. laufenden Reader freigeben + delete pReader; +} + +/************************************************************************* +|* +|* Dir::ImpSetSort() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MA 04.11.91 +|* Letzte Aenderung MI 05.02.92 +|* +*************************************************************************/ + +FSysError Dir::ImpSetSort( std::va_list pArgs, int nFirstSort ) +{ + BOOL bLast; + FSysSort *pSort; + FSysSortList *pNewSortLst = new FSysSortList; + + *( pSort = new FSysSort ) = nFirstSort; + do + { + // letztes Kriterium? + bLast = FSYS_SORT_END == (*pSort & FSYS_SORT_END); + *pSort &= ~FSYS_SORT_END; + + FSysSort nSort = *pSort & ~(USHORT)FSYS_SORT_ASCENDING + & ~(USHORT)FSYS_SORT_DESCENDING; + + // g"utliges Sortierkriterium? + if ( ( nSort == FSYS_SORT_NAME ) || + ( nSort == FSYS_SORT_SIZE ) || + ( nSort == FSYS_SORT_EXT ) || + ( nSort == FSYS_SORT_CREATED ) || + ( nSort == FSYS_SORT_MODIFYED ) || + ( nSort == FSYS_SORT_ACCESSED ) || + ( nSort == FSYS_SORT_KIND ) ) + { + pNewSortLst->Insert( pSort, APPEND ); + *(pSort = new FSysSort) = va_arg( pArgs, FSysSort ); + } + else + { // ungueltiger Sort oder FSYS_SORT_NONE + FSysSort* pEntry = pNewSortLst->First(); + while (pEntry) + { + FSysSort* pNext = pNewSortLst->Next(); + delete pEntry; + pEntry = pNext; + } + pNewSortLst->Clear(); + delete pNewSortLst; + if ( *pSort == FSYS_SORT_NONE ) + { + delete pSort; + if ( pSortLst ) + delete pSortLst; + return FSYS_ERR_OK; + } + else + { + delete pSort; + return FSYS_ERR_NOTSUPPORTED; + } + } + } while ( !bLast ); + + va_end( pArgs ); + delete pSort; // JP:6.3.00 - delete the initial pointer + + //Enfernen der alten Sort-Elemente + if ( pSortLst ) + { + FSysSort* pEntry = pSortLst->First(); + while (pEntry) + { + FSysSort* pNext = pSortLst->Next(); + delete pEntry; + pEntry = pNext; + } + pSortLst->Clear(); + delete pSortLst; + } + pSortLst = pNewSortLst; + + //Jetzt noch neu Sortieren... + + //Wenn keine FileStats da sind, aber nun welche gebraucht werden, + //ist der Aufruf von Update() die einfachste Moeglichkeit + if ( !pStatLst && pSortLst ) + { + pSortLst->First(); + do + { + if ( *(pSortLst->GetCurObject()) & + ( FSYS_SORT_CREATED | FSYS_SORT_MODIFYED | FSYS_SORT_SIZE | + FSYS_SORT_ACCESSED | FSYS_SORT_KIND ) ) + { + Update(); + return FSYS_ERR_OK; + } + } while ( !pStatLst && pSortLst->Next() ); + } + + if ( pLst ) { //Keine DirEntry's, kein Sort. + DirEntryList *pOldLst = pLst; //alte Liste merken + pLst = new DirEntryList(); //neue Liste (zu Sortieren) + + FileStatList *pOldStatLst = NULL; //alte StatListe merken + if ( pStatLst ) { + pOldStatLst = pStatLst; + pStatLst = new FileStatList(); //neue StatListe (zu Sortieren) + } + pOldLst->First(); + do + { + //Sortiertes Einfuegen der Elemente aus den gemerkten Listen + //in die 'richtigen' Listen + if ( pOldStatLst ) + ImpSortedInsert( pOldLst->GetCurObject(), + pOldStatLst->GetObject( pOldLst->GetCurPos() ) ); + else + ImpSortedInsert( pOldLst->GetCurObject(), NULL ); + } while( pOldLst->Next() ); + + delete pOldLst; + if ( pOldStatLst ) + delete pOldStatLst; + } + return FSYS_ERR_OK; +} + +/************************************************************************* +|* +|* Dir::SetSort() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MA 04.11.91 +|* Letzte Aenderung MI 05.02.92 +|* +*************************************************************************/ + +FSysError Dir::SetSort( FSysSort nSort, ... ) +{ + std::va_list pArgs; + va_start( pArgs, nSort ); + return ImpSetSort( pArgs, nSort ); +} + +/************************************************************************* +|* +|* Dir::operator[]() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 16.05.91 +|* Letzte Aenderung MI 16.05.91 +|* +*************************************************************************/ + +DirEntry& Dir::operator[] ( USHORT nIndex ) const +{ + DBG_ASSERT( nIndex < Count(), "Dir::operator[] : nIndex > Count()" ); + + DirEntry *pEntry = pLst->GetObject( nIndex ); + return *pEntry; +} + +/************************************************************************* +|* +|* Dir::operator+= () +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 16.05.91 +|* Letzte Aenderung MI 16.05.91 +|* +*************************************************************************/ + +Dir& Dir::operator+=( const Dir& rDir ) +{ + // ggf. erst den Rest lesen + if ( pReader ) + Scan( USHRT_MAX ); + DBG_ASSERT( !rDir.pReader, "Dir::+= with incomplete Dir" ); + + // ggf. initiale Liste erzeugen + if ( !pLst ) + pLst = new DirEntryList(); + + //Verlangen die Sortierkriterien FileStat's? + BOOL bStat = FALSE; + if ( pSortLst ) { + pSortLst->First(); + do { + if ( *(pSortLst->GetCurObject()) & + ( FSYS_SORT_CREATED | FSYS_SORT_MODIFYED | FSYS_SORT_SIZE | + FSYS_SORT_ACCESSED | FSYS_SORT_KIND ) ) + bStat = TRUE; + } while ( !bStat && pSortLst->Next() ); + } + FileStat * stat = NULL; + for ( USHORT nNr = 0; nNr < rDir.Count(); nNr++ ) + { + if ( bStat ) + { + if ( rDir.pStatLst ) + stat = new FileStat( *rDir.pStatLst->GetObject(nNr) ); + else + stat = new FileStat( rDir[nNr] ); + } + ImpSortedInsert( new DirEntry( rDir[nNr] ), stat ); + } + return *this; +} + +/************************************************************************* +|* +|* Dir::Count() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 16.05.91 +|* Letzte Aenderung MI 18.09.96 +|* +*************************************************************************/ + + +USHORT Dir::Count( BOOL bUpdated ) const +{ + // ggf. erst den Rest lesen + if ( bUpdated && pReader ) + ((Dir*)this)->Scan( USHRT_MAX ); + + return pLst == NULL ? 0 : (USHORT) pLst->Count(); +} diff --git a/tools/source/fsys/tempfile.cxx b/tools/source/fsys/tempfile.cxx new file mode 100644 index 000000000000..f7b0a691cd7c --- /dev/null +++ b/tools/source/fsys/tempfile.cxx @@ -0,0 +1,304 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: tempfile.cxx,v $ + * $Revision: 1.12 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <tools/tempfile.hxx> +#include "comdep.hxx" + +#include <rtl/ustring.hxx> +#include <osl/file.hxx> +#include <rtl/instance.hxx> +#include <tools/time.hxx> +#include <tools/debug.hxx> +#include <stdio.h> + +#ifdef UNX +#define _MAX_PATH 260 +#endif + +using namespace osl; + +namespace { struct TempNameBase_Impl : public rtl::Static< ::rtl::OUString, TempNameBase_Impl > {}; } + +struct TempFile_Impl +{ + String aName; + sal_Bool bIsDirectory; +}; + +String GetSystemTempDir_Impl() +{ + char sBuf[_MAX_PATH]; + const char *pDir = TempDirImpl(sBuf); + + ::rtl::OString aTmpA( pDir ); + ::rtl::OUString aTmp = ::rtl::OStringToOUString( aTmpA, osl_getThreadTextEncoding() ); + rtl::OUString aRet; + FileBase::getFileURLFromSystemPath( aTmp, aRet ); + String aName = aRet; + if( aName.GetChar(aName.Len()-1) != '/' ) + aName += '/'; + return aName; +} + +#define TMPNAME_SIZE ( 1 + 5 + 5 + 4 + 1 ) +String ConstructTempDir_Impl( const String* pParent ) +{ + String aName; + if ( pParent && pParent->Len() ) + { + // if parent given try to use it + rtl::OUString aTmp( *pParent ); + rtl::OUString aRet; + + // test for valid filename + { + ::osl::DirectoryItem aItem; + sal_Int32 i = aRet.getLength(); + if ( aRet[i-1] == '/' ) + i--; + + if ( DirectoryItem::get( ::rtl::OUString( aRet, i ), aItem ) == FileBase::E_None ) + aName = aRet; + } + } + + if ( !aName.Len() ) + { + // if no parent or invalid parent : use system directory + ::rtl::OUString& rTempNameBase_Impl = TempNameBase_Impl::get(); + if ( !rTempNameBase_Impl.getLength() ) + rTempNameBase_Impl = GetSystemTempDir_Impl(); + aName = rTempNameBase_Impl; + } + + // Make sure that directory ends with a separator + xub_StrLen i = aName.Len(); + if( i>0 && aName.GetChar(i-1) != '/' ) + aName += '/'; + + return aName; +} + +void CreateTempName_Impl( String& rName, sal_Bool bKeep, sal_Bool bDir = sal_True ) +{ + // add a suitable tempname + // Prefix can have 5 chars, leaving 3 for numbers. 26 ** 3 == 17576 + // ER 13.07.00 why not radix 36 [0-9A-Z] ?!? + const unsigned nRadix = 26; + String aName( rName ); + aName += String::CreateFromAscii( "sv" ); + + rName.Erase(); + static unsigned long u = Time::GetSystemTicks(); + for ( unsigned long nOld = u; ++u != nOld; ) + { + u %= (nRadix*nRadix*nRadix); + String aTmp( aName ); + aTmp += String::CreateFromInt32( (sal_Int32) (unsigned) u, nRadix ); + aTmp += String::CreateFromAscii( ".tmp" ); + + if ( bDir ) + { + FileBase::RC err = Directory::create( aTmp ); + if ( err == FileBase::E_None ) + { + // !bKeep: only for creating a name, not a file or directory + if ( bKeep || Directory::remove( aTmp ) == FileBase::E_None ) + rName = aTmp; + break; + } + else if ( err != FileBase::E_EXIST ) + { + // if f.e. name contains invalid chars stop trying to create dirs + break; + } + } + else + { + DBG_ASSERT( bKeep, "Too expensive, use directory for creating name!" ); + File aFile( aTmp ); + FileBase::RC err = aFile.open(osl_File_OpenFlag_Create); + if ( err == FileBase::E_None ) + { + rName = aTmp; + aFile.close(); + break; + } + else if ( err != FileBase::E_EXIST ) + { + // if f.e. name contains invalid chars stop trying to create files + break; + } + } + } +} + +String TempFile::CreateTempName( const String* pParent ) +{ + // get correct directory + String aName = ConstructTempDir_Impl( pParent ); + + // get TempFile name with default naming scheme + CreateTempName_Impl( aName, sal_False ); + + // convert to file URL + rtl::OUString aTmp; + if ( aName.Len() ) + aTmp = aName; + return aTmp; +} + +TempFile::TempFile( const String* pParent, sal_Bool bDirectory ) + : pImp( new TempFile_Impl ) + , bKillingFileEnabled( sal_False ) +{ + pImp->bIsDirectory = bDirectory; + + // get correct directory + pImp->aName = ConstructTempDir_Impl( pParent ); + + // get TempFile with default naming scheme + CreateTempName_Impl( pImp->aName, sal_True, bDirectory ); +} + +TempFile::TempFile( const String& rLeadingChars, const String* pExtension, const String* pParent, sal_Bool bDirectory ) + : pImp( new TempFile_Impl ) + , bKillingFileEnabled( sal_False ) +{ + pImp->bIsDirectory = bDirectory; + + // get correct directory + String aName = ConstructTempDir_Impl( pParent ); + + // now use special naming scheme ( name takes leading chars and an index counting up from zero + aName += rLeadingChars; + for ( sal_Int32 i=0;; i++ ) + { + String aTmp( aName ); + aTmp += String::CreateFromInt32( i ); + if ( pExtension ) + aTmp += *pExtension; + else + aTmp += String::CreateFromAscii( ".tmp" ); + if ( bDirectory ) + { + FileBase::RC err = Directory::create( aTmp ); + if ( err == FileBase::E_None ) + { + pImp->aName = aTmp; + break; + } + else if ( err != FileBase::E_EXIST ) + // if f.e. name contains invalid chars stop trying to create dirs + break; + } + else + { + File aFile( aTmp ); + FileBase::RC err = aFile.open(osl_File_OpenFlag_Create); + if ( err == FileBase::E_None ) + { + pImp->aName = aTmp; + aFile.close(); + break; + } + else if ( err != FileBase::E_EXIST ) + // if f.e. name contains invalid chars stop trying to create dirs + break; + } + } +} + +TempFile::~TempFile() +{ + if ( bKillingFileEnabled ) + { + if ( pImp->bIsDirectory ) + { + // at the moment no recursiv algorithm present + Directory::remove( pImp->aName ); + } + else + { + File::remove( pImp->aName ); + } + } + + delete pImp; +} + +sal_Bool TempFile::IsValid() const +{ + return pImp->aName.Len() != 0; +} + +String TempFile::GetName() const +{ + rtl::OUString aTmp; + aTmp = pImp->aName; + return aTmp; +} + +String TempFile::SetTempNameBaseDirectory( const String &rBaseName ) +{ + String aName( rBaseName ); + + ::rtl::OUString& rTempNameBase_Impl = TempNameBase_Impl::get(); + + FileBase::RC err= Directory::create( aName ); + if ( err == FileBase::E_None || err == FileBase::E_EXIST ) + { + rTempNameBase_Impl = aName; + rTempNameBase_Impl += String( '/' ); + + TempFile aBase( NULL, sal_True ); + if ( aBase.IsValid() ) + rTempNameBase_Impl = aBase.pImp->aName; + } + + rtl::OUString aTmp; + aTmp = rTempNameBase_Impl; + return aTmp; +} + +String TempFile::GetTempNameBaseDirectory() +{ + ::rtl::OUString& rTempNameBase_Impl = TempNameBase_Impl::get(); + if ( !rTempNameBase_Impl.getLength() ) + rTempNameBase_Impl = GetSystemTempDir_Impl(); + + rtl::OUString aTmp; + aTmp = rTempNameBase_Impl; + return aTmp; +} + diff --git a/tools/source/fsys/unx.cxx b/tools/source/fsys/unx.cxx new file mode 100644 index 000000000000..76910683df13 --- /dev/null +++ b/tools/source/fsys/unx.cxx @@ -0,0 +1,663 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: unx.cxx,v $ + * $Revision: 1.12 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> +#include <utime.h> +#if defined HPUX || defined LINUX || defined IRIX +#include <mntent.h> +#define mnttab mntent +#elif defined SCO +#include <mnttab.h> +#elif defined AIX +#include <sys/mntctl.h> +#include <sys/vmount.h> +extern "C" int mntctl( int cmd, size_t size, char* buf ); +#elif defined(NETBSD) +#include <sys/mount.h> +#elif defined(FREEBSD) || defined(MACOSX) +#elif defined DECUNIX +struct mnttab +{ + char *mnt_dir; + char *mnt_fsname; +}; +#else +#include <sys/mnttab.h> +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +#include <tools/debug.hxx> +#include <tools/list.hxx> +#include <tools/fsys.hxx> +#include "comdep.hxx" +#include <rtl/instance.hxx> + +DECLARE_LIST( DirEntryList, DirEntry* ) +DECLARE_LIST( FSysSortList, FSysSort* ) +DECLARE_LIST( FileStatList, FileStat* ) + +#if defined SOLARIS || defined SINIX +#define MOUNTSPECIAL mnt_special +#define MOUNTPOINT mnt_mountp +#define MOUNTOPTS mnt_mntopts +#define MOUNTFS mnt_fstype +#elif defined SCO +#define MNTTAB "/etc/mnttab" +#define MOUNTSPECIAL mt_dev +#define MOUNTPOINT mt_filsys +#else +#define MOUNTSPECIAL mnt_fsname +#define MOUNTPOINT mnt_dir +#define MOUNTFS mnt_type +#endif + +struct mymnttab +{ + dev_t mountdevice; + ByteString mountspecial; + ByteString mountpoint; + ByteString mymnttab_filesystem; + mymnttab() { mountdevice = (dev_t) -1; } +}; + + +#if defined(NETBSD) || defined(FREEBSD) || defined(MACOSX) +BOOL GetMountEntry(dev_t /* dev */, struct mymnttab * /* mytab */ ) +{ + DBG_WARNING( "Sorry, not implemented: GetMountEntry" ); + return FALSE; +} + +#elif defined AIX +BOOL GetMountEntry(dev_t dev, struct mymnttab *mytab) +{ + int bufsize; + if (mntctl (MCTL_QUERY, sizeof bufsize, (char*) &bufsize)) + return FALSE; + + char* buffer = (char *)malloc( bufsize * sizeof(char) ); + if (mntctl (MCTL_QUERY, bufsize, buffer) != -1) + for ( char* vmt = buffer; + vmt < buffer + bufsize; + vmt += ((struct vmount*)vmt)->vmt_length) + { + struct stat buf; + char *mountp = vmt2dataptr((struct vmount*)vmt, VMT_STUB); + if ((stat (mountp, &buf) != -1) && (buf.st_dev == dev)) + { + mytab->mountpoint = mountp; + mytab->mountspecial + = vmt2dataptr((struct vmount*)vmt, VMT_HOSTNAME); + if (mytab->mountspecial.Len()) + mytab->mountspecial += ':'; + mytab->mountspecial + += vmt2dataptr((struct vmount*)vmt, VMT_OBJECT); + mytab->mountdevice = dev; + free( buffer ); + return TRUE; + } + } + free( buffer ); + return FALSE; +} + +#else + + +static BOOL GetMountEntry(dev_t dev, struct mymnttab *mytab) +{ +#if defined SOLARIS || defined SINIX + FILE *fp = fopen (MNTTAB, "r"); + if (! fp) + return FALSE; + struct mnttab mnt[1]; + while (getmntent (fp, mnt) != -1) +#elif defined SCO + FILE *fp = fopen (MNTTAB, "r"); + if (! fp) + return FALSE; + struct mnttab mnt[1]; + while (fread (&mnt, sizeof mnt, 1, fp) > 0) +#elif defined DECUNIX || defined AIX + FILE *fp = NULL; + if (! fp) + return FALSE; + struct mnttab mnt[1]; + while ( 0 ) +#else + FILE *fp = setmntent (MOUNTED, "r"); + if (! fp) + return FALSE; + struct mnttab *mnt; + while ((mnt = getmntent (fp)) != NULL) +#endif + { +#ifdef SOLARIS + char *devopt = NULL; + if ( mnt->MOUNTOPTS != NULL ) + devopt = strstr (mnt->MOUNTOPTS, "dev="); + if (devopt) + { + if (dev != (dev_t) strtoul (devopt+4, NULL, 16)) + continue; + } + else +#endif + { + struct stat buf; + if ((stat (mnt->MOUNTPOINT, &buf) == -1) || (buf.st_dev != dev)) + continue; + } +# ifdef LINUX + /* #61624# File mit setmntent oeffnen und mit fclose schliessen stoesst + bei der glibc-2.1 auf wenig Gegenliebe */ + endmntent( fp ); +# else + fclose (fp); +# endif + mytab->mountspecial = mnt->MOUNTSPECIAL; + mytab->mountpoint = mnt->MOUNTPOINT; + mytab->mountdevice = dev; +#ifndef SCO + mytab->mymnttab_filesystem = mnt->MOUNTFS; +#else + mytab->mymnttab_filesystem = "ext2"; //default ist case sensitiv unter unix +#endif + return TRUE; + } +# ifdef LINUX + /* #61624# dito */ + endmntent( fp ); +# else + fclose (fp); +# endif + return FALSE; +} + +#endif + +/************************************************************************ +|* +|* DirEntry::IsCaseSensitive() +|* +|* Beschreibung +|* Ersterstellung TPF 25.02.1999 +|* Letzte Aenderung TPF 25.02.1999 +|* +*************************************************************************/ + +BOOL DirEntry::IsCaseSensitive( FSysPathStyle eFormatter ) const +{ + + if (eFormatter==FSYS_STYLE_HOST) + { +#ifdef NETBSD + return TRUE; +#else + struct stat buf; + DirEntry aPath(*this); + aPath.ToAbs(); + + while (stat (ByteString(aPath.GetFull(), osl_getThreadTextEncoding()).GetBuffer(), &buf)) + { + if (aPath.Level() == 1) + { + return TRUE; // ich bin unter UNIX, also ist der default im Zweifelsfall case sensitiv + } + aPath = aPath [1]; + } + + struct mymnttab fsmnt; + GetMountEntry(buf.st_dev, &fsmnt); + if ((fsmnt.mymnttab_filesystem.CompareTo("msdos")==COMPARE_EQUAL) || + (fsmnt.mymnttab_filesystem.CompareTo("umsdos")==COMPARE_EQUAL) || + (fsmnt.mymnttab_filesystem.CompareTo("vfat")==COMPARE_EQUAL) || + (fsmnt.mymnttab_filesystem.CompareTo("hpfs")==COMPARE_EQUAL) || + (fsmnt.mymnttab_filesystem.CompareTo("smb") ==COMPARE_EQUAL) || + (fsmnt.mymnttab_filesystem.CompareTo("ncpfs")==COMPARE_EQUAL)) + { + return FALSE; + } + else + { + return TRUE; + } +#endif + } + else + { + BOOL isCaseSensitive = TRUE; // ich bin unter UNIX, also ist der default im Zweifelsfall case sensitiv + switch ( eFormatter ) + { + case FSYS_STYLE_MAC: + case FSYS_STYLE_FAT: + case FSYS_STYLE_VFAT: + case FSYS_STYLE_NTFS: + case FSYS_STYLE_NWFS: + case FSYS_STYLE_HPFS: + { + isCaseSensitive = FALSE; + break; + } + case FSYS_STYLE_SYSV: + case FSYS_STYLE_BSD: + case FSYS_STYLE_DETECT: + { + isCaseSensitive = TRUE; + break; + } + default: + { + isCaseSensitive = TRUE; // ich bin unter UNIX, also ist der default im Zweifelsfall case sensitiv + break; + } + } + return isCaseSensitive; + } +} + +/************************************************************************ +|* +|* DirEntry::ToAbs() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 13:30 +|* +*************************************************************************/ + +BOOL DirEntry::ToAbs() +{ + if ( FSYS_FLAG_VOLUME == eFlag ) + { + eFlag = FSYS_FLAG_ABSROOT; + return TRUE; + } + + if ( IsAbs() ) + return TRUE; + + char sBuf[MAXPATHLEN + 1]; + *this = DirEntry( String( getcwd( sBuf, MAXPATHLEN ), osl_getThreadTextEncoding() ) ) + *this; + return IsAbs(); +} + +/************************************************************************* +|* +|* DirEntry::GetVolume() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 04.03.92 +|* Letzte Aenderung +|* +*************************************************************************/ + +namespace { struct mymnt : public rtl::Static< mymnttab, mymnt > {}; } + +String DirEntry::GetVolume() const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + DirEntry aPath( *this ); + aPath.ToAbs(); + + struct stat buf; + while (stat (ByteString(aPath.GetFull(), osl_getThreadTextEncoding()).GetBuffer(), &buf)) + { + if (aPath.Level() <= 1) + return String(); + aPath = aPath [1]; + } + mymnttab &rMnt = mymnt::get(); + return ((buf.st_dev == rMnt.mountdevice || + GetMountEntry(buf.st_dev, &rMnt)) ? + String(rMnt.mountspecial, osl_getThreadTextEncoding()) : + String()); +} + +DirEntry DirEntry::GetDevice() const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + DirEntry aPath( *this ); + aPath.ToAbs(); + + struct stat buf; + while (stat (ByteString(aPath.GetFull(), osl_getThreadTextEncoding()).GetBuffer(), &buf)) + { + if (aPath.Level() <= 1) + return String(); + aPath = aPath [1]; + } + mymnttab &rMnt = mymnt::get(); + return ((buf.st_dev == rMnt.mountdevice || + GetMountEntry(buf.st_dev, &rMnt)) ? + String( rMnt.mountpoint, osl_getThreadTextEncoding()) : + String()); +} + +/************************************************************************* +|* +|* DirEntry::SetCWD() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung DV 04.11.92 +|* +*************************************************************************/ + +BOOL DirEntry::SetCWD( BOOL bSloppy ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + + ByteString aPath( GetFull(), osl_getThreadTextEncoding()); + if ( !chdir( aPath.GetBuffer() ) ) + { + return TRUE; + } + else + { + if ( bSloppy && !chdir(aPath.GetBuffer()) ) + { + return TRUE; + } + else + { + return FALSE; + } + } +} + +//------------------------------------------------------------------------- + +USHORT DirReader_Impl::Init() +{ + return 0; +} + +//------------------------------------------------------------------------- + +USHORT DirReader_Impl::Read() +{ + if (!pDosDir) + { + pDosDir = opendir( (char*) ByteString(aPath, osl_getThreadTextEncoding()).GetBuffer() ); + } + + if (!pDosDir) + { + bReady = TRUE; + return 0; + } + + // Directories und Files auflisten? + if ( ( pDir->eAttrMask & FSYS_KIND_DIR || pDir->eAttrMask & FSYS_KIND_FILE ) && + ( ( pDosEntry = readdir( pDosDir ) ) != NULL ) ) + { + String aD_Name(pDosEntry->d_name, osl_getThreadTextEncoding()); + if ( pDir->aNameMask.Matches( aD_Name ) ) + { + DirEntryFlag eFlag = + 0 == strcmp( pDosEntry->d_name, "." ) ? FSYS_FLAG_CURRENT + : 0 == strcmp( pDosEntry->d_name, ".." ) ? FSYS_FLAG_PARENT + : FSYS_FLAG_NORMAL; + DirEntry *pTemp = new DirEntry( ByteString(pDosEntry->d_name), eFlag, FSYS_STYLE_UNX ); + if ( pParent ) + pTemp->ImpChangeParent( new DirEntry( *pParent ), FALSE); + FileStat aStat( *pTemp ); + if ( ( ( ( pDir->eAttrMask & FSYS_KIND_DIR ) && + ( aStat.IsKind( FSYS_KIND_DIR ) ) ) || + ( ( pDir->eAttrMask & FSYS_KIND_FILE ) && + !( aStat.IsKind( FSYS_KIND_DIR ) ) ) ) && + !( pDir->eAttrMask & FSYS_KIND_VISIBLE && + pDosEntry->d_name[0] == '.' ) ) + { + if ( pDir->pStatLst ) //Status fuer Sort gewuenscht? + pDir->ImpSortedInsert( pTemp, new FileStat( aStat ) ); + else + pDir->ImpSortedInsert( pTemp, NULL );; + return 1; + } + else + delete pTemp; + } + } + else + bReady = TRUE; + return 0; +} + +/************************************************************************* +|* +|* FileStat::FileStat() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MA 05.11.91 +|* Letzte Aenderung MA 07.11.91 +|* +*************************************************************************/ + +FileStat::FileStat( const void *, const void * ): + aDateCreated(0), + aTimeCreated(0), + aDateModified(0), + aTimeModified(0), + aDateAccessed(0), + aTimeAccessed(0) +{ +} + +/************************************************************************* +|* +|* FileStat::Update() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 11.06.91 +|* Letzte Aenderung MA 07.11.91 +|* +*************************************************************************/ +BOOL FileStat::Update( const DirEntry& rDirEntry, BOOL ) +{ + + nSize = 0; + nKindFlags = 0; + aCreator.Erase(); + aType.Erase(); + aDateCreated = Date(0); + aTimeCreated = Time(0); + aDateModified = Date(0); + aTimeModified = Time(0); + aDateAccessed = Date(0); + aTimeAccessed = Time(0); + + if ( !rDirEntry.IsValid() ) + { + nError = FSYS_ERR_NOTEXISTS; + return FALSE; + } + + // Sonderbehandlung falls es sich um eine Root handelt + if ( rDirEntry.eFlag == FSYS_FLAG_ABSROOT ) + { + nKindFlags = FSYS_KIND_DIR; + nError = FSYS_ERR_OK; + return TRUE; + } + + struct stat aStat; + ByteString aPath( rDirEntry.GetFull(), osl_getThreadTextEncoding() ); + if ( stat( (char*) aPath.GetBuffer(), &aStat ) ) + { + // pl: #67851# + // do this here, because an existing filename containing "wildcards" + // should be handled as a file, not a wildcard + // note that this is not a solution, since filenames containing special characters + // are handled badly across the whole Office + + // Sonderbehandlung falls es sich um eine Wildcard handelt + ByteString aTempName( rDirEntry.GetName(), osl_getThreadTextEncoding() ); + if ( strchr( (char*) aTempName.GetBuffer(), '?' ) || + strchr( (char*) aTempName.GetBuffer(), '*' ) || + strchr( (char*) aTempName.GetBuffer(), ';' ) ) + { + nKindFlags = FSYS_KIND_WILD; + nError = FSYS_ERR_OK; + return TRUE; + } + + nError = FSYS_ERR_NOTEXISTS; + return FALSE; + } + + nError = FSYS_ERR_OK; + nSize = aStat.st_size; + + nKindFlags = FSYS_KIND_UNKNOWN; + if ( ( aStat.st_mode & S_IFDIR ) == S_IFDIR ) + nKindFlags = nKindFlags | FSYS_KIND_DIR; + if ( ( aStat.st_mode & S_IFREG ) == S_IFREG ) + nKindFlags = nKindFlags | FSYS_KIND_FILE; + if ( ( aStat.st_mode & S_IFCHR ) == S_IFCHR ) + nKindFlags = nKindFlags | FSYS_KIND_DEV | FSYS_KIND_CHAR; + if ( ( aStat.st_mode & S_IFBLK ) == S_IFBLK ) + nKindFlags = nKindFlags | FSYS_KIND_DEV | FSYS_KIND_BLOCK; + if ( nKindFlags == FSYS_KIND_UNKNOWN ) + nKindFlags = nKindFlags | FSYS_KIND_FILE; + + Unx2DateAndTime( aStat.st_ctime, aTimeCreated, aDateCreated ); + Unx2DateAndTime( aStat.st_mtime, aTimeModified, aDateModified ); + Unx2DateAndTime( aStat.st_atime, aTimeAccessed, aDateAccessed ); + + return TRUE; +} + +//==================================================================== + +const char *TempDirImpl( char *pBuf ) +{ +#ifdef MACOSX + // P_tmpdir is /var/tmp on Mac OS X, and it is not cleaned up on system + // startup + strcpy( pBuf, "/tmp" ); +#else + const char *pValue = getenv( "TEMP" ); + if ( !pValue ) + pValue = getenv( "TMP" ); + if ( pValue ) + strcpy( pBuf, pValue ); + else + // auf Solaris und Linux ist P_tmpdir vorgesehen + strcpy( pBuf, P_tmpdir ); + // hart auf "/tmp" sollte wohl nur im Notfall verwendet werden + //strcpy( pBuf, "/tmp" ); +#endif /* MACOSX */ + + return pBuf; +} + +/************************************************************************* +|* +|* DirEntry::GetPathStyle() const +|* +|* Beschreibung +|* Ersterstellung MI 11.05.95 +|* Letzte Aenderung MI 11.05.95 +|* +*************************************************************************/ + +FSysPathStyle DirEntry::GetPathStyle( const String & ) +{ + return FSYS_STYLE_UNX; +} + +/************************************************************************* +|* +|* FileStat::SetDateTime +|* +|* Ersterstellung PB 27.06.97 +|* Letzte Aenderung +|* +*************************************************************************/ + +void FileStat::SetDateTime( const String& rFileName, + const DateTime& rNewDateTime ) +{ + tm times; + + times.tm_year = rNewDateTime.GetYear() - 1900; // 1997 -> 97 + times.tm_mon = rNewDateTime.GetMonth() - 1; // 0 == Januar! + times.tm_mday = rNewDateTime.GetDay(); + + times.tm_hour = rNewDateTime.GetHour(); + times.tm_min = rNewDateTime.GetMin(); + times.tm_sec = rNewDateTime.GetSec(); + + times.tm_wday = 0; + times.tm_yday = 0; +#ifdef SOLARIS + times.tm_isdst = -1; +#else + times.tm_isdst = 0; +#endif + + time_t time = mktime (×); + + if (time != (time_t) -1) + { + struct utimbuf u_time; + u_time.actime = time; + u_time.modtime = time; + utime (ByteString(rFileName, osl_getThreadTextEncoding()).GetBuffer(), &u_time); + } +} + +//========================================================================= + +ErrCode FileStat::QueryDiskSpace( const String &, BigInt &, BigInt & ) +{ + return ERRCODE_IO_NOTSUPPORTED; +} + +//========================================================================= + +void FSysEnableSysErrorBox( BOOL ) +{ +} + diff --git a/tools/source/fsys/unx.hxx b/tools/source/fsys/unx.hxx new file mode 100644 index 000000000000..233b72a9b807 --- /dev/null +++ b/tools/source/fsys/unx.hxx @@ -0,0 +1,98 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: unx.hxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _unx_hxx +#define _unx_hxx + +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <dirent.h> +#include <unistd.h> +/* #include <sysent.h> */ + +#define FSYS_UNIX TRUE +#define DRIVE_EXISTS(c) ( TRUE ) + +#define _mkdir(p) mkdir(p, 0777) +#define _rmdir rmdir +#define _chdir chdir +#define _unlink unlink +#define _getcwd getcwd +#define _access access + +#ifdef SYSV3 +#define DEFSTYLE FSYS_STYLE_SYSV +#else +#define DEFSTYLE FSYS_STYLE_BSD +#endif + +#define CMP_LOWER(s) (s) +#define TEMPNAME() tmpnam(0) +#define LOWER(aString) (aString.Lower()) + +#include <time.h> +#include <tools/datetime.hxx> + +inline Time Unx2Time( time_t nTime ) +{ + tm atm; + tm *pTime; + pTime = localtime_r( &nTime, &atm ); + return Time( pTime->tm_hour, + pTime->tm_min, + pTime->tm_sec ); +} + +inline Date Unx2Date( time_t nDate ) +{ + tm atm; + tm *pTime; + pTime = localtime_r( &nDate, &atm ); + return Date( pTime->tm_mday, + pTime->tm_mon + 1, + pTime->tm_year + 1900 ); +} + +inline void Unx2DateAndTime( time_t nDate, Time& rTime, Date& rDate ) +{ + tm atm; + tm *pTime; + pTime = localtime_r( &nDate, &atm ); + rTime = Time( pTime->tm_hour, pTime->tm_min, pTime->tm_sec ); + rDate = Date( pTime->tm_mday, pTime->tm_mon + 1, pTime->tm_year + 1900 ); +} + +const char* TempDirImpl( char *pBuf ); + +#define FSysFailOnErrorImpl() + +#endif diff --git a/tools/source/fsys/urlobj.cxx b/tools/source/fsys/urlobj.cxx new file mode 100644 index 000000000000..f7ffed5e4dd1 --- /dev/null +++ b/tools/source/fsys/urlobj.cxx @@ -0,0 +1,5559 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: urlobj.cxx,v $ + * $Revision: 1.63.36.1 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" +#include <tools/urlobj.hxx> +#include <tools/debug.hxx> +#include <tools/inetmime.hxx> +#include "com/sun/star/uno/Reference.hxx" +#include "com/sun/star/util/XStringWidth.hpp" +#include "osl/diagnose.h" +#include "osl/file.hxx" +#include "rtl/string.h" +#include "rtl/textenc.h" +#include "rtl/ustring.hxx" +#include "sal/types.h" + +#ifndef INCLUDED_ALGORITHM +#include <algorithm> +#define INCLUDED_ALGORITHM +#endif +#ifndef INCLUDED_LIMITS +#include <limits> +#define INCLUDED_LIMITS +#endif + +#include <string.h> + +namespace unnamed_tools_urlobj {} using namespace unnamed_tools_urlobj; + // unnamed namespaces don't work well yet... + +using namespace com::sun; + +//============================================================================ +// +// INetURLObject +// +//============================================================================ + +/* The URI grammar (using RFC 2234 conventions). + + Constructs of the form + {reference <rule1> using rule2} + stand for a rule matching the given rule1 specified in the given reference, + encoded to URI syntax using rule2 (as specified in this URI grammar). + + + ; RFC 1738, RFC 2396, RFC 2732, private + login = [user [":" password] "@"] hostport + user = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ";" / "=" / "_" / "~") + password = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ";" / "=" / "_" / "~") + hostport = host [":" port] + host = incomplete-hostname / hostname / IPv4address / IPv6reference + incomplete-hostname = *(domainlabel ".") domainlabel + hostname = *(domainlabel ".") toplabel ["."] + domainlabel = alphanum [*(alphanum / "-") alphanum] + toplabel = ALPHA [*(alphanum / "-") alphanum] + IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT + IPv6reference = "[" hexpart [":" IPv4address] "]" + hexpart = (hexseq ["::" [hexseq]]) / ("::" [hexseq]) + hexseq = hex4 *(":" hex4) + hex4 = 1*4HEXDIG + port = *DIGIT + escaped = "%" HEXDIG HEXDIG + reserved = "$" / "&" / "+" / "," / "/" / ":" / ";" / "=" / "?" / "@" / "[" / "]" + mark = "!" / "'" / "(" / ")" / "*" / "-" / "." / "_" / "~" + alphanum = ALPHA / DIGIT + unreserved = alphanum / mark + uric = escaped / reserved / unreserved + pchar = escaped / unreserved / "$" / "&" / "+" / "," / ":" / "=" / "@" + + + ; RFC 1738, RFC 2396 + ftp-url = "FTP://" login ["/" segment *("/" segment) [";TYPE=" ("A" / "D" / "I")]] + segment = *pchar + + + ; RFC 1738, RFC 2396 + http-url = "HTTP://" hostport ["/" segment *("/" segment) ["?" *uric]] + segment = *(pchar / ";") + + + ; RFC 1738, RFC 2396, <http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q188997&> + file-url = "FILE://" [host / "LOCALHOST" / netbios-name] ["/" segment *("/" segment)] + segment = *pchar + netbios-name = 1*{<alphanum / "!" / "#" / "$" / "%" / "&" / "'" / "(" / ")" / "-" / "." / "@" / "^" / "_" / "{" / "}" / "~"> using (escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "-" / "." / "@" / "_" / "~")} + + + ; RFC 2368, RFC 2396 + mailto-url = "MAILTO:" [to] [headers] + to = {RFC 822 <#mailbox> using *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")} + headers = "?" header *("&" header) + header = hname "=" hvalue + hname = {RFC 822 <field-name> using *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")} / "BODY" + hvalue = {RFC 822 <field-body> using *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")} + + + ; private (see RFC 1738, RFC 2396) + vnd-sun-star-webdav-url = "VND.SUN.STAR.WEBDAV://" hostport ["/" segment *("/" segment) ["?" *uric]] + segment = *(pchar / ";") + + + ; RFC 1738, RFC 2396, RFC 2732 + news-url = "NEWS:" grouppart + grouppart = "*" / group / article + group = alpha *(alphanum / "+" / "-" / "." / "_") + article = 1*(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "?" / "_" / "~") "@" host + + + ; private + private-url = "PRIVATE:" path ["?" *uric] + path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~") + + + ; private + vnd-sun-star-help-url = "VND.SUN.STAR.HELP://" name *("/" segment) ["?" *uric] + name = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~") + segment = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~") + + + ; private + https-url = "HTTPS://" hostport ["/" segment *("/" segment) ["?" *uric]] + segment = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~") + + + ; private + slot-url = "SLOT:" path ["?" *uric] + path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~") + + + ; private + macro-url = "MACRO:" path ["?" *uric] + path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~") + + + ; private + javascript-url = "JAVASCRIPT:" *uric + + + ; private (see RFC 2192) + imap-url = "IMAP://" user [";AUTH=" auth] "@" hostport "/" segment *("/" segment) ["/;UID=" nz_number] + user = 1*{RFC 2060 <CHAR8> using (escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "=" / "_" / "~")} + auth = {RFC 2060 <atom> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "+" / "," / "-" / "." / "=" / "_" / "~")} + segment = *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / "=" / "@" / "_" / "~") + nz_number = {RFC 2060 <nz_number> using *DIGIT} + + + ; private + pop3-url = "POP3://" login ["/" ["<" *uric ">"]] + + + ; RFC 2397 + data-url = "DATA:" [mediatype] [";BASE64"] "," *uric + mediatype = [type "/" subtype] *(";" attribute "=" value) + type = {RFC 2045 <type> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")} + subtype = {RFC 2045 <subtype> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")} + attribute = {RFC 2045 <subtype> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")} + value = {RFC 2045 <subtype> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / ":" / "?" / "@" / "_" / "~")} + + + ; RFC 2392, RFC 2396 + cid-url = "CID:" {RFC 822 <addr-spec> using *uric} + + + ; private + out-url = "OUT:///~" name ["/" *uric] + name = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "?" / "@" / "_" / "~" + + + ; prvate (see RFC 1738, RFC 2396) + vnd-sun-star-wfs-url = "VND.SUN.STAR.WFS://" [host / "LOCALHOST"] ["/" segment *("/" segment)] + segment = *pchar + + + ; private + vnd-sun-star-hier-url = "VND.SUN.STAR.HIER:" ["//"reg_name] *("/" *pchar) + reg_name = 1*(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~") + + ; private + vim-url = "VIM://" +vimc [":" *vimc] ["/" [("INBOX" message) / ("NEWSGROUPS" ["/" [+vimc message]])]] + message = ["/" [+vimc [":" +DIGIT "." +DIGIT "." +DIGIT]]] + vimc = ("=" HEXDIG HEXDIG) / alphanum + + + ; private + uno-url = ".UNO:" path ["?" *uric] + path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~") + + + ; private + component-url = ".COMPONENT:" path ["?" *uric] + path = *(escaped / alphanum / "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~") + + + ; private + vnd-sun-star-pkg-url = "VND.SUN.STAR.PKG://" reg_name *("/" *pchar) ["?" *uric] + reg_name = 1*(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / ":" / ";" / "=" / "@" / "_" / "~") + + + ; RFC 2255 + ldap-url = "LDAP://" [hostport] ["/" [dn ["?" [attrdesct *("," attrdesc)] ["?" ["base" / "one" / "sub"] ["?" [filter] ["?" extension *("," extension)]]]]]] + dn = {RFC 2253 <distinguishedName> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")} + attrdesc = {RFC 2251 <AttributeDescription> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")} + filter = {RFC 2254 <filter> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")} + extension = ["!"] ["X-"] extoken ["=" exvalue] + extoken = {RFC 2252 <oid> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / "/" / ":" / ";" / "@" / "_" / "~")} + exvalue = {RFC 2251 <LDAPString> using *(escaped / alphanum / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "-" / "." / "/" / ":" / ";" / "=" / "@" / "_" / "~")} + + + ; private + db-url = "DB:" *uric + + + ; private + vnd-sun-star-cmd-url = "VND.SUN.STAR.CMD:" opaque_part + opaque_part = uric_no_slash *uric + uric_no_slash = unreserved / escaped / ";" / "?" / ":" / "@" / "&" / "=" / "+" / "$" / "," + + + ; private + vnd-sun-star-url = "VND.SUN.STAR.ODMA:" ["/" *uric_no_slash] + uric_no_slash = unreserved / escaped / ";" / "?" / ":" / "@" / "&" / "=" / "+" / "$" / "," + + + ; RFC 1738 + telnet-url = "TELNET://" login ["/"] + + + ; private + vnd-sun-star-expand-url = "VND.SUN.STAR.EXPAND:" opaque_part + opaque_part = uric_no_slash *uric + uric_no_slash = unreserved / escaped / ";" / "?" / ":" / "@" / "&" / "=" / "+" / "$" / "," + + + ; private + vnd-sun-star-tdoc-url = "VND.SUN.STAR.TDOC:/" segment *("/" segment) + segment = *pchar + + + ; private + unknown-url = scheme ":" 1*uric + scheme = ALPHA *(alphanum / "+" / "-" / ".") + + + ; private (http://ubiqx.org/cifs/Appendix-D.html): + smb-url = "SMB://" login ["/" segment *("/" segment) ["?" *uric]] + segment = *(pchar / ";") + */ + +//============================================================================ +inline sal_Int32 INetURLObject::SubString::clear() +{ + sal_Int32 nDelta = -m_nLength; + m_nBegin = -1; + m_nLength = 0; + return nDelta; +} + +inline sal_Int32 INetURLObject::SubString::set(rtl::OUStringBuffer & rString, + rtl::OUString const & rSubString) +{ + rtl::OUString sTemp(rString.makeStringAndClear()); + sal_Int32 nDelta = set(sTemp, rSubString); + rString.append(sTemp); + return nDelta; +} + +inline sal_Int32 INetURLObject::SubString::set(rtl::OUString & rString, + rtl::OUString const & rSubString) +{ + sal_Int32 nDelta = rSubString.getLength() - m_nLength; + + rString = rString.replaceAt(m_nBegin, m_nLength, rSubString); + + m_nLength = rSubString.getLength(); + return nDelta; +} + +inline sal_Int32 INetURLObject::SubString::set(rtl::OUStringBuffer & rString, + rtl::OUString const & rSubString, + sal_Int32 nTheBegin) +{ + m_nBegin = nTheBegin; + return set(rString, rSubString); +} + +//============================================================================ +inline void INetURLObject::SubString::operator +=(sal_Int32 nDelta) +{ + if (isPresent()) + m_nBegin = m_nBegin + nDelta; +} + +//============================================================================ +int INetURLObject::SubString::compare(SubString const & rOther, + rtl::OUStringBuffer const & rThisString, + rtl::OUStringBuffer const & rOtherString) const +{ + sal_Int32 len = std::min(m_nLength, rOther.m_nLength); + sal_Unicode const * p1 = rThisString.getStr() + m_nBegin; + sal_Unicode const * end = p1 + len; + sal_Unicode const * p2 = rOtherString.getStr() + rOther.m_nBegin; + while (p1 != end) { + if (*p1 < *p2) { + return -1; + } else if (*p1 > *p2) { + return 1; + } + ++p1; + ++p2; + } + return m_nLength < rOther.m_nLength ? -1 + : m_nLength > rOther.m_nLength ? 1 + : 0; +} + +//============================================================================ +struct INetURLObject::SchemeInfo +{ + sal_Char const * m_pScheme; + sal_Char const * m_pPrefix; + sal_uInt16 m_nDefaultPort; + bool m_bAuthority; + bool m_bUser; + bool m_bAuth; + bool m_bPassword; + bool m_bHost; + bool m_bPort; + bool m_bHierarchical; + bool m_bQuery; +}; + +//============================================================================ +struct INetURLObject::PrefixInfo +{ + enum Kind { OFFICIAL, INTERNAL, EXTERNAL, ALIAS }; // order is important! + + sal_Char const * m_pPrefix; + sal_Char const * m_pTranslatedPrefix; + INetProtocol m_eScheme; + Kind m_eKind; +}; + +//============================================================================ +static INetURLObject::SchemeInfo const aSchemeInfoMap[INET_PROT_END] + = { { "", "", 0, false, false, false, false, false, false, false, + false }, + { "ftp", "ftp://", 21, true, true, false, true, true, true, true, + false }, + { "http", "http://", 80, true, false, false, false, true, true, + true, true }, + { "file", "file://", 0, true, false, false, false, true, false, + true, false }, + { "mailto", "mailto:", 0, false, false, false, false, false, + false, false, true }, + { "vnd.sun.star.webdav", "vnd.sun.star.webdav://", 80, true, false, + false, false, true, true, true, true }, + { "news", "news:", 0, false, false, false, false, false, false, false, + false }, + { "private", "private:", 0, false, false, false, false, false, + false, false, true }, + { "vnd.sun.star.help", "vnd.sun.star.help://", 0, true, false, false, + false, false, false, true, true }, + { "https", "https://", 443, true, false, false, false, true, true, + true, true }, + { "slot", "slot:", 0, false, false, false, false, false, false, + false, true }, + { "macro", "macro:", 0, false, false, false, false, false, false, + false, true }, + { "javascript", "javascript:", 0, false, false, false, false, + false, false, false, false }, + { "imap", "imap://", 143, true, true, true, false, true, true, + true, false }, + { "pop3", "pop3://", 110, true, true, false, true, true, true, + false, false }, + { "data", "data:", 0, false, false, false, false, false, false, + false, false }, + { "cid", "cid:", 0, false, false, false, false, false, false, + false, false }, + { "out", "out://", 0, true, false, false, false, false, false, + false, false }, + { "vnd.sun.star.wfs", "vnd.sun.star.wfs://", 0, true, false, false, + false, true, true, true, false }, + { "vnd.sun.star.hier", "vnd.sun.star.hier:", 0, true, false, false, + false, false, false, true, false }, + { "vim", "vim://", 0, true, true, false, true, false, false, true, + false }, + { ".uno", ".uno:", 0, false, false, false, false, false, false, + false, true }, + { ".component", ".component:", 0, false, false, false, false, + false, false, false, true }, + { "vnd.sun.star.pkg", "vnd.sun.star.pkg://", 0, true, false, false, + false, false, false, true, true }, + { "ldap", "ldap://", 389, true, false, false, false, true, true, + false, true }, + { "db", "db:", 0, false, false, false, false, false, false, false, + false }, + { "vnd.sun.star.cmd", "vnd.sun.star.cmd:", 0, false, false, false, + false, false, false, false, false }, + { "vnd.sun.star.odma", "vnd.sun.star.odma:", 0, false, false, false, + false, false, false, true, false }, + { "telnet", "telnet://", 23, true, true, false, true, true, true, true, + false }, + { "vnd.sun.star.expand", "vnd.sun.star.expand:", 0, false, false, false, + false, false, false, false, false }, + { "vnd.sun.star.tdoc", "vnd.sun.star.tdoc:", 0, false, false, false, + false, false, false, true, false }, + { "", "", 0, false, false, false, false, false, false, false, false }, + { "smb", "smb://", 139, true, true, false, true, true, true, true, + true } }; + +// static +inline INetURLObject::SchemeInfo const & +INetURLObject::getSchemeInfo(INetProtocol eTheScheme) +{ + return aSchemeInfoMap[eTheScheme]; +}; + +//============================================================================ +inline INetURLObject::SchemeInfo const & INetURLObject::getSchemeInfo() const +{ + return getSchemeInfo(m_eScheme); +} + +//============================================================================ +// static +inline void INetURLObject::appendEscape(rtl::OUStringBuffer & rTheText, + sal_Char cEscapePrefix, + sal_uInt32 nOctet) +{ + rTheText.append(sal_Unicode(cEscapePrefix)); + rTheText.append(sal_Unicode(INetMIME::getHexDigit(int(nOctet >> 4)))); + rTheText.append(sal_Unicode(INetMIME::getHexDigit(int(nOctet & 15)))); +} + +//============================================================================ +namespace unnamed_tools_urlobj { + +enum +{ + PA = INetURLObject::PART_OBSOLETE_NORMAL, + PB = INetURLObject::PART_OBSOLETE_FILE, + PC = INetURLObject::PART_OBSOLETE_PARAM, + PD = INetURLObject::PART_USER_PASSWORD, + PE = INetURLObject::PART_IMAP_ACHAR, + PF = INetURLObject::PART_VIM, + PG = INetURLObject::PART_HOST_EXTRA, + PH = INetURLObject::PART_FPATH, + PI = INetURLObject::PART_AUTHORITY, + PJ = INetURLObject::PART_PATH_SEGMENTS_EXTRA, + PK = INetURLObject::PART_REL_SEGMENT_EXTRA, + PL = INetURLObject::PART_URIC, + PM = INetURLObject::PART_HTTP_PATH, + PN = INetURLObject::PART_FILE_SEGMENT_EXTRA, + PO = INetURLObject::PART_MESSAGE_ID, + PP = INetURLObject::PART_MESSAGE_ID_PATH, + PQ = INetURLObject::PART_MAILTO, + PR = INetURLObject::PART_PATH_BEFORE_QUERY, + PS = INetURLObject::PART_PCHAR, + PT = INetURLObject::PART_FRAGMENT, + PU = INetURLObject::PART_VISIBLE, + PV = INetURLObject::PART_VISIBLE_NONSPECIAL, + PW = INetURLObject::PART_CREATEFRAGMENT, + PX = INetURLObject::PART_UNO_PARAM_VALUE, + PY = INetURLObject::PART_UNAMBIGUOUS, + PZ = INetURLObject::PART_URIC_NO_SLASH, + P1 = INetURLObject::PART_HTTP_QUERY, + P2 = INetURLObject::PART_NEWS_ARTICLE_LOCALPART +}; + +static sal_uInt32 const aMustEncodeMap[128] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* */ PY, +/* ! */ PC+PD+PE +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* " */ PU+PV +PY, +/* # */ PU, +/* $ */ PD+PE +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* % */ PU, +/* & */ PA+PB+PC+PD+PE +PH+PI+PJ+PK+PL+PM+PN+PO+PP +PR+PS+PT+PU+PV+PW+PX +PZ+P1+P2, +/* ' */ PD+PE +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* ( */ PD+PE +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* ) */ PD+PE +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* * */ PA+PB+PC+PD+PE +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* + */ PA+PB+PC+PD+PE +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX +PZ+P1+P2, +/* , */ PA+PB+PC+PD+PE +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW +PZ+P1+P2, +/* - */ PA+PB+PC+PD+PE +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* . */ PA+PB+PC+PD+PE +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* / */ PA+PB+PC +PH +PJ +PL+PM +PP+PQ+PR +PT+PU+PV +PX +P2, +/* 0 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* 1 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* 2 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* 3 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* 4 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* 5 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* 6 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* 7 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* 8 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* 9 */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* : */ PB+PC +PH+PI+PJ +PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX +PZ+P1+P2, +/* ; */ PC+PD +PI+PJ+PK+PL+PM +PO+PP+PQ+PR +PT+PU +PW +PZ+P1+P2, +/* < */ PC +PO+PP +PU+PV +PY, +/* = */ PA+PB+PC+PD+PE +PH+PI+PJ+PK+PL+PM+PN +PR+PS+PT+PU+PV+PW +PZ+P1+P2, +/* > */ PC +PO+PP +PU+PV +PY, +/* ? */ PC +PL +PT+PU +PW+PX +PZ +P2, +/* @ */ PC +PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1, +/* A */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* B */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* C */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* D */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* E */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* F */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* G */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* H */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* I */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* J */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* K */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* L */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* M */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* N */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* O */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* P */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* Q */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* R */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* S */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* T */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* U */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* V */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* W */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* X */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* Y */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* Z */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* [ */ PL +PU+PV +PX, +/* \ */ PB +PU+PV +PY, +/* ] */ PL +PU+PV +PX, +/* ^ */ PU+PV +PY, +/* _ */ PA+PB+PC+PD+PE +PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* ` */ PU+PV +PY, +/* a */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* b */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* c */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* d */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* e */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* f */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* g */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* h */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* i */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* j */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* k */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* l */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* m */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* n */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* o */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* p */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* q */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* r */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* s */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* t */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* u */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* v */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* w */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* x */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* y */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* z */ PA+PB+PC+PD+PE+PF+PG+PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ+P1+P2, +/* { */ PU+PV +PY, +/* | */ PB+PC +PN +PT+PU+PV +PY, +/* } */ PU+PV +PY, +/* ~ */ PA+PB+PC+PD+PE +PH+PI+PJ+PK+PL+PM+PN+PO+PP+PQ+PR+PS+PT+PU+PV+PW+PX+PY+PZ +P2, + 0 }; + +inline bool mustEncode(sal_uInt32 nUTF32, INetURLObject::Part ePart) +{ + return !INetMIME::isUSASCII(nUTF32) || !(aMustEncodeMap[nUTF32] & ePart); +} + +} + +//============================================================================ +void INetURLObject::setInvalid() +{ + m_aAbsURIRef.setLength(0); + m_eScheme = INET_PROT_NOT_VALID; + m_aScheme.clear(); + m_aUser.clear(); + m_aAuth.clear(); + m_aHost.clear(); + m_aPort.clear(); + m_aPath.clear(); + m_aQuery.clear(); + m_aFragment.clear(); +} + +//============================================================================ + +namespace unnamed_tools_urlobj { + +INetURLObject::FSysStyle +guessFSysStyleByCounting(sal_Unicode const * pBegin, + sal_Unicode const * pEnd, + INetURLObject::FSysStyle eStyle) +{ + DBG_ASSERT(eStyle + & (INetURLObject::FSYS_UNX + | INetURLObject::FSYS_DOS + | INetURLObject::FSYS_MAC), + "guessFSysStyleByCounting(): Bad style"); + DBG_ASSERT(std::numeric_limits< sal_Int32 >::min() < pBegin - pEnd + && pEnd - pBegin <= std::numeric_limits< sal_Int32 >::max(), + "guessFSysStyleByCounting(): Too big"); + sal_Int32 nSlashCount + = eStyle & INetURLObject::FSYS_UNX ? + 0 : std::numeric_limits< sal_Int32 >::min(); + sal_Int32 nBackslashCount + = eStyle & INetURLObject::FSYS_DOS ? + 0 : std::numeric_limits< sal_Int32 >::min(); + sal_Int32 nColonCount + = eStyle & INetURLObject::FSYS_MAC ? + 0 : std::numeric_limits< sal_Int32 >::min(); + while (pBegin != pEnd) + switch (*pBegin++) + { + case '/': + ++nSlashCount; + break; + + case '\\': + ++nBackslashCount; + break; + + case ':': + ++nColonCount; + break; + } + return nSlashCount >= nBackslashCount ? + nSlashCount >= nColonCount ? + INetURLObject::FSYS_UNX : INetURLObject::FSYS_MAC : + nBackslashCount >= nColonCount ? + INetURLObject::FSYS_DOS : INetURLObject::FSYS_MAC; +} + +rtl::OUString parseScheme( + sal_Unicode const ** begin, sal_Unicode const * end, + sal_uInt32 fragmentDelimiter) +{ + sal_Unicode const * p = *begin; + if (p != end && INetMIME::isAlpha(*p)) { + do { + ++p; + } while (p != end + && (INetMIME::isAlphanumeric(*p) || *p == '+' || *p == '-' + || *p == '.')); + // #i34835# To avoid problems with Windows file paths like "C:\foo", + // do not accept generic schemes that are only one character long: + if (end - p > 1 && p[0] == ':' && p[1] != fragmentDelimiter + && p - *begin >= 2) + { + rtl::OUString scheme( + rtl::OUString(*begin, p - *begin).toAsciiLowerCase()); + *begin = p + 1; + return scheme; + } + } + return rtl::OUString(); +} + +} + +bool INetURLObject::setAbsURIRef(rtl::OUString const & rTheAbsURIRef, + bool bOctets, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset, + bool bSmart, + FSysStyle eStyle) +{ + sal_Unicode const * pPos = rTheAbsURIRef.getStr(); + sal_Unicode const * pEnd = pPos + rTheAbsURIRef.getLength(); + + setInvalid(); + + sal_uInt32 nFragmentDelimiter = '#'; + + rtl::OUStringBuffer aSynAbsURIRef; + + // Parse <scheme>: + sal_Unicode const * p = pPos; + PrefixInfo const * pPrefix = getPrefix(p, pEnd); + if (pPrefix) + { + pPos = p; + m_eScheme = pPrefix->m_eScheme; + + rtl::OUString sTemp(rtl::OUString::createFromAscii(pPrefix->m_eKind + >= PrefixInfo::EXTERNAL ? + pPrefix->m_pTranslatedPrefix : + pPrefix->m_pPrefix)); + aSynAbsURIRef.append(sTemp); + m_aScheme = SubString( 0, sTemp.indexOf(static_cast< sal_Unicode >(':')) ); + } + else + { + if (bSmart) + { + // For scheme detection, the first (if any) of the following + // productions that matches the input string (and for which the + // appropriate style bit is set in eStyle, if applicable) + // determines the scheme. The productions use the auxiliary rules + // + // domain = label *("." label) + // label = alphanum [*(alphanum / "-") alphanum] + // alphanum = ALPHA / DIGIT + // IPv6reference = "[" IPv6address "]" + // IPv6address = hexpart [":" IPv4address] + // IPv4address = 1*3DIGIT 3("." 1*3DIGIT) + // hexpart = (hexseq ["::" [hexseq]]) / ("::" [hexseq]) + // hexseq = hex4 *(":" hex4) + // hex4 = 1*4HEXDIG + // UCS4 = <any UCS4 character> + // + // 1st Production (known scheme): + // <one of the known schemes, ignoring case> ":" *UCS4 + // + // 2nd Production (mailto): + // domain "@" domain + // + // 3rd Production (ftp): + // "FTP" 2*("." label) ["/" *UCS4] + // + // 4th Production (http): + // label 2*("." label) ["/" *UCS4] + // + // 5th Production (file): + // "//" (domain / IPv6reference) ["/" *UCS4] + // + // 6th Production (Unix file): + // "/" *UCS4 + // + // 7th Production (UNC file; FSYS_DOS only): + // "\\" domain ["\" *UCS4] + // + // 8th Production (Unix-like DOS file; FSYS_DOS only): + // ALPHA ":" ["/" *UCS4] + // + // 9th Production (DOS file; FSYS_DOS only): + // ALPHA ":" ["\" *UCS4] + // + // For the 'non URL' file productions 6--9, the interpretation of + // the input as a (degenerate) URI is turned off, i.e., escape + // sequences and fragments are never detected as such, but are + // taken as literal characters. + + sal_Unicode const * p1 = pPos; + if (eStyle & FSYS_DOS + && pEnd - p1 >= 2 + && INetMIME::isAlpha(p1[0]) + && p1[1] == ':' + && (pEnd - p1 == 2 || p1[2] == '/' || p1[2] == '\\')) + { + m_eScheme = INET_PROT_FILE; // 8th, 9th + eMechanism = ENCODE_ALL; + nFragmentDelimiter = 0x80000000; + } + else if (pEnd - p1 >= 2 && p1[0] == '/' && p1[1] == '/') + { + p1 += 2; + if ((scanDomain(p1, pEnd) > 0 || scanIPv6reference(p1, pEnd)) + && (p1 == pEnd || *p1 == '/')) + m_eScheme = INET_PROT_FILE; // 5th + } + else if (p1 != pEnd && *p1 == '/') + { + m_eScheme = INET_PROT_FILE; // 6th + eMechanism = ENCODE_ALL; + nFragmentDelimiter = 0x80000000; + } + else if (eStyle & FSYS_DOS + && pEnd - p1 >= 2 + && p1[0] == '\\' + && p1[1] == '\\') + { + p1 += 2; + sal_Int32 n = rtl_ustr_indexOfChar_WithLength( + p1, pEnd - p1, '\\'); + sal_Unicode const * pe = n == -1 ? pEnd : p1 + n; + if ( + parseHostOrNetBiosName( + p1, pe, bOctets, ENCODE_ALL, RTL_TEXTENCODING_DONTKNOW, + true, NULL) || + (scanDomain(p1, pe) > 0 && p1 == pe) + ) + { + m_eScheme = INET_PROT_FILE; // 7th + eMechanism = ENCODE_ALL; + nFragmentDelimiter = 0x80000000; + } + } + else + { + sal_Unicode const * pDomainEnd = p1; + sal_uInt32 nLabels = scanDomain(pDomainEnd, pEnd); + if (nLabels > 0 && pDomainEnd != pEnd && *pDomainEnd == '@') + { + ++pDomainEnd; + if (scanDomain(pDomainEnd, pEnd) > 0 + && pDomainEnd == pEnd) + m_eScheme = INET_PROT_MAILTO; // 2nd + } + else if (nLabels >= 3 + && (pDomainEnd == pEnd || *pDomainEnd == '/')) + m_eScheme + = pDomainEnd - p1 >= 4 + && (p1[0] == 'f' || p1[0] == 'F') + && (p1[1] == 't' || p1[1] == 'T') + && (p1[2] == 'p' || p1[2] == 'P') + && p1[3] == '.' ? + INET_PROT_FTP : INET_PROT_HTTP; // 3rd, 4th + } + } + + rtl::OUString aSynScheme; + if (m_eScheme == INET_PROT_NOT_VALID) { + sal_Unicode const * p1 = pPos; + aSynScheme = parseScheme(&p1, pEnd, nFragmentDelimiter); + if (aSynScheme.getLength() > 0) + { + m_eScheme = INET_PROT_GENERIC; + pPos = p1; + } + } + + if (bSmart && m_eScheme == INET_PROT_NOT_VALID && pPos != pEnd + && *pPos != nFragmentDelimiter) + { + m_eScheme = m_eSmartScheme; + } + + if (m_eScheme == INET_PROT_NOT_VALID) + { + setInvalid(); + return false; + } + + if (m_eScheme != INET_PROT_GENERIC) { + aSynScheme = rtl::OUString::createFromAscii(getSchemeInfo().m_pScheme); + } + m_aScheme.set(aSynAbsURIRef, aSynScheme, aSynAbsURIRef.getLength()); + aSynAbsURIRef.append(sal_Unicode(':')); + } + + sal_Char cEscapePrefix = getEscapePrefix(); + sal_uInt32 nSegmentDelimiter = '/'; + sal_uInt32 nAltSegmentDelimiter = 0x80000000; + bool bSkippedInitialSlash = false; + + // Parse //<user>;AUTH=<auth>@<host>:<port> or + // //<user>:<password>@<host>:<port> or + // //<reg_name> + if (getSchemeInfo().m_bAuthority) + { + sal_Unicode const * pUserInfoBegin = 0; + sal_Unicode const * pUserInfoEnd = 0; + sal_Unicode const * pHostPortBegin = 0; + sal_Unicode const * pHostPortEnd = 0; + + switch (m_eScheme) + { + case INET_PROT_VND_SUN_STAR_HELP: + { + if (pEnd - pPos < 2 || *pPos++ != '/' || *pPos++ != '/') + { + setInvalid(); + return false; + } + aSynAbsURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + rtl::OUStringBuffer aSynAuthority; + while (pPos < pEnd + && *pPos != '/' && *pPos != '?' + && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + cEscapePrefix, eMechanism, + eCharset, eEscapeType); + appendUCS4(aSynAuthority, nUTF32, eEscapeType, bOctets, + PART_AUTHORITY, cEscapePrefix, eCharset, + false); + } + m_aHost.set(aSynAbsURIRef, + aSynAuthority.makeStringAndClear(), + aSynAbsURIRef.getLength()); + // misusing m_aHost to store the authority + break; + } + + case INET_PROT_VND_SUN_STAR_HIER: + { + if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/') + { + pPos += 2; + aSynAbsURIRef. + appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + rtl::OUStringBuffer aSynAuthority; + while (pPos < pEnd + && *pPos != '/' && *pPos != '?' + && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, + pEnd, + bOctets, + cEscapePrefix, + eMechanism, + eCharset, + eEscapeType); + appendUCS4(aSynAuthority, + nUTF32, + eEscapeType, + bOctets, + PART_AUTHORITY, + cEscapePrefix, + eCharset, + false); + } + if (aSynAuthority.getLength() == 0) + { + setInvalid(); + return false; + } + m_aHost.set(aSynAbsURIRef, + aSynAuthority.makeStringAndClear(), + aSynAbsURIRef.getLength()); + // misusing m_aHost to store the authority + } + break; + } + + case INET_PROT_VND_SUN_STAR_PKG: + { + if (pEnd - pPos < 2 || *pPos++ != '/' || *pPos++ != '/') + { + setInvalid(); + return false; + } + aSynAbsURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + rtl::OUStringBuffer aSynAuthority; + while (pPos < pEnd + && *pPos != '/' && *pPos != '?' + && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + cEscapePrefix, eMechanism, + eCharset, eEscapeType); + appendUCS4(aSynAuthority, nUTF32, eEscapeType, bOctets, + PART_AUTHORITY, cEscapePrefix, eCharset, + false); + } + if (aSynAuthority.getLength() == 0) + { + setInvalid(); + return false; + } + m_aHost.set(aSynAbsURIRef, + aSynAuthority.makeStringAndClear(), + aSynAbsURIRef.getLength()); + // misusing m_aHost to store the authority + break; + } + + case INET_PROT_FILE: + if (bSmart) + { + // The first of the following seven productions that + // matches the rest of the input string (and for which the + // appropriate style bit is set in eStyle, if applicable) + // determines the used notation. The productions use the + // auxiliary rules + // + // domain = label *("." label) + // label = alphanum [*(alphanum / "-") alphanum] + // alphanum = ALPHA / DIGIT + // IPv6reference = "[" IPv6address "]" + // IPv6address = hexpart [":" IPv4address] + // IPv4address = 1*3DIGIT 3("." 1*3DIGIT) + // hexpart = (hexseq ["::" [hexseq]]) / ("::" [hexseq]) + // hexseq = hex4 *(":" hex4) + // hex4 = 1*4HEXDIG + // path = <any UCS4 character except "#"> + // UCS4 = <any UCS4 character> + + // 1st Production (URL): + // "//" [domain / IPv6reference] ["/" *path] + // ["#" *UCS4] + // becomes + // "file://" domain "/" *path ["#" *UCS4] + if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/') + { + sal_Unicode const * p1 = pPos + 2; + if ( + p1 == pEnd || *p1 == nFragmentDelimiter || *p1 == '/' || + ( + ( + scanDomain(p1, pEnd) > 0 || + scanIPv6reference(p1, pEnd) + ) && + (p1 == pEnd || *p1 == nFragmentDelimiter || *p1 == '/') + ) + ) + { + aSynAbsURIRef. + appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + pHostPortBegin = pPos + 2; + pHostPortEnd = p1; + pPos = p1; + break; + } + } + + // 2nd Production (MS IE generated 1; FSYS_DOS only): + // "//" ALPHA ":" ["/" *path] ["#" *UCS4] + // becomes + // "file:///" ALPHA ":" ["/" *path] ["#" *UCS4] + // replacing "\" by "/" within <*path> + // + // 3rd Production (MS IE generated 2; FSYS_DOS only): + // "//" ALPHA ":" ["\" *path] ["#" *UCS4] + // becomes + // "file:///" ALPHA ":" ["/" *path] ["#" *UCS4] + // replacing "\" by "/" within <*path> + // + // 4th Production (misscounted slashes): + // "//" *path ["#" *UCS4] + // becomes + // "file:///" *path ["#" *UCS4] + if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/') + { + aSynAbsURIRef. + appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + pPos += 2; + bSkippedInitialSlash = true; + if ((eStyle & FSYS_DOS) != 0 + && pEnd - pPos >= 2 + && INetMIME::isAlpha(pPos[0]) + && pPos[1] == ':' + && (pEnd - pPos == 2 + || pPos[2] == '/' || pPos[2] == '\\')) + nAltSegmentDelimiter = '\\'; + break; + } + + // 5th Production (Unix): + // "/" *path ["#" *UCS4] + // becomes + // "file:///" *path ["#" *UCS4] + if (pPos < pEnd && *pPos == '/') + { + aSynAbsURIRef. + appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + break; + } + + // 6th Production (UNC; FSYS_DOS only): + // "\\" domain ["\" *path] ["#" *UCS4] + // becomes + // "file://" domain "/" *path ["#" *UCS4] + // replacing "\" by "/" within <*path> + if (eStyle & FSYS_DOS + && pEnd - pPos >= 2 + && pPos[0] == '\\' + && pPos[1] == '\\') + { + sal_Unicode const * p1 = pPos + 2; + sal_Unicode const * pe = p1; + while (pe < pEnd && *pe != '\\' && + *pe != nFragmentDelimiter) + { + ++pe; + } + if ( + parseHostOrNetBiosName( + p1, pe, bOctets, ENCODE_ALL, + RTL_TEXTENCODING_DONTKNOW, true, NULL) || + (scanDomain(p1, pe) > 0 && p1 == pe) + ) + { + aSynAbsURIRef. + appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + pHostPortBegin = pPos + 2; + pHostPortEnd = pe; + pPos = pe; + nSegmentDelimiter = '\\'; + break; + } + } + + // 7th Production (Unix-like DOS; FSYS_DOS only): + // ALPHA ":" ["/" *path] ["#" *UCS4] + // becomes + // "file:///" ALPHA ":" ["/" *path] ["#" *UCS4] + // replacing "\" by "/" within <*path> + // + // 8th Production (DOS; FSYS_DOS only): + // ALPHA ":" ["\" *path] ["#" *UCS4] + // becomes + // "file:///" ALPHA ":" ["/" *path] ["#" *UCS4] + // replacing "\" by "/" within <*path> + if (eStyle & FSYS_DOS + && pEnd - pPos >= 2 + && INetMIME::isAlpha(pPos[0]) + && pPos[1] == ':' + && (pEnd - pPos == 2 + || pPos[2] == '/' + || pPos[2] == '\\')) + { + aSynAbsURIRef. + appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + nAltSegmentDelimiter = '\\'; + bSkippedInitialSlash = true; + break; + } + + // 9th Production (any): + // *path ["#" *UCS4] + // becomes + // "file:///" *path ["#" *UCS4] + // replacing the delimiter by "/" within <*path>. The + // delimiter is that character from the set { "/", "\", + // ":" } which appears most often in <*path> (if FSYS_UNX + // is not among the style bits, "/" is removed from the + // set; if FSYS_DOS is not among the style bits, "\" is + // removed from the set; if FSYS_MAC is not among the + // style bits, ":" is removed from the set). If two or + // more characters appear the same number of times, the + // character mentioned first in that set is chosen. If + // the first character of <*path> is the delimiter, that + // character is not copied. + if (eStyle & (FSYS_UNX | FSYS_DOS | FSYS_MAC)) + { + aSynAbsURIRef. + appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + switch (guessFSysStyleByCounting(pPos, pEnd, eStyle)) + { + case FSYS_UNX: + nSegmentDelimiter = '/'; + break; + + case FSYS_DOS: + nSegmentDelimiter = '\\'; + break; + + case FSYS_MAC: + nSegmentDelimiter = ':'; + break; + + default: + DBG_ERROR( + "INetURLObject::setAbsURIRef():" + " Bad guessFSysStyleByCounting"); + break; + } + bSkippedInitialSlash + = pPos != pEnd && *pPos != nSegmentDelimiter; + break; + } + } + default: + { + // For INET_PROT_FILE, allow an empty authority ("//") to be + // missing if the following path starts with an explicit "/" + // (Java is notorious in generating such file URLs, so be + // liberal here): + if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/') + pPos += 2; + else if (!bSmart + && !(m_eScheme == INET_PROT_FILE + && pPos != pEnd && *pPos == '/')) + { + setInvalid(); + return false; + } + aSynAbsURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + + sal_Unicode const * pAuthority = pPos; + sal_uInt32 c = getSchemeInfo().m_bQuery ? '?' : 0x80000000; + while (pPos < pEnd && *pPos != '/' && *pPos != c + && *pPos != nFragmentDelimiter) + ++pPos; + if (getSchemeInfo().m_bUser) + if (getSchemeInfo().m_bHost) + { + sal_Unicode const * p1 = pAuthority; + while (p1 < pPos && *p1 != '@') + ++p1; + if (p1 == pPos) + { + pHostPortBegin = pAuthority; + pHostPortEnd = pPos; + } + else + { + pUserInfoBegin = pAuthority; + pUserInfoEnd = p1; + pHostPortBegin = p1 + 1; + pHostPortEnd = pPos; + } + } + else + { + pUserInfoBegin = pAuthority; + pUserInfoEnd = pPos; + } + else if (getSchemeInfo().m_bHost) + { + pHostPortBegin = pAuthority; + pHostPortEnd = pPos; + } + else if (pPos != pAuthority) + { + setInvalid(); + return false; + } + break; + } + } + + if (pUserInfoBegin) + { + Part ePart = m_eScheme == INET_PROT_IMAP ? + PART_IMAP_ACHAR : + m_eScheme == INET_PROT_VIM ? + PART_VIM : + PART_USER_PASSWORD; + bool bSupportsPassword = getSchemeInfo().m_bPassword; + bool bSupportsAuth + = !bSupportsPassword && getSchemeInfo().m_bAuth; + bool bHasAuth = false; + rtl::OUStringBuffer aSynUser; + sal_Unicode const * p1 = pUserInfoBegin; + while (p1 < pUserInfoEnd) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(p1, pUserInfoEnd, bOctets, + cEscapePrefix, eMechanism, + eCharset, eEscapeType); + if (eEscapeType == ESCAPE_NO) + { + if (nUTF32 == ':' && bSupportsPassword) + { + bHasAuth = true; + break; + } + else if (nUTF32 == ';' && bSupportsAuth + && pUserInfoEnd - p1 + > RTL_CONSTASCII_LENGTH("auth=") + && INetMIME::equalIgnoreCase( + p1, + p1 + RTL_CONSTASCII_LENGTH("auth="), + "auth=")) + { + p1 += RTL_CONSTASCII_LENGTH("auth="); + bHasAuth = true; + break; + } + } + appendUCS4(aSynUser, nUTF32, eEscapeType, bOctets, ePart, + cEscapePrefix, eCharset, false); + } + m_aUser.set(aSynAbsURIRef, aSynUser.makeStringAndClear(), + aSynAbsURIRef.getLength()); + if (bHasAuth) + { + if (bSupportsPassword) + { + aSynAbsURIRef.append(sal_Unicode(':')); + rtl::OUStringBuffer aSynAuth; + while (p1 < pUserInfoEnd) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(p1, pUserInfoEnd, bOctets, + cEscapePrefix, + eMechanism, eCharset, + eEscapeType); + appendUCS4(aSynAuth, nUTF32, eEscapeType, bOctets, + ePart, cEscapePrefix, eCharset, false); + } + m_aAuth.set(aSynAbsURIRef, aSynAuth.makeStringAndClear(), + aSynAbsURIRef.getLength()); + } + else + { + aSynAbsURIRef. + appendAscii(RTL_CONSTASCII_STRINGPARAM(";AUTH=")); + rtl::OUStringBuffer aSynAuth; + while (p1 < pUserInfoEnd) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(p1, pUserInfoEnd, bOctets, + cEscapePrefix, + eMechanism, eCharset, + eEscapeType); + if (!INetMIME::isIMAPAtomChar(nUTF32)) + { + setInvalid(); + return false; + } + appendUCS4(aSynAuth, nUTF32, eEscapeType, bOctets, + ePart, cEscapePrefix, eCharset, false); + } + m_aAuth.set(aSynAbsURIRef, aSynAuth.makeStringAndClear(), + aSynAbsURIRef.getLength()); + } + } + if (pHostPortBegin) + aSynAbsURIRef.append(sal_Unicode('@')); + } + + if (pHostPortBegin) + { + sal_Unicode const * pPort = pHostPortEnd; + if (getSchemeInfo().m_bPort && pHostPortBegin < pHostPortEnd) + { + sal_Unicode const * p1 = pHostPortEnd - 1; + while (p1 > pHostPortBegin && INetMIME::isDigit(*p1)) + --p1; + if (*p1 == ':') + pPort = p1; + } + bool bNetBiosName = false; + switch (m_eScheme) + { + case INET_PROT_FILE: + case INET_PROT_VND_SUN_STAR_WFS: + // If the host equals "LOCALHOST" (unencoded and ignoring + // case), turn it into an empty host: + if (INetMIME::equalIgnoreCase(pHostPortBegin, pPort, + "localhost")) + pHostPortBegin = pPort; + bNetBiosName = true; + break; + + case INET_PROT_LDAP: + case INET_PROT_SMB: + if (pHostPortBegin == pPort && pPort != pHostPortEnd) + { + setInvalid(); + return false; + } + break; + + default: + if (pHostPortBegin == pPort) + { + setInvalid(); + return false; + } + break; + } + rtl::OUStringBuffer aSynHost; + if (!parseHostOrNetBiosName( + pHostPortBegin, pPort, bOctets, eMechanism, eCharset, + bNetBiosName, &aSynHost)) + { + setInvalid(); + return false; + } + m_aHost.set(aSynAbsURIRef, aSynHost.makeStringAndClear(), + aSynAbsURIRef.getLength()); + if (pPort != pHostPortEnd) + { + aSynAbsURIRef.append(sal_Unicode(':')); + m_aPort.set(aSynAbsURIRef, + rtl::OUString(pPort + 1, pHostPortEnd - (pPort + 1)), + aSynAbsURIRef.getLength()); + } + } + } + + // Parse <path> + rtl::OUStringBuffer aSynPath; + if (!parsePath(m_eScheme, &pPos, pEnd, bOctets, eMechanism, eCharset, + bSkippedInitialSlash, nSegmentDelimiter, + nAltSegmentDelimiter, + getSchemeInfo().m_bQuery ? '?' : 0x80000000, + nFragmentDelimiter, aSynPath)) + { + setInvalid(); + return false; + } + m_aPath.set(aSynAbsURIRef, aSynPath.makeStringAndClear(), + aSynAbsURIRef.getLength()); + + // Parse ?<query> + if (getSchemeInfo().m_bQuery && pPos < pEnd && *pPos == '?') + { + aSynAbsURIRef.append(sal_Unicode('?')); + rtl::OUStringBuffer aSynQuery; + for (++pPos; pPos < pEnd && *pPos != nFragmentDelimiter;) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, cEscapePrefix, + eMechanism, eCharset, eEscapeType); + appendUCS4(aSynQuery, nUTF32, eEscapeType, bOctets, + PART_URIC, cEscapePrefix, eCharset, true); + } + m_aQuery.set(aSynAbsURIRef, aSynQuery.makeStringAndClear(), + aSynAbsURIRef.getLength()); + } + + // Parse #<fragment> + if (pPos < pEnd && *pPos == nFragmentDelimiter) + { + aSynAbsURIRef.append(sal_Unicode(nFragmentDelimiter)); + rtl::OUStringBuffer aSynFragment; + for (++pPos; pPos < pEnd;) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, cEscapePrefix, + eMechanism, eCharset, eEscapeType); + appendUCS4(aSynFragment, nUTF32, eEscapeType, bOctets, PART_URIC, + cEscapePrefix, eCharset, true); + } + m_aFragment.set(aSynAbsURIRef, aSynFragment.makeStringAndClear(), + aSynAbsURIRef.getLength()); + } + + if (pPos != pEnd) + { + setInvalid(); + return false; + } + + m_aAbsURIRef = aSynAbsURIRef; + + return true; +} + +//============================================================================ +bool INetURLObject::convertRelToAbs(rtl::OUString const & rTheRelURIRef, + bool bOctets, + INetURLObject & rTheAbsURIRef, + bool & rWasAbsolute, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset, + bool bIgnoreFragment, bool bSmart, + bool bRelativeNonURIs, FSysStyle eStyle) + const +{ + sal_Unicode const * p = rTheRelURIRef.getStr(); + sal_Unicode const * pEnd = p + rTheRelURIRef.getLength(); + + sal_Unicode const * pPrefixBegin = p; + PrefixInfo const * pPrefix = getPrefix(pPrefixBegin, pEnd); + bool hasScheme = pPrefix != 0; + if (!hasScheme) { + pPrefixBegin = p; + hasScheme = parseScheme(&pPrefixBegin, pEnd, '#').getLength() > 0; + } + + sal_uInt32 nSegmentDelimiter = '/'; + sal_uInt32 nQueryDelimiter + = !bSmart || getSchemeInfo().m_bQuery ? '?' : 0x80000000; + sal_uInt32 nFragmentDelimiter = '#'; + Part ePart = PART_VISIBLE; + + if (!hasScheme && bSmart) + { + // If the input matches any of the following productions (for which + // the appropriate style bit is set in eStyle), it is assumed to be an + // absolute file system path, rather than a relative URI reference. + // (This is only a subset of the productions used for scheme detection + // in INetURLObject::setAbsURIRef(), because most of those productions + // interfere with the syntax of relative URI references.) The + // productions use the auxiliary rules + // + // domain = label *("." label) + // label = alphanum [*(alphanum / "-") alphanum] + // alphanum = ALPHA / DIGIT + // UCS4 = <any UCS4 character> + // + // 1st Production (UNC file; FSYS_DOS only): + // "\\" domain ["\" *UCS4] + // + // 2nd Production (Unix-like DOS file; FSYS_DOS only): + // ALPHA ":" ["/" *UCS4] + // + // 3rd Production (DOS file; FSYS_DOS only): + // ALPHA ":" ["\" *UCS4] + if (eStyle & FSYS_DOS) + { + bool bFSys = false; + sal_Unicode const * q = p; + if (pEnd - q >= 2 + && INetMIME::isAlpha(q[0]) + && q[1] == ':' + && (pEnd - q == 2 || q[2] == '/' || q[2] == '\\')) + bFSys = true; // 2nd, 3rd + else if (pEnd - q >= 2 && q[0] == '\\' && q[1] == '\\') + { + q += 2; + sal_Int32 n = rtl_ustr_indexOfChar_WithLength( + q, pEnd - q, '\\'); + sal_Unicode const * qe = n == -1 ? pEnd : q + n; + if (parseHostOrNetBiosName( + q, qe, bOctets, ENCODE_ALL, RTL_TEXTENCODING_DONTKNOW, + true, NULL)) + { + bFSys = true; // 1st + } + } + if (bFSys) + { + INetURLObject aNewURI; + aNewURI.setAbsURIRef(rTheRelURIRef, bOctets, eMechanism, + eCharset, true, eStyle); + if (!aNewURI.HasError()) + { + rTheAbsURIRef = aNewURI; + rWasAbsolute = true; + return true; + } + } + } + + // When the base URL is a file URL, accept relative file system paths + // using "\" or ":" as delimiter (and ignoring URI conventions for "%" + // and "#"), as well as relative URIs using "/" as delimiter: + if (m_eScheme == INET_PROT_FILE) + switch (guessFSysStyleByCounting(p, pEnd, eStyle)) + { + case FSYS_UNX: + nSegmentDelimiter = '/'; + break; + + case FSYS_DOS: + nSegmentDelimiter = '\\'; + bRelativeNonURIs = true; + break; + + case FSYS_MAC: + nSegmentDelimiter = ':'; + bRelativeNonURIs = true; + break; + + default: + DBG_ERROR("INetURLObject::convertRelToAbs():" + " Bad guessFSysStyleByCounting"); + break; + } + + if (bRelativeNonURIs) + { + eMechanism = ENCODE_ALL; + nQueryDelimiter = 0x80000000; + nFragmentDelimiter = 0x80000000; + ePart = PART_VISIBLE_NONSPECIAL; + } + } + + // If the relative URI has the same scheme as the base URI, and that + // scheme is hierarchical, then ignore its presence in the relative + // URI in order to be backward compatible (cf. RFC 2396 section 5.2 + // step 3): + if (pPrefix && pPrefix->m_eScheme == m_eScheme + && getSchemeInfo().m_bHierarchical) + { + hasScheme = false; + while (p != pEnd && *p++ != ':') ; + } + rWasAbsolute = hasScheme; + + // Fast solution for non-relative URIs: + if (hasScheme) + { + INetURLObject aNewURI(rTheRelURIRef, eMechanism, eCharset); + if (aNewURI.HasError()) + { + rWasAbsolute = false; + return false; + } + + if (bIgnoreFragment) + aNewURI.clearFragment(); + rTheAbsURIRef = aNewURI; + return true; + } + + enum State { STATE_AUTH, STATE_ABS_PATH, STATE_REL_PATH, STATE_FRAGMENT, + STATE_DONE }; + + rtl::OUStringBuffer aSynAbsURIRef; + aSynAbsURIRef.appendAscii(getSchemeInfo().m_pScheme); + aSynAbsURIRef.append(sal_Unicode(':')); + + sal_Char cEscapePrefix = getEscapePrefix(); + + State eState = STATE_AUTH; + bool bSameDoc = true; + + if (getSchemeInfo().m_bAuthority) + { + if (pEnd - p >= 2 && p[0] == '/' && p[1] == '/') + { + aSynAbsURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + p += 2; + eState = STATE_ABS_PATH; + bSameDoc = false; + while (p != pEnd) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 + = getUTF32(p, pEnd, bOctets, cEscapePrefix, eMechanism, + eCharset, eEscapeType); + if (eEscapeType == ESCAPE_NO) + { + if (nUTF32 == nSegmentDelimiter) + break; + else if (nUTF32 == nFragmentDelimiter) + { + eState = STATE_FRAGMENT; + break; + } + } + appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, bOctets, + PART_VISIBLE, cEscapePrefix, eCharset, true); + } + } + else + { + SubString aAuthority(getAuthority()); + aSynAbsURIRef.append(m_aAbsURIRef.getStr() + + aAuthority.getBegin(), + aAuthority.getLength()); + } + } + + if (eState == STATE_AUTH) + { + if (p == pEnd) + eState = STATE_DONE; + else if (*p == nFragmentDelimiter) + { + ++p; + eState = STATE_FRAGMENT; + } + else if (*p == nSegmentDelimiter) + { + ++p; + eState = STATE_ABS_PATH; + bSameDoc = false; + } + else + { + eState = STATE_REL_PATH; + bSameDoc = false; + } + } + + if (eState == STATE_ABS_PATH) + { + aSynAbsURIRef.append(sal_Unicode('/')); + eState = STATE_DONE; + while (p != pEnd) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 + = getUTF32(p, pEnd, bOctets, cEscapePrefix, eMechanism, + eCharset, eEscapeType); + if (eEscapeType == ESCAPE_NO) + { + if (nUTF32 == nFragmentDelimiter) + { + eState = STATE_FRAGMENT; + break; + } + else if (nUTF32 == nSegmentDelimiter) + nUTF32 = '/'; + } + appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, bOctets, ePart, + cEscapePrefix, eCharset, true); + } + } + else if (eState == STATE_REL_PATH) + { + if (!getSchemeInfo().m_bHierarchical) + { + // Detect cases where a relative input could not be made absolute + // because the given base URL is broken (most probably because it is + // empty): + OSL_ASSERT(!HasError()); + rWasAbsolute = false; + return false; + } + + sal_Unicode const * pBasePathBegin + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pBasePathEnd + = pBasePathBegin + m_aPath.getLength(); + while (pBasePathEnd != pBasePathBegin) + if (*(--pBasePathEnd) == '/') + { + ++pBasePathEnd; + break; + } + + sal_Int32 nPathBegin = aSynAbsURIRef.getLength(); + aSynAbsURIRef.append(pBasePathBegin, pBasePathEnd - pBasePathBegin); + DBG_ASSERT(aSynAbsURIRef.getLength() > nPathBegin + && aSynAbsURIRef.charAt(aSynAbsURIRef.getLength() - 1) == '/', + "INetURLObject::convertRelToAbs(): Bad base path"); + + while (p != pEnd && *p != nQueryDelimiter && *p != nFragmentDelimiter) + { + if (*p == '.') + { + if (pEnd - p == 1 + || p[1] == nSegmentDelimiter + || p[1] == nQueryDelimiter + || p[1] == nFragmentDelimiter) + { + ++p; + if (p != pEnd && *p == nSegmentDelimiter) + ++p; + continue; + } + else if (pEnd - p >= 2 + && p[1] == '.' + && (pEnd - p == 2 + || p[2] == nSegmentDelimiter + || p[2] == nQueryDelimiter + || p[2] == nFragmentDelimiter) + && aSynAbsURIRef.getLength() - nPathBegin > 1) + { + p += 2; + if (p != pEnd && *p == nSegmentDelimiter) + ++p; + + sal_Int32 i = aSynAbsURIRef.getLength() - 2; + while (i > nPathBegin && aSynAbsURIRef.charAt(i) != '/') + --i; + aSynAbsURIRef.setLength(i + 1); + DBG_ASSERT( + aSynAbsURIRef.getLength() > nPathBegin + && aSynAbsURIRef.charAt(aSynAbsURIRef.getLength() - 1) + == '/', + "INetURLObject::convertRelToAbs(): Bad base path"); + continue; + } + } + + while (p != pEnd + && *p != nSegmentDelimiter + && *p != nQueryDelimiter + && *p != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 + = getUTF32(p, pEnd, bOctets, cEscapePrefix, eMechanism, + eCharset, eEscapeType); + appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, bOctets, ePart, + cEscapePrefix, eCharset, true); + } + if (p != pEnd && *p == nSegmentDelimiter) + { + aSynAbsURIRef.append(sal_Unicode('/')); + ++p; + } + } + + while (p != pEnd && *p != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 + = getUTF32(p, pEnd, bOctets, cEscapePrefix, eMechanism, + eCharset, eEscapeType); + appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, bOctets, ePart, + cEscapePrefix, eCharset, true); + } + + if (p == pEnd) + eState = STATE_DONE; + else + { + ++p; + eState = STATE_FRAGMENT; + } + } + else if (bSameDoc) + { + aSynAbsURIRef.append(m_aAbsURIRef.getStr() + m_aPath.getBegin(), + m_aPath.getLength()); + if (m_aQuery.isPresent()) + aSynAbsURIRef.append(m_aAbsURIRef.getStr() + + m_aQuery.getBegin() - 1, + m_aQuery.getLength() + 1); + } + + if (eState == STATE_FRAGMENT && !bIgnoreFragment) + { + aSynAbsURIRef.append(sal_Unicode('#')); + while (p != pEnd) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 + = getUTF32(p, pEnd, bOctets, cEscapePrefix, eMechanism, + eCharset, eEscapeType); + appendUCS4(aSynAbsURIRef, nUTF32, eEscapeType, bOctets, + PART_VISIBLE, cEscapePrefix, eCharset, true); + } + } + + INetURLObject aNewURI(aSynAbsURIRef.makeStringAndClear()); + if (aNewURI.HasError()) + { + // Detect cases where a relative input could not be made absolute + // because the given base URL is broken (most probably because it is + // empty): + OSL_ASSERT(!HasError()); + rWasAbsolute = false; + return false; + } + + rTheAbsURIRef = aNewURI; + return true; +} + +//============================================================================ +bool INetURLObject::convertAbsToRel(rtl::OUString const & rTheAbsURIRef, + bool bOctets, rtl::OUString & rTheRelURIRef, + EncodeMechanism eEncodeMechanism, + DecodeMechanism eDecodeMechanism, + rtl_TextEncoding eCharset, + FSysStyle eStyle) const +{ + // Check for hierarchical base URL: + if (!getSchemeInfo().m_bHierarchical) + { + rTheRelURIRef + = decode(rTheAbsURIRef, + getEscapePrefix(CompareProtocolScheme(rTheAbsURIRef)), + eDecodeMechanism, eCharset); + return false; + } + + // Convert the input (absolute or relative URI ref) to an absolute URI + // ref: + INetURLObject aSubject; + bool bWasAbsolute; + if (!convertRelToAbs(rTheAbsURIRef, bOctets, aSubject, bWasAbsolute, + eEncodeMechanism, eCharset, false, false, false, + eStyle)) + { + rTheRelURIRef + = decode(rTheAbsURIRef, + getEscapePrefix(CompareProtocolScheme(rTheAbsURIRef)), + eDecodeMechanism, eCharset); + return false; + } + + // Check for differing scheme or authority parts: + if ((m_aScheme.compare( + aSubject.m_aScheme, m_aAbsURIRef, aSubject.m_aAbsURIRef) + != 0) + || (m_aUser.compare( + aSubject.m_aUser, m_aAbsURIRef, aSubject.m_aAbsURIRef) + != 0) + || (m_aAuth.compare( + aSubject.m_aAuth, m_aAbsURIRef, aSubject.m_aAbsURIRef) + != 0) + || (m_aHost.compare( + aSubject.m_aHost, m_aAbsURIRef, aSubject.m_aAbsURIRef) + != 0) + || (m_aPort.compare( + aSubject.m_aPort, m_aAbsURIRef, aSubject.m_aAbsURIRef) + != 0)) + { + rTheRelURIRef = aSubject.GetMainURL(eDecodeMechanism, eCharset); + return false; + } + + sal_Unicode const * pBasePathBegin + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pBasePathEnd = pBasePathBegin + m_aPath.getLength(); + sal_Unicode const * pSubjectPathBegin + = aSubject.m_aAbsURIRef.getStr() + aSubject.m_aPath.getBegin(); + sal_Unicode const * pSubjectPathEnd + = pSubjectPathBegin + aSubject.m_aPath.getLength(); + + // Make nMatch point past the last matching slash, or past the end of the + // paths, in case they are equal: + sal_Unicode const * pSlash = 0; + sal_Unicode const * p1 = pBasePathBegin; + sal_Unicode const * p2 = pSubjectPathBegin; + for (;;) + { + if (p1 == pBasePathEnd || p2 == pSubjectPathEnd) + { + if (p1 == pBasePathEnd && p2 == pSubjectPathEnd) + pSlash = p1; + break; + } + + sal_Unicode c = *p1++; + if (c != *p2++) + break; + if (c == '/') + pSlash = p1; + } + if (!pSlash) + { + // One of the paths does not start with '/': + rTheRelURIRef = aSubject.GetMainURL(eDecodeMechanism, eCharset); + return false; + } + sal_Int32 nMatch = pSlash - pBasePathBegin; + + // If the two URLs are DOS file URLs starting with different volumes + // (e.g., file:///a:/... and file:///b:/...), the subject is not made + // relative (it could be, but some people do not like that): + if (m_eScheme == INET_PROT_FILE + && nMatch <= 1 + && hasDosVolume(eStyle) + && aSubject.hasDosVolume(eStyle)) //TODO! ok to use eStyle for these? + { + rTheRelURIRef = aSubject.GetMainURL(eDecodeMechanism, eCharset); + return false; + } + + // For every slash in the base path after nMatch, a prefix of "../" is + // added to the new relative URL (if the common prefix of the two paths is + // only "/"---but see handling of file URLs above---, the complete subject + // path could go into the new relative URL instead, but some people don't + // like that): + rtl::OUStringBuffer aSynRelURIRef; +// if (nMatch <= 1) nMatch = 0; else // see comment above + for (sal_Unicode const * p = pBasePathBegin + nMatch; p != pBasePathEnd; + ++p) + { + if (*p == '/') + aSynRelURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("../")); + } + + // If the new relative URL would start with "//" (i.e., it would be + // mistaken for a relative URL starting with an authority part), or if the + // new relative URL would neither be empty nor start with <"/"> nor start + // with <1*rseg> (i.e., it could be mistaken for an absolute URL starting + // with a scheme part), then the new relative URL is prefixed with "./": + if (aSynRelURIRef.getLength() == 0) + { + if (pSubjectPathEnd - pSubjectPathBegin >= nMatch + 2 + && pSubjectPathBegin[nMatch] == '/' + && pSubjectPathBegin[nMatch + 1] == '/') + { + aSynRelURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("./")); + } + else + { + for (sal_Unicode const * p = pSubjectPathBegin + nMatch; + p != pSubjectPathEnd && *p != '/'; ++p) + { + if (mustEncode(*p, PART_REL_SEGMENT_EXTRA)) + { + aSynRelURIRef. + appendAscii(RTL_CONSTASCII_STRINGPARAM("./")); + break; + } + } + } + } + + // The remainder of the subject path, starting at nMatch, is appended to + // the new relative URL: + sal_Char cEscapePrefix = getEscapePrefix(); + aSynRelURIRef.append(decode(pSubjectPathBegin + nMatch, pSubjectPathEnd, + cEscapePrefix, eDecodeMechanism, eCharset)); + + // If the subject has defined query or fragment parts, they are appended + // to the new relative URL: + if (aSubject.m_aQuery.isPresent()) + { + aSynRelURIRef.append(sal_Unicode('?')); + aSynRelURIRef.append(aSubject.decode(aSubject.m_aQuery, cEscapePrefix, + eDecodeMechanism, eCharset)); + } + if (aSubject.m_aFragment.isPresent()) + { + aSynRelURIRef.append(sal_Unicode('#')); + aSynRelURIRef.append(aSubject.decode(aSubject.m_aFragment, + cEscapePrefix, eDecodeMechanism, eCharset)); + } + + rTheRelURIRef = aSynRelURIRef.makeStringAndClear(); + return true; +} + +//============================================================================ +// static +bool INetURLObject::convertIntToExt(rtl::OUString const & rTheIntURIRef, + bool bOctets, rtl::OUString & rTheExtURIRef, + DecodeMechanism eDecodeMechanism, + rtl_TextEncoding eCharset) +{ + sal_Char cEscapePrefix + = getEscapePrefix(CompareProtocolScheme(rTheIntURIRef)); + rtl::OUString aSynExtURIRef(encodeText(rTheIntURIRef, bOctets, PART_VISIBLE, + cEscapePrefix, NOT_CANONIC, eCharset, + true)); + sal_Unicode const * pBegin = aSynExtURIRef.getStr(); + sal_Unicode const * pEnd = pBegin + aSynExtURIRef.getLength(); + sal_Unicode const * p = pBegin; + PrefixInfo const * pPrefix = getPrefix(p, pEnd); + bool bConvert = pPrefix && pPrefix->m_eKind == PrefixInfo::INTERNAL; + if (bConvert) + { + aSynExtURIRef = + aSynExtURIRef.replaceAt(0, p - pBegin, + rtl::OUString::createFromAscii(pPrefix->m_pTranslatedPrefix)); + } + rTheExtURIRef = decode(aSynExtURIRef, cEscapePrefix, eDecodeMechanism, + eCharset); + return bConvert; +} + +//============================================================================ +// static +bool INetURLObject::convertExtToInt(rtl::OUString const & rTheExtURIRef, + bool bOctets, rtl::OUString & rTheIntURIRef, + DecodeMechanism eDecodeMechanism, + rtl_TextEncoding eCharset) +{ + sal_Char cEscapePrefix + = getEscapePrefix(CompareProtocolScheme(rTheExtURIRef)); + rtl::OUString aSynIntURIRef(encodeText(rTheExtURIRef, bOctets, PART_VISIBLE, + cEscapePrefix, NOT_CANONIC, eCharset, + true)); + sal_Unicode const * pBegin = aSynIntURIRef.getStr(); + sal_Unicode const * pEnd = pBegin + aSynIntURIRef.getLength(); + sal_Unicode const * p = pBegin; + PrefixInfo const * pPrefix = getPrefix(p, pEnd); + bool bConvert = pPrefix && pPrefix->m_eKind == PrefixInfo::EXTERNAL; + if (bConvert) + { + aSynIntURIRef = + aSynIntURIRef.replaceAt(0, p - pBegin, + rtl::OUString::createFromAscii(pPrefix->m_pTranslatedPrefix)); + } + rTheIntURIRef = decode(aSynIntURIRef, cEscapePrefix, eDecodeMechanism, + eCharset); + return bConvert; +} + +//============================================================================ +// static +INetURLObject::PrefixInfo const * +INetURLObject::getPrefix(sal_Unicode const *& rBegin, + sal_Unicode const * pEnd) +{ + static PrefixInfo const aMap[] + = { // dummy entry at front needed, because pLast may point here: + { 0, 0, INET_PROT_NOT_VALID, PrefixInfo::INTERNAL }, + { ".component:", "staroffice.component:", INET_PROT_COMPONENT, + PrefixInfo::INTERNAL }, + { ".uno:", "staroffice.uno:", INET_PROT_UNO, + PrefixInfo::INTERNAL }, + { "cid:", 0, INET_PROT_CID, PrefixInfo::OFFICIAL }, + { "data:", 0, INET_PROT_DATA, PrefixInfo::OFFICIAL }, + { "db:", "staroffice.db:", INET_PROT_DB, PrefixInfo::INTERNAL }, + { "file:", 0, INET_PROT_FILE, PrefixInfo::OFFICIAL }, + { "ftp:", 0, INET_PROT_FTP, PrefixInfo::OFFICIAL }, + { "http:", 0, INET_PROT_HTTP, PrefixInfo::OFFICIAL }, + { "https:", 0, INET_PROT_HTTPS, PrefixInfo::OFFICIAL }, + { "imap:", 0, INET_PROT_IMAP, PrefixInfo::OFFICIAL }, + { "javascript:", 0, INET_PROT_JAVASCRIPT, PrefixInfo::OFFICIAL }, + { "ldap:", 0, INET_PROT_LDAP, PrefixInfo::OFFICIAL }, + { "macro:", "staroffice.macro:", INET_PROT_MACRO, + PrefixInfo::INTERNAL }, + { "mailto:", 0, INET_PROT_MAILTO, PrefixInfo::OFFICIAL }, + { "news:", 0, INET_PROT_NEWS, PrefixInfo::OFFICIAL }, + { "out:", "staroffice.out:", INET_PROT_OUT, + PrefixInfo::INTERNAL }, + { "pop3:", "staroffice.pop3:", INET_PROT_POP3, + PrefixInfo::INTERNAL }, + { "private:", "staroffice.private:", INET_PROT_PRIV_SOFFICE, + PrefixInfo::INTERNAL }, + { "private:factory/", "staroffice.factory:", + INET_PROT_PRIV_SOFFICE, PrefixInfo::INTERNAL }, + { "private:helpid/", "staroffice.helpid:", INET_PROT_PRIV_SOFFICE, + PrefixInfo::INTERNAL }, + { "private:java/", "staroffice.java:", INET_PROT_PRIV_SOFFICE, + PrefixInfo::INTERNAL }, + { "private:searchfolder:", "staroffice.searchfolder:", + INET_PROT_PRIV_SOFFICE, PrefixInfo::INTERNAL }, + { "private:trashcan:", "staroffice.trashcan:", + INET_PROT_PRIV_SOFFICE, PrefixInfo::INTERNAL }, + { "slot:", "staroffice.slot:", INET_PROT_SLOT, + PrefixInfo::INTERNAL }, + { "smb:", 0, INET_PROT_SMB, PrefixInfo::OFFICIAL }, + { "staroffice.component:", ".component:", INET_PROT_COMPONENT, + PrefixInfo::EXTERNAL }, + { "staroffice.db:", "db:", INET_PROT_DB, PrefixInfo::EXTERNAL }, + { "staroffice.factory:", "private:factory/", + INET_PROT_PRIV_SOFFICE, PrefixInfo::EXTERNAL }, + { "staroffice.helpid:", "private:helpid/", INET_PROT_PRIV_SOFFICE, + PrefixInfo::EXTERNAL }, + { "staroffice.java:", "private:java/", INET_PROT_PRIV_SOFFICE, + PrefixInfo::EXTERNAL }, + { "staroffice.macro:", "macro:", INET_PROT_MACRO, + PrefixInfo::EXTERNAL }, + { "staroffice.out:", "out:", INET_PROT_OUT, + PrefixInfo::EXTERNAL }, + { "staroffice.pop3:", "pop3:", INET_PROT_POP3, + PrefixInfo::EXTERNAL }, + { "staroffice.private:", "private:", INET_PROT_PRIV_SOFFICE, + PrefixInfo::EXTERNAL }, + { "staroffice.searchfolder:", "private:searchfolder:", + INET_PROT_PRIV_SOFFICE, PrefixInfo::EXTERNAL }, + { "staroffice.slot:", "slot:", INET_PROT_SLOT, + PrefixInfo::EXTERNAL }, + { "staroffice.trashcan:", "private:trashcan:", + INET_PROT_PRIV_SOFFICE, PrefixInfo::EXTERNAL }, + { "staroffice.uno:", ".uno:", INET_PROT_UNO, + PrefixInfo::EXTERNAL }, + { "staroffice.vim:", "vim:", INET_PROT_VIM, + PrefixInfo::EXTERNAL }, + { "staroffice:", "private:", INET_PROT_PRIV_SOFFICE, + PrefixInfo::EXTERNAL }, + { "telnet:", 0, INET_PROT_TELNET, PrefixInfo::OFFICIAL }, + { "vim:", "staroffice.vim:", INET_PROT_VIM, + PrefixInfo::INTERNAL }, + { "vnd.sun.star.cmd:", 0, INET_PROT_VND_SUN_STAR_CMD, + PrefixInfo::OFFICIAL }, + { "vnd.sun.star.expand:", 0, INET_PROT_VND_SUN_STAR_EXPAND, + PrefixInfo::OFFICIAL }, + { "vnd.sun.star.help:", 0, INET_PROT_VND_SUN_STAR_HELP, + PrefixInfo::OFFICIAL }, + { "vnd.sun.star.hier:", 0, INET_PROT_VND_SUN_STAR_HIER, + PrefixInfo::OFFICIAL }, + { "vnd.sun.star.odma:", 0, INET_PROT_VND_SUN_STAR_ODMA, + PrefixInfo::OFFICIAL }, + { "vnd.sun.star.pkg:", 0, INET_PROT_VND_SUN_STAR_PKG, + PrefixInfo::OFFICIAL }, + { "vnd.sun.star.tdoc:", 0, INET_PROT_VND_SUN_STAR_TDOC, + PrefixInfo::OFFICIAL }, + { "vnd.sun.star.webdav:", 0, INET_PROT_VND_SUN_STAR_WEBDAV, + PrefixInfo::OFFICIAL }, + { "vnd.sun.star.wfs:", 0, INET_PROT_VND_SUN_STAR_WFS, + PrefixInfo::OFFICIAL }, + { "wfs:", "vnd.sun.star.wfs:", INET_PROT_VND_SUN_STAR_WFS, + PrefixInfo::ALIAS } }; + PrefixInfo const * pFirst = aMap + 1; + PrefixInfo const * pLast = aMap + sizeof aMap / sizeof (PrefixInfo) - 1; + PrefixInfo const * pMatch = 0; + sal_Unicode const * pMatched = rBegin; + sal_Unicode const * p = rBegin; + sal_Int32 i = 0; + for (; pFirst < pLast; ++i) + { + if (pFirst->m_pPrefix[i] == '\0') + { + pMatch = pFirst++; + pMatched = p; + } + if (p >= pEnd) + break; + sal_uInt32 nChar = INetMIME::toLowerCase(*p++); + while (pFirst <= pLast && sal_uChar(pFirst->m_pPrefix[i]) < nChar) + ++pFirst; + while (pFirst <= pLast && sal_uChar(pLast->m_pPrefix[i]) > nChar) + --pLast; + } + if (pFirst == pLast) + { + sal_Char const * q = pFirst->m_pPrefix + i; + while (p < pEnd && *q != '\0' + && INetMIME::toLowerCase(*p) == sal_uChar(*q)) + { + ++p; + ++q; + } + if (*q == '\0') + { + rBegin = p; + return pFirst; + } + } + rBegin = pMatched; + return pMatch; +} + +//============================================================================ +sal_Int32 INetURLObject::getAuthorityBegin() const +{ + DBG_ASSERT(getSchemeInfo().m_bAuthority, + "INetURLObject::getAuthority(): Bad scheme"); + sal_Int32 nBegin; + if (m_aUser.isPresent()) + nBegin = m_aUser.getBegin(); + else if (m_aHost.isPresent()) + nBegin = m_aHost.getBegin(); + else + nBegin = m_aPath.getBegin(); + nBegin -= RTL_CONSTASCII_LENGTH("//"); + DBG_ASSERT(m_aAbsURIRef.charAt(nBegin) == '/' + && m_aAbsURIRef.charAt(nBegin + 1) == '/', + "INetURLObject::getAuthority(): Bad authority"); + return nBegin; +} + +//============================================================================ +INetURLObject::SubString INetURLObject::getAuthority() const +{ + sal_Int32 nBegin = getAuthorityBegin(); + sal_Int32 nEnd = m_aPort.isPresent() ? m_aPort.getEnd() : + m_aHost.isPresent() ? m_aHost.getEnd() : + m_aAuth.isPresent() ? m_aAuth.getEnd() : + m_aUser.isPresent() ? m_aUser.getEnd() : + nBegin + RTL_CONSTASCII_LENGTH("//"); + return SubString(nBegin, nEnd - nBegin); +} + +//============================================================================ +bool INetURLObject::setUser(rtl::OUString const & rTheUser, + bool bOctets, EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + if ( + !getSchemeInfo().m_bUser || + (m_eScheme == INET_PROT_IMAP && rTheUser.getLength() == 0) + ) + { + return false; + } + + rtl::OUString aNewUser(encodeText(rTheUser, bOctets, + m_eScheme == INET_PROT_IMAP ? + PART_IMAP_ACHAR : + m_eScheme == INET_PROT_VIM ? + PART_VIM : + PART_USER_PASSWORD, + getEscapePrefix(), eMechanism, eCharset, + false)); + sal_Int32 nDelta; + if (m_aUser.isPresent()) + nDelta = m_aUser.set(m_aAbsURIRef, aNewUser); + else if (m_aHost.isPresent()) + { + m_aAbsURIRef.insert(m_aHost.getBegin(), sal_Unicode('@')); + nDelta = m_aUser.set(m_aAbsURIRef, aNewUser, m_aHost.getBegin()) + 1; + } + else if (getSchemeInfo().m_bHost) + return false; + else + nDelta = m_aUser.set(m_aAbsURIRef, aNewUser, m_aPath.getBegin()); + m_aAuth += nDelta; + m_aHost += nDelta; + m_aPort += nDelta; + m_aPath += nDelta; + m_aQuery += nDelta; + m_aFragment += nDelta; + return true; +} + +namespace +{ + void lcl_Erase(rtl::OUStringBuffer &rBuf, sal_Int32 index, sal_Int32 count) + { + rtl::OUString sTemp(rBuf.makeStringAndClear()); + rBuf.append(sTemp.replaceAt(index, count, rtl::OUString())); + } +} + +//============================================================================ +bool INetURLObject::clearPassword() +{ + if (!getSchemeInfo().m_bPassword) + return false; + if (m_aAuth.isPresent()) + { + lcl_Erase(m_aAbsURIRef, m_aAuth.getBegin() - 1, + m_aAuth.getLength() + 1); + sal_Int32 nDelta = m_aAuth.clear() - 1; + m_aHost += nDelta; + m_aPort += nDelta; + m_aPath += nDelta; + m_aQuery += nDelta; + m_aFragment += nDelta; + } + return true; +} + +//============================================================================ +bool INetURLObject::setPassword(rtl::OUString const & rThePassword, + bool bOctets, EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + if (!getSchemeInfo().m_bPassword) + return false; + rtl::OUString aNewAuth(encodeText(rThePassword, bOctets, + m_eScheme == INET_PROT_VIM ? + PART_VIM : PART_USER_PASSWORD, + getEscapePrefix(), eMechanism, eCharset, + false)); + sal_Int32 nDelta; + if (m_aAuth.isPresent()) + nDelta = m_aAuth.set(m_aAbsURIRef, aNewAuth); + else if (m_aUser.isPresent()) + { + m_aAbsURIRef.insert(m_aUser.getEnd(), sal_Unicode(':')); + nDelta + = m_aAuth.set(m_aAbsURIRef, aNewAuth, m_aUser.getEnd() + 1) + 1; + } + else if (m_aHost.isPresent()) + { + m_aAbsURIRef.insert(m_aHost.getBegin(), + rtl::OUString::createFromAscii(":@")); + m_aUser.set(m_aAbsURIRef, rtl::OUString(), m_aHost.getBegin()); + nDelta + = m_aAuth.set(m_aAbsURIRef, aNewAuth, m_aHost.getBegin() + 1) + 2; + } + else if (getSchemeInfo().m_bHost) + return false; + else + { + m_aAbsURIRef.insert(m_aPath.getBegin(), sal_Unicode(':')); + m_aUser.set(m_aAbsURIRef, rtl::OUString(), m_aPath.getBegin()); + nDelta + = m_aAuth.set(m_aAbsURIRef, aNewAuth, m_aPath.getBegin() + 1) + 1; + } + m_aHost += nDelta; + m_aPort += nDelta; + m_aPath += nDelta; + m_aQuery += nDelta; + m_aFragment += nDelta; + return true; +} + +//============================================================================ +// static +bool INetURLObject::parseHost( + sal_Unicode const *& rBegin, sal_Unicode const * pEnd, + rtl::OUString & rCanonic) +{ + // RFC 2373 is inconsistent about how to write an IPv6 address in which an + // IPv4 address directly follows the abbreviating "::". The ABNF in + // Appendix B suggests ":::13.1.68.3", while an example in 2.2/3 explicitly + // mentions "::13:1.68.3". This algorithm accepts both variants: + enum State { STATE_INITIAL, STATE_LABEL, STATE_LABEL_HYPHEN, + STATE_LABEL_DOT, STATE_TOPLABEL, STATE_TOPLABEL_HYPHEN, + STATE_TOPLABEL_DOT, STATE_IP4, STATE_IP4_DOT, STATE_IP6, + STATE_IP6_COLON, STATE_IP6_2COLON, STATE_IP6_3COLON, + STATE_IP6_HEXSEQ1, STATE_IP6_HEXSEQ1_COLON, + STATE_IP6_HEXSEQ1_MAYBE_IP4, STATE_IP6_HEXSEQ2, + STATE_IP6_HEXSEQ2_COLON, STATE_IP6_HEXSEQ2_MAYBE_IP4, + STATE_IP6_IP4, STATE_IP6_IP4_DOT, STATE_IP6_DONE }; + rtl::OUStringBuffer aTheCanonic; + sal_uInt32 nNumber = 0; + int nDigits = 0; + int nOctets = 0; + State eState = STATE_INITIAL; + sal_Unicode const * p = rBegin; + for (; p != pEnd; ++p) + switch (eState) + { + case STATE_INITIAL: + if (*p == '[') + { + aTheCanonic.append(sal_Unicode('[')); + eState = STATE_IP6; + } + else if (INetMIME::isAlpha(*p)) + eState = STATE_TOPLABEL; + else if (INetMIME::isDigit(*p)) + { + nNumber = INetMIME::getWeight(*p); + nDigits = 1; + nOctets = 1; + eState = STATE_IP4; + } + else + goto done; + break; + + case STATE_LABEL: + if (*p == '.') + eState = STATE_LABEL_DOT; + else if (*p == '-') + eState = STATE_LABEL_HYPHEN; + else if (!INetMIME::isAlphanumeric(*p)) + goto done; + break; + + case STATE_LABEL_HYPHEN: + if (INetMIME::isAlphanumeric(*p)) + eState = STATE_LABEL; + else if (*p != '-') + goto done; + break; + + case STATE_LABEL_DOT: + if (INetMIME::isAlpha(*p)) + eState = STATE_TOPLABEL; + else if (INetMIME::isDigit(*p)) + eState = STATE_LABEL; + else + goto done; + break; + + case STATE_TOPLABEL: + if (*p == '.') + eState = STATE_TOPLABEL_DOT; + else if (*p == '-') + eState = STATE_TOPLABEL_HYPHEN; + else if (!INetMIME::isAlphanumeric(*p)) + goto done; + break; + + case STATE_TOPLABEL_HYPHEN: + if (INetMIME::isAlphanumeric(*p)) + eState = STATE_TOPLABEL; + else if (*p != '-') + goto done; + break; + + case STATE_TOPLABEL_DOT: + if (INetMIME::isAlpha(*p)) + eState = STATE_TOPLABEL; + else if (INetMIME::isDigit(*p)) + eState = STATE_LABEL; + else + goto done; + break; + + case STATE_IP4: + if (*p == '.') + if (nOctets < 4) + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber))); + aTheCanonic.append(sal_Unicode('.')); + ++nOctets; + eState = STATE_IP4_DOT; + } + else + eState = STATE_LABEL_DOT; + else if (*p == '-') + eState = STATE_LABEL_HYPHEN; + else if (INetMIME::isAlpha(*p)) + eState = STATE_LABEL; + else if (INetMIME::isDigit(*p)) + if (nDigits < 3) + { + nNumber = 10 * nNumber + INetMIME::getWeight(*p); + ++nDigits; + } + else + eState = STATE_LABEL; + else + goto done; + break; + + case STATE_IP4_DOT: + if (INetMIME::isAlpha(*p)) + eState = STATE_TOPLABEL; + else if (INetMIME::isDigit(*p)) + { + nNumber = INetMIME::getWeight(*p); + nDigits = 1; + eState = STATE_IP4; + } + else + goto done; + break; + + case STATE_IP6: + if (*p == ':') + eState = STATE_IP6_COLON; + else if (INetMIME::isHexDigit(*p)) + { + nNumber = INetMIME::getHexWeight(*p); + nDigits = 1; + eState = STATE_IP6_HEXSEQ1; + } + else + goto done; + break; + + case STATE_IP6_COLON: + if (*p == ':') + { + aTheCanonic.appendAscii(RTL_CONSTASCII_STRINGPARAM("::")); + eState = STATE_IP6_2COLON; + } + else + goto done; + break; + + case STATE_IP6_2COLON: + if (*p == ']') + eState = STATE_IP6_DONE; + else if (*p == ':') + { + aTheCanonic.append(sal_Unicode(':')); + eState = STATE_IP6_3COLON; + } + else if (INetMIME::isDigit(*p)) + { + nNumber = INetMIME::getWeight(*p); + nDigits = 1; + eState = STATE_IP6_HEXSEQ2_MAYBE_IP4; + } + else if (INetMIME::isHexDigit(*p)) + { + nNumber = INetMIME::getHexWeight(*p); + nDigits = 1; + eState = STATE_IP6_HEXSEQ2; + } + else + goto done; + break; + + case STATE_IP6_3COLON: + if (INetMIME::isDigit(*p)) + { + nNumber = INetMIME::getWeight(*p); + nDigits = 1; + nOctets = 1; + eState = STATE_IP6_IP4; + } + else + goto done; + break; + + case STATE_IP6_HEXSEQ1: + if (*p == ']') + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber), 16)); + eState = STATE_IP6_DONE; + } + else if (*p == ':') + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber), 16)); + aTheCanonic.append(sal_Unicode(':')); + eState = STATE_IP6_HEXSEQ1_COLON; + } + else if (INetMIME::isHexDigit(*p) && nDigits < 4) + { + nNumber = 16 * nNumber + INetMIME::getHexWeight(*p); + ++nDigits; + } + else + goto done; + break; + + case STATE_IP6_HEXSEQ1_COLON: + if (*p == ':') + { + aTheCanonic.append(sal_Unicode(':')); + eState = STATE_IP6_2COLON; + } + else if (INetMIME::isDigit(*p)) + { + nNumber = INetMIME::getWeight(*p); + nDigits = 1; + eState = STATE_IP6_HEXSEQ1_MAYBE_IP4; + } + else if (INetMIME::isHexDigit(*p)) + { + nNumber = INetMIME::getHexWeight(*p); + nDigits = 1; + eState = STATE_IP6_HEXSEQ1; + } + else + goto done; + break; + + case STATE_IP6_HEXSEQ1_MAYBE_IP4: + if (*p == ']') + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber), 16)); + eState = STATE_IP6_DONE; + } + else if (*p == ':') + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber), 16)); + aTheCanonic.append(sal_Unicode(':')); + eState = STATE_IP6_HEXSEQ1_COLON; + } + else if (*p == '.') + { + nNumber = 100 * (nNumber >> 8) + 10 * (nNumber >> 4 & 15) + + (nNumber & 15); + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber))); + aTheCanonic.append(sal_Unicode('.')); + nOctets = 2; + eState = STATE_IP6_IP4_DOT; + } + else if (INetMIME::isDigit(*p) && nDigits < 3) + { + nNumber = 16 * nNumber + INetMIME::getWeight(*p); + ++nDigits; + } + else if (INetMIME::isHexDigit(*p) && nDigits < 4) + { + nNumber = 16 * nNumber + INetMIME::getHexWeight(*p); + ++nDigits; + eState = STATE_IP6_HEXSEQ1; + } + else + goto done; + break; + + case STATE_IP6_HEXSEQ2: + if (*p == ']') + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber), 16)); + eState = STATE_IP6_DONE; + } + else if (*p == ':') + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber), 16)); + aTheCanonic.append(sal_Unicode(':')); + eState = STATE_IP6_HEXSEQ2_COLON; + } + else if (INetMIME::isHexDigit(*p) && nDigits < 4) + { + nNumber = 16 * nNumber + INetMIME::getHexWeight(*p); + ++nDigits; + } + else + goto done; + break; + + case STATE_IP6_HEXSEQ2_COLON: + if (INetMIME::isDigit(*p)) + { + nNumber = INetMIME::getWeight(*p); + nDigits = 1; + eState = STATE_IP6_HEXSEQ2_MAYBE_IP4; + } + else if (INetMIME::isHexDigit(*p)) + { + nNumber = INetMIME::getHexWeight(*p); + nDigits = 1; + eState = STATE_IP6_HEXSEQ2; + } + else + goto done; + break; + + case STATE_IP6_HEXSEQ2_MAYBE_IP4: + if (*p == ']') + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber), 16)); + eState = STATE_IP6_DONE; + } + else if (*p == ':') + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber), 16)); + aTheCanonic.append(sal_Unicode(':')); + eState = STATE_IP6_HEXSEQ2_COLON; + } + else if (*p == '.') + { + nNumber = 100 * (nNumber >> 8) + 10 * (nNumber >> 4 & 15) + + (nNumber & 15); + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber))); + aTheCanonic.append(sal_Unicode('.')); + nOctets = 2; + eState = STATE_IP6_IP4_DOT; + } + else if (INetMIME::isDigit(*p) && nDigits < 3) + { + nNumber = 16 * nNumber + INetMIME::getWeight(*p); + ++nDigits; + } + else if (INetMIME::isHexDigit(*p) && nDigits < 4) + { + nNumber = 16 * nNumber + INetMIME::getHexWeight(*p); + ++nDigits; + eState = STATE_IP6_HEXSEQ2; + } + else + goto done; + break; + + case STATE_IP6_IP4: + if (*p == ']') + if (nOctets == 4) + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber))); + eState = STATE_IP6_DONE; + } + else + goto done; + else if (*p == '.') + if (nOctets < 4) + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber))); + aTheCanonic.append(sal_Unicode('.')); + ++nOctets; + eState = STATE_IP6_IP4_DOT; + } + else + goto done; + else if (INetMIME::isDigit(*p) && nDigits < 3) + { + nNumber = 10 * nNumber + INetMIME::getWeight(*p); + ++nDigits; + } + else + goto done; + break; + + case STATE_IP6_IP4_DOT: + if (INetMIME::isDigit(*p)) + { + nNumber = INetMIME::getWeight(*p); + nDigits = 1; + eState = STATE_IP6_IP4; + } + else + goto done; + break; + + case STATE_IP6_DONE: + goto done; + } + done: + switch (eState) + { + case STATE_LABEL: + case STATE_TOPLABEL: + case STATE_TOPLABEL_DOT: + aTheCanonic.setLength(0); + aTheCanonic.append(rBegin, p - rBegin); + rBegin = p; + rCanonic = aTheCanonic.makeStringAndClear(); + return true; + + case STATE_IP4: + if (nOctets == 4) + { + aTheCanonic.append( + rtl::OUString::valueOf(sal_Int32(nNumber))); + rBegin = p; + rCanonic = aTheCanonic.makeStringAndClear(); + return true; + } + return false; + + case STATE_IP6_DONE: + aTheCanonic.append(sal_Unicode(']')); + rBegin = p; + rCanonic = aTheCanonic.makeStringAndClear(); + return true; + + default: + return false; + } +} + +//============================================================================ +// static +bool INetURLObject::parseHostOrNetBiosName( + sal_Unicode const * pBegin, sal_Unicode const * pEnd, bool bOctets, + EncodeMechanism eMechanism, rtl_TextEncoding eCharset, bool bNetBiosName, + rtl::OUStringBuffer* pCanonic) +{ + rtl::OUString aTheCanonic; + if (pBegin < pEnd) + { + sal_Unicode const * p = pBegin; + if (!parseHost(p, pEnd, aTheCanonic) || p != pEnd) + { + if (bNetBiosName) + { + rtl::OUStringBuffer buf; + while (pBegin < pEnd) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pBegin, pEnd, bOctets, '%', + eMechanism, eCharset, + eEscapeType); + if (!INetMIME::isVisible(nUTF32)) + return false; + if (!INetMIME::isAlphanumeric(nUTF32)) + switch (nUTF32) + { + case '"': + case '*': + case '+': + case ',': + case '/': + case ':': + case ';': + case '<': + case '=': + case '>': + case '?': + case '[': + case '\\': + case ']': + case '`': + case '|': + return false;; + } + if (pCanonic != NULL) { + appendUCS4( + buf, nUTF32, eEscapeType, bOctets, PART_URIC, '%', + eCharset, true); + } + } + aTheCanonic = buf.makeStringAndClear(); + } + else + return false; + } + } + if (pCanonic != NULL) { + *pCanonic = aTheCanonic; + } + return true; +} + +//============================================================================ +// static +rtl::OUString INetURLObject::encodeHostPort(rtl::OUString const & rTheHostPort, + bool bOctets, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + sal_Int32 nPort = rTheHostPort.getLength(); + if (nPort != 0) + { + sal_Int32 i = nPort - 1; + while (i != 0 && INetMIME::isDigit(rTheHostPort.getStr()[i])) + --i; + if (rTheHostPort.getStr()[i] == ':') + nPort = i; + } + rtl::OUString aResult(encodeText(rTheHostPort.copy(0, nPort), bOctets, + PART_HOST_EXTRA, '%', eMechanism, eCharset, + true)); + aResult += rTheHostPort.copy(nPort); + return aResult; +} + +//============================================================================ +bool INetURLObject::setHost(rtl::OUString const & rTheHost, bool bOctets, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + if (!getSchemeInfo().m_bHost) + return false; + rtl::OUStringBuffer aSynHost(rTheHost); + bool bNetBiosName = false; + switch (m_eScheme) + { + case INET_PROT_FILE: + case INET_PROT_VND_SUN_STAR_WFS: + { + rtl::OUString sTemp(aSynHost); + if (sTemp.equalsIgnoreAsciiCaseAsciiL( + RTL_CONSTASCII_STRINGPARAM("localhost"))) + { + aSynHost.setLength(0); + } + bNetBiosName = true; + } + break; + case INET_PROT_LDAP: + if (aSynHost.getLength() == 0 && m_aPort.isPresent()) + return false; + break; + + default: + if (aSynHost.getLength() == 0) + return false; + break; + } + if (!parseHostOrNetBiosName( + aSynHost.getStr(), aSynHost.getStr() + aSynHost.getLength(), + bOctets, eMechanism, eCharset, bNetBiosName, &aSynHost)) + return false; + sal_Int32 nDelta = m_aHost.set(m_aAbsURIRef, aSynHost.makeStringAndClear()); + m_aPort += nDelta; + m_aPath += nDelta; + m_aQuery += nDelta; + m_aFragment += nDelta; + return true; +} + +//============================================================================ +// static +bool INetURLObject::parsePath(INetProtocol eScheme, + sal_Unicode const ** pBegin, + sal_Unicode const * pEnd, + bool bOctets, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset, + bool bSkippedInitialSlash, + sal_uInt32 nSegmentDelimiter, + sal_uInt32 nAltSegmentDelimiter, + sal_uInt32 nQueryDelimiter, + sal_uInt32 nFragmentDelimiter, + rtl::OUStringBuffer &rSynPath) +{ + DBG_ASSERT(pBegin, "INetURLObject::parsePath(): Null output param"); + + sal_Unicode const * pPos = *pBegin; + rtl::OUStringBuffer aTheSynPath; + + switch (eScheme) + { + case INET_PROT_NOT_VALID: + return false; + + case INET_PROT_FTP: + case INET_PROT_IMAP: + if (pPos < pEnd && *pPos != '/') + return false; + while (pPos < pEnd && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_HTTP_PATH, '%', eCharset, true); + } + if (aTheSynPath.getLength() == 0) + aTheSynPath.append(sal_Unicode('/')); + break; + + case INET_PROT_HTTP: + case INET_PROT_VND_SUN_STAR_WEBDAV: + case INET_PROT_HTTPS: + case INET_PROT_SMB: + if (pPos < pEnd && *pPos != '/') + return false; + while (pPos < pEnd && *pPos != nQueryDelimiter + && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_HTTP_PATH, '%', eCharset, true); + } + if (aTheSynPath.getLength() == 0) + aTheSynPath.append(sal_Unicode('/')); + break; + + case INET_PROT_FILE: + case INET_PROT_VND_SUN_STAR_WFS: + { + if (bSkippedInitialSlash) + aTheSynPath.append(sal_Unicode('/')); + else if (pPos < pEnd + && *pPos != nSegmentDelimiter + && *pPos != nAltSegmentDelimiter) + return false; + while (pPos < pEnd && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + if (eEscapeType == ESCAPE_NO) + { + if (nUTF32 == nSegmentDelimiter + || nUTF32 == nAltSegmentDelimiter) + { + aTheSynPath.append(sal_Unicode('/')); + continue; + } + else if (nUTF32 == '|' + && (pPos == pEnd + || *pPos == nFragmentDelimiter + || *pPos == nSegmentDelimiter + || *pPos == nAltSegmentDelimiter) + && aTheSynPath.getLength() == 2 + && INetMIME::isAlpha(aTheSynPath.charAt(1))) + { + // A first segment of <ALPHA "|"> is translated to + // <ALPHA ":">: + aTheSynPath.append(sal_Unicode(':')); + continue; + } + } + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_PCHAR, '%', eCharset, true); + } + if (aTheSynPath.getLength() == 0) + aTheSynPath.append(sal_Unicode('/')); + break; + } + + case INET_PROT_MAILTO: + while (pPos < pEnd && *pPos != nQueryDelimiter + && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_MAILTO, '%', eCharset, true); + } + break; + + case INET_PROT_NEWS: + if (pPos == pEnd || *pPos == nQueryDelimiter + || *pPos == nFragmentDelimiter) + return false; + + // Match <"*">: + if (*pPos == '*' + && (pEnd - pPos == 1 || pPos[1] == nQueryDelimiter + || pPos[1] == nFragmentDelimiter)) + { + ++pPos; + aTheSynPath.append(sal_Unicode('*')); + break; + } + + // Match <group>: + if (INetMIME::isAlpha(*pPos)) + for (sal_Unicode const * p = pPos + 1;; ++p) + if (p == pEnd || *p == nQueryDelimiter + || *p == nFragmentDelimiter) + { + aTheSynPath.setLength(0); + aTheSynPath.append(pPos, p - pPos); + pPos = p; + goto done; + } + else if (!INetMIME::isAlphanumeric(*p) && *p != '+' + && *p != '-' && *p != '.' && *p != '_') + break; + + // Match <article>: + for (;;) + { + if (pPos == pEnd || *pPos == nQueryDelimiter + || *pPos == nFragmentDelimiter) + return false; + if (*pPos == '@') + break; + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, '%', + eMechanism, eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_NEWS_ARTICLE_LOCALPART, '%', eCharset, true); + } + if (aTheSynPath.getLength() == 0) + return false; + ++pPos; + aTheSynPath.append(sal_Unicode('@')); + { + sal_Unicode const * p = pPos; + while (p < pEnd && *pPos != nQueryDelimiter + && *pPos != nFragmentDelimiter) + ++p; + rtl::OUString aCanonic; + if (!parseHost(pPos, p, aCanonic)) + return false; + aTheSynPath.append(aCanonic); + } + + done: + break; + + case INET_PROT_POP3: + while (pPos < pEnd && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_MESSAGE_ID_PATH, '%', eCharset, + true); + } + break; + + case INET_PROT_PRIV_SOFFICE: + case INET_PROT_SLOT: + case INET_PROT_MACRO: + case INET_PROT_UNO: + case INET_PROT_COMPONENT: + case INET_PROT_LDAP: + while (pPos < pEnd && *pPos != nQueryDelimiter + && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_PATH_BEFORE_QUERY, '%', eCharset, + true); + } + break; + + case INET_PROT_VND_SUN_STAR_HELP: + if (pPos == pEnd + || *pPos == nQueryDelimiter + || *pPos == nFragmentDelimiter) + aTheSynPath.append(sal_Unicode('/')); + else + { + if (*pPos != '/') + return false; + while (pPos < pEnd && *pPos != nQueryDelimiter + && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_HTTP_PATH, '%', eCharset, true); + } + } + break; + + case INET_PROT_JAVASCRIPT: + case INET_PROT_DATA: + case INET_PROT_CID: + case INET_PROT_DB: + while (pPos < pEnd && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_URIC, '%', eCharset, true); + } + break; + + case INET_PROT_OUT: + if (pEnd - pPos < 2 || *pPos++ != '/' || *pPos++ != '~') + return false; + aTheSynPath.appendAscii(RTL_CONSTASCII_STRINGPARAM("/~")); + while (pPos < pEnd && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_URIC, '%', eCharset, true); + } + break; + + case INET_PROT_VND_SUN_STAR_HIER: + case INET_PROT_VND_SUN_STAR_PKG: + if (pPos < pEnd && *pPos != '/' + && *pPos != nQueryDelimiter && *pPos != nFragmentDelimiter) + return false; + while (pPos < pEnd && *pPos != nQueryDelimiter + && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + if (eEscapeType == ESCAPE_NO && nUTF32 == '/') + aTheSynPath.append(sal_Unicode('/')); + else + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_PCHAR, '%', eCharset, false); + } + if (aTheSynPath.getLength() == 0) + aTheSynPath.append(sal_Unicode('/')); + break; + + case INET_PROT_VIM: + { +/* test had to be taken out to make parsePath static; ok since INET_PROT_VIM is + obsolete, anyway + if (m_aUser.isEmpty()) + return false; +*/ + sal_Unicode const * pPathEnd = pPos; + while (pPathEnd < pEnd && *pPathEnd != nFragmentDelimiter) + ++pPathEnd; + aTheSynPath.append(sal_Unicode('/')); + if (pPos == pPathEnd) + break; + else if (*pPos++ != '/') + return false; + if (pPos == pPathEnd) + break; + while (pPos < pPathEnd && *pPos != '/') + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pPathEnd, bOctets, + '=', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, + eEscapeType == ESCAPE_NO ? + INetMIME::toLowerCase(nUTF32) : nUTF32, + eEscapeType, bOctets, PART_VIM, '=', + eCharset, false); + } + bool bInbox; + rtl::OUString sCompare(aTheSynPath); + if (sCompare.equalsAscii("/inbox")) + bInbox = true; + else if (sCompare.equalsAscii("/newsgroups")) + bInbox = false; + else + return false; + aTheSynPath.append(sal_Unicode('/')); + if (pPos == pPathEnd) + break; + else if (*pPos++ != '/') + return false; + if (!bInbox) + { + bool bEmpty = true; + while (pPos < pPathEnd && *pPos != '/') + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pPathEnd, bOctets, + '=', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_VIM, '=', eCharset, false); + bEmpty = false; + } + if (bEmpty) + return false; + aTheSynPath.append(sal_Unicode('/')); + if (pPos == pPathEnd) + break; + else if (*pPos++ != '/') + return false; + } + bool bEmpty = true; + while (pPos < pPathEnd && *pPos != ':') + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pPathEnd, bOctets, + '=', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_VIM, '=', eCharset, false); + bEmpty = false; + } + if (bEmpty) + return false; + if (pPos == pPathEnd) + break; + else if (*pPos++ != ':') + return false; + aTheSynPath.append(sal_Unicode(':')); + for (int i = 0; i < 3; ++i) + { + if (i != 0) + { + if (pPos == pPathEnd || *pPos++ != '.') + return false; + aTheSynPath.append(sal_Unicode('.')); + } + bEmpty = true; + while (pPos < pPathEnd && *pPos != '.') + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pPathEnd, bOctets, + '=', eMechanism, + eCharset, eEscapeType); + if (!INetMIME::isDigit(nUTF32)) + return false; + aTheSynPath.append(sal_Unicode(nUTF32)); + bEmpty = false; + } + if (bEmpty) + return false; + } + if (pPos != pPathEnd) + return false; + break; + } + + case INET_PROT_VND_SUN_STAR_CMD: + case INET_PROT_VND_SUN_STAR_EXPAND: + { + if (pPos == pEnd || *pPos == nFragmentDelimiter) + return false; + Part ePart = PART_URIC_NO_SLASH; + while (pPos != pEnd && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, ePart, + '%', eCharset, true); + ePart = PART_URIC; + } + break; + } + + case INET_PROT_VND_SUN_STAR_ODMA: + if (pPos < pEnd) + { + if (*pPos == '/') + ++pPos; + else + return false; + } + aTheSynPath.append(sal_Unicode('/')); + while (pPos < pEnd && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_URIC_NO_SLASH, '%', eCharset, true); + } + break; + + case INET_PROT_TELNET: + if (pPos < pEnd) + { + if (*pPos != '/' || pEnd - pPos > 1) + return false; + ++pPos; + } + aTheSynPath.append(sal_Unicode('/')); + break; + + case INET_PROT_VND_SUN_STAR_TDOC: + if (pPos == pEnd || *pPos != '/') + return false; + while (pPos < pEnd && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + if (eEscapeType == ESCAPE_NO && nUTF32 == '/') + aTheSynPath.append(sal_Unicode('/')); + else + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_PCHAR, '%', eCharset, false); + } + break; + + case INET_PROT_GENERIC: + while (pPos < pEnd && *pPos != nFragmentDelimiter) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pPos, pEnd, bOctets, + '%', eMechanism, + eCharset, eEscapeType); + appendUCS4(aTheSynPath, nUTF32, eEscapeType, bOctets, + PART_URIC, '%', eCharset, true); + } + if (aTheSynPath.getLength() == 0) + return false; + break; + + default: + OSL_ASSERT(false); + break; + } + + *pBegin = pPos; + rSynPath = aTheSynPath; + return true; +} + +//============================================================================ +bool INetURLObject::setPath(rtl::OUString const & rThePath, bool bOctets, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + rtl::OUStringBuffer aSynPath; + sal_Unicode const * p = rThePath.getStr(); + sal_Unicode const * pEnd = p + rThePath.getLength(); + if (!parsePath(m_eScheme, &p, pEnd, bOctets, eMechanism, eCharset, false, + '/', 0x80000000, 0x80000000, 0x80000000, aSynPath) + || p != pEnd) + return false; + sal_Int32 nDelta = m_aPath.set(m_aAbsURIRef, aSynPath.makeStringAndClear()); + m_aQuery += nDelta; + m_aFragment += nDelta; + return true; +} + +//============================================================================ +bool INetURLObject::checkHierarchical() const { + if (m_eScheme == INET_PROT_VND_SUN_STAR_EXPAND) { + OSL_ENSURE( + false, "INetURLObject::checkHierarchical vnd.sun.star.expand"); + return true; + } else { + return getSchemeInfo().m_bHierarchical; + } +} + +//============================================================================ +bool INetURLObject::appendSegment(rtl::OUString const & rTheSegment, + bool bOctets, EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + return insertName(rTheSegment, bOctets, false, LAST_SEGMENT, true, + eMechanism, eCharset); +} + +//============================================================================ +INetURLObject::SubString INetURLObject::getSegment(sal_Int32 nIndex, + bool bIgnoreFinalSlash) + const +{ + DBG_ASSERT(nIndex >= 0 || nIndex == LAST_SEGMENT, + "INetURLObject::getSegment(): Bad index"); + + if (!checkHierarchical()) + return SubString(); + + sal_Unicode const * pPathBegin + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength(); + sal_Unicode const * pSegBegin; + sal_Unicode const * pSegEnd; + if (nIndex == LAST_SEGMENT) + { + pSegEnd = pPathEnd; + if (bIgnoreFinalSlash && pSegEnd > pPathBegin && pSegEnd[-1] == '/') + --pSegEnd; + if (pSegEnd <= pPathBegin) + return SubString(); + pSegBegin = pSegEnd - 1; + while (pSegBegin > pPathBegin && *pSegBegin != '/') + --pSegBegin; + } + else + { + pSegBegin = pPathBegin; + while (nIndex-- > 0) + do + { + ++pSegBegin; + if (pSegBegin >= pPathEnd) + return SubString(); + } + while (*pSegBegin != '/'); + pSegEnd = pSegBegin + 1; + while (pSegEnd < pPathEnd && *pSegEnd != '/') + ++pSegEnd; + } + + return SubString(pSegBegin - m_aAbsURIRef.getStr(), + pSegEnd - pSegBegin); +} + +//============================================================================ +bool INetURLObject::insertName(rtl::OUString const & rTheName, bool bOctets, + bool bAppendFinalSlash, sal_Int32 nIndex, + bool bIgnoreFinalSlash, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + DBG_ASSERT(nIndex >= 0 || nIndex == LAST_SEGMENT, + "INetURLObject::insertName(): Bad index"); + + if (!checkHierarchical()) + return false; + + sal_Unicode const * pPathBegin + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength(); + sal_Unicode const * pPrefixEnd; + bool bInsertSlash; + sal_Unicode const * pSuffixBegin; + if (nIndex == LAST_SEGMENT) + { + pPrefixEnd = pPathEnd; + if (bIgnoreFinalSlash && pPrefixEnd > pPathBegin && + pPrefixEnd[-1] == '/') + { + --pPrefixEnd; + } + bInsertSlash = bAppendFinalSlash; + pSuffixBegin = pPathEnd; + } + else if (nIndex == 0) + { + pPrefixEnd = pPathBegin; + bInsertSlash = + (pPathBegin < pPathEnd && *pPathBegin != '/') || + (pPathBegin == pPathEnd && bAppendFinalSlash); + pSuffixBegin = + (pPathEnd - pPathBegin == 1 && *pPathBegin == '/' && + !bAppendFinalSlash && bIgnoreFinalSlash) + ? pPathEnd : pPathBegin; + } + else + { + pPrefixEnd = pPathBegin; + sal_Unicode const * pEnd = pPathEnd; + if (bIgnoreFinalSlash && pEnd > pPathBegin && pEnd[-1] == '/') + --pEnd; + bool bSkip = pPrefixEnd < pEnd && *pPrefixEnd == '/'; + bInsertSlash = false; + pSuffixBegin = pPathEnd; + while (nIndex-- > 0) + for (;;) + { + if (bSkip) + ++pPrefixEnd; + bSkip = true; + if (pPrefixEnd >= pEnd) + { + if (nIndex == 0) + { + bInsertSlash = bAppendFinalSlash; + break; + } + else + return false; + } + if (*pPrefixEnd == '/') + { + pSuffixBegin = pPrefixEnd; + break; + } + } + } + + rtl::OUStringBuffer aNewPath; + aNewPath.append(pPathBegin, pPrefixEnd - pPathBegin); + aNewPath.append(sal_Unicode('/')); + aNewPath.append(encodeText(rTheName, bOctets, PART_PCHAR, getEscapePrefix(), + eMechanism, eCharset, true)); + if (bInsertSlash) { + aNewPath.append(sal_Unicode('/')); + } + aNewPath.append(pSuffixBegin, pPathEnd - pSuffixBegin); + + return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, + RTL_TEXTENCODING_UTF8); +} + +//============================================================================ +bool INetURLObject::clearQuery() +{ + if (HasError()) + return false; + if (m_aQuery.isPresent()) + { + lcl_Erase(m_aAbsURIRef, m_aQuery.getBegin() - 1, + m_aQuery.getLength() + 1); + m_aFragment += m_aQuery.clear() - 1; + } + return false; +} + +//============================================================================ +bool INetURLObject::setQuery(rtl::OUString const & rTheQuery, bool bOctets, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + if (!getSchemeInfo().m_bQuery) + return false; + rtl::OUString aNewQuery(encodeText(rTheQuery, bOctets, PART_URIC, + getEscapePrefix(), eMechanism, eCharset, + true)); + sal_Int32 nDelta; + if (m_aQuery.isPresent()) + nDelta = m_aQuery.set(m_aAbsURIRef, aNewQuery); + else + { + m_aAbsURIRef.insert(m_aPath.getEnd(), sal_Unicode('?')); + nDelta = m_aQuery.set(m_aAbsURIRef, aNewQuery, m_aPath.getEnd() + 1) + + 1; + } + m_aFragment += nDelta; + return true; +} + +//============================================================================ +bool INetURLObject::clearFragment() +{ + if (HasError()) + return false; + if (m_aFragment.isPresent()) + { + m_aAbsURIRef.setLength(m_aFragment.getBegin() - 1); + m_aFragment.clear(); + } + return true; +} + +//============================================================================ +bool INetURLObject::setFragment(rtl::OUString const & rTheFragment, + bool bOctets, EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + if (HasError()) + return false; + rtl::OUString aNewFragment(encodeText(rTheFragment, bOctets, PART_URIC, + getEscapePrefix(), eMechanism, + eCharset, true)); + if (m_aFragment.isPresent()) + m_aFragment.set(m_aAbsURIRef, aNewFragment); + else + { + m_aAbsURIRef.append(sal_Unicode('#')); + m_aFragment.set(m_aAbsURIRef, aNewFragment, m_aAbsURIRef.getLength()); + } + return true; +} + +//============================================================================ +INetURLObject::FTPType INetURLObject::getFTPType() const +{ + if (m_eScheme == INET_PROT_FTP + && m_aPath.getLength() >= RTL_CONSTASCII_LENGTH(";type=") + 1 + && rtl::OUString(m_aAbsURIRef).copy( + m_aPath.getEnd() - (RTL_CONSTASCII_LENGTH(";type=") + 1), + RTL_CONSTASCII_LENGTH(";type=")).equalsIgnoreAsciiCaseAscii(";type=")) + switch (m_aAbsURIRef.charAt(m_aPath.getEnd())) + { + case 'A': + case 'a': + return FTP_TYPE_A; + + case 'D': + case 'd': + return FTP_TYPE_D; + + case 'I': + case 'i': + return FTP_TYPE_I; + } + return FTP_TYPE_NONE; +} + +//============================================================================ +bool INetURLObject::hasDosVolume(FSysStyle eStyle) const +{ + sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + return (eStyle & FSYS_DOS) != 0 + && m_aPath.getLength() >= 3 + && p[0] == '/' + && INetMIME::isAlpha(p[1]) + && p[2] == ':' + && (m_aPath.getLength() == 3 || p[3] == '/'); +} + +//============================================================================ +sal_uInt32 INetURLObject::getIMAPUID() const +{ + if (m_eScheme == INET_PROT_IMAP + && m_aPath.getLength() >= RTL_CONSTASCII_LENGTH("/;uid=") + 1) + { + sal_Unicode const * pBegin = m_aAbsURIRef.getStr() + + m_aPath.getBegin() + + RTL_CONSTASCII_LENGTH("/;uid="); + sal_Unicode const * pEnd = pBegin + m_aPath.getLength(); + sal_Unicode const * p = pEnd; + while (p > pBegin && INetMIME::isDigit(p[-1])) + --p; + if (p < pEnd && *--p != '0' + && rtl::OUString(m_aAbsURIRef).copy( + p - RTL_CONSTASCII_LENGTH("/;uid=") - m_aAbsURIRef.getStr(), + RTL_CONSTASCII_LENGTH("/;uid=")).equalsIgnoreAsciiCaseAscii("/;uid=") + ) + { + sal_uInt32 nUID; + if (INetMIME::scanUnsigned(p, pEnd, false, nUID)) + return nUID; + } + } + return 0; +} + +//============================================================================ +// static +rtl::OUString INetURLObject::encodeText(sal_Unicode const * pBegin, + sal_Unicode const * pEnd, bool bOctets, + Part ePart, sal_Char cEscapePrefix, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset, + bool bKeepVisibleEscapes) +{ + rtl::OUStringBuffer aResult; + while (pBegin < pEnd) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pBegin, pEnd, bOctets, cEscapePrefix, + eMechanism, eCharset, eEscapeType); + appendUCS4(aResult, nUTF32, eEscapeType, bOctets, ePart, + cEscapePrefix, eCharset, bKeepVisibleEscapes); + } + return aResult.makeStringAndClear(); +} + +//============================================================================ +// static +rtl::OUString INetURLObject::decode(sal_Unicode const * pBegin, + sal_Unicode const * pEnd, + sal_Char cEscapePrefix, + DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + switch (eMechanism) + { + case NO_DECODE: + return rtl::OUString(pBegin, pEnd - pBegin); + + case DECODE_TO_IURI: + eCharset = RTL_TEXTENCODING_UTF8; + break; + + default: + break; + } + rtl::OUStringBuffer aResult; + while (pBegin < pEnd) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(pBegin, pEnd, false, cEscapePrefix, + WAS_ENCODED, eCharset, eEscapeType); + switch (eEscapeType) + { + case ESCAPE_NO: + aResult.append(sal_Unicode(nUTF32)); + break; + + case ESCAPE_OCTET: + appendEscape(aResult, cEscapePrefix, nUTF32); + break; + + case ESCAPE_UTF32: + if ( + INetMIME::isUSASCII(nUTF32) && + ( + eMechanism == DECODE_TO_IURI || + ( + eMechanism == DECODE_UNAMBIGUOUS && + mustEncode(nUTF32, PART_UNAMBIGUOUS) + ) + ) + ) + { + appendEscape(aResult, cEscapePrefix, nUTF32); + } + else + aResult.append(sal_Unicode(nUTF32)); + break; + } + } + return aResult.makeStringAndClear(); +} + +//============================================================================ +rtl::OUString INetURLObject::GetURLNoPass(DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) const +{ + INetURLObject aTemp(*this); + aTemp.clearPassword(); + return aTemp.GetMainURL(eMechanism, eCharset); +} + +//============================================================================ +rtl::OUString INetURLObject::GetURLNoMark(DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) const +{ + INetURLObject aTemp(*this); + aTemp.clearFragment(); + return aTemp.GetMainURL(eMechanism, eCharset); +} + +//============================================================================ +rtl::OUString +INetURLObject::getAbbreviated( + star::uno::Reference< star::util::XStringWidth > const & rStringWidth, + sal_Int32 nWidth, + DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) + const +{ + OSL_ENSURE(rStringWidth.is(), "specification violation"); + sal_Char cEscapePrefix = getEscapePrefix(); + rtl::OUStringBuffer aBuffer; + aBuffer.appendAscii(getSchemeInfo().m_pScheme); + aBuffer.append(static_cast< sal_Unicode >(':')); + bool bAuthority = getSchemeInfo().m_bAuthority; + sal_Unicode const * pCoreBegin + = m_aAbsURIRef.getStr() + (bAuthority ? getAuthorityBegin() : + m_aPath.getBegin()); + sal_Unicode const * pCoreEnd + = m_aAbsURIRef.getStr() + m_aPath.getBegin() + m_aPath.getLength(); + bool bSegment = false; + if (getSchemeInfo().m_bHierarchical) + { + rtl::OUString aRest; + if (m_aQuery.isPresent()) + aRest = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("?...")); + else if (m_aFragment.isPresent()) + aRest = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("#...")); + rtl::OUStringBuffer aTrailer; + sal_Unicode const * pBegin = pCoreBegin; + sal_Unicode const * pEnd = pCoreEnd; + sal_Unicode const * pPrefixBegin = pBegin; + sal_Unicode const * pSuffixEnd = pEnd; + bool bPrefix = true; + bool bSuffix = true; + do + { + if (bSuffix) + { + sal_Unicode const * p = pSuffixEnd - 1; + if (pSuffixEnd == pCoreEnd && *p == '/') + --p; + while (*p != '/') + --p; + if (bAuthority && p == pCoreBegin + 1) + --p; + rtl::OUString + aSegment(decode(p + (p == pBegin && pBegin != pCoreBegin ? + 1 : 0), + pSuffixEnd, + cEscapePrefix, + eMechanism, + eCharset)); + pSuffixEnd = p; + rtl::OUStringBuffer aResult(aBuffer); + if (pSuffixEnd != pBegin) + aResult.appendAscii(RTL_CONSTASCII_STRINGPARAM("...")); + aResult.append(aSegment); + aResult.append(aTrailer); + aResult.append(aRest); + if (rStringWidth-> + queryStringWidth(aResult.makeStringAndClear()) + <= nWidth) + { + aTrailer.insert(0, aSegment); + bSegment = true; + pEnd = pSuffixEnd; + } + else + bSuffix = false; + if (pPrefixBegin > pSuffixEnd) + pPrefixBegin = pSuffixEnd; + if (pBegin == pEnd) + break; + } + if (bPrefix) + { + sal_Unicode const * p + = pPrefixBegin + + (bAuthority && pPrefixBegin == pCoreBegin ? 2 : + 1); + OSL_ASSERT(p <= pEnd); + while (p < pEnd && *p != '/') + ++p; + if (p == pCoreEnd - 1 && *p == '/') + ++p; + rtl::OUString + aSegment(decode(pPrefixBegin + + (pPrefixBegin == pCoreBegin ? 0 : + 1), + p == pEnd ? p : p + 1, + cEscapePrefix, + eMechanism, + eCharset)); + pPrefixBegin = p; + rtl::OUStringBuffer aResult(aBuffer); + aResult.append(aSegment); + if (pPrefixBegin != pEnd) + aResult.appendAscii(RTL_CONSTASCII_STRINGPARAM("...")); + aResult.append(aTrailer); + aResult.append(aRest); + if (rStringWidth-> + queryStringWidth(aResult.makeStringAndClear()) + <= nWidth) + { + aBuffer.append(aSegment); + bSegment = true; + pBegin = pPrefixBegin; + } + else + bPrefix = false; + if (pPrefixBegin > pSuffixEnd) + pSuffixEnd = pPrefixBegin; + if (pBegin == pEnd) + break; + } + } + while (bPrefix || bSuffix); + if (bSegment) + { + if (pPrefixBegin != pBegin || pSuffixEnd != pEnd) + aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM("...")); + aBuffer.append(aTrailer); + } + } + if (!bSegment) + aBuffer.append(decode(pCoreBegin, + pCoreEnd, + cEscapePrefix, + eMechanism, + eCharset)); + if (m_aQuery.isPresent()) + { + aBuffer.append(static_cast< sal_Unicode >('?')); + aBuffer.append(decode(m_aQuery, cEscapePrefix, eMechanism, eCharset)); + } + if (m_aFragment.isPresent()) + { + aBuffer.append(static_cast< sal_Unicode >('#')); + aBuffer. + append(decode(m_aFragment, cEscapePrefix, eMechanism, eCharset)); + } + if (aBuffer.getLength() != 0) + { + rtl::OUStringBuffer aResult(aBuffer); + if (rStringWidth->queryStringWidth(aResult.makeStringAndClear()) + > nWidth) + for (sal_Int32 i = aBuffer.getLength();;) + { + if (i == 0) + { + aBuffer.setLength(aBuffer.getLength() - 1); + if (aBuffer.getLength() == 0) + break; + } + else + { + aBuffer.setLength(--i); + aBuffer.appendAscii(RTL_CONSTASCII_STRINGPARAM("...")); + } + aResult = aBuffer; + if (rStringWidth-> + queryStringWidth(aResult.makeStringAndClear()) + <= nWidth) + break; + } + } + return aBuffer.makeStringAndClear(); +} + +//============================================================================ +bool INetURLObject::operator ==(INetURLObject const & rObject) const +{ + if (m_eScheme != rObject.m_eScheme) + return false; + if (m_eScheme == INET_PROT_NOT_VALID) + return (m_aAbsURIRef == rObject.m_aAbsURIRef) != false; + if ((m_aScheme.compare( + rObject.m_aScheme, m_aAbsURIRef, rObject.m_aAbsURIRef) + != 0) + || GetUser(NO_DECODE) != rObject.GetUser(NO_DECODE) + || GetPass(NO_DECODE) != rObject.GetPass(NO_DECODE) + || !GetHost(NO_DECODE).equalsIgnoreAsciiCase( + rObject.GetHost(NO_DECODE)) + || GetPort() != rObject.GetPort() + || HasParam() != rObject.HasParam() + || GetParam(NO_DECODE) != rObject.GetParam(NO_DECODE) + || GetMsgId(NO_DECODE) != rObject.GetMsgId(NO_DECODE)) + return false; + rtl::OUString aPath1(GetURLPath(NO_DECODE)); + rtl::OUString aPath2(rObject.GetURLPath(NO_DECODE)); + switch (m_eScheme) + { + case INET_PROT_FILE: + case INET_PROT_VND_SUN_STAR_WFS: + { + // If the URL paths of two file URLs only differ in that one has a + // final '/' and the other has not, take the two paths as + // equivalent (this could be usefull for other schemes, too): + sal_Int32 nLength = aPath1.getLength(); + switch (nLength - aPath2.getLength()) + { + case -1: + if (aPath2.getStr()[nLength] != '/') + return false; + break; + + case 0: + break; + + case 1: + if (aPath1.getStr()[--nLength] != '/') + return false; + break; + + default: + return false; + } + return aPath1.compareTo(aPath2, nLength) == 0; + } + + default: + return (aPath1 == aPath2) != false; + } +} + +//============================================================================ +bool INetURLObject::operator <(INetURLObject const & rObject) const +{ + sal_Int32 nCompare = m_aScheme.compare( + rObject.m_aScheme, m_aAbsURIRef, rObject.m_aAbsURIRef); + if (nCompare < 0) { + return true; + } else if (nCompare > 0) { + return false; + } + sal_uInt32 nPort1 = GetPort(); + sal_uInt32 nPort2 = rObject.GetPort(); + if (nPort1 < nPort2) + return true; + else if (nPort1 > nPort2) + return false; + nCompare = GetUser(NO_DECODE).compareTo(rObject.GetUser(NO_DECODE)); + if (nCompare < 0) + return true; + else if (nCompare > 0) + return false; + nCompare = GetPass(NO_DECODE).compareTo(rObject.GetPass(NO_DECODE)); + if (nCompare < 0) + return true; + else if (nCompare > 0) + return false; + nCompare = GetHost(NO_DECODE).compareTo(rObject.GetHost(NO_DECODE)); + if (nCompare < 0) + return true; + else if (nCompare > 0) + return false; + const rtl::OUString &rPath1(GetURLPath(NO_DECODE)); + const rtl::OUString &rPath2(rObject.GetURLPath(NO_DECODE)); + nCompare = rPath1.compareTo(rPath2); + if (nCompare < 0) + return true; + else if (nCompare > 0) + return false; + nCompare = GetParam(NO_DECODE).compareTo(rObject.GetParam(NO_DECODE)); + if (nCompare < 0) + return true; + else if (nCompare > 0) + return false; + return GetMsgId(NO_DECODE).compareTo(rObject.GetMsgId(NO_DECODE)) < 0; +} + +//============================================================================ +bool INetURLObject::ConcatData(INetProtocol eTheScheme, + rtl::OUString const & rTheUser, + rtl::OUString const & rThePassword, + rtl::OUString const & rTheHost, + sal_uInt32 nThePort, + rtl::OUString const & rThePath, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + setInvalid(); + m_eScheme = eTheScheme; + if (HasError() || m_eScheme == INET_PROT_GENERIC) + return false; + m_aAbsURIRef.setLength(0); + m_aAbsURIRef.appendAscii(getSchemeInfo().m_pScheme); + m_aAbsURIRef.append(sal_Unicode(':')); + if (getSchemeInfo().m_bAuthority) + { + m_aAbsURIRef.appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + bool bUserInfo = false; + if (getSchemeInfo().m_bUser) + { + if (m_eScheme == INET_PROT_IMAP && rTheUser.getLength() == 0) + { + setInvalid(); + return false; + } + if (rTheUser.getLength() != 0) + { + m_aUser.set(m_aAbsURIRef, + encodeText(rTheUser, false, + m_eScheme == INET_PROT_IMAP ? + PART_IMAP_ACHAR : + m_eScheme == INET_PROT_VIM ? + PART_VIM : + PART_USER_PASSWORD, + getEscapePrefix(), eMechanism, + eCharset, false), + m_aAbsURIRef.getLength()); + bUserInfo = true; + } + } + else if (rTheUser.getLength() != 0) + { + setInvalid(); + return false; + } + if (rThePassword.getLength() != 0) + { + if (getSchemeInfo().m_bPassword) + { + m_aAbsURIRef.append(sal_Unicode(':')); + m_aAuth.set(m_aAbsURIRef, + encodeText(rThePassword, false, + m_eScheme == INET_PROT_VIM ? + PART_VIM : PART_USER_PASSWORD, + getEscapePrefix(), eMechanism, + eCharset, false), + m_aAbsURIRef.getLength()); + bUserInfo = true; + } + else + { + setInvalid(); + return false; + } + } + if (bUserInfo && getSchemeInfo().m_bHost) + m_aAbsURIRef.append(sal_Unicode('@')); + if (getSchemeInfo().m_bHost) + { + rtl::OUStringBuffer aSynHost(rTheHost); + bool bNetBiosName = false; + switch (m_eScheme) + { + case INET_PROT_FILE: + case INET_PROT_VND_SUN_STAR_WFS: + { + rtl::OUString sTemp(aSynHost); + if (sTemp.equalsIgnoreAsciiCaseAsciiL( + RTL_CONSTASCII_STRINGPARAM("localhost"))) + { + aSynHost.setLength(0); + } + bNetBiosName = true; + } + break; + + case INET_PROT_LDAP: + if (aSynHost.getLength() == 0 && nThePort != 0) + { + setInvalid(); + return false; + } + break; + + default: + if (aSynHost.getLength() == 0) + { + setInvalid(); + return false; + } + break; + } + if (!parseHostOrNetBiosName( + aSynHost.getStr(), aSynHost.getStr() + aSynHost.getLength(), + false, eMechanism, eCharset, bNetBiosName, &aSynHost)) + { + setInvalid(); + return false; + } + m_aHost.set(m_aAbsURIRef, aSynHost.makeStringAndClear(), + m_aAbsURIRef.getLength()); + if (nThePort != 0) + { + if (getSchemeInfo().m_bPort) + { + m_aAbsURIRef.append(sal_Unicode(':')); + m_aPort.set(m_aAbsURIRef, + rtl::OUString::valueOf(sal_Int64(nThePort)), + m_aAbsURIRef.getLength()); + } + else + { + setInvalid(); + return false; + } + } + } + else if (rTheHost.getLength() != 0 || nThePort != 0) + { + setInvalid(); + return false; + } + } + rtl::OUStringBuffer aSynPath; + sal_Unicode const * p = rThePath.getStr(); + sal_Unicode const * pEnd = p + rThePath.getLength(); + if (!parsePath(m_eScheme, &p, pEnd, false, eMechanism, eCharset, false, '/', + 0x80000000, 0x80000000, 0x80000000, aSynPath) + || p != pEnd) + { + setInvalid(); + return false; + } + m_aPath.set(m_aAbsURIRef, aSynPath.makeStringAndClear(), + m_aAbsURIRef.getLength()); + return true; +} + +//============================================================================ +// static +rtl::OUString INetURLObject::GetAbsURL(rtl::OUString const & rTheBaseURIRef, + rtl::OUString const & rTheRelURIRef, + bool bIgnoreFragment, + EncodeMechanism eEncodeMechanism, + DecodeMechanism eDecodeMechanism, + rtl_TextEncoding eCharset, + FSysStyle eStyle) +{ + // Backwards compatibility: + if (rTheRelURIRef.getLength() == 0 || rTheRelURIRef[0] == '#') + return rTheRelURIRef; + + INetURLObject aTheAbsURIRef; + bool bWasAbsolute; + return INetURLObject(rTheBaseURIRef, eEncodeMechanism, eCharset). + convertRelToAbs(rTheRelURIRef, false, aTheAbsURIRef, + bWasAbsolute, eEncodeMechanism, + eCharset, bIgnoreFragment, false, + false, eStyle) + || eEncodeMechanism != WAS_ENCODED + || eDecodeMechanism != DECODE_TO_IURI + || eCharset != RTL_TEXTENCODING_UTF8 ? + aTheAbsURIRef.GetMainURL(eDecodeMechanism, eCharset) : + rTheRelURIRef; +} + +//============================================================================ +rtl::OUString INetURLObject::getExternalURL(DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) const +{ + rtl::OUString aTheExtURIRef; + translateToExternal( + rtl::OUString(m_aAbsURIRef), aTheExtURIRef, eMechanism, eCharset); + return aTheExtURIRef; +} + +//============================================================================ +// static +rtl::OUString INetURLObject::GetScheme(INetProtocol eTheScheme) +{ + return rtl::OUString::createFromAscii(getSchemeInfo(eTheScheme).m_pPrefix); +} + +//============================================================================ +// static +INetProtocol INetURLObject::CompareProtocolScheme(rtl::OUString const & + rTheAbsURIRef) +{ + sal_Unicode const * p = rTheAbsURIRef.getStr(); + PrefixInfo const * pPrefix = getPrefix(p, p + rTheAbsURIRef.getLength()); + return pPrefix ? pPrefix->m_eScheme : INET_PROT_NOT_VALID; +} + +//============================================================================ +bool INetURLObject::hasPassword() const +{ + return m_aAuth.isPresent() && getSchemeInfo().m_bPassword; +} + +//============================================================================ +void INetURLObject::makeAuthCanonic() +{ + if (m_eScheme == INET_PROT_IMAP && m_aAuth.getLength() == 1 + && m_aAbsURIRef.charAt(m_aAuth.getBegin()) == '*') + { + lcl_Erase(m_aAbsURIRef, m_aAuth.getBegin() + - RTL_CONSTASCII_LENGTH(";AUTH="), + RTL_CONSTASCII_LENGTH(";AUTH=*")); + sal_Int32 nDelta = m_aAuth.clear() - RTL_CONSTASCII_LENGTH(";AUTH="); + m_aPath += nDelta; + m_aQuery += nDelta; + m_aFragment += nDelta; + } +} + +//============================================================================ +rtl::OUString INetURLObject::GetHostPort(DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + // Check because PROT_VND_SUN_STAR_HELP, PROT_VND_SUN_STAR_HIER, and + // PROT_VND_SUN_STAR_PKG misuse m_aHost: + if (!getSchemeInfo().m_bHost) + return rtl::OUString(); + rtl::OUStringBuffer aHostPort(decode(m_aHost, getEscapePrefix(), + eMechanism, eCharset)); + if (m_aPort.isPresent()) + { + aHostPort.append(sal_Unicode(':')); + aHostPort.append(decode(m_aPort, getEscapePrefix(), + eMechanism, eCharset)); + } + return aHostPort.makeStringAndClear(); +} + +//============================================================================ +sal_uInt32 INetURLObject::GetPort() const +{ + if (m_aPort.isPresent()) + { + sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPort.getBegin(); + sal_Unicode const * pEnd = p + m_aPort.getLength(); + sal_uInt32 nThePort; + if (INetMIME::scanUnsigned(p, pEnd, true, nThePort) && p == pEnd) + return nThePort; + } + return 0; +} + +//============================================================================ +bool INetURLObject::SetPort(sal_uInt32 nThePort) +{ + if (getSchemeInfo().m_bPort && m_aHost.isPresent()) + { + rtl::OUString aNewPort(rtl::OUString::valueOf(sal_Int64(nThePort))); + sal_Int32 nDelta; + if (m_aPort.isPresent()) + nDelta = m_aPort.set(m_aAbsURIRef, aNewPort); + else + { + m_aAbsURIRef.insert(m_aHost.getEnd(), sal_Unicode(':')); + nDelta = m_aPort.set(m_aAbsURIRef, aNewPort, m_aHost.getEnd() + 1) + + 1; + } + m_aPath += nDelta; + m_aQuery += nDelta; + m_aFragment += nDelta; + return true; + } + return false; +} + +//============================================================================ +void INetURLObject::makePortCanonic() +{ + if (m_aPort.isPresent()) + { + sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPort.getBegin(); + sal_Unicode const * pEnd = p + m_aPort.getLength(); + sal_uInt32 nThePort; + if (INetMIME::scanUnsigned(p, pEnd, true, nThePort) && p == pEnd) + { + sal_Int32 nDelta; + if (nThePort != 0 && nThePort == getSchemeInfo().m_nDefaultPort) + { + lcl_Erase(m_aAbsURIRef, m_aPort.getBegin() - 1, + m_aPort.getLength() + 1); + nDelta = m_aPort.clear() - 1; + } + else + nDelta = m_aPort.set(m_aAbsURIRef, + rtl::OUString::valueOf(sal_Int64(nThePort))); + m_aPath += nDelta; + m_aQuery += nDelta; + m_aFragment += nDelta; + } + } +} + +//============================================================================ +sal_Int32 INetURLObject::getSegmentCount(bool bIgnoreFinalSlash) const +{ + if (!checkHierarchical()) + return 0; + + sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pEnd = p + m_aPath.getLength(); + if (bIgnoreFinalSlash && pEnd > p && pEnd[-1] == '/') + --pEnd; + sal_Int32 n = p == pEnd || *p == '/' ? 0 : 1; + while (p != pEnd) + if (*p++ == '/') + ++n; + return n; +} + +//============================================================================ +bool INetURLObject::removeSegment(sal_Int32 nIndex, bool bIgnoreFinalSlash) +{ + SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash)); + if (!aSegment.isPresent()) + return false; + + rtl::OUStringBuffer aNewPath; + aNewPath.append(m_aAbsURIRef.getStr() + m_aPath.getBegin(), + aSegment.getBegin() - m_aPath.getBegin()); + if (bIgnoreFinalSlash && aSegment.getEnd() == m_aPath.getEnd()) + aNewPath.append(sal_Unicode('/')); + else + aNewPath.append(m_aAbsURIRef.getStr() + aSegment.getEnd(), + m_aPath.getEnd() - aSegment.getEnd()); + if (aNewPath.getLength() == 0 && !aSegment.isEmpty() && + m_aAbsURIRef[aSegment.getBegin()] == '/') + { + aNewPath.append(sal_Unicode('/')); + } + + return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, + RTL_TEXTENCODING_UTF8); +} + +//============================================================================ +rtl::OUString INetURLObject::getName(sal_Int32 nIndex, bool bIgnoreFinalSlash, + DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) const +{ + SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash)); + if (!aSegment.isPresent()) + return rtl::OUString(); + + sal_Unicode const * pSegBegin + = m_aAbsURIRef.getStr() + aSegment.getBegin(); + sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength(); + + if (pSegBegin < pSegEnd && *pSegBegin == '/') + ++pSegBegin; + sal_Unicode const * p = pSegBegin; + while (p != pSegEnd && *p != ';') + ++p; + + return decode(pSegBegin, p, getEscapePrefix(), eMechanism, eCharset); +} + +//============================================================================ +bool INetURLObject::setName(rtl::OUString const & rTheName, sal_Int32 nIndex, + bool bIgnoreFinalSlash, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash)); + if (!aSegment.isPresent()) + return false; + + sal_Unicode const * pPathBegin + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength(); + sal_Unicode const * pSegBegin + = m_aAbsURIRef.getStr() + aSegment.getBegin(); + sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength(); + + if (pSegBegin < pSegEnd && *pSegBegin == '/') + ++pSegBegin; + sal_Unicode const * p = pSegBegin; + while (p != pSegEnd && *p != ';') + ++p; + + rtl::OUStringBuffer aNewPath; + aNewPath.append(pPathBegin, pSegBegin - pPathBegin); + aNewPath.append(encodeText(rTheName, false, PART_PCHAR, getEscapePrefix(), + eMechanism, eCharset, true)); + aNewPath.append(p, pPathEnd - p); + + return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, + RTL_TEXTENCODING_UTF8); +} + +//============================================================================ +bool INetURLObject::hasExtension(sal_Int32 nIndex, bool bIgnoreFinalSlash) + const +{ + SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash)); + if (!aSegment.isPresent()) + return false; + + sal_Unicode const * pSegBegin + = m_aAbsURIRef.getStr() + aSegment.getBegin(); + sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength(); + + if (pSegBegin < pSegEnd && *pSegBegin == '/') + ++pSegBegin; + for (sal_Unicode const * p = pSegBegin; p != pSegEnd && *p != ';'; ++p) + if (*p == '.' && p != pSegBegin) + return true; + return false; +} + +//============================================================================ +rtl::OUString INetURLObject::getBase(sal_Int32 nIndex, bool bIgnoreFinalSlash, + DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) const +{ + SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash)); + if (!aSegment.isPresent()) + return rtl::OUString(); + + sal_Unicode const * pSegBegin + = m_aAbsURIRef.getStr() + aSegment.getBegin(); + sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength(); + + if (pSegBegin < pSegEnd && *pSegBegin == '/') + ++pSegBegin; + sal_Unicode const * pExtension = 0; + sal_Unicode const * p = pSegBegin; + for (; p != pSegEnd && *p != ';'; ++p) + if (*p == '.' && p != pSegBegin) + pExtension = p; + if (!pExtension) + pExtension = p; + + return decode(pSegBegin, pExtension, getEscapePrefix(), eMechanism, + eCharset); +} + +//============================================================================ +bool INetURLObject::setBase(rtl::OUString const & rTheBase, sal_Int32 nIndex, + bool bIgnoreFinalSlash, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash)); + if (!aSegment.isPresent()) + return false; + + sal_Unicode const * pPathBegin + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength(); + sal_Unicode const * pSegBegin + = m_aAbsURIRef.getStr() + aSegment.getBegin(); + sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength(); + + if (pSegBegin < pSegEnd && *pSegBegin == '/') + ++pSegBegin; + sal_Unicode const * pExtension = 0; + sal_Unicode const * p = pSegBegin; + for (; p != pSegEnd && *p != ';'; ++p) + if (*p == '.' && p != pSegBegin) + pExtension = p; + if (!pExtension) + pExtension = p; + + rtl::OUStringBuffer aNewPath; + aNewPath.append(pPathBegin, pSegBegin - pPathBegin); + aNewPath.append(encodeText(rTheBase, false, PART_PCHAR, getEscapePrefix(), + eMechanism, eCharset, true)); + aNewPath.append(pExtension, pPathEnd - pExtension); + + return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, + RTL_TEXTENCODING_UTF8); +} + +//============================================================================ +rtl::OUString INetURLObject::getExtension(sal_Int32 nIndex, + bool bIgnoreFinalSlash, + DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) const +{ + SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash)); + if (!aSegment.isPresent()) + return rtl::OUString(); + + sal_Unicode const * pSegBegin + = m_aAbsURIRef.getStr() + aSegment.getBegin(); + sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength(); + + if (pSegBegin < pSegEnd && *pSegBegin == '/') + ++pSegBegin; + sal_Unicode const * pExtension = 0; + sal_Unicode const * p = pSegBegin; + for (; p != pSegEnd && *p != ';'; ++p) + if (*p == '.' && p != pSegBegin) + pExtension = p; + + if (!pExtension) + return rtl::OUString(); + + return decode(pExtension + 1, p, getEscapePrefix(), eMechanism, eCharset); +} + +//============================================================================ +bool INetURLObject::setExtension(rtl::OUString const & rTheExtension, + sal_Int32 nIndex, bool bIgnoreFinalSlash, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash)); + if (!aSegment.isPresent()) + return false; + + sal_Unicode const * pPathBegin + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength(); + sal_Unicode const * pSegBegin + = m_aAbsURIRef.getStr() + aSegment.getBegin(); + sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength(); + + if (pSegBegin < pSegEnd && *pSegBegin == '/') + ++pSegBegin; + sal_Unicode const * pExtension = 0; + sal_Unicode const * p = pSegBegin; + for (; p != pSegEnd && *p != ';'; ++p) + if (*p == '.' && p != pSegBegin) + pExtension = p; + if (!pExtension) + pExtension = p; + + rtl::OUStringBuffer aNewPath; + aNewPath.append(pPathBegin, pExtension - pPathBegin); + aNewPath.append(sal_Unicode('.')); + aNewPath.append(encodeText(rTheExtension, false, PART_PCHAR, + getEscapePrefix(), eMechanism, eCharset, true)); + aNewPath.append(p, pPathEnd - p); + + return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, + RTL_TEXTENCODING_UTF8); +} + +//============================================================================ +bool INetURLObject::removeExtension(sal_Int32 nIndex, bool bIgnoreFinalSlash) +{ + SubString aSegment(getSegment(nIndex, bIgnoreFinalSlash)); + if (!aSegment.isPresent()) + return false; + + sal_Unicode const * pPathBegin + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength(); + sal_Unicode const * pSegBegin + = m_aAbsURIRef.getStr() + aSegment.getBegin(); + sal_Unicode const * pSegEnd = pSegBegin + aSegment.getLength(); + + if (pSegBegin < pSegEnd && *pSegBegin == '/') + ++pSegBegin; + sal_Unicode const * pExtension = 0; + sal_Unicode const * p = pSegBegin; + for (; p != pSegEnd && *p != ';'; ++p) + if (*p == '.' && p != pSegBegin) + pExtension = p; + if (!pExtension) + return true; + + rtl::OUStringBuffer aNewPath; + aNewPath.append(pPathBegin, pExtension - pPathBegin); + aNewPath.append(p, pPathEnd - p); + + return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, + RTL_TEXTENCODING_UTF8); +} + +//============================================================================ +bool INetURLObject::hasFinalSlash() const +{ + if (!checkHierarchical()) + return false; + + sal_Unicode const * pPathBegin + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength(); + return pPathEnd > pPathBegin && pPathEnd[-1] == '/'; +} + +//============================================================================ +bool INetURLObject::setFinalSlash() +{ + if (!checkHierarchical()) + return false; + + sal_Unicode const * pPathBegin + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength(); + if (pPathEnd > pPathBegin && pPathEnd[-1] == '/') + return true; + + rtl::OUStringBuffer aNewPath; + aNewPath.append(pPathBegin, pPathEnd - pPathBegin); + aNewPath.append(sal_Unicode('/')); + + return setPath(aNewPath.makeStringAndClear(), false, NOT_CANONIC, + RTL_TEXTENCODING_UTF8); +} + +//============================================================================ +bool INetURLObject::removeFinalSlash() +{ + if (!checkHierarchical()) + return false; + + sal_Unicode const * pPathBegin + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pPathEnd = pPathBegin + m_aPath.getLength(); + if (pPathEnd <= pPathBegin || pPathEnd[-1] != '/') + return true; + + --pPathEnd; + if (pPathEnd == pPathBegin && *pPathBegin == '/') + return false; + rtl::OUString aNewPath(pPathBegin, pPathEnd - pPathBegin); + + return setPath(aNewPath, false, NOT_CANONIC, RTL_TEXTENCODING_UTF8); +} + +//============================================================================ +// static +rtl::OUString INetURLObject::createFragment(rtl::OUString const & rText) +{ + rtl::OUString aFragment(rText); + for (sal_Int32 i = 0; i < aFragment.getLength();) + { + sal_Unicode c = aFragment.getStr()[i]; + if (mustEncode(c, PART_CREATEFRAGMENT)) + aFragment = aFragment.replaceAt(i, 1, rtl::OUString()); + else + ++i; + } + return aFragment; +} + +//============================================================================ +bool INetURLObject::setFSysPath(rtl::OUString const & rFSysPath, + FSysStyle eStyle) +{ + sal_Unicode const * pFSysBegin = rFSysPath.getStr(); + sal_Unicode const * pFSysEnd = pFSysBegin + rFSysPath.getLength(); + + switch ((eStyle & FSYS_VOS ? 1 : 0) + + (eStyle & FSYS_UNX ? 1 : 0) + + (eStyle & FSYS_DOS ? 1 : 0) + + (eStyle & FSYS_MAC ? 1 : 0)) + { + case 0: + return false; + + case 1: + break; + + default: + if (eStyle & FSYS_VOS + && pFSysEnd - pFSysBegin >= 2 + && pFSysBegin[0] == '/' + && pFSysBegin[1] == '/') + { + if (pFSysEnd - pFSysBegin >= 3 + && pFSysBegin[2] == '.' + && (pFSysEnd - pFSysBegin == 3 || pFSysBegin[3] == '/')) + { + eStyle = FSYS_VOS; // Production T1 + break; + } + + sal_Unicode const * p = pFSysBegin + 2; + rtl::OUString aHost; + if (parseHost(p, pFSysEnd, aHost) + && (p == pFSysEnd || *p == '/')) + { + eStyle = FSYS_VOS; // Production T2 + break; + } + } + + if (eStyle & FSYS_DOS + && pFSysEnd - pFSysBegin >= 2 + && pFSysBegin[0] == '\\' + && pFSysBegin[1] == '\\') + { + sal_Unicode const * p = pFSysBegin + 2; + rtl::OUString aHost; + if (parseHost(p, pFSysEnd, aHost) + && (p == pFSysEnd || *p == '\\')) + { + eStyle = FSYS_DOS; // Production T3 + break; + } + } + + if (eStyle & FSYS_DOS + && pFSysEnd - pFSysBegin >= 2 + && INetMIME::isAlpha(pFSysBegin[0]) + && pFSysBegin[1] == ':' + && (pFSysEnd - pFSysBegin == 2 + || pFSysBegin[2] == '/' + || pFSysBegin[2] == '\\')) + { + eStyle = FSYS_DOS; // Productions T4, T5 + break; + } + + if (!(eStyle & (FSYS_UNX | FSYS_DOS | FSYS_MAC))) + return false; + + eStyle = guessFSysStyleByCounting(pFSysBegin, pFSysEnd, eStyle); + // Production T6 + break; + } + + rtl::OUStringBuffer aSynAbsURIRef(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("file://"))); + + switch (eStyle) + { + case FSYS_VOS: + { + sal_Unicode const * p = pFSysBegin; + if (pFSysEnd - p < 2 || *p++ != '/' || *p++ != '/') + return false; + if (p != pFSysEnd && *p == '.' + && (pFSysEnd - p == 1 || p[1] == '/')) + ++p; + for (; p != pFSysEnd; ++p) + switch (*p) + { + case '#': + case '%': + appendEscape(aSynAbsURIRef, '%', *p); + break; + + default: + aSynAbsURIRef.append(*p); + break; + } + break; + } + + case FSYS_UNX: + { + sal_Unicode const * p = pFSysBegin; + if (p != pFSysEnd && *p != '/') + return false; + for (; p != pFSysEnd; ++p) + switch (*p) + { + case '|': + case '#': + case '%': + appendEscape(aSynAbsURIRef, '%', *p); + break; + + default: + aSynAbsURIRef.append(*p); + break; + } + break; + } + + case FSYS_DOS: + { + sal_uInt32 nAltDelimiter = 0x80000000; + sal_Unicode const * p = pFSysBegin; + if (pFSysEnd - p >= 3 && p[0] == '\\' && p[1] == '\\') + p += 2; + else + { + aSynAbsURIRef.append(sal_Unicode('/')); + if (pFSysEnd - p >= 2 + && INetMIME::isAlpha(p[0]) + && p[1] == ':' + && (pFSysEnd - p == 2 || p[2] == '\\' || p[2] == '/')) + nAltDelimiter = '/'; + } + for (; p != pFSysEnd; ++p) + if (*p == '\\' || *p == nAltDelimiter) + aSynAbsURIRef.append(sal_Unicode('/')); + else + switch (*p) + { + case '/': + case '#': + case '%': + appendEscape(aSynAbsURIRef, '%', *p); + break; + + default: + aSynAbsURIRef.append(*p); + break; + } + break; + } + + case FSYS_MAC: + aSynAbsURIRef.append(sal_Unicode('/')); + {for (sal_Unicode const * p = pFSysBegin; p != pFSysEnd; ++p) + switch (*p) + { + case ':': + aSynAbsURIRef.append(sal_Unicode('/')); + break; + + case '/': + case '|': + case '#': + case '%': + appendEscape(aSynAbsURIRef, '%', *p); + break; + + default: + aSynAbsURIRef.append(*p); + break; + } + } + break; + + default: + OSL_ASSERT(false); + break; + } + + INetURLObject aTemp(aSynAbsURIRef.makeStringAndClear(), WAS_ENCODED, + RTL_TEXTENCODING_UTF8); + if (aTemp.HasError()) + return false; + + *this = aTemp; + return true; +} + +//============================================================================ +rtl::OUString INetURLObject::getFSysPath(FSysStyle eStyle, + sal_Unicode * pDelimiter) const +{ + if (m_eScheme != INET_PROT_FILE) + return rtl::OUString(); + + if ((eStyle & FSYS_VOS ? 1 : 0) + + (eStyle & FSYS_UNX ? 1 : 0) + + (eStyle & FSYS_DOS ? 1 : 0) + + (eStyle & FSYS_MAC ? 1 : 0) + > 1) + { + eStyle = eStyle & FSYS_VOS + && m_aHost.isPresent() + && m_aHost.getLength() > 0 ? + FSYS_VOS : + hasDosVolume(eStyle) + || ((eStyle & FSYS_DOS) != 0 + && m_aHost.isPresent() + && m_aHost.getLength() > 0) ? + FSYS_DOS : + eStyle & FSYS_UNX + && (!m_aHost.isPresent() || m_aHost.getLength() == 0) ? + FSYS_UNX : + FSysStyle(0); + } + + switch (eStyle) + { + case FSYS_VOS: + { + if (pDelimiter) + *pDelimiter = '/'; + + rtl::OUStringBuffer aSynFSysPath; + aSynFSysPath.appendAscii(RTL_CONSTASCII_STRINGPARAM("//")); + if (m_aHost.isPresent() && m_aHost.getLength() > 0) + aSynFSysPath.append(decode(m_aHost, '%', DECODE_WITH_CHARSET, + RTL_TEXTENCODING_UTF8)); + else + aSynFSysPath.append(sal_Unicode('.')); + aSynFSysPath.append(decode(m_aPath, '%', DECODE_WITH_CHARSET, + RTL_TEXTENCODING_UTF8)); + return aSynFSysPath.makeStringAndClear(); + } + + case FSYS_UNX: + { + if (m_aHost.isPresent() && m_aHost.getLength() > 0) + return rtl::OUString(); + + if (pDelimiter) + *pDelimiter = '/'; + + return decode(m_aPath, '%', DECODE_WITH_CHARSET, + RTL_TEXTENCODING_UTF8); + } + + case FSYS_DOS: + { + if (pDelimiter) + *pDelimiter = '\\'; + + rtl::OUStringBuffer aSynFSysPath; + if (m_aHost.isPresent() && m_aHost.getLength() > 0) + { + aSynFSysPath.appendAscii(RTL_CONSTASCII_STRINGPARAM("\\\\")); + aSynFSysPath.append(decode(m_aHost, '%', DECODE_WITH_CHARSET, + RTL_TEXTENCODING_UTF8)); + aSynFSysPath.append(sal_Unicode('\\')); + } + sal_Unicode const * p + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pEnd = p + m_aPath.getLength(); + DBG_ASSERT(p < pEnd && *p == '/', + "INetURLObject::getFSysPath(): Bad path"); + ++p; + while (p < pEnd) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(p, pEnd, false, '%', WAS_ENCODED, + RTL_TEXTENCODING_UTF8, + eEscapeType); + if (eEscapeType == ESCAPE_NO && nUTF32 == '/') + aSynFSysPath.append(sal_Unicode('\\')); + else + aSynFSysPath.appendUtf32(nUTF32); + } + return aSynFSysPath.makeStringAndClear(); + } + + case FSYS_MAC: + { + if (m_aHost.isPresent() && m_aHost.getLength() > 0) + return rtl::OUString(); + + if (pDelimiter) + *pDelimiter = ':'; + + rtl::OUStringBuffer aSynFSysPath; + sal_Unicode const * p + = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pEnd = p + m_aPath.getLength(); + DBG_ASSERT(p < pEnd && *p == '/', + "INetURLObject::getFSysPath(): Bad path"); + ++p; + while (p < pEnd) + { + EscapeType eEscapeType; + sal_uInt32 nUTF32 = getUTF32(p, pEnd, false, '%', WAS_ENCODED, + RTL_TEXTENCODING_UTF8, + eEscapeType); + if (eEscapeType == ESCAPE_NO && nUTF32 == '/') + aSynFSysPath.append(sal_Unicode(':')); + else + aSynFSysPath.appendUtf32(nUTF32); + } + return aSynFSysPath.makeStringAndClear(); + } + + default: + return rtl::OUString(); + } +} + +//============================================================================ +bool INetURLObject::HasMsgId() const +{ + if (m_eScheme != INET_PROT_POP3) + return false; + sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pEnd = p + m_aPath.getLength(); + for (; p < pEnd; ++p) + if (*p == '<') + return true; + return false; +} + +//============================================================================ +rtl::OUString INetURLObject::GetMsgId(DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) const +{ + if (m_eScheme != INET_PROT_POP3) + return rtl::OUString(); + sal_Unicode const * p = m_aAbsURIRef.getStr() + m_aPath.getBegin(); + sal_Unicode const * pEnd = p + m_aPath.getLength(); + for (; p < pEnd; ++p) + if (*p == '<') + return decode(p, pEnd, getEscapePrefix(), eMechanism, eCharset); + return rtl::OUString(); +} + +//============================================================================ +// static +void INetURLObject::appendUCS4Escape(rtl::OUStringBuffer & rTheText, + sal_Char cEscapePrefix, sal_uInt32 nUCS4) +{ + DBG_ASSERT(nUCS4 < 0x80000000, + "INetURLObject::appendUCS4Escape(): Bad char"); + if (nUCS4 < 0x80) + appendEscape(rTheText, cEscapePrefix, nUCS4); + else if (nUCS4 < 0x800) + { + appendEscape(rTheText, cEscapePrefix, nUCS4 >> 6 | 0xC0); + appendEscape(rTheText, cEscapePrefix, (nUCS4 & 0x3F) | 0x80); + } + else if (nUCS4 < 0x10000) + { + appendEscape(rTheText, cEscapePrefix, nUCS4 >> 12 | 0xE0); + appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 6 & 0x3F) | 0x80); + appendEscape(rTheText, cEscapePrefix, (nUCS4 & 0x3F) | 0x80); + } + else if (nUCS4 < 0x200000) + { + appendEscape(rTheText, cEscapePrefix, nUCS4 >> 18 | 0xF0); + appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 12 & 0x3F) | 0x80); + appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 6 & 0x3F) | 0x80); + appendEscape(rTheText, cEscapePrefix, (nUCS4 & 0x3F) | 0x80); + } + else if (nUCS4 < 0x4000000) + { + appendEscape(rTheText, cEscapePrefix, nUCS4 >> 24 | 0xF8); + appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 18 & 0x3F) | 0x80); + appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 12 & 0x3F) | 0x80); + appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 6 & 0x3F) | 0x80); + appendEscape(rTheText, cEscapePrefix, (nUCS4 & 0x3F) | 0x80); + } + else + { + appendEscape(rTheText, cEscapePrefix, nUCS4 >> 30 | 0xFC); + appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 24 & 0x3F) | 0x80); + appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 18 & 0x3F) | 0x80); + appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 12 & 0x3F) | 0x80); + appendEscape(rTheText, cEscapePrefix, (nUCS4 >> 6 & 0x3F) | 0x80); + appendEscape(rTheText, cEscapePrefix, (nUCS4 & 0x3F) | 0x80); + } +} + +//============================================================================ +// static +void INetURLObject::appendUCS4(rtl::OUStringBuffer& rTheText, sal_uInt32 nUCS4, + EscapeType eEscapeType, bool bOctets, + Part ePart, sal_Char cEscapePrefix, + rtl_TextEncoding eCharset, + bool bKeepVisibleEscapes) +{ + bool bEscape; + rtl_TextEncoding eTargetCharset = RTL_TEXTENCODING_DONTKNOW; + switch (eEscapeType) + { + case ESCAPE_NO: + if (mustEncode(nUCS4, ePart)) + { + bEscape = true; + eTargetCharset = bOctets ? RTL_TEXTENCODING_ISO_8859_1 : + RTL_TEXTENCODING_UTF8; + } + else + bEscape = false; + break; + + case ESCAPE_OCTET: + bEscape = true; + eTargetCharset = RTL_TEXTENCODING_ISO_8859_1; + break; + + case ESCAPE_UTF32: + if (mustEncode(nUCS4, ePart)) + { + bEscape = true; + eTargetCharset = eCharset; + } + else if (bKeepVisibleEscapes && INetMIME::isVisible(nUCS4)) + { + bEscape = true; + eTargetCharset = RTL_TEXTENCODING_ASCII_US; + } + else + bEscape = false; + break; + default: + bEscape = false; + } + + if (bEscape) + { + switch (eTargetCharset) + { + default: + DBG_ERROR("INetURLObject::appendUCS4(): Unsupported charset"); + case RTL_TEXTENCODING_ASCII_US: + case RTL_TEXTENCODING_ISO_8859_1: + appendEscape(rTheText, cEscapePrefix, nUCS4); + break; + + case RTL_TEXTENCODING_UTF8: + appendUCS4Escape(rTheText, cEscapePrefix, nUCS4); + break; + } + } + else + rTheText.append(sal_Unicode(nUCS4)); +} + +//============================================================================ +// static +sal_uInt32 INetURLObject::getUTF32(sal_Unicode const *& rBegin, + sal_Unicode const * pEnd, bool bOctets, + sal_Char cEscapePrefix, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset, + EscapeType & rEscapeType) +{ + DBG_ASSERT(rBegin < pEnd, "INetURLObject::getUTF32(): Bad sequence"); + sal_uInt32 nUTF32 = bOctets ? *rBegin++ : + INetMIME::getUTF32Character(rBegin, pEnd); + switch (eMechanism) + { + case ENCODE_ALL: + rEscapeType = ESCAPE_NO; + break; + + case WAS_ENCODED: + { + int nWeight1; + int nWeight2; + if (nUTF32 == sal_uChar(cEscapePrefix) && rBegin + 1 < pEnd + && (nWeight1 = INetMIME::getHexWeight(rBegin[0])) >= 0 + && (nWeight2 = INetMIME::getHexWeight(rBegin[1])) >= 0) + { + rBegin += 2; + nUTF32 = nWeight1 << 4 | nWeight2; + switch (eCharset) + { + default: + DBG_ERROR( + "INetURLObject::getUTF32(): Unsupported charset"); + case RTL_TEXTENCODING_ASCII_US: + rEscapeType = INetMIME::isUSASCII(nUTF32) ? + ESCAPE_UTF32 : ESCAPE_OCTET; + break; + + case RTL_TEXTENCODING_ISO_8859_1: + rEscapeType = ESCAPE_UTF32; + break; + + case RTL_TEXTENCODING_UTF8: + if (INetMIME::isUSASCII(nUTF32)) + rEscapeType = ESCAPE_UTF32; + else + { + if (nUTF32 >= 0xC0 && nUTF32 <= 0xF4) + { + sal_uInt32 nEncoded; + int nShift; + sal_uInt32 nMin; + if (nUTF32 <= 0xDF) + { + nEncoded = (nUTF32 & 0x1F) << 6; + nShift = 0; + nMin = 0x80; + } + else if (nUTF32 <= 0xEF) + { + nEncoded = (nUTF32 & 0x0F) << 12; + nShift = 6; + nMin = 0x800; + } + else + { + nEncoded = (nUTF32 & 0x07) << 18; + nShift = 12; + nMin = 0x10000; + } + sal_Unicode const * p = rBegin; + bool bUTF8 = true; + for (;;) + { + if (pEnd - p < 3 + || p[0] != cEscapePrefix + || (nWeight1 + = INetMIME::getHexWeight(p[1])) + < 8 + || nWeight1 > 11 + || (nWeight2 + = INetMIME::getHexWeight(p[2])) + < 0) + { + bUTF8 = false; + break; + } + p += 3; + nEncoded + |= ((nWeight1 & 3) << 4 | nWeight2) + << nShift; + if (nShift == 0) + break; + nShift -= 6; + } + if (bUTF8 && nEncoded >= nMin + && !INetMIME::isHighSurrogate(nEncoded) + && !INetMIME::isLowSurrogate(nEncoded) + && nEncoded <= 0x10FFFF) + { + rBegin = p; + nUTF32 = nEncoded; + rEscapeType = ESCAPE_UTF32; + break; + } + } + rEscapeType = ESCAPE_OCTET; + } + break; + } + } + else + rEscapeType = ESCAPE_NO; + break; + } + + case NOT_CANONIC: + { + int nWeight1; + int nWeight2; + if (nUTF32 == sal_uChar(cEscapePrefix) && rBegin + 1 < pEnd + && ((nWeight1 = INetMIME::getHexWeight(rBegin[0])) >= 0) + && ((nWeight2 = INetMIME::getHexWeight(rBegin[1])) >= 0)) + { + rBegin += 2; + nUTF32 = nWeight1 << 4 | nWeight2; + rEscapeType = ESCAPE_OCTET; + } + else + rEscapeType = ESCAPE_NO; + break; + } + } + return nUTF32; +} + +//============================================================================ +// static +sal_uInt32 INetURLObject::scanDomain(sal_Unicode const *& rBegin, + sal_Unicode const * pEnd, + bool bEager) +{ + enum State { STATE_DOT, STATE_LABEL, STATE_HYPHEN }; + State eState = STATE_DOT; + sal_Int32 nLabels = 0; + sal_Unicode const * pLastAlphanumeric = 0; + for (sal_Unicode const * p = rBegin;; ++p) + switch (eState) + { + case STATE_DOT: + if (p != pEnd && INetMIME::isAlphanumeric(*p)) + { + ++nLabels; + eState = STATE_LABEL; + break; + } + if (bEager || nLabels == 0) + return 0; + rBegin = p - 1; + return nLabels; + + case STATE_LABEL: + if (p != pEnd) + { + if (INetMIME::isAlphanumeric(*p)) + break; + else if (*p == '.') + { + eState = STATE_DOT; + break; + } + else if (*p == '-') + { + pLastAlphanumeric = p; + eState = STATE_HYPHEN; + break; + } + } + rBegin = p; + return nLabels; + + case STATE_HYPHEN: + if (p != pEnd) + { + if (INetMIME::isAlphanumeric(*p)) + { + eState = STATE_LABEL; + break; + } + else if (*p == '-') + break; + } + if (bEager) + return 0; + rBegin = pLastAlphanumeric; + return nLabels; + } +} + +//============================================================================ +// static +bool INetURLObject::scanIPv6reference(sal_Unicode const *& rBegin, + sal_Unicode const * pEnd) +{ + if (rBegin != pEnd && *rBegin == '[') { + sal_Unicode const * p = rBegin + 1; + //TODO: check for valid IPv6address (RFC 2373): + while (p != pEnd && (INetMIME::isHexDigit(*p) || *p == ':' || *p == '.')) + { + ++p; + } + if (p != pEnd && *p == ']') { + rBegin = p + 1; + return true; + } + } + return false; +} + +//============================================================================ +rtl::OUString INetURLObject::GetPartBeforeLastName(DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) + const +{ + if (!checkHierarchical()) + return rtl::OUString(); + INetURLObject aTemp(*this); + aTemp.clearFragment(); + aTemp.clearQuery(); + aTemp.removeSegment(LAST_SEGMENT, false); + aTemp.setFinalSlash(); + return aTemp.GetMainURL(eMechanism, eCharset); +} + +//============================================================================ +rtl::OUString INetURLObject::GetLastName(DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) const +{ + return getName(LAST_SEGMENT, true, eMechanism, eCharset); +} + +//============================================================================ +rtl::OUString INetURLObject::GetFileExtension(DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) const +{ + return getExtension(LAST_SEGMENT, false, eMechanism, eCharset); +} + +//============================================================================ +bool INetURLObject::CutLastName() +{ + INetURLObject aTemp(*this); + aTemp.clearFragment(); + aTemp.clearQuery(); + if (!aTemp.removeSegment(LAST_SEGMENT, false)) + return false; + *this = aTemp; + return true; +} + +//============================================================================ +rtl::OUString INetURLObject::PathToFileName() const +{ + if (m_eScheme != INET_PROT_FILE) + return rtl::OUString(); + rtl::OUString aSystemPath; + if (osl::FileBase::getSystemPathFromFileURL( + decode(m_aAbsURIRef.getStr(), + m_aAbsURIRef.getStr() + m_aPath.getEnd(), + getEscapePrefix(), NO_DECODE, RTL_TEXTENCODING_UTF8), + aSystemPath) + != osl::FileBase::E_None) + return rtl::OUString(); + return aSystemPath; +} + +//============================================================================ +rtl::OUString INetURLObject::GetFull() const +{ + INetURLObject aTemp(*this); + aTemp.removeFinalSlash(); + return aTemp.PathToFileName(); +} + +//============================================================================ +rtl::OUString INetURLObject::GetPath() const +{ + INetURLObject aTemp(*this); + aTemp.removeSegment(LAST_SEGMENT, true); + aTemp.removeFinalSlash(); + return aTemp.PathToFileName(); +} + +//============================================================================ +void INetURLObject::SetBase(rtl::OUString const & rTheBase) +{ + setBase(rTheBase, LAST_SEGMENT, true, ENCODE_ALL); +} + +//============================================================================ +rtl::OUString INetURLObject::GetBase() const +{ + return getBase(LAST_SEGMENT, true, DECODE_WITH_CHARSET); +} + +//============================================================================ +void INetURLObject::SetName(rtl::OUString const & rTheName, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + INetURLObject aTemp(*this); + if (aTemp.removeSegment(LAST_SEGMENT, true) + && aTemp.insertName(rTheName, false, LAST_SEGMENT, true, eMechanism, + eCharset)) + *this = aTemp; +} + +//============================================================================ +rtl::OUString INetURLObject::CutName(DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + rtl::OUString aTheName(getName(LAST_SEGMENT, true, eMechanism, eCharset)); + return removeSegment(LAST_SEGMENT, true) ? aTheName : rtl::OUString(); +} + +//============================================================================ +void INetURLObject::SetExtension(rtl::OUString const & rTheExtension, + EncodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + setExtension(rTheExtension, LAST_SEGMENT, false, eMechanism, eCharset); +} + +//============================================================================ +rtl::OUString INetURLObject::CutExtension(DecodeMechanism eMechanism, + rtl_TextEncoding eCharset) +{ + rtl::OUString aTheExtension(getExtension(LAST_SEGMENT, false, eMechanism, + eCharset)); + return removeExtension(LAST_SEGMENT, false) + ? aTheExtension : rtl::OUString(); +} + +//============================================================================ +bool INetURLObject::IsCaseSensitive() const +{ + return true; +} diff --git a/tools/source/fsys/wldcrd.cxx b/tools/source/fsys/wldcrd.cxx new file mode 100644 index 000000000000..7b4802501edf --- /dev/null +++ b/tools/source/fsys/wldcrd.cxx @@ -0,0 +1,146 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: wldcrd.cxx,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" +#include <tools/wldcrd.hxx> + +/************************************************************************* +|* +|* WildCard::Match() +|* +|* Beschreibung WLDCRD.SDW +|* Ersterstellung MA 19.06.91 +|* Letzte Aenderung MA 03.07.91 +|* +*************************************************************************/ + +/* Diese Methode ueberprueft, ob die Wilde Karte in pWild mit dem String + * in pStr matscht. + * Vertragen sich die beiden, so wird 1 zurueckgegeben, sonst 0. + * + * ein '*' in pWild bedeutet n beliebige Zeichen, mit n>=0 + * ein '?' in pWild bedeutet genau ein beliebiges Zeichen + * + */ + +USHORT WildCard::ImpMatch( const char *pWild, const char *pStr ) const +{ + int pos=0; + int flag=0; + + while ( *pWild || flag ) + { + switch (*pWild) + { + case '?': + if ( *pStr == '\0' ) + return 0; + break; + + default: + if ( (*pWild == '\\') && ((*(pWild+1)=='?') || (*(pWild+1) == '*')) ) + pWild++; + if ( *pWild != *pStr ) + if ( !pos ) + return 0; + else + pWild += pos; + else + break; // ACHTUNG laeuft unter bestimmten + // Umstaenden in den nachsten case rein!! + case '*': + while ( *pWild == '*' ) + pWild++; + if ( *pWild == '\0' ) + return 1; + flag = 1; + pos = 0; + if ( *pStr == '\0' ) + return ( *pWild == '\0' ); + while ( *pStr && *pStr != *pWild ) + { + if ( *pWild == '?' ) { + pWild++; + while ( *pWild == '*' ) + pWild++; + } + pStr++; + if ( *pStr == '\0' ) + return ( *pWild == '\0' ); + } + break; + } + if ( *pWild != '\0' ) + pWild++; + if ( *pStr != '\0' ) + pStr++; + else + flag = 0; + if ( flag ) + pos--; + } + return ( *pStr == '\0' ) && ( *pWild == '\0' ); +} + +/************************************************************************* +|* +|* WildCard::Matches() +|* +|* Beschreibung WLDCRD.SDW +|* Ersterstellung MA 19.06.91 +|* Letzte Aenderung TH 02.02.96 +|* +*************************************************************************/ + +BOOL WildCard::Matches( const String& rString ) const +{ + ByteString aTmpWild = aWildString; + ByteString aString(rString, osl_getThreadTextEncoding()); + + USHORT nSepPos; + + if ( cSepSymbol != '\0' ) + { + while ( (nSepPos = aTmpWild.Search( cSepSymbol )) != STRING_NOTFOUND ) + { + // alle getrennten WildCard's pruefen + if ( ImpMatch( aTmpWild.Copy( 0, nSepPos ).GetBuffer(), aString.GetBuffer() ) ) + return TRUE; + aTmpWild.Erase( 0, nSepPos + 1 ); // Trennsymbol entfernen + } + // und noch den hinter dem letzen Trennsymbol bzw. den einzigen + } + + if ( ImpMatch( aTmpWild.GetBuffer(), aString.GetBuffer() ) ) + return TRUE; + else + return FALSE; +} diff --git a/tools/source/fsys/wntmsc.cxx b/tools/source/fsys/wntmsc.cxx new file mode 100644 index 000000000000..153fbf37de2e --- /dev/null +++ b/tools/source/fsys/wntmsc.cxx @@ -0,0 +1,1084 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: wntmsc.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#ifdef _MSC_VER +#pragma warning (push,1) +#endif +#include <stdio.h> +#include <ctype.h> +#include <limits.h> +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +#include "wntmsc.hxx" +#include <tools/errinf.hxx> +#include <tools/debug.hxx> +#include <tools/list.hxx> +#include <tools/wldcrd.hxx> +#include <tools/fsys.hxx> +#include <tools/bigint.hxx> + +DECLARE_LIST( DirEntryList, DirEntry* ); +DECLARE_LIST( FSysSortList, FSysSort* ); +DECLARE_LIST( FileStatList, FileStat* ); + +int Sys2SolarError_Impl( int nSysErr ); + +static BOOL bLastCaseSensitive = FALSE; + +//-------------------------------------------------------------------- + +ByteString Upper_Impl( const ByteString &rStr ) +{ + ByteString aRet( rStr.GetBuffer() ); // es muss ein neuer String entstehen! + CharUpperBuff( (char*) aRet.GetBuffer(), aRet.Len() ); + return aRet; +} + +//-------------------------------------------------------------------- + +DIR *opendir( const char* pPfad ) +{ + DIR *pDir = new DIR; + if ( pDir ) + pDir->p = (char*) pPfad; + return pDir; +} + +struct dirent *readdir( DIR *pDir ) +{ + bool bOk = false; + if ( pDir->p ) + { + char *pBuf = new char[ strlen( pDir->p ) + 5 ]; + if ( pBuf ) + { + // *.* dahinter, ggf mit "\\" abtrennen (falls nicht schon da) + strcpy( pBuf, pDir->p ); + strcat( pBuf, "\\*.*" + ( *(pBuf + strlen( pBuf ) - 1 ) == '\\' ) ); + CharUpperBuff( pBuf, strlen(pBuf) ); + pDir->h = FindFirstFile( pBuf, &pDir->aDirEnt ); + bOk = pDir->h != INVALID_HANDLE_VALUE; + pDir->p = NULL; + delete [] pBuf; + } + else + pDir->h = INVALID_HANDLE_VALUE; + } + else + { + bOk = FindNextFile( pDir->h, &pDir->aDirEnt ); + } + + return bOk ? &pDir->aDirEnt : NULL; +} + +int closedir( DIR *pDir ) +{ + BOOL bOk = FALSE; + if ( pDir ) + { + bOk = 0 != pDir->p || FindClose( pDir->h ); + delete pDir; + } + return bOk; +} + +/************************************************************************* +|* +|* DirEntry::GetPathStyle() const +|* +|* Beschreibung +|* Ersterstellung MI 11.05.95 +|* Letzte Aenderung MI 11.05.95 +|* +*************************************************************************/ + +ErrCode GetPathStyle_Impl( const String &rDevice, FSysPathStyle &rStyle ) +{ + ByteString aRootDir(rDevice, osl_getThreadTextEncoding()); + if ( aRootDir.Len() && aRootDir.GetBuffer()[aRootDir.Len()-1] != '\\' ) + aRootDir += '\\'; + + char sVolumeName[256]; + char sFileSysName[16]; + DWORD nSerial[2]; + DWORD nMaxCompLen[2]; + DWORD nFlags[2]; + + // Windows95 hat VFAT, WindowsNT nicht + DWORD nVer = GetVersion(); + BOOL bW95 = ( nVer & 0xFF ) >= 4; + + FSysFailOnErrorImpl(); + rStyle = FSYS_STYLE_UNKNOWN; + if ( GetVolumeInformation( + (char*) aRootDir.GetBuffer(), + sVolumeName, 256, (LPDWORD) &nSerial, (LPDWORD) &nMaxCompLen, + (LPDWORD) &nFlags, sFileSysName, 16 ) ) + { + // FAT/VFAT? + if ( 0 == strcmp( "FAT", sFileSysName ) ) + rStyle = bW95 ? FSYS_STYLE_VFAT : FSYS_STYLE_FAT; + + // NTFS? + else if ( 0 == strcmp( "NTFS", sFileSysName ) ) + rStyle = FSYS_STYLE_NTFS; + + // HPFS? + else if ( 0 == strcmp( "HPFS", sFileSysName ) ) + rStyle = FSYS_STYLE_HPFS; + + // NWCOMPA/NWFS? + else if ( 0 == strncmp( "NW", sFileSysName, 2 ) ) + rStyle = FSYS_STYLE_NWFS; + + return ERRCODE_NONE; + } + + return ERRCODE_IO_INVALIDDEVICE; +} + +FSysPathStyle DirEntry::GetPathStyle( const String &rDevice ) +{ + + FSysPathStyle eStyle; + GetPathStyle_Impl( rDevice, eStyle ); + return eStyle; +} + +/************************************************************************* +|* +|* DirEntry::IsCaseSensitive() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 10.06.93 +|* Letzte Aenderung TPF 26.02.1999 +|* +*************************************************************************/ + +BOOL DirEntry::IsCaseSensitive( FSysPathStyle eFormatter ) const +{ + + if (eFormatter==FSYS_STYLE_HOST) + { +/* + DirEntry aRoot(*this); + aRoot.ToAbs(); + aRoot = aRoot[Level()-1]; + String aRootDir = aRoot.GetFull(FSYS_STYLE_HOST, TRUE); + + char sVolumeName[256]; + DWORD nVolumeSerial; + DWORD nMaxCompLen; + DWORD nFlags; + char sFileSysName[16]; + + if ( GetVolumeInformation( (char*) aRootDir.GetStr(), + sVolumeName, + 256, + (LPDWORD) &nVolumeSerial, + (LPDWORD) &nMaxCompLen, + (LPDWORD) &nFlags, + sFileSysName, + 16 )) + { + return (nFlags & FS_CASE_SENSITIVE) ? TRUE : FALSE; + } + else + { + return FALSE; + } +*/ + // + // guter versuch, aber FS_CASE_SENSITIVE ist D?nnsinn in T?ten: + // + // sFileSysName FS_CASE_SENSITIVE + // FAT FALSE + // NTFS TRUE !!! + // NWCompat FALSE + // Samba FALSE + // + // NT spricht auch NTFS lediglich case preserving an, also ist unter NT alles case insensitiv + // + + return FALSE; + } + else + { + BOOL isCaseSensitive = FALSE; // ich bin unter win32, also ist der default case insensitiv + switch ( eFormatter ) + { + case FSYS_STYLE_MAC: + case FSYS_STYLE_FAT: + case FSYS_STYLE_VFAT: + case FSYS_STYLE_NTFS: + case FSYS_STYLE_NWFS: + case FSYS_STYLE_HPFS: + case FSYS_STYLE_DETECT: + { + isCaseSensitive = FALSE; + break; + } + case FSYS_STYLE_SYSV: + case FSYS_STYLE_BSD: + { + isCaseSensitive = TRUE; + break; + } + default: + { + isCaseSensitive = FALSE; // ich bin unter win32, also ist der default case insensitiv + break; + } + } + return isCaseSensitive; + } +} + +/************************************************************************* +|* +|* DirEntry::ToAbs() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MA 02.12.91 +|* +*************************************************************************/ + +BOOL DirEntry::ToAbs() +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + if ( FSYS_FLAG_VOLUME == eFlag ) + { + eFlag = FSYS_FLAG_ABSROOT; + return TRUE; + } + + if ( IsAbs() ) + { + return TRUE; + } + + + char sBuf[256]; + char *pOld; + ByteString aFullName( GetFull(), osl_getThreadTextEncoding() ); + FSysFailOnErrorImpl(); + if ( GetFullPathName((char*)aFullName.GetBuffer(),256,sBuf,&pOld) > 511 ) + return FALSE; + + *this = DirEntry( String(sBuf, osl_getThreadTextEncoding() )); + return TRUE; +} + + +/************************************************************************* +|* +|* DirEntry::GetVolume() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 27.08.92 +|* Letzte Aenderung MI 28.08.92 +|* +*************************************************************************/ + +String DirEntry::GetVolume() const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + String aRet; + const DirEntry *pTop = ImpGetTopPtr(); + ByteString aName = ByteString( pTop->aName ).ToLowerAscii(); + + if ( ( pTop->eFlag == FSYS_FLAG_ABSROOT || + pTop->eFlag == FSYS_FLAG_RELROOT || + pTop->eFlag == FSYS_FLAG_VOLUME ) + && aName != "a:" && aName != "b:" && Exists() ) + { + char sFileSysName[256]; + char sVolumeName[256]; + DWORD nVolumeNameLen = 256; + DWORD nSerial[2]; + DWORD nMaxCompLen[2]; + DWORD nFlags[2]; + ByteString aRootDir = pTop->aName; + FSysFailOnErrorImpl(); + + // Network-Device zuerst probieren wegen langsamer Samba-Drives + if ( !WNetGetConnection( (char*) aRootDir.GetBuffer(), + sVolumeName, &nVolumeNameLen ) ) + aRet = String( sVolumeName, osl_getThreadTextEncoding()); + + // dann den VolumeNamen fuer lokale Drives + if ( aRet.Len() == 0 ) + { + aRootDir += "\\"; + if ( GetVolumeInformation( (char*) aRootDir.GetBuffer(), + sVolumeName, 256, + (LPDWORD) &nSerial, (LPDWORD) &nMaxCompLen, + (LPDWORD) &nFlags, sFileSysName, 256 ) ) + aRet = String( sVolumeName, osl_getThreadTextEncoding()); + } + } + + return aRet; +} + +/************************************************************************* +|* +|* DirEntry::SetCWD() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 26.04.91 +|* Letzte Aenderung MI 21.05.92 +|* +*************************************************************************/ + +BOOL DirEntry::SetCWD( BOOL bSloppy ) const +{ + DBG_CHKTHIS( DirEntry, ImpCheckDirEntry ); + + FSysFailOnErrorImpl(); + + if ( eFlag == FSYS_FLAG_CURRENT && !aName.Len() ) + return TRUE; + + if ( SetCurrentDirectory(ByteString(GetFull(), osl_getThreadTextEncoding()).GetBuffer()) ) + { + return TRUE; + } + + if ( bSloppy && pParent && + SetCurrentDirectory(ByteString(pParent->GetFull(), osl_getThreadTextEncoding()).GetBuffer()) ) + { + return TRUE; + } + + return FALSE; +} + +//------------------------------------------------------------------------- + +USHORT DirReader_Impl::Init() +{ + // Block-Devices auflisten? + if ( pDir->eAttrMask & FSYS_KIND_BLOCK ) + { + // CWD merken + DirEntry aCurrentDir; + aCurrentDir.ToAbs(); + + // einzeln auf Existenz und Masken-konformit"at pr"ufen + USHORT nRead = 0; + char sDrive[3] = { '?', ':', 0 }; + char sRoot[4] = { '?', ':', '\\', 0 }; + for ( char c = 'a'; c <= 'z'; c++ ) + { + sDrive[0] = c; + sRoot[0] = c; + DirEntry* pDrive = new DirEntry( sDrive, FSYS_FLAG_VOLUME, FSYS_STYLE_HOST ); + if ( pDir->aNameMask.Matches( String( ByteString(sDrive), osl_getThreadTextEncoding())) && GetDriveType( sRoot ) != 1 ) + { + if ( pDir->pStatLst ) //Status fuer Sort gewuenscht? + { + FileStat *pNewStat = new FileStat( *pDrive ); + pDir->ImpSortedInsert( pDrive, pNewStat ); + } + else + pDir->ImpSortedInsert( pDrive, NULL ); + ++nRead; + } + else + delete pDrive; + } + + // CWD restaurieren + aCurrentDir.SetCWD(); + return nRead; + } + + return 0; +} + +//------------------------------------------------------------------------- + +USHORT DirReader_Impl::Read() +{ + // Directories und Files auflisten? + if ( ( pDir->eAttrMask & FSYS_KIND_DIR || + pDir->eAttrMask & FSYS_KIND_FILE ) && + ( ( pDosEntry = readdir( pDosDir ) ) != NULL ) ) + { + // Gross/Kleinschreibung nicht beruecksichtigen + ByteString aLowerName = pDosEntry->d_name; + CharLowerBuff( (char*) aLowerName.GetBuffer(), aLowerName.Len() ); + + // Flags pruefen + BOOL bIsDirAndWantsDir = + ( ( pDir->eAttrMask & FSYS_KIND_DIR ) && +#ifdef ICC + ( pDosEntry->d_type & ( strcmp(pDosEntry->d_name,".") || + strcmp(pDosEntry->d_name,"..")) ) ); +#else + ( pDosEntry->d_type & DOS_DIRECT ) ); +#endif + BOOL bIsFileAndWantsFile = + ( ( pDir->eAttrMask & FSYS_KIND_FILE ) && +#ifdef ICC + !( pDosEntry->d_type & ( strcmp(pDosEntry->d_name,".") || + strcmp(pDosEntry->d_name,"..")) ) && +#else + !( pDosEntry->d_type & DOS_DIRECT ) && +#endif + !( pDosEntry->d_type & DOS_VOLUMEID ) ); + BOOL bIsHidden = (pDosEntry->d_type & _A_HIDDEN) != 0; + BOOL bWantsHidden = 0 == ( pDir->eAttrMask & FSYS_KIND_VISIBLE ); + if ( ( bIsDirAndWantsDir || bIsFileAndWantsFile ) && + ( bWantsHidden || !bIsHidden ) && + pDir->aNameMask.Matches( String(aLowerName, osl_getThreadTextEncoding()) ) ) + { +#ifdef DBG_UTIL + DbgOutf( "%s %s flags:%x found", + pDosEntry->d_name, + bIsFileAndWantsFile ? "file" : "dir", + pDosEntry->d_type ); +#endif + DirEntryFlag eFlag = + 0 == strcmp( pDosEntry->d_name, "." ) ? FSYS_FLAG_CURRENT + : 0 == strcmp( pDosEntry->d_name, ".." ) ? FSYS_FLAG_PARENT + : FSYS_FLAG_NORMAL; + DirEntry *pTemp = new DirEntry( ByteString(pDosEntry->d_name), + eFlag, FSYS_STYLE_NTFS ); +#ifdef FEAT_FSYS_DOUBLESPEED + pTemp->ImpSetStat( new FileStat( (void*) pDosDir, (void*) 0 ) ); +#endif + if ( pParent ) + pTemp->ImpChangeParent( new DirEntry( *pParent ), FALSE ); + if ( pDir->pStatLst ) //Status fuer Sort gewuenscht? + { + FileStat *pNewStat = new FileStat( (void*) pDosDir, (void*) 0 ); + pDir->ImpSortedInsert( pTemp, pNewStat ); + } + else + pDir->ImpSortedInsert( pTemp, NULL ); + return 1; + } +#ifdef DBG_UTIL + else + DbgOutf( "%s flags:%x skipped", + pDosEntry->d_name, + pDosEntry->d_type ); +#endif + + } + else + bReady = TRUE; + return 0; +} + +/************************************************************************* +|* +|* InitFileStat() +|* +|* Beschreibung gemeinsamer Teil der Ctoren fuer FileStat +|* Ersterstellung MI 28.08.92 +|* Letzte Aenderung MI 28.08.92 +|* +*************************************************************************/ + +void FileStat::ImpInit( void* p ) +{ + _WIN32_FIND_DATAA *pDirEnt = (_WIN32_FIND_DATAA*) p; + + nError = FSYS_ERR_OK; + nSize = pDirEnt->nFileSizeLow; + + SYSTEMTIME aSysTime; + FILETIME aLocTime; + + // use the last write date / time when the creation date / time isn't set + if ( ( pDirEnt->ftCreationTime.dwLowDateTime == 0 ) && + ( pDirEnt->ftCreationTime.dwHighDateTime == 0 ) ) + { + pDirEnt->ftCreationTime.dwLowDateTime = pDirEnt->ftLastWriteTime.dwLowDateTime; + pDirEnt->ftCreationTime.dwHighDateTime = pDirEnt->ftLastWriteTime.dwHighDateTime; + } + + // use the last write date / time when the last accessed date / time isn't set + if ( ( pDirEnt->ftLastAccessTime.dwLowDateTime == 0 ) && + ( pDirEnt->ftLastAccessTime.dwHighDateTime == 0 ) ) + { + pDirEnt->ftLastAccessTime.dwLowDateTime = pDirEnt->ftLastWriteTime.dwLowDateTime; + pDirEnt->ftLastAccessTime.dwHighDateTime = pDirEnt->ftLastWriteTime.dwHighDateTime; + } + + FileTimeToLocalFileTime( &pDirEnt->ftCreationTime, &aLocTime ); + FileTimeToSystemTime( &aLocTime, &aSysTime ); + aDateCreated = Date( aSysTime.wDay, aSysTime.wMonth, aSysTime.wYear ); + aTimeCreated = Time( aSysTime.wHour, aSysTime.wMinute, + aSysTime.wSecond, 0 ); + + FileTimeToLocalFileTime( &pDirEnt->ftLastWriteTime, &aLocTime ); + FileTimeToSystemTime( &aLocTime, &aSysTime ); + aDateModified = Date( aSysTime.wDay, aSysTime.wMonth, aSysTime.wYear ); + aTimeModified = Time( aSysTime.wHour, aSysTime.wMinute, + aSysTime.wSecond, 0 ); + + FileTimeToLocalFileTime( &pDirEnt->ftLastAccessTime, &aLocTime ); + FileTimeToSystemTime( &aLocTime, &aSysTime ); + aDateAccessed = Date( aSysTime.wDay, aSysTime.wMonth, aSysTime.wYear ); + aTimeAccessed = Time( aSysTime.wHour, aSysTime.wMinute, + aSysTime.wSecond, 0 ); + + nKindFlags = FSYS_KIND_FILE; + if ( pDirEnt->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + nKindFlags = FSYS_KIND_DIR; +} + +/************************************************************************* +|* +|* FileStat::FileStat() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 27.08.92 +|* Letzte Aenderung MI 28.08.92 +|* +*************************************************************************/ + +FileStat::FileStat( const void *pInfo, // struct dirent + const void * ): // dummy + aDateCreated(0), + aTimeCreated(0), + aDateModified(0), + aTimeModified(0), + aDateAccessed(0), + aTimeAccessed(0) +{ + ImpInit( ( (dirent*) pInfo ) ); +} + +/************************************************************************* +|* +|* FileStat::Update() +|* +|* Beschreibung FSYS.SDW +|* Ersterstellung MI 27.08.92 +|* Letzte Aenderung MI 28.08.92 +|* +*************************************************************************/ + +#ifdef _MSC_VER +#pragma warning(push, 1) +#pragma warning(disable: 4917) +#endif +#include <shlobj.h> +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef UNICODE +#define lstrchr wcschr +#define lstrncmp wcsncmp +#else +#define lstrchr strchr +#define lstrncmp strncmp +#endif + +//--------------------------------------------------------------------------- + +void SHFreeMem( void *p ) +{ + LPMALLOC pMalloc = NULL; + + if ( SUCCEEDED(SHGetMalloc(&pMalloc)) ) + { + pMalloc->Free( p ); + pMalloc->Release(); + } +} + +//--------------------------------------------------------------------------- + +HRESULT SHGetIDListFromPath( HWND hwndOwner, LPCTSTR pszPath, LPITEMIDLIST *ppidl ) +{ + if ( IsBadWritePtr(ppidl, sizeof(LPITEMIDLIST)) ) + return E_INVALIDARG; + + LPSHELLFOLDER pDesktopFolder = NULL; + + HRESULT hResult = SHGetDesktopFolder( &pDesktopFolder ); + if ( FAILED(hResult) ) + return hResult; + + ULONG chEaten = lstrlen( pszPath ); + DWORD dwAttributes = FILE_ATTRIBUTE_DIRECTORY; + +#ifdef UNICODE + LPOLESTR wszPath = pszPath; +#else + WCHAR wszPath[MAX_PATH]; + MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pszPath, -1, wszPath, MAX_PATH ); +#endif + + hResult = pDesktopFolder->ParseDisplayName( hwndOwner, (LPBC)NULL, wszPath, &chEaten, ppidl, &dwAttributes ); + pDesktopFolder->Release(); + + return hResult; +} + +//--------------------------------------------------------------------------- + +HRESULT SHGetFolderFromIDList( LPCITEMIDLIST pidl, LPSHELLFOLDER *ppFolder ) +{ + if ( IsBadWritePtr(ppFolder, sizeof(LPSHELLFOLDER)) ) + return E_INVALIDARG; + + *ppFolder = NULL; + + LPSHELLFOLDER pDesktopFolder = NULL; + + HRESULT hResult = SHGetDesktopFolder( &pDesktopFolder ); + if ( FAILED(hResult) ) + return hResult; + + hResult = pDesktopFolder->BindToObject( pidl, (LPBC)NULL, IID_IShellFolder, (LPVOID *)ppFolder ); + pDesktopFolder->Release(); + + return hResult; +} + +//--------------------------------------------------------------------------- + +HRESULT SHResolvePath( HWND hwndOwner, LPCTSTR pszPath, LPITEMIDLIST *ppidl ) +{ + // If hwndOwner is NULL, use the desktop window, because dialogs need a parent + +#ifdef BOOTSTRAP + return NO_ERROR; +#else + if ( !hwndOwner ) + hwndOwner = GetDesktopWindow(); + + HRESULT hResult = NOERROR; + LPTSTR pszPathCopy; + LPTSTR pszTrailingPath; + TCHAR cBackup = 0; + + // First make a copy of the path + + pszPathCopy = new TCHAR[lstrlen(pszPath) + 1]; + if ( pszPathCopy ) + lstrcpy( pszPathCopy, pszPath ); + else + return E_OUTOFMEMORY; + + // Determine the first token + + if ( !lstrncmp( pszPathCopy, "\\\\", 2 ) ) + pszTrailingPath = lstrchr( pszPathCopy + 2, '\\' ); + else + pszTrailingPath = lstrchr( pszPathCopy, '\\' ); + + // Now scan the path tokens + + while ( SUCCEEDED(hResult) ) + { + if ( pszTrailingPath ) + { + cBackup = *(++pszTrailingPath); + *pszTrailingPath = 0; + } + + LPITEMIDLIST pidl = NULL; + + // Make item ID list from leading path + + hResult = SHGetIDListFromPath( hwndOwner, pszPathCopy, &pidl ); + + // if path exists try to open it as folder + + if ( SUCCEEDED(hResult) ) + { + // Only open the folder if it was not the last token + + if ( pszTrailingPath ) + { + LPSHELLFOLDER pFolder; + + // Create a folder instance + hResult = SHGetFolderFromIDList( pidl, &pFolder); + + // Is it a folder ? + if ( SUCCEEDED(hResult) ) + { + // No try to instantiate an enumerator. + // This should popup a login dialog if any + + LPENUMIDLIST pEnum = NULL; + + hResult = pFolder->EnumObjects( hwndOwner, + SHCONTF_NONFOLDERS | SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN, + &pEnum ); + + // Release the enumerator interface + if ( SUCCEEDED(hResult) ) + pEnum->Release(); + + // Release the folder interface + pFolder->Release(); + } + + SHFreeMem( pidl ); + } + else // It was the last token + { + if ( ppidl ) + *ppidl = pidl; + else + SHFreeMem( pidl ); + } + } + + + // Forward to next token + + if ( pszTrailingPath ) + { + *pszTrailingPath = cBackup; + pszTrailingPath = lstrchr( pszTrailingPath, '\\' ); + } + else + break; + } + + // Free the working copy of the path + delete pszPathCopy; + + // NOERROR or OLE error code + return hResult; +#endif +} + +//--------------------------------------------------------------------------- +// The Wrapper +//--------------------------------------------------------------------------- + +BOOL Exists_Impl( const ByteString & crPath ) +{ + // We do not know if OLE was initialized for this thread + + CoInitialize( NULL ); + + BOOL bSuccess = SUCCEEDED( SHResolvePath(NULL, crPath.GetBuffer(), NULL) ); + + CoUninitialize(); + + return bSuccess; +} + +//--------------------------------------------------------------------------- + +BOOL FileStat::Update( const DirEntry& rDirEntry, BOOL bForceAccess ) +{ + nSize = 0; + nKindFlags = 0; + aCreator.Erase(); + aType.Erase(); + aDateCreated = Date(0); + aTimeCreated = Time(0); + aDateModified = Date(0); + aTimeModified = Time(0); + aDateAccessed = Date(0); + aTimeAccessed = Time(0); + + if ( !rDirEntry.IsValid() ) + { + nError = FSYS_ERR_UNKNOWN; + nKindFlags = 0; + return FALSE; + } + + // Sonderbehandlung falls es sich um eine Root ohne Laufwerk handelt + + if ( !rDirEntry.aName.Len() && rDirEntry.eFlag == FSYS_FLAG_ABSROOT ) + { + nKindFlags = FSYS_KIND_DIR; + nError = FSYS_ERR_OK; + return TRUE; + } + + // keine Error-Boxen anzeigen + FSysFailOnErrorImpl(); + + // Redirect + String aPath( rDirEntry.GetFull() ); +#ifndef BOOTSTRAP + FSysRedirector::DoRedirect( aPath ); +#endif + DirEntry aDirEntry( aPath ); + + // ist ein Medium im Laufwerk? + HACK("wie?") + BOOL bAccess = TRUE; + const DirEntry *pTop = aDirEntry.ImpGetTopPtr(); + ByteString aName = ByteString(pTop->aName).ToLowerAscii(); + if ( !bForceAccess && + ( pTop->eFlag == FSYS_FLAG_ABSROOT || + pTop->eFlag == FSYS_FLAG_RELROOT || + pTop->eFlag == FSYS_FLAG_VOLUME ) ) + if ( aName == "a:" || aName == "b:" ) + bAccess = FALSE; + else + DBG_TRACE( "FSys: will access removable device!" ); + if ( bAccess && ( aName == "a:" || aName == "b:" ) ) { + DBG_WARNING( "floppy will clatter" ); + } + + // Sonderbehandlung, falls es sich um ein Volume handelt + if ( aDirEntry.eFlag == FSYS_FLAG_VOLUME || + aDirEntry.eFlag == FSYS_FLAG_ABSROOT ) + { + if ( aDirEntry.eFlag == FSYS_FLAG_VOLUME ) + nKindFlags = FSYS_KIND_DEV | ( aDirEntry.aName.Len() == 2 + ? FSYS_KIND_BLOCK + : FSYS_KIND_CHAR ); + else + nKindFlags = FSYS_KIND_DIR; + + if ( !bAccess ) + { + if ( aDirEntry.eFlag == FSYS_FLAG_VOLUME ) + nKindFlags |= FSYS_KIND_REMOVEABLE; + nError = FSYS_ERR_NOTEXISTS; + nKindFlags = 0; + return FALSE; + } + + ByteString aRootDir = aDirEntry.aName; + aRootDir += ByteString( "\\" ); + UINT nType = GetDriveType( (char *) aRootDir.GetBuffer() ); //TPF: 2i + if ( nType == 1 || nType == 0 ) + { + nError = FSYS_ERR_NOTEXISTS; + nKindFlags = 0; + return FALSE; + } + + if ( aDirEntry.eFlag == FSYS_FLAG_VOLUME ) + nKindFlags = nKindFlags | + ( ( nType == DRIVE_REMOVABLE ) ? FSYS_KIND_REMOVEABLE : 0 ) | + ( ( nType == DRIVE_FIXED ) ? FSYS_KIND_FIXED : 0 ) | + ( ( nType == DRIVE_REMOTE ) ? FSYS_KIND_REMOTE : 0 ) | + ( ( nType == DRIVE_RAMDISK ) ? FSYS_KIND_RAM : 0 ) | + ( ( nType == DRIVE_CDROM ) ? FSYS_KIND_CDROM : 0 ) | + ( ( nType == 0 ) ? FSYS_KIND_UNKNOWN : 0 ); + + nError = ERRCODE_NONE; + + return TRUE; + } + + // Statusinformation vom Betriebssystem holen + HANDLE h; //() + _WIN32_FIND_DATAA aEntry = {}; + DirEntry aAbsEntry( aDirEntry ); + if ( bAccess && aAbsEntry.ToAbs() ) + { + // im Namen k"onnen auch ';*?' als normale Zeichen vorkommen + ByteString aFilePath( aAbsEntry.GetFull(), osl_getThreadTextEncoding() ); + + // MI: dann gehen Umlaute auf Novell-Servern nicht / wozu ueberhaupt + // CharUpperBuff( (char*) aFilePath.GetStr(), aFilePath.Len() ); + DBG_TRACE1( "FileStat: %s", aFilePath.GetBuffer() ); + h = aFilePath.Len() < 230 + // die Win32-API ist hier sehr schwammig + ? FindFirstFile( (char *) aFilePath.GetBuffer(), &aEntry )//TPF: 2i + : INVALID_HANDLE_VALUE; + + if ( INVALID_HANDLE_VALUE != h ) + { + if ( !( aEntry.dwFileAttributes & 0x40 ) ) // com1: etc. e.g. not encrypted (means normal) + { + ByteString aUpperName = Upper_Impl(ByteString(aAbsEntry.GetName(), osl_getThreadTextEncoding())); + + // HRO: #74051# Compare also with short alternate filename + if ( aUpperName != Upper_Impl( aEntry.cFileName ) && aUpperName != Upper_Impl( aEntry.cAlternateFileName ) ) + h = INVALID_HANDLE_VALUE; + } + } + + if ( INVALID_HANDLE_VALUE == h ) + { + DWORD dwError = GetLastError(); + + if ( ERROR_BAD_NET_NAME == dwError ) + { + nKindFlags = FSYS_KIND_UNKNOWN; + nError = FSYS_ERR_NOTEXISTS; + return FALSE; + } + + // UNC-Volume? + DirEntry *pTop = aAbsEntry.ImpGetTopPtr(); + if ( pTop->GetFlag() == FSYS_FLAG_ABSROOT && + ( pTop->aName.Len() > 1 && (pTop->aName.GetBuffer()[1] != ':' )) ) + { + if ( bForceAccess ) + { + if ( Exists_Impl( aFilePath ) ) + { + nKindFlags = FSYS_KIND_DIR|FSYS_KIND_REMOTE; + nError = FSYS_ERR_OK; + return TRUE; + } + else + { + nKindFlags = FSYS_KIND_UNKNOWN; + nError = FSYS_ERR_NOTEXISTS; + return FALSE; + } + } + } + } + } + else + h = INVALID_HANDLE_VALUE; + + if ( h == INVALID_HANDLE_VALUE ) + { + // Sonderbehandlung falls es sich um eine Wildcard handelt + ByteString aTempName( aDirEntry.GetName(), osl_getThreadTextEncoding() ); + if ( strchr( aTempName.GetBuffer(), '?' ) || + strchr( aTempName.GetBuffer(), '*' ) || + strchr( aTempName.GetBuffer(), ';' ) ) + { + nKindFlags = FSYS_KIND_WILD; + nError = FSYS_ERR_OK; + return TRUE; + } + + if ( bAccess ) + { + nError = FSYS_ERR_NOTEXISTS; + nKindFlags = FSYS_KIND_UNKNOWN; + } + else + nKindFlags = FSYS_KIND_REMOVEABLE; + } + else + { + ImpInit( &aEntry ); + FindClose( h ); + } + + if ( 0 != nError ) + nKindFlags = 0; + + return 0 == nError; + +} + +BOOL IsRedirectable_Impl( const ByteString &rPath ) +{ + if ( rPath.Len() >= 3 && ':' == rPath.GetBuffer()[1] ) + { + ByteString aVolume = rPath.Copy( 0, 3 ); + UINT nType = GetDriveType( (char *) aVolume.GetBuffer() ); + SetLastError( ERROR_SUCCESS ); + return DRIVE_FIXED != nType; + } + return FALSE; +} + +/************************************************************************* +|* +|* TempDirImpl() +|* +|* Beschreibung liefert den Namens des Directories fuer temporaere +|* Dateien +|* Ersterstellung MI 16.03.94 +|* Letzte Aenderung MI 16.03.94 +|* +*************************************************************************/ + +const char* TempDirImpl( char *pBuf ) +{ + if ( !GetTempPath( MAX_PATH, pBuf ) && + !GetWindowsDirectory( pBuf, MAX_PATH ) && + !GetEnvironmentVariable( "HOMEPATH", pBuf, MAX_PATH ) ) + return 0; + + return pBuf; +} + +//======================================================================= + +ErrCode FileStat::QueryDiskSpace( const String &rPath, + BigInt &rFreeBytes, BigInt &rTotalBytes ) +{ + DWORD nSectorsPerCluster; /* address of sectors per cluster */ + DWORD nBytesPerSector; /* address of bytes per sector */ + DWORD nFreeClusters; /* address of number of free clusters */ + DWORD nClusters; /* address of total number of clusters */ + + ByteString aVol( DirEntry(rPath).ImpGetTopPtr()->GetName(), osl_getThreadTextEncoding()); + bool bOK = GetDiskFreeSpace( aVol.GetBuffer(), + &nSectorsPerCluster, &nBytesPerSector, + &nFreeClusters, &nClusters ); + if ( !bOK ) + return Sys2SolarError_Impl( GetLastError() ); + + BigInt aBytesPerCluster( BigInt(nSectorsPerCluster) * + BigInt(nBytesPerSector) ); + rFreeBytes = aBytesPerCluster * BigInt(nFreeClusters); + rTotalBytes = aBytesPerCluster * BigInt(nClusters); + return 0; +} + +//========================================================================= + +void FSysEnableSysErrorBox( BOOL bEnable ) +{ // Preserve other Bits!! + sal_uInt32 nErrorMode = SetErrorMode( bEnable ? 0 : SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX ); + if ( bEnable ) + nErrorMode &= ~(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + else + nErrorMode |= (SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); + SetErrorMode( nErrorMode ); +} + + + diff --git a/tools/source/fsys/wntmsc.hxx b/tools/source/fsys/wntmsc.hxx new file mode 100644 index 000000000000..2215e3d13561 --- /dev/null +++ b/tools/source/fsys/wntmsc.hxx @@ -0,0 +1,105 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: wntmsc.hxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _dosmsc_hxx +#define _dosmsc_hxx + +#include <string.h> + +#ifndef ICC +#include <io.h> +#endif +#include <sys\types.h> +#include <sys\stat.h> +#include <direct.h> + +#include <tools/svwin.h> +#ifdef _MSC_VER +#pragma warning (push,1) +#endif +#include <winbase.h> +#ifdef _MSC_VER +#pragma warning (pop) +#endif +#include <tools/solar.h> + +#include <tools/string.hxx> + +//-------------------------------------------------------------------- + +#define FSYS_UNIX FALSE + +#define DOS_DIRECT _A_SUBDIR +#define DOS_VOLUMEID 0x08 +#ifndef S_IFBLK +#define S_IFBLK 0x6000 +#endif +#define setdrive(n,a) _chdrive(n) +#define GETDRIVE(n) (n = _getdrive()) + +#define dirent _WIN32_FIND_DATAA +#define d_name cFileName +#define d_type dwFileAttributes + +#if defined (TCPP) || defined (tcpp) +#define _mkdir mkdir +#define _rmdir rmdir +#define _chdir chdir +#define _unlink unlink +#define _getcwd getcwd +#define _access access +#endif + +typedef struct +{ + _WIN32_FIND_DATAA aDirEnt; + HANDLE h; + const char *p; +} DIR; + +#define PATHDELIMITER ";" +#define DEFSTYLE FSYS_STYLE_NTFS +#define MKDIR( p ) mkdir( p ) +#define CMP_LOWER(s) ( ByteString(s).ToLowerAscii() ) + +#define START_DRV 'a' + +inline BOOL DRIVE_EXISTS(char c) +{ + ByteString aDriveRoot( c ); + aDriveRoot += ":\\"; + return GetDriveType( aDriveRoot.GetBuffer() ) > 1; +} + +const char* TempDirImpl( char *pBuf ); + +#define FSysFailOnErrorImpl() + +#endif diff --git a/tools/source/generic/bigint.cxx b/tools/source/generic/bigint.cxx new file mode 100644 index 000000000000..85f46318efad --- /dev/null +++ b/tools/source/generic/bigint.cxx @@ -0,0 +1,1144 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: bigint.cxx,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <math.h> +#include <tools/tools.h> + +#include <tools/bigint.hxx> +#include <tools/string.hxx> +#include <tools/debug.hxx> + +#include <string.h> +#include <ctype.h> + +static const long MY_MAXLONG = 0x3fffffff; +static const long MY_MINLONG = -MY_MAXLONG; +static const long MY_MAXSHORT = 0x00007fff; +static const long MY_MINSHORT = -MY_MAXSHORT; + +/* Die ganzen Algorithmen zur Addition, Subtraktion, Multiplikation und + * Division von langen Zahlen stammen aus SEMINUMERICAL ALGORITHMS von + * DONALD E. KNUTH aus der Reihe The Art of Computer Programming. Zu finden + * sind diese Algorithmen im Kapitel 4.3.1. The Classical Algorithms. + */ + +// Muss auf UINT16/INT16/UINT32/INT32 umgestellt werden !!! W.P. + +// ----------------------------------------------------------------------- + +void BigInt::MakeBigInt( const BigInt& rVal ) +{ + if ( rVal.bIsBig ) + { + memcpy( (void*)this, (const void*)&rVal, sizeof( BigInt ) ); + while ( nLen > 1 && nNum[nLen-1] == 0 ) + nLen--; + } + else + { + long nTmp = rVal.nVal; + + nVal = rVal.nVal; + bIsBig = sal_True; + if ( nTmp < 0 ) + { + bIsNeg = sal_True; + nTmp = -nTmp; + } + else + bIsNeg = sal_False; + + nNum[0] = (sal_uInt16)(nTmp & 0xffffL); + nNum[1] = (sal_uInt16)(nTmp >> 16); +#ifndef _WIN16 + if ( nTmp & 0xffff0000L ) +#else + long l = 0xffff0000L; + if ( nTmp & l ) +#endif + nLen = 2; + else + nLen = 1; + } +} + +// ----------------------------------------------------------------------- + +void BigInt::Normalize() +{ + if ( bIsBig ) + { + while ( nLen > 1 && nNum[nLen-1] == 0 ) + nLen--; + + if ( nLen < 3 ) + { + if ( nLen < 2 ) + nVal = nNum[0]; + else if ( nNum[1] & 0x8000 ) + return; + else + nVal = ((long)nNum[1] << 16) + nNum[0]; + + bIsBig = sal_False; + + if ( bIsNeg ) + nVal = -nVal; + } + // else ist nVal undefiniert !!! W.P. + } + // wozu, nLen ist doch undefiniert ??? W.P. + else if ( nVal & 0xFFFF0000L ) + nLen = 2; + else + nLen = 1; +} + +// ----------------------------------------------------------------------- + +void BigInt::Mult( const BigInt &rVal, sal_uInt16 nMul ) +{ + sal_uInt16 nK = 0; + for ( int i = 0; i < rVal.nLen; i++ ) + { + sal_uInt32 nTmp = (sal_uInt32)rVal.nNum[i] * (sal_uInt32)nMul + nK; + nK = (sal_uInt16)(nTmp >> 16); + nNum[i] = (sal_uInt16)nTmp; + } + + if ( nK ) + { + nNum[rVal.nLen] = nK; + nLen = rVal.nLen + 1; + } + else + nLen = rVal.nLen; + + bIsBig = sal_True; + bIsNeg = rVal.bIsNeg; +} + +// ----------------------------------------------------------------------- + +void BigInt::Div( sal_uInt16 nDiv, sal_uInt16& rRem ) +{ + sal_uInt32 nK = 0; + for ( int i = nLen - 1; i >= 0; i-- ) + { + sal_uInt32 nTmp = (sal_uInt32)nNum[i] + (nK << 16); + nNum[i] = (sal_uInt16)(nTmp / nDiv); + nK = nTmp % nDiv; + } + rRem = (sal_uInt16)nK; + + if ( nNum[nLen-1] == 0 ) + nLen -= 1; +} + +// ----------------------------------------------------------------------- + +sal_Bool BigInt::IsLess( const BigInt& rVal ) const +{ + if ( rVal.nLen < nLen) + return sal_True; + if ( rVal.nLen > nLen ) + return sal_False; + + int i; + for ( i = nLen - 1; i > 0 && nNum[i] == rVal.nNum[i]; i-- ) + { + } + return rVal.nNum[i] < nNum[i]; +} + +// ----------------------------------------------------------------------- + +void BigInt::AddLong( BigInt& rB, BigInt& rErg ) +{ + if ( bIsNeg == rB.bIsNeg ) + { + int i; + char len; + + // wenn die Zahlen unterschiedlich lang sind, sollte zunaechst bei + // der kleineren Zahl die fehlenden Ziffern mit 0 initialisert werden + if (nLen >= rB.nLen) + { + len = nLen; + for (i = rB.nLen; i < len; i++) + rB.nNum[i] = 0; + } + else + { + len = rB.nLen; + for (i = nLen; i < len; i++) + nNum[i] = 0; + } + + // Die Ziffern werden von hinten nach vorne addiert + long k; + long nZ = 0; + for (i = 0, k = 0; i < len; i++) { + nZ = (long)nNum[i] + (long)rB.nNum[i] + k; + if (nZ & 0xff0000L) + k = 1; + else + k = 0; + rErg.nNum[i] = (sal_uInt16)(nZ & 0xffffL); + } + // Trat nach der letzten Addition ein Ueberlauf auf, muss dieser + // noch ins Ergebis uebernommen werden + if (nZ & 0xff0000L) // oder if(k) + { + rErg.nNum[i] = 1; + len++; + } + // Die Laenge und das Vorzeichen setzen + rErg.nLen = len; + rErg.bIsNeg = bIsNeg && rB.bIsNeg; + rErg.bIsBig = sal_True; + } + // Wenn nur einer der beiden Operanten negativ ist, wird aus der + // Addition eine Subtaktion + else if (bIsNeg) + { + bIsNeg = sal_False; + rB.SubLong(*this, rErg); + bIsNeg = sal_True; + } + else + { + rB.bIsNeg = sal_False; + SubLong(rB, rErg); + rB.bIsNeg = sal_True; + } +} + +// ----------------------------------------------------------------------- + +void BigInt::SubLong( BigInt& rB, BigInt& rErg ) +{ + if ( bIsNeg == rB.bIsNeg ) + { + int i; + char len; + long nZ, k; + + // wenn die Zahlen unterschiedlich lang sind, sollte zunaechst bei + // der kleineren Zahl die fehlenden Ziffern mit 0 initialisert werden + if (nLen >= rB.nLen) + { + len = nLen; + for (i = rB.nLen; i < len; i++) + rB.nNum[i] = 0; + } + else + { + len = rB.nLen; + for (i = nLen; i < len; i++) + nNum[i] = 0; + } + + if ( IsLess(rB) ) + { + for (i = 0, k = 0; i < len; i++) + { + nZ = (long)nNum[i] - (long)rB.nNum[i] + k; + if (nZ < 0) + k = -1; + else + k = 0; + rErg.nNum[i] = (sal_uInt16)(nZ & 0xffffL); + } + rErg.bIsNeg = bIsNeg; + } + else + { + for (i = 0, k = 0; i < len; i++) + { + nZ = (long)rB.nNum[i] - (long)nNum[i] + k; + if (nZ < 0) + k = -1; + else + k = 0; + rErg.nNum[i] = (sal_uInt16)(nZ & 0xffffL); + } + // wenn a < b, dann Vorzeichen vom Ergebnis umdrehen + rErg.bIsNeg = !bIsNeg; + } + rErg.nLen = len; + rErg.bIsBig = sal_True; + } + // Wenn nur einer der beiden Operanten negativ ist, wird aus der + // Subtaktion eine Addition + else if (bIsNeg) + { + bIsNeg = sal_False; + AddLong(rB, rErg); + bIsNeg = sal_True; + rErg.bIsNeg = sal_True; + } + else + { + rB.bIsNeg = sal_False; + AddLong(rB, rErg); + rB.bIsNeg = sal_True; + rErg.bIsNeg = sal_False; + } +} + +// ----------------------------------------------------------------------- + +void BigInt::MultLong( const BigInt& rB, BigInt& rErg ) const +{ + int i, j; + sal_uInt32 nZ, k; + + rErg.bIsNeg = bIsNeg != rB.bIsNeg; + rErg.bIsBig = sal_True; + rErg.nLen = nLen + rB.nLen; + + for (i = 0; i < rErg.nLen; i++) + rErg.nNum[i] = 0; + + for (j = 0; j < rB.nLen; j++) + { + for (i = 0, k = 0; i < nLen; i++) + { + nZ = (sal_uInt32)nNum[i] * (sal_uInt32)rB.nNum[j] + + (sal_uInt32)rErg.nNum[i + j] + k; + rErg.nNum[i + j] = (sal_uInt16)(nZ & 0xffffUL); + k = nZ >> 16; + } + rErg.nNum[i + j] = (sal_uInt16)k; + } +} + +// ----------------------------------------------------------------------- + +void BigInt::DivLong( const BigInt& rB, BigInt& rErg ) const +{ + int i, j; + long nTmp; + sal_uInt16 nK, nQ, nMult; + short nLenB = rB.nLen; + short nLenB1 = rB.nLen - 1; + BigInt aTmpA, aTmpB; + + nMult = (sal_uInt16)(0x10000L / ((long)rB.nNum[nLenB1] + 1)); + + aTmpA.Mult( *this, nMult ); + if ( aTmpA.nLen == nLen ) + { + aTmpA.nNum[aTmpA.nLen] = 0; + aTmpA.nLen++; + } + + aTmpB.Mult( rB, nMult ); + + for (j = aTmpA.nLen - 1; j >= nLenB; j--) + { // Raten des Divisors + nTmp = ( (long)aTmpA.nNum[j] << 16 ) + aTmpA.nNum[j - 1]; + if (aTmpA.nNum[j] == aTmpB.nNum[nLenB1]) + nQ = 0xFFFF; + else + nQ = (sal_uInt16)(((sal_uInt32)nTmp) / aTmpB.nNum[nLenB1]); + + if ( ((sal_uInt32)aTmpB.nNum[nLenB1 - 1] * nQ) > + ((((sal_uInt32)nTmp) - aTmpB.nNum[nLenB1] * nQ) << 16) + aTmpA.nNum[j - 2]) + nQ--; + // Und hier faengt das Teilen an + nK = 0; + nTmp = 0; + for (i = 0; i < nLenB; i++) + { + nTmp = (long)aTmpA.nNum[j - nLenB + i] + - ((long)aTmpB.nNum[i] * nQ) + - nK; + aTmpA.nNum[j - nLenB + i] = (sal_uInt16)nTmp; + nK = (sal_uInt16) (nTmp >> 16); + if ( nK ) + nK = (sal_uInt16)(0x10000UL - nK); + } + unsigned short& rNum( aTmpA.nNum[j - nLenB + i] ); + rNum = rNum - nK; // MSVC yields a warning on -= here, so don't use it + if (aTmpA.nNum[j - nLenB + i] == 0) + rErg.nNum[j - nLenB] = nQ; + else + { + rErg.nNum[j - nLenB] = nQ - 1; + nK = 0; + for (i = 0; i < nLenB; i++) + { + nTmp = aTmpA.nNum[j - nLenB + i] + aTmpB.nNum[i] + nK; + aTmpA.nNum[j - nLenB + i] = (sal_uInt16)(nTmp & 0xFFFFL); + if (nTmp & 0xFFFF0000L) + nK = 1; + else + nK = 0; + } + } + } + + rErg.bIsNeg = bIsNeg != rB.bIsNeg; + rErg.bIsBig = sal_True; + rErg.nLen = nLen - rB.nLen + 1; +} + +// ----------------------------------------------------------------------- + +void BigInt::ModLong( const BigInt& rB, BigInt& rErg ) const +{ + short i, j; + long nTmp; + sal_uInt16 nK, nQ, nMult; + short nLenB = rB.nLen; + short nLenB1 = rB.nLen - 1; + BigInt aTmpA, aTmpB; + + nMult = (sal_uInt16)(0x10000L / ((long)rB.nNum[nLenB1] + 1)); + + aTmpA.Mult( *this, nMult); + if ( aTmpA.nLen == nLen ) + { + aTmpA.nNum[aTmpA.nLen] = 0; + aTmpA.nLen++; + } + + aTmpB.Mult( rB, nMult); + + for (j = aTmpA.nLen - 1; j >= nLenB; j--) + { // Raten des Divisors + nTmp = ( (long)aTmpA.nNum[j] << 16 ) + aTmpA.nNum[j - 1]; + if (aTmpA.nNum[j] == aTmpB.nNum[nLenB1]) + nQ = 0xFFFF; + else + nQ = (sal_uInt16)(((sal_uInt32)nTmp) / aTmpB.nNum[nLenB1]); + + if ( ((sal_uInt32)aTmpB.nNum[nLenB1 - 1] * nQ) > + ((((sal_uInt32)nTmp) - aTmpB.nNum[nLenB1] * nQ) << 16) + aTmpA.nNum[j - 2]) + nQ--; + // Und hier faengt das Teilen an + nK = 0; + nTmp = 0; + for (i = 0; i < nLenB; i++) + { + nTmp = (long)aTmpA.nNum[j - nLenB + i] + - ((long)aTmpB.nNum[i] * nQ) + - nK; + aTmpA.nNum[j - nLenB + i] = (sal_uInt16)nTmp; + nK = (sal_uInt16) (nTmp >> 16); + if ( nK ) + nK = (sal_uInt16)(0x10000UL - nK); + } + unsigned short& rNum( aTmpA.nNum[j - nLenB + i] ); + rNum = rNum - nK; + if (aTmpA.nNum[j - nLenB + i] == 0) + rErg.nNum[j - nLenB] = nQ; + else + { + rErg.nNum[j - nLenB] = nQ - 1; + nK = 0; + for (i = 0; i < nLenB; i++) { + nTmp = aTmpA.nNum[j - nLenB + i] + aTmpB.nNum[i] + nK; + aTmpA.nNum[j - nLenB + i] = (sal_uInt16)(nTmp & 0xFFFFL); + if (nTmp & 0xFFFF0000L) + nK = 1; + else + nK = 0; + } + } + } + + rErg = aTmpA; + rErg.Div( nMult, nQ ); +} + +// ----------------------------------------------------------------------- + +sal_Bool BigInt::ABS_IsLess( const BigInt& rB ) const +{ + if (bIsBig || rB.bIsBig) + { + BigInt nA, nB; + nA.MakeBigInt( *this ); + nB.MakeBigInt( rB ); + if (nA.nLen == nB.nLen) + { + int i; + for (i = nA.nLen - 1; i > 0 && nA.nNum[i] == nB.nNum[i]; i--) + { + } + return nA.nNum[i] < nB.nNum[i]; + } + else + return nA.nLen < nB.nLen; + } + if ( nVal < 0 ) + if ( rB.nVal < 0 ) + return nVal > rB.nVal; + else + return nVal > -rB.nVal; + else + if ( rB.nVal < 0 ) + return nVal < -rB.nVal; + else + return nVal < rB.nVal; +} + +// ----------------------------------------------------------------------- + +BigInt::BigInt( const BigInt& rBigInt ) +{ + if ( rBigInt.bIsBig ) + memcpy( (void*)this, (const void*)&rBigInt, sizeof( BigInt ) ); + else + { + bIsSet = rBigInt.bIsSet; + bIsBig = sal_False; + nVal = rBigInt.nVal; + } +} + +// ----------------------------------------------------------------------- + +BigInt::BigInt( const ByteString& rString ) +{ + bIsSet = sal_True; + bIsNeg = sal_False; + bIsBig = sal_False; + nVal = 0; + + sal_Bool bNeg = sal_False; + const sal_Char* p = rString.GetBuffer(); + if ( *p == '-' ) + { + bNeg = sal_True; + p++; + } + while( *p >= '0' && *p <= '9' ) + { + *this *= 10; + *this += *p - '0'; + p++; + } + if ( bIsBig ) + bIsNeg = bNeg; + else if( bNeg ) + nVal = -nVal; +} + +// ----------------------------------------------------------------------- + +BigInt::BigInt( const UniString& rString ) +{ + bIsSet = sal_True; + bIsNeg = sal_False; + bIsBig = sal_False; + nVal = 0; + + sal_Bool bNeg = sal_False; + const sal_Unicode* p = rString.GetBuffer(); + if ( *p == '-' ) + { + bNeg = sal_True; + p++; + } + while( *p >= '0' && *p <= '9' ) + { + *this *= 10; + *this += *p - '0'; + p++; + } + if ( bIsBig ) + bIsNeg = bNeg; + else if( bNeg ) + nVal = -nVal; +} + +// ----------------------------------------------------------------------- + +BigInt::BigInt( double nValue ) +{ + bIsSet = sal_True; + + if ( nValue < 0 ) + { + nValue *= -1; + bIsNeg = sal_True; + } + else + { + bIsNeg = sal_False; + } + + if ( nValue < 1 ) + { + bIsBig = sal_False; + nVal = 0; + } + else + { + bIsBig = sal_True; + + int i=0; + + while ( ( nValue > 65536.0 ) && ( i < MAX_DIGITS ) ) + { + nNum[i] = (sal_uInt16) fmod( nValue, 65536.0 ); + nValue -= nNum[i]; + nValue /= 65536.0; + i++; + } + if ( i < MAX_DIGITS ) + nNum[i++] = (sal_uInt16) nValue; + + nLen = i; + + if ( i < 3 ) + Normalize(); + } +} + +// ----------------------------------------------------------------------- + +BigInt::BigInt( sal_uInt32 nValue ) +{ + bIsSet = sal_True; + if ( nValue & 0x80000000UL ) + { + bIsBig = sal_True; + bIsNeg = sal_False; + nNum[0] = (sal_uInt16)(nValue & 0xffffUL); + nNum[1] = (sal_uInt16)(nValue >> 16); + nLen = 2; + } + else + { + bIsBig = sal_False; + nVal = nValue; + } +} + +// ----------------------------------------------------------------------- + +BigInt::operator ULONG() const +{ + if ( !bIsBig ) + return (sal_uInt32)nVal; + else if ( nLen == 2 ) + { + sal_uInt32 nRet; + nRet = ((sal_uInt32)nNum[1]) << 16; + nRet += nNum[0]; + return nRet; + } + return 0; +} + +// ----------------------------------------------------------------------- + +BigInt::operator double() const +{ + if ( !bIsBig ) + return (double) nVal; + else + { + int i = nLen-1; + double nRet = (double) ((sal_uInt32)nNum[i]); + + while ( i ) + { + nRet *= 65536.0; + i--; + nRet += (double) ((sal_uInt32)nNum[i]); + } + + if ( bIsNeg ) + nRet *= -1; + + return nRet; + } +} + +// ----------------------------------------------------------------------- + +ByteString BigInt::GetByteString() const +{ + ByteString aString; + + if ( !bIsBig ) + aString = ByteString::CreateFromInt32( nVal ); + else + { + BigInt aTmp( *this ); + BigInt a1000000000( 1000000000L ); + aTmp.Abs(); + + do + { + BigInt a = aTmp; + a %= a1000000000; + aTmp /= a1000000000; + + ByteString aStr = aString; + if ( a.nVal < 100000000L ) + { // leading 0s + aString = ByteString::CreateFromInt32( a.nVal + 1000000000L ); + aString.Erase( 0, 1 ); + } + else + aString = ByteString::CreateFromInt32( a.nVal ); + aString += aStr; + } + while( aTmp.bIsBig ); + + ByteString aStr = aString; + if ( bIsNeg ) + aString = ByteString::CreateFromInt32( -aTmp.nVal ); + else + aString = ByteString::CreateFromInt32( aTmp.nVal ); + aString += aStr; + } + + return aString; +} + +// ----------------------------------------------------------------------- + +UniString BigInt::GetString() const +{ + UniString aString; + + if ( !bIsBig ) + aString = UniString::CreateFromInt32( nVal ); + else + { + BigInt aTmp( *this ); + BigInt a1000000000( 1000000000L ); + aTmp.Abs(); + + do + { + BigInt a = aTmp; + a %= a1000000000; + aTmp /= a1000000000; + + UniString aStr = aString; + if ( a.nVal < 100000000L ) + { // leading 0s + aString = UniString::CreateFromInt32( a.nVal + 1000000000L ); + aString.Erase(0,1); + } + else + aString = UniString::CreateFromInt32( a.nVal ); + aString += aStr; + } + while( aTmp.bIsBig ); + + UniString aStr = aString; + if ( bIsNeg ) + aString = UniString::CreateFromInt32( -aTmp.nVal ); + else + aString = UniString::CreateFromInt32( aTmp.nVal ); + aString += aStr; + } + + return aString; +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator=( const BigInt& rBigInt ) +{ + if ( rBigInt.bIsBig ) + memcpy( (void*)this, (const void*)&rBigInt, sizeof( BigInt ) ); + else + { + bIsSet = rBigInt.bIsSet; + bIsBig = sal_False; + nVal = rBigInt.nVal; + } + return *this; +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator+=( const BigInt& rVal ) +{ + if ( !bIsBig && !rVal.bIsBig ) + { + if( nVal <= MY_MAXLONG && rVal.nVal <= MY_MAXLONG + && nVal >= MY_MINLONG && rVal.nVal >= MY_MINLONG ) + { // wir bewegen uns im ungefaehrlichem Bereich + nVal += rVal.nVal; + return *this; + } + + if( (nVal < 0) != (rVal.nVal < 0) ) + { // wir bewegen uns im ungefaehrlichem Bereich + nVal += rVal.nVal; + return *this; + } + } + + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( *this ); + aTmp2.MakeBigInt( rVal ); + aTmp1.AddLong( aTmp2, *this ); + Normalize(); + return *this; +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator-=( const BigInt& rVal ) +{ + if ( !bIsBig && !rVal.bIsBig ) + { + if ( nVal <= MY_MAXLONG && rVal.nVal <= MY_MAXLONG && + nVal >= MY_MINLONG && rVal.nVal >= MY_MINLONG ) + { // wir bewegen uns im ungefaehrlichem Bereich + nVal -= rVal.nVal; + return *this; + } + + if ( (nVal < 0) == (rVal.nVal < 0) ) + { // wir bewegen uns im ungefaehrlichem Bereich + nVal -= rVal.nVal; + return *this; + } + } + + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( *this ); + aTmp2.MakeBigInt( rVal ); + aTmp1.SubLong( aTmp2, *this ); + Normalize(); + return *this; +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator*=( const BigInt& rVal ) +{ + if ( !bIsBig && !rVal.bIsBig + && nVal <= MY_MAXSHORT && rVal.nVal <= MY_MAXSHORT + && nVal >= MY_MINSHORT && rVal.nVal >= MY_MINSHORT ) + // nicht optimal !!! W.P. + { // wir bewegen uns im ungefaehrlichem Bereich + nVal *= rVal.nVal; + } + else + { + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( rVal ); + aTmp2.MakeBigInt( *this ); + aTmp1.MultLong(aTmp2, *this); + Normalize(); + } + return *this; +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator/=( const BigInt& rVal ) +{ + if ( !rVal.bIsBig ) + { + if ( rVal.nVal == 0 ) + { + DBG_ERROR( "BigInt::operator/ --> divide by zero" ); + return *this; + } + + if ( !bIsBig ) + { + // wir bewegen uns im ungefaehrlichem Bereich + nVal /= rVal.nVal; + return *this; + } + + if ( rVal.nVal == 1 ) + return *this; + + if ( rVal.nVal == -1 ) + { + bIsNeg = !bIsNeg; + return *this; + } + + if ( rVal.nVal <= (long)0xFFFF && rVal.nVal >= -(long)0xFFFF ) + { + // ein BigInt durch ein sal_uInt16 teilen + sal_uInt16 nTmp; + if ( rVal.nVal < 0 ) + { + nTmp = (sal_uInt16) -rVal.nVal; + bIsNeg = !bIsNeg; + } + else + nTmp = (sal_uInt16) rVal.nVal; + + Div( nTmp, nTmp ); + Normalize(); + return *this; + } + } + + if ( ABS_IsLess( rVal ) ) + { + *this = BigInt( (long)0 ); + return *this; + } + + // BigInt durch BigInt teilen + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( *this ); + aTmp2.MakeBigInt( rVal ); + aTmp1.DivLong(aTmp2, *this); + Normalize(); + return *this; +} + +// ----------------------------------------------------------------------- + +void BigInt::DivMod( const BigInt& rVal, BigInt& rMod ) +{ + if ( !rVal.bIsBig ) + { + if ( rVal.nVal == 0 ) + { + DBG_ERROR( "BigInt::operator/ --> divide by zero" ); + return; + } + + if ( !bIsBig ) + { + // wir bewegen uns im ungefaehrlichem Bereich + rMod = BigInt( nVal % rVal.nVal ); + nVal /= rVal.nVal; + return; + } + + if ( rVal.nVal == 1 ) + { + rMod = BigInt( (long)0 ); + return; + } + + if ( rVal.nVal == -1 ) + { + rMod = BigInt( (long)0 ); + bIsNeg = !bIsNeg; + return; + } + + if ( rVal.nVal <= (long)0xFFFF && rVal.nVal >= -(long)0xFFFF ) + { + // ein BigInt durch ein sal_uInt16 teilen + sal_uInt16 nTmp; + if ( rVal.nVal < 0 ) + { + nTmp = (sal_uInt16) -rVal.nVal; + bIsNeg = !bIsNeg; + } + else + nTmp = (sal_uInt16) rVal.nVal; + + Div( nTmp, nTmp ); + rMod = BigInt( (long)nTmp ); + Normalize(); + return; + } + } + + if ( ABS_IsLess( rVal ) ) + { + rMod = *this; + *this = BigInt( (long)0 ); + return; + } + + // BigInt durch BigInt teilen + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( *this ); + aTmp2.MakeBigInt( rVal ); + aTmp1.DivLong(aTmp2, *this); + Normalize(); + aTmp1.ModLong(aTmp2, rMod); // nicht optimal + rMod.Normalize(); +} + +// ----------------------------------------------------------------------- + +BigInt& BigInt::operator%=( const BigInt& rVal ) +{ + if ( !rVal.bIsBig ) + { + if ( rVal.nVal == 0 ) + { + DBG_ERROR( "BigInt::operator/ --> divide by zero" ); + return *this; + } + + if ( !bIsBig ) + { + // wir bewegen uns im ungefaehrlichem Bereich + nVal %= rVal.nVal; + return *this; + } + + if ( rVal.nVal <= (long)0xFFFF && rVal.nVal >= -(long)0xFFFF ) + { + // ein BigInt durch ein short teilen + sal_uInt16 nTmp; + if ( rVal.nVal < 0 ) + { + nTmp = (sal_uInt16) -rVal.nVal; + bIsNeg = !bIsNeg; + } + else + nTmp = (sal_uInt16) rVal.nVal; + + Div( nTmp, nTmp ); + *this = BigInt( (long)nTmp ); + return *this; + } + } + + if ( ABS_IsLess( rVal ) ) + return *this; + + // BigInt durch BigInt teilen + BigInt aTmp1, aTmp2; + aTmp1.MakeBigInt( *this ); + aTmp2.MakeBigInt( rVal ); + aTmp1.ModLong(aTmp2, *this); + Normalize(); + return *this; +} + +// ----------------------------------------------------------------------- + +sal_Bool operator==( const BigInt& rVal1, const BigInt& rVal2 ) +{ + if ( rVal1.bIsBig || rVal2.bIsBig ) + { + BigInt nA, nB; + nA.MakeBigInt( rVal1 ); + nB.MakeBigInt( rVal2 ); + if ( nA.bIsNeg == nB.bIsNeg ) + { + if ( nA.nLen == nB.nLen ) + { + int i; + for ( i = nA.nLen - 1; i > 0 && nA.nNum[i] == nB.nNum[i]; i-- ) + { + } + + return nA.nNum[i] == nB.nNum[i]; + } + return sal_False; + } + return sal_False; + } + return rVal1.nVal == rVal2.nVal; +} + +// ----------------------------------------------------------------------- + +sal_Bool operator<( const BigInt& rVal1, const BigInt& rVal2 ) +{ + if ( rVal1.bIsBig || rVal2.bIsBig ) + { + BigInt nA, nB; + nA.MakeBigInt( rVal1 ); + nB.MakeBigInt( rVal2 ); + if ( nA.bIsNeg == nB.bIsNeg ) + { + if ( nA.nLen == nB.nLen ) + { + int i; + for ( i = nA.nLen - 1; i > 0 && nA.nNum[i] == nB.nNum[i]; i-- ) + { + } + + if ( nA.bIsNeg ) + return nA.nNum[i] > nB.nNum[i]; + else + return nA.nNum[i] < nB.nNum[i]; + } + if ( nA.bIsNeg ) + return nA.nLen > nB.nLen; + else + return nA.nLen < nB.nLen; + } + return !nB.bIsNeg; + } + return rVal1.nVal < rVal2.nVal; +} + +// ----------------------------------------------------------------------- + +sal_Bool operator >(const BigInt& rVal1, const BigInt& rVal2 ) +{ + if ( rVal1.bIsBig || rVal2.bIsBig ) + { + BigInt nA, nB; + nA.MakeBigInt( rVal1 ); + nB.MakeBigInt( rVal2 ); + if ( nA.bIsNeg == nB.bIsNeg ) + { + if ( nA.nLen == nB.nLen ) + { + int i; + for ( i = nA.nLen - 1; i > 0 && nA.nNum[i] == nB.nNum[i]; i-- ) + { + } + + if ( nA.bIsNeg ) + return nA.nNum[i] < nB.nNum[i]; + else + return nA.nNum[i] > nB.nNum[i]; + } + if ( nA.bIsNeg ) + return nA.nLen < nB.nLen; + else + return nA.nLen > nB.nLen; + } + return !nA.bIsNeg; + } + + return rVal1.nVal > rVal2.nVal; +} diff --git a/tools/source/generic/color.cxx b/tools/source/generic/color.cxx new file mode 100644 index 000000000000..9a0b37959916 --- /dev/null +++ b/tools/source/generic/color.cxx @@ -0,0 +1,513 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: color.cxx,v $ + * $Revision: 1.14 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <stdlib.h> +#include <vos/macros.hxx> +#include <tools/color.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <tools/rc.hxx> +#include <tools/rcid.h> +#include <tools/resid.hxx> +#ifndef _SV_RC_H +#include <tools/rc.h> +#endif + +// ----------- +// - Inlines - +// ----------- + +static inline long _FRound( double fVal ) +{ + return( fVal > 0.0 ? (long) ( fVal + 0.5 ) : -(long) ( -fVal + 0.5 ) ); +} + +// --------- +// - Color - +// --------- + +Color::Color( const ResId& rResId ) +{ + rResId.SetRT( RSC_COLOR ); + ResMgr* pResMgr = rResId.GetResMgr(); + if ( pResMgr && pResMgr->GetResource( rResId ) ) + { + // Header ueberspringen + pResMgr->Increment( sizeof( RSHEADER_TYPE ) ); + + // Daten laden + USHORT nRed = pResMgr->ReadShort(); + USHORT nGreen = pResMgr->ReadShort(); + USHORT nBlue = pResMgr->ReadShort(); + // one more historical ULONG + pResMgr->ReadLong(); + + // RGB-Farbe + mnColor = RGB_COLORDATA( nRed>>8, nGreen>>8, nBlue>>8 ); + } + else + { + mnColor = RGB_COLORDATA( 0, 0, 0 ); + } +} +UINT8 Color::GetColorError( const Color& rCompareColor ) const +{ + const long nErrAbs = labs( (long) rCompareColor.GetRed() - GetRed() ) + + labs( (long) rCompareColor.GetGreen() - GetGreen() ) + + labs( (long) rCompareColor.GetBlue() - GetBlue() ); + + return (UINT8) _FRound( nErrAbs * 0.3333333333 ); +} + +// ----------------------------------------------------------------------- + +void Color::IncreaseLuminance( UINT8 cLumInc ) +{ + SetRed( (UINT8) VOS_BOUND( (long) COLORDATA_RED( mnColor ) + cLumInc, 0L, 255L ) ); + SetGreen( (UINT8) VOS_BOUND( (long) COLORDATA_GREEN( mnColor ) + cLumInc, 0L, 255L ) ); + SetBlue( (UINT8) VOS_BOUND( (long) COLORDATA_BLUE( mnColor ) + cLumInc, 0L, 255L ) ); +} + +// ----------------------------------------------------------------------- + +void Color::DecreaseLuminance( UINT8 cLumDec ) +{ + SetRed( (UINT8) VOS_BOUND( (long) COLORDATA_RED( mnColor ) - cLumDec, 0L, 255L ) ); + SetGreen( (UINT8) VOS_BOUND( (long) COLORDATA_GREEN( mnColor ) - cLumDec, 0L, 255L ) ); + SetBlue( (UINT8) VOS_BOUND( (long) COLORDATA_BLUE( mnColor ) - cLumDec, 0L, 255L ) ); +} + +// ----------------------------------------------------------------------- + +void Color::IncreaseContrast( UINT8 cContInc ) +{ + if( cContInc) + { + const double fM = 128.0 / ( 128.0 - 0.4985 * cContInc ); + const double fOff = 128.0 - fM * 128.0; + + SetRed( (UINT8) VOS_BOUND( _FRound( COLORDATA_RED( mnColor ) * fM + fOff ), 0L, 255L ) ); + SetGreen( (UINT8) VOS_BOUND( _FRound( COLORDATA_GREEN( mnColor ) * fM + fOff ), 0L, 255L ) ); + SetBlue( (UINT8) VOS_BOUND( _FRound( COLORDATA_BLUE( mnColor ) * fM + fOff ), 0L, 255L ) ); + } +} + +// ----------------------------------------------------------------------- + +void Color::DecreaseContrast( UINT8 cContDec ) +{ + if( cContDec ) + { + const double fM = ( 128.0 - 0.4985 * cContDec ) / 128.0; + const double fOff = 128.0 - fM * 128.0; + + SetRed( (UINT8) VOS_BOUND( _FRound( COLORDATA_RED( mnColor ) * fM + fOff ), 0L, 255L ) ); + SetGreen( (UINT8) VOS_BOUND( _FRound( COLORDATA_GREEN( mnColor ) * fM + fOff ), 0L, 255L ) ); + SetBlue( (UINT8) VOS_BOUND( _FRound( COLORDATA_BLUE( mnColor ) * fM + fOff ), 0L, 255L ) ); + } +} + +// ----------------------------------------------------------------------- + +void Color::Invert() +{ + SetRed( ~COLORDATA_RED( mnColor ) ); + SetGreen( ~COLORDATA_GREEN( mnColor ) ); + SetBlue( ~COLORDATA_BLUE( mnColor ) ); +} + +// ----------------------------------------------------------------------- + +BOOL Color::IsDark() const +{ + return GetLuminance() <= 38; +} + +// ----------------------------------------------------------------------- + +BOOL Color::IsBright() const +{ + return GetLuminance() >= 245; +} + +// ----------------------------------------------------------------------- +// color space conversion +// ----------------------------------------------------------------------- + +void Color::RGBtoHSB( USHORT& nHue, USHORT& nSat, USHORT& nBri ) const +{ + UINT8 c[3]; + UINT8 cMax, cMin; + + c[0] = GetRed(); + c[1] = GetGreen(); + c[2] = GetBlue(); + + cMax = c[0]; + if( c[1] > cMax ) + cMax = c[1]; + if( c[2] > cMax ) + cMax = c[2]; + + // Brightness = max(R, G, B); + nBri = cMax * 100 / 255; + + cMin = c[0]; + if( c[1] < cMin ) + cMin = c[1]; + if( c[2] < cMin ) + cMin = c[2]; + + UINT8 cDelta = cMax - cMin; + + // Saturation = max - min / max + if( nBri > 0 ) + nSat = cDelta * 100 / cMax; + else + nSat = 0; + + if( nSat == 0 ) + nHue = 0; // Default = undefined + else + { + double dHue = 0.0; + + if( c[0] == cMax ) + { + dHue = (double)( c[1] - c[2] ) / (double)cDelta; + } + else if( c[1] == cMax ) + { + dHue = 2.0 + (double)( c[2] - c[0] ) / (double)cDelta; + } + else if ( c[2] == cMax ) + { + dHue = 4.0 + (double)( c[0] - c[1] ) / (double)cDelta; + } + dHue *= 60.0; + + if( dHue < 0.0 ) + dHue += 360.0; + + nHue = (UINT16) dHue; + } +} + +ColorData Color::HSBtoRGB( USHORT nHue, USHORT nSat, USHORT nBri ) +{ + UINT8 cR=0,cG=0,cB=0; + UINT8 nB = (UINT8) ( nBri * 255 / 100 ); + + if( nSat == 0 ) + { + cR = nB; + cG = nB; + cB = nB; + } + else + { + double dH = nHue; + double f; + UINT16 n; + if( dH == 360.0 ) + dH = 0.0; + + dH /= 60.0; + n = (UINT16) dH; + f = dH - n; + + UINT8 a = (UINT8) ( nB * ( 100 - nSat ) / 100 ); + UINT8 b = (UINT8) ( nB * ( 100 - ( (double)nSat * f + 0.5 ) ) / 100 ); + UINT8 c = (UINT8) ( nB * ( 100 - ( (double)nSat * ( 1.0 - f ) + 0.5 ) ) / 100 ); + + switch( n ) + { + case 0: cR = nB; cG = c; cB = a; break; + case 1: cR = b; cG = nB; cB = a; break; + case 2: cR = a; cG = nB; cB = c; break; + case 3: cR = a; cG = b; cB = nB; break; + case 4: cR = c; cG = a; cB = nB; break; + case 5: cR = nB; cG = a; cB = b; break; + } + } + + return RGB_COLORDATA( cR, cG, cB ); +} + +// ----------------------------------------------------------------------- + +SvStream& Color::Read( SvStream& rIStm, BOOL bNewFormat ) +{ + if ( bNewFormat ) + rIStm >> mnColor; + else + rIStm >> *this; + + return rIStm; +} + +// ----------------------------------------------------------------------- + +SvStream& Color::Write( SvStream& rOStm, BOOL bNewFormat ) +{ + if ( bNewFormat ) + rOStm << mnColor; + else + rOStm << *this; + + return rOStm; +} + +// ----------------------------------------------------------------------- + +#define COL_NAME_USER ((USHORT)0x8000) +#define COL_RED_1B ((USHORT)0x0001) +#define COL_RED_2B ((USHORT)0x0002) +#define COL_GREEN_1B ((USHORT)0x0010) +#define COL_GREEN_2B ((USHORT)0x0020) +#define COL_BLUE_1B ((USHORT)0x0100) +#define COL_BLUE_2B ((USHORT)0x0200) + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStream, Color& rColor ) +{ + DBG_ASSERTWARNING( rIStream.GetVersion(), "Color::>> - Solar-Version not set on rIStream" ); + + USHORT nColorName; + USHORT nRed; + USHORT nGreen; + USHORT nBlue; + + rIStream >> nColorName; + + if ( nColorName & COL_NAME_USER ) + { + if ( rIStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cAry[6]; + USHORT i = 0; + + nRed = 0; + nGreen = 0; + nBlue = 0; + + if ( nColorName & COL_RED_2B ) + i += 2; + else if ( nColorName & COL_RED_1B ) + i++; + if ( nColorName & COL_GREEN_2B ) + i += 2; + else if ( nColorName & COL_GREEN_1B ) + i++; + if ( nColorName & COL_BLUE_2B ) + i += 2; + else if ( nColorName & COL_BLUE_1B ) + i++; + + rIStream.Read( cAry, i ); + i = 0; + + if ( nColorName & COL_RED_2B ) + { + nRed = cAry[i]; + nRed <<= 8; + i++; + nRed |= cAry[i]; + i++; + } + else if ( nColorName & COL_RED_1B ) + { + nRed = cAry[i]; + nRed <<= 8; + i++; + } + if ( nColorName & COL_GREEN_2B ) + { + nGreen = cAry[i]; + nGreen <<= 8; + i++; + nGreen |= cAry[i]; + i++; + } + else if ( nColorName & COL_GREEN_1B ) + { + nGreen = cAry[i]; + nGreen <<= 8; + i++; + } + if ( nColorName & COL_BLUE_2B ) + { + nBlue = cAry[i]; + nBlue <<= 8; + i++; + nBlue |= cAry[i]; + i++; + } + else if ( nColorName & COL_BLUE_1B ) + { + nBlue = cAry[i]; + nBlue <<= 8; + i++; + } + } + else + { + rIStream >> nRed; + rIStream >> nGreen; + rIStream >> nBlue; + } + + rColor.mnColor = RGB_COLORDATA( nRed>>8, nGreen>>8, nBlue>>8 ); + } + else + { + static ColorData aColAry[] = + { + 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 ( nColorName < (sizeof( aColAry )/sizeof(ColorData)) ) + rColor.mnColor = aColAry[nColorName]; + else + rColor.mnColor = COL_BLACK; + } + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const Color& rColor ) +{ + DBG_ASSERTWARNING( rOStream.GetVersion(), "Color::<< - Solar-Version not set on rOStream" ); + + USHORT nColorName = COL_NAME_USER; + USHORT nRed = rColor.GetRed(); + USHORT nGreen = rColor.GetGreen(); + USHORT nBlue = rColor.GetBlue(); + nRed = (nRed<<8) + nRed; + nGreen = (nGreen<<8) + nGreen; + nBlue = (nBlue<<8) + nBlue; + + if ( rOStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cAry[6]; + USHORT i = 0; + + if ( nRed & 0x00FF ) + { + nColorName |= COL_RED_2B; + cAry[i] = (unsigned char)(nRed & 0xFF); + i++; + cAry[i] = (unsigned char)((nRed >> 8) & 0xFF); + i++; + } + else if ( nRed & 0xFF00 ) + { + nColorName |= COL_RED_1B; + cAry[i] = (unsigned char)((nRed >> 8) & 0xFF); + i++; + } + if ( nGreen & 0x00FF ) + { + nColorName |= COL_GREEN_2B; + cAry[i] = (unsigned char)(nGreen & 0xFF); + i++; + cAry[i] = (unsigned char)((nGreen >> 8) & 0xFF); + i++; + } + else if ( nGreen & 0xFF00 ) + { + nColorName |= COL_GREEN_1B; + cAry[i] = (unsigned char)((nGreen >> 8) & 0xFF); + i++; + } + if ( nBlue & 0x00FF ) + { + nColorName |= COL_BLUE_2B; + cAry[i] = (unsigned char)(nBlue & 0xFF); + i++; + cAry[i] = (unsigned char)((nBlue >> 8) & 0xFF); + i++; + } + else if ( nBlue & 0xFF00 ) + { + nColorName |= COL_BLUE_1B; + cAry[i] = (unsigned char)((nBlue >> 8) & 0xFF); + i++; + } + + rOStream << nColorName; + rOStream.Write( cAry, i ); + } + else + { + rOStream << nColorName; + rOStream << nRed; + rOStream << nGreen; + rOStream << nBlue; + } + + return rOStream; +} diff --git a/tools/source/generic/config.cxx b/tools/source/generic/config.cxx new file mode 100644 index 000000000000..c978f844746f --- /dev/null +++ b/tools/source/generic/config.cxx @@ -0,0 +1,1307 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: config.cxx,v $ + * $Revision: 1.18 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _CONFIG_CXX + +#include <cstddef> +#include <cstdlib> +#include <limits> +#include <new> +#include <string.h> + +#ifdef WNT +#include "stdlib.h" +#endif +#include <osl/file.hxx> +#include <tools/stream.hxx> +#include <tools/debug.hxx> +#include <tools/config.hxx> +#include <osl/security.h> + +#define MAXBUFLEN 1024 // Fuer Buffer bei VOS-Funktionen + +// ----------------- +// - ImplConfigData - +// ----------------- + +struct ImplKeyData +{ + ImplKeyData* mpNext; + ByteString maKey; + ByteString maValue; + BOOL mbIsComment; +}; + +struct ImplGroupData +{ + ImplGroupData* mpNext; + ImplKeyData* mpFirstKey; + ByteString maGroupName; + USHORT mnEmptyLines; +}; + +struct ImplConfigData +{ + ImplGroupData* mpFirstGroup; + XubString maFileName; + ULONG mnDataUpdateId; + ULONG mnTimeStamp; + LineEnd meLineEnd; + USHORT mnRefCount; + BOOL mbModified; + BOOL mbRead; + BOOL mbIsUTF8BOM; +}; + +// ======================================================================= + +static ByteString& getEmptyByteString() +{ + static ByteString aEmpty; + return aEmpty; +} + +// ======================================================================= + +static String toUncPath( const String& rPath ) +{ + ::rtl::OUString aFileURL; + + // check if rFileName is already a URL; if not make it so + if( rPath.CompareToAscii( "file://", 7 ) == COMPARE_EQUAL ) + aFileURL = rPath; + else if( ::osl::FileBase::getFileURLFromSystemPath( rPath, aFileURL ) != ::osl::FileBase::E_None ) + aFileURL = rPath; + + return aFileURL; +} + +static ULONG ImplSysGetConfigTimeStamp( const XubString& rFileName ) +{ + ULONG nTimeStamp = 0; + ::osl::DirectoryItem aItem; + ::osl::FileStatus aStatus( osl_FileStatus_Mask_ModifyTime ); + + int nError = 0; + if( ( nError = ::osl::DirectoryItem::get( rFileName, aItem ) ) == ::osl::FileBase::E_None && + aItem.getFileStatus( aStatus ) == ::osl::FileBase::E_None ) + { + nTimeStamp = aStatus.getModifyTime().Seconds; + } + + return nTimeStamp; +} + +// ----------------------------------------------------------------------- + +static BYTE* ImplSysReadConfig( const XubString& rFileName, + sal_uInt64& rRead, BOOL& rbRead, BOOL& rbIsUTF8BOM, ULONG& rTimeStamp ) +{ + BYTE* pBuf = NULL; + ::osl::File aFile( rFileName ); + + if( aFile.open( osl_File_OpenFlag_Read ) == ::osl::FileBase::E_None ) + { + sal_uInt64 nPos = 0, nRead = 0; + if( aFile.getSize( nPos ) == ::osl::FileBase::E_None ) + { + if (nPos > std::numeric_limits< std::size_t >::max()) { + aFile.close(); + return 0; + } + pBuf = new BYTE[static_cast< std::size_t >(nPos)]; + if( aFile.read( pBuf, nPos, nRead ) == ::osl::FileBase::E_None && nRead == nPos ) + { + //skip the byte-order-mark 0xEF 0xBB 0xBF, if it was UTF8 files + unsigned char BOM[3] = {0xEF, 0xBB, 0xBF}; + if (nRead > 2 && memcmp(pBuf, BOM, 3) == 0) + { + nRead -= 3; + rtl_moveMemory(pBuf, pBuf + 3, sal::static_int_cast<sal_Size>(nRead * sizeof(BYTE)) ); + rbIsUTF8BOM = TRUE; + } + + rTimeStamp = ImplSysGetConfigTimeStamp( rFileName ); + rbRead = TRUE; + rRead = nRead; + } + else + { + delete[] pBuf; + pBuf = NULL; + } + } + aFile.close(); + } + + return pBuf; +} + +// ----------------------------------------------------------------------- + +static BOOL ImplSysWriteConfig( const XubString& rFileName, + const BYTE* pBuf, ULONG nBufLen, BOOL rbIsUTF8BOM, ULONG& rTimeStamp ) +{ + BOOL bSuccess = FALSE; + BOOL bUTF8BOMSuccess = FALSE; + + ::osl::File aFile( rFileName ); + ::osl::FileBase::RC eError = aFile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ); + if( eError != ::osl::FileBase::E_None ) + eError = aFile.open( osl_File_OpenFlag_Write ); + if( eError == ::osl::FileBase::E_None ) + { + // truncate + aFile.setSize( 0 ); + sal_uInt64 nWritten; + + //write the the byte-order-mark 0xEF 0xBB 0xBF first , if it was UTF8 files + if ( rbIsUTF8BOM ) + { + unsigned char BOM[3] = {0xEF, 0xBB, 0xBF}; + sal_uInt64 nUTF8BOMWritten; + if( aFile.write( BOM, 3, nUTF8BOMWritten ) == ::osl::FileBase::E_None && 3 == nUTF8BOMWritten ) + { + bUTF8BOMSuccess = TRUE; + } + } + + if( aFile.write( pBuf, nBufLen, nWritten ) == ::osl::FileBase::E_None && nWritten == nBufLen ) + { + bSuccess = TRUE; + } + if ( rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess ) + { + rTimeStamp = ImplSysGetConfigTimeStamp( rFileName ); + } + } + + return rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess; +} + +// ----------------------------------------------------------------------- + +static String ImplMakeConfigName( const XubString* pFileName, + const XubString* pPathName ) +{ + ::rtl::OUString aFileName; + ::rtl::OUString aPathName; + if ( pFileName ) + { +#ifdef UNX + aFileName = ::rtl::OUString::createFromAscii( "." ); + aFileName += *pFileName; + aFileName += ::rtl::OUString::createFromAscii( "rc" ); +#else + aFileName = *pFileName; + aFileName += ::rtl::OUString::createFromAscii( ".ini" ); +#endif + } + else + { +#ifdef UNX + aFileName = ::rtl::OUString::createFromAscii( ".sversionrc" ); +#else + aFileName = ::rtl::OUString::createFromAscii( "sversion.ini" ); +#endif + } + + // #88208# in case pPathName is set but empty and pFileName is set + // and not empty just return the filename; on the default case + // prepend default path as usual + if ( pPathName && pPathName->Len() ) + aPathName = toUncPath( *pPathName ); + else if( pPathName && pFileName && pFileName->Len() ) + return aFileName; + else + { + oslSecurity aSec = osl_getCurrentSecurity(); + osl_getConfigDir( aSec, &aPathName.pData ); + osl_freeSecurityHandle( aSec ); + } + + ::rtl::OUString aName( aPathName ); + aName += ::rtl::OUString::createFromAscii( "/" ); + aName += aFileName; + + return aName; +} + +// ----------------------------------------------------------------------- + +namespace { + +ByteString makeByteString(BYTE const * p, sal_uInt64 n) { + if (n > STRING_MAXLEN) { + #ifdef WNT + abort(); + #else + ::std::abort(); //TODO: handle this gracefully + #endif + } + return ByteString( + reinterpret_cast< char const * >(p), + sal::static_int_cast< xub_StrLen >(n)); +} + +} + +static void ImplMakeConfigList( ImplConfigData* pData, + const BYTE* pBuf, sal_uInt64 nLen ) +{ + // kein Buffer, keine Daten + if ( !nLen ) + return; + + // Buffer parsen und Liste zusammenbauen + sal_uInt64 nStart; + sal_uInt64 nLineLen; + xub_StrLen nNameLen; + xub_StrLen nKeyLen; + sal_uInt64 i; + const BYTE* pLine; + ImplKeyData* pPrevKey = NULL; + ImplKeyData* pKey; + ImplGroupData* pPrevGroup = NULL; + ImplGroupData* pGroup = NULL; + i = 0; + while ( i < nLen ) + { + // Ctrl+Z + if ( pBuf[i] == 0x1A ) + break; + + // Spaces und Tabs entfernen + while ( (pBuf[i] == ' ') || (pBuf[i] == '\t') ) + i++; + + // Zeilenanfang merken + nStart = i; + pLine = pBuf+i; + + // Zeilenende suchen + while ( (i < nLen) && pBuf[i] && (pBuf[i] != '\r') && (pBuf[i] != '\n') && + (pBuf[i] != 0x1A) ) + i++; + + nLineLen = i-nStart; + + // Wenn Zeilenende (CR/LF), dann noch einen weiterschalten + if ( (i+1 < nLen) && + (pBuf[i] != pBuf[i+1]) && + ((pBuf[i+1] == '\r') || (pBuf[i+1] == '\n')) ) + i++; + i++; + + // Zeile auswerten + if ( *pLine == '[' ) + { + pGroup = new ImplGroupData; + pGroup->mpNext = NULL; + pGroup->mpFirstKey = NULL; + pGroup->mnEmptyLines = 0; + if ( pPrevGroup ) + pPrevGroup->mpNext = pGroup; + else + pData->mpFirstGroup = pGroup; + pPrevGroup = pGroup; + pPrevKey = NULL; + pKey = NULL; + + // Gruppennamen rausfiltern + pLine++; + nLineLen--; + // Spaces und Tabs entfernen + while ( (*pLine == ' ') || (*pLine == '\t') ) + { + nLineLen--; + pLine++; + } + nNameLen = 0; + while ( (nNameLen < nLineLen) && (pLine[nNameLen] != ']') ) + nNameLen++; + if ( nNameLen ) + { + while ( (pLine[nNameLen-1] == ' ') || (pLine[nNameLen-1] == '\t') ) + nNameLen--; + } + pGroup->maGroupName = ByteString( (const sal_Char*)pLine, nNameLen ); + } + else + { + if ( nLineLen ) + { + // Wenn noch keine Gruppe existiert, dann alle Keys in die + // Default-Gruppe + if ( !pGroup ) + { + pGroup = new ImplGroupData; + pGroup->mpNext = NULL; + pGroup->mpFirstKey = NULL; + pGroup->mnEmptyLines = 0; + if ( pPrevGroup ) + pPrevGroup->mpNext = pGroup; + else + pData->mpFirstGroup = pGroup; + pPrevGroup = pGroup; + pPrevKey = NULL; + } + + // Falls Leerzeile vorhanden, dann anhaengen + if ( pPrevKey ) + { + while ( pGroup->mnEmptyLines ) + { + pKey = new ImplKeyData; + pKey->mbIsComment = TRUE; + pPrevKey->mpNext = pKey; + pPrevKey = pKey; + pGroup->mnEmptyLines--; + } + } + + // Neuen Key erzeugen + pKey = new ImplKeyData; + pKey->mpNext = NULL; + if ( pPrevKey ) + pPrevKey->mpNext = pKey; + else + pGroup->mpFirstKey = pKey; + pPrevKey = pKey; + if ( pLine[0] == ';' ) + { + pKey->maValue = makeByteString(pLine, nLineLen); + pKey->mbIsComment = TRUE; + } + else + { + pKey->mbIsComment = FALSE; + nNameLen = 0; + while ( (nNameLen < nLineLen) && (pLine[nNameLen] != '=') ) + nNameLen++; + nKeyLen = nNameLen; + // Spaces und Tabs entfernen + if ( nNameLen ) + { + while ( (pLine[nNameLen-1] == ' ') || (pLine[nNameLen-1] == '\t') ) + nNameLen--; + } + pKey->maKey = ByteString( (const sal_Char*)pLine, nNameLen ); + nKeyLen++; + if ( nKeyLen < nLineLen ) + { + pLine += nKeyLen; + nLineLen -= nKeyLen; + // Spaces und Tabs entfernen + while ( (*pLine == ' ') || (*pLine == '\t') ) + { + nLineLen--; + pLine++; + } + if ( nLineLen ) + { + while ( (pLine[nLineLen-1] == ' ') || (pLine[nLineLen-1] == '\t') ) + nLineLen--; + pKey->maValue = makeByteString(pLine, nLineLen); + } + } + } + } + else + { + // Leerzeilen werden nur gezaehlt und beim Erzeugen des + // naechsten Keys angehaengt, da wir Leerzeilen am Ende + // einer Gruppe auch nach hinzufuegen von neuen Keys nur + // am Ende der Gruppe wieder speichern wollen + if ( pGroup ) + pGroup->mnEmptyLines++; + } + } + } +} + +// ----------------------------------------------------------------------- + +static BYTE* ImplGetConfigBuffer( const ImplConfigData* pData, ULONG& rLen ) +{ + BYTE* pWriteBuf; + BYTE* pBuf; + BYTE aLineEndBuf[2] = {0, 0}; + ImplKeyData* pKey; + ImplGroupData* pGroup; + unsigned int nBufLen; + USHORT nValueLen; + USHORT nKeyLen; + USHORT nLineEndLen; + + if ( pData->meLineEnd == LINEEND_CR ) + { + aLineEndBuf[0] = _CR; + nLineEndLen = 1; + } + else if ( pData->meLineEnd == LINEEND_LF ) + { + aLineEndBuf[0] = _LF; + nLineEndLen = 1; + } + else + { + aLineEndBuf[0] = _CR; + aLineEndBuf[1] = _LF; + nLineEndLen = 2; + } + + // Buffergroesse ermitteln + nBufLen = 0; + pGroup = pData->mpFirstGroup; + while ( pGroup ) + { + // Leere Gruppen werden nicht geschrieben + if ( pGroup->mpFirstKey ) + { + nBufLen += pGroup->maGroupName.Len() + nLineEndLen + 2; + pKey = pGroup->mpFirstKey; + while ( pKey ) + { + nValueLen = pKey->maValue.Len(); + if ( pKey->mbIsComment ) + nBufLen += nValueLen + nLineEndLen; + else + nBufLen += pKey->maKey.Len() + nValueLen + nLineEndLen + 1; + + pKey = pKey->mpNext; + } + + // Leerzeile nach jeder Gruppe auch wieder speichern + if ( !pGroup->mnEmptyLines ) + pGroup->mnEmptyLines = 1; + nBufLen += nLineEndLen * pGroup->mnEmptyLines; + } + + pGroup = pGroup->mpNext; + } + + // Laenge dem Aufrufer mitteilen + rLen = nBufLen; + if ( !nBufLen ) + { + pWriteBuf = new BYTE[nLineEndLen]; + if ( pWriteBuf ) + { + pWriteBuf[0] = aLineEndBuf[0]; + if ( nLineEndLen == 2 ) + pWriteBuf[1] = aLineEndBuf[1]; + return pWriteBuf; + } + else + return 0; + } + + // Schreibbuffer anlegen (wird vom Aufrufer zerstoert) + pWriteBuf = new BYTE[nBufLen]; + if ( !pWriteBuf ) + return 0; + + // Buffer fuellen + pBuf = pWriteBuf; + pGroup = pData->mpFirstGroup; + while ( pGroup ) + { + // Leere Gruppen werden nicht geschrieben + if ( pGroup->mpFirstKey ) + { + *pBuf = '['; pBuf++; + memcpy( pBuf, pGroup->maGroupName.GetBuffer(), pGroup->maGroupName.Len() ); + pBuf += pGroup->maGroupName.Len(); + *pBuf = ']'; pBuf++; + *pBuf = aLineEndBuf[0]; pBuf++; + if ( nLineEndLen == 2 ) + { + *pBuf = aLineEndBuf[1]; pBuf++; + } + pKey = pGroup->mpFirstKey; + while ( pKey ) + { + nValueLen = pKey->maValue.Len(); + if ( pKey->mbIsComment ) + { + if ( nValueLen ) + { + memcpy( pBuf, pKey->maValue.GetBuffer(), nValueLen ); + pBuf += nValueLen; + } + *pBuf = aLineEndBuf[0]; pBuf++; + if ( nLineEndLen == 2 ) + { + *pBuf = aLineEndBuf[1]; pBuf++; + } + } + else + { + nKeyLen = pKey->maKey.Len(); + memcpy( pBuf, pKey->maKey.GetBuffer(), nKeyLen ); + pBuf += nKeyLen; + *pBuf = '='; pBuf++; + memcpy( pBuf, pKey->maValue.GetBuffer(), nValueLen ); + pBuf += nValueLen; + *pBuf = aLineEndBuf[0]; pBuf++; + if ( nLineEndLen == 2 ) + { + *pBuf = aLineEndBuf[1]; pBuf++; + } + } + + pKey = pKey->mpNext; + } + + // Leerzeile nach jeder Gruppe auch wieder speichern + USHORT nEmptyLines = pGroup->mnEmptyLines; + while ( nEmptyLines ) + { + *pBuf = aLineEndBuf[0]; pBuf++; + if ( nLineEndLen == 2 ) + { + *pBuf = aLineEndBuf[1]; pBuf++; + } + nEmptyLines--; + } + } + + pGroup = pGroup->mpNext; + } + + return pWriteBuf; +} + +// ----------------------------------------------------------------------- + +static void ImplReadConfig( ImplConfigData* pData ) +{ + ULONG nTimeStamp = 0; + sal_uInt64 nRead = 0; + BOOL bRead = FALSE; + BOOL bIsUTF8BOM =FALSE; + BYTE* pBuf = ImplSysReadConfig( pData->maFileName, nRead, bRead, bIsUTF8BOM, nTimeStamp ); + + // Aus dem Buffer die Config-Verwaltungsliste aufbauen + if ( pBuf ) + { + ImplMakeConfigList( pData, pBuf, nRead ); + delete[] pBuf; + } + pData->mnTimeStamp = nTimeStamp; + pData->mbModified = FALSE; + if ( bRead ) + pData->mbRead = TRUE; + if ( bIsUTF8BOM ) + pData->mbIsUTF8BOM = TRUE; +} + +// ----------------------------------------------------------------------- + +static void ImplWriteConfig( ImplConfigData* pData ) +{ +#ifdef DBG_UTIL + if ( DbgIsAssert() ) + { + if ( pData->mnTimeStamp != ImplSysGetConfigTimeStamp( pData->maFileName ) ) + { + DBG_ERROR1( "Config overwrites modified configfile:\n %s", ByteString( pData->maFileName, RTL_TEXTENCODING_UTF8 ).GetBuffer() ); + } + } +#endif + + // Aus der Config-Liste einen Buffer zusammenbauen + ULONG nBufLen; + BYTE* pBuf = ImplGetConfigBuffer( pData, nBufLen ); + if ( pBuf ) + { + if ( ImplSysWriteConfig( pData->maFileName, pBuf, nBufLen, pData->mbIsUTF8BOM, pData->mnTimeStamp ) ) + pData->mbModified = FALSE; + delete[] pBuf; + } + else + pData->mbModified = FALSE; +} + +// ----------------------------------------------------------------------- + +static void ImplDeleteConfigData( ImplConfigData* pData ) +{ + ImplKeyData* pTempKey; + ImplKeyData* pKey; + ImplGroupData* pTempGroup; + ImplGroupData* pGroup = pData->mpFirstGroup; + while ( pGroup ) + { + pTempGroup = pGroup->mpNext; + + // Alle Keys loeschen + pKey = pGroup->mpFirstKey; + while ( pKey ) + { + pTempKey = pKey->mpNext; + delete pKey; + pKey = pTempKey; + } + + // Gruppe loeschen und weiterschalten + delete pGroup; + pGroup = pTempGroup; + } + + pData->mpFirstGroup = NULL; +} + +// ======================================================================= + +static ImplConfigData* ImplGetConfigData( const XubString& rFileName ) +{ + ImplConfigData* pData; + + pData = new ImplConfigData; + pData->maFileName = rFileName; + pData->mpFirstGroup = NULL; + pData->mnDataUpdateId = 0; + pData->meLineEnd = LINEEND_CRLF; + pData->mnRefCount = 0; + pData->mbRead = FALSE; + pData->mbIsUTF8BOM = FALSE; + ImplReadConfig( pData ); + + return pData; +} + +// ----------------------------------------------------------------------- + +static void ImplFreeConfigData( ImplConfigData* pDelData ) +{ + ImplDeleteConfigData( pDelData ); + delete pDelData; +} + +// ======================================================================= + +BOOL Config::ImplUpdateConfig() const +{ + // Wenn sich TimeStamp unterscheidet, dann Datei neu einlesen + if ( mpData->mnTimeStamp != ImplSysGetConfigTimeStamp( maFileName ) ) + { + ImplDeleteConfigData( mpData ); + ImplReadConfig( mpData ); + mpData->mnDataUpdateId++; + return TRUE; + } + else + return FALSE; +} + +// ----------------------------------------------------------------------- + +ImplGroupData* Config::ImplGetGroup() const +{ + if ( !mpActGroup || (mnDataUpdateId != mpData->mnDataUpdateId) ) + { + ImplGroupData* pPrevGroup = NULL; + ImplGroupData* pGroup = mpData->mpFirstGroup; + while ( pGroup ) + { + if ( pGroup->maGroupName.EqualsIgnoreCaseAscii( maGroupName ) ) + break; + + pPrevGroup = pGroup; + pGroup = pGroup->mpNext; + } + + // Falls Gruppe noch nicht existiert, dann dazufuegen + if ( !pGroup ) + { + pGroup = new ImplGroupData; + pGroup->mpNext = NULL; + pGroup->mpFirstKey = NULL; + pGroup->mnEmptyLines = 1; + if ( pPrevGroup ) + pPrevGroup->mpNext = pGroup; + else + mpData->mpFirstGroup = pGroup; + } + + // Gruppenname immer uebernehmen, da er auch in dieser Form + // geschrieben werden soll. Ausserdem die Cache-Members der + // Config-Klasse updaten + pGroup->maGroupName = maGroupName; + ((Config*)this)->mnDataUpdateId = mpData->mnDataUpdateId; + ((Config*)this)->mpActGroup = pGroup; + } + + return mpActGroup; +} + +// ======================================================================= + +Config::Config() +{ + // Daten initialisieren und einlesen + maFileName = ImplMakeConfigName( NULL, NULL ); + mpData = ImplGetConfigData( maFileName ); + mpActGroup = NULL; + mnDataUpdateId = 0; + mnLockCount = 1; + mbPersistence = TRUE; + +#ifdef DBG_UTIL + DBG_TRACE( "Config::Config()" ); +#endif +} + +// ----------------------------------------------------------------------- + +Config::Config( const XubString& rFileName ) +{ + // Daten initialisieren und einlesen + maFileName = toUncPath( rFileName ); + mpData = ImplGetConfigData( maFileName ); + mpActGroup = NULL; + mnDataUpdateId = 0; + mnLockCount = 1; + mbPersistence = TRUE; + +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::Config( " ); + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + aTraceStr += " )"; + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif +} + +// ----------------------------------------------------------------------- + +Config::~Config() +{ +#ifdef DBG_UTIL + DBG_TRACE( "Config::~Config()" ); +#endif + + Flush(); + ImplFreeConfigData( mpData ); +} + +// ----------------------------------------------------------------------- + +String Config::GetDefDirectory() +{ + ::rtl::OUString aDefConfig; + oslSecurity aSec = osl_getCurrentSecurity(); + osl_getConfigDir( aSec, &aDefConfig.pData ); + osl_freeSecurityHandle( aSec ); + + return aDefConfig; +} + +// ----------------------------------------------------------------------- + +XubString Config::GetConfigName( const XubString& rPath, + const XubString& rBaseName ) +{ + return ImplMakeConfigName( &rBaseName, &rPath ); +} + +// ----------------------------------------------------------------------- + +void Config::SetGroup( const ByteString& rGroup ) +{ + // Wenn neue Gruppe gesetzt wird, muss beim naechsten mal die + // Gruppe neu ermittelt werden + if ( maGroupName != rGroup ) + { + maGroupName = rGroup; + mnDataUpdateId = mpData->mnDataUpdateId-1; + } +} + +// ----------------------------------------------------------------------- + +void Config::DeleteGroup( const ByteString& rGroup ) +{ + // Config-Daten evt. updaten + if ( !mnLockCount || !mpData->mbRead ) + { + ImplUpdateConfig(); + mpData->mbRead = TRUE; + } + + ImplGroupData* pPrevGroup = NULL; + ImplGroupData* pGroup = mpData->mpFirstGroup; + while ( pGroup ) + { + if ( pGroup->maGroupName.EqualsIgnoreCaseAscii( rGroup ) ) + break; + + pPrevGroup = pGroup; + pGroup = pGroup->mpNext; + } + + if ( pGroup ) + { + // Alle Keys loeschen + ImplKeyData* pTempKey; + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + pTempKey = pKey->mpNext; + delete pKey; + pKey = pTempKey; + } + + // Gruppe weiterschalten und loeschen + if ( pPrevGroup ) + pPrevGroup->mpNext = pGroup->mpNext; + else + mpData->mpFirstGroup = pGroup->mpNext; + delete pGroup; + + // Config-Datei neu schreiben + if ( !mnLockCount && mbPersistence ) + ImplWriteConfig( mpData ); + else + { + mpData->mbModified = TRUE; + } + + // Gruppen auf ungluetig setzen + mnDataUpdateId = mpData->mnDataUpdateId; + mpData->mnDataUpdateId++; + } +} + +// ----------------------------------------------------------------------- + +ByteString Config::GetGroupName( USHORT nGroup ) const +{ + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + ImplGroupData* pGroup = mpData->mpFirstGroup; + USHORT nGroupCount = 0; + ByteString aGroupName; + while ( pGroup ) + { + if ( nGroup == nGroupCount ) + { + aGroupName = pGroup->maGroupName; + break; + } + + nGroupCount++; + pGroup = pGroup->mpNext; + } + + return aGroupName; +} + +// ----------------------------------------------------------------------- + +USHORT Config::GetGroupCount() const +{ + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + ImplGroupData* pGroup = mpData->mpFirstGroup; + USHORT nGroupCount = 0; + while ( pGroup ) + { + nGroupCount++; + pGroup = pGroup->mpNext; + } + + return nGroupCount; +} + +// ----------------------------------------------------------------------- + +BOOL Config::HasGroup( const ByteString& rGroup ) const +{ + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + ImplGroupData* pGroup = mpData->mpFirstGroup; + BOOL bRet = FALSE; + + while( pGroup ) + { + if( pGroup->maGroupName.EqualsIgnoreCaseAscii( rGroup ) ) + { + bRet = TRUE; + break; + } + + pGroup = pGroup->mpNext; + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +ByteString Config::ReadKey( const ByteString& rKey ) const +{ + return ReadKey( rKey, getEmptyByteString() ); +} + +// ----------------------------------------------------------------------- + +UniString Config::ReadKey( const ByteString& rKey, rtl_TextEncoding eEncoding ) const +{ + if ( mpData->mbIsUTF8BOM ) + eEncoding = RTL_TEXTENCODING_UTF8; + return UniString( ReadKey( rKey ), eEncoding ); +} + +// ----------------------------------------------------------------------- + +ByteString Config::ReadKey( const ByteString& rKey, const ByteString& rDefault ) const +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::ReadKey( " ); + aTraceStr += rKey; + aTraceStr += " ) from "; + aTraceStr += GetGroup(); + aTraceStr += " in "; + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + // Key suchen und Value zurueckgeben + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment && pKey->maKey.EqualsIgnoreCaseAscii( rKey ) ) + return pKey->maValue; + + pKey = pKey->mpNext; + } + } + + return rDefault; +} + +// ----------------------------------------------------------------------- + +void Config::WriteKey( const ByteString& rKey, const ByteString& rStr ) +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::WriteKey( " ); + aTraceStr += rKey; + aTraceStr += ", "; + aTraceStr += rStr; + aTraceStr += " ) to "; + aTraceStr += GetGroup(); + aTraceStr += " in "; + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + DBG_TRACE( aTraceStr.GetBuffer() ); + DBG_ASSERTWARNING( rStr != ReadKey( rKey ), "Config::WriteKey() with the same Value" ); +#endif + + // Config-Daten evt. updaten + if ( !mnLockCount || !mpData->mbRead ) + { + ImplUpdateConfig(); + mpData->mbRead = TRUE; + } + + // Key suchen und Value setzen + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pPrevKey = NULL; + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment && pKey->maKey.EqualsIgnoreCaseAscii( rKey ) ) + break; + + pPrevKey = pKey; + pKey = pKey->mpNext; + } + + BOOL bNewValue; + if ( !pKey ) + { + pKey = new ImplKeyData; + pKey->mpNext = NULL; + pKey->maKey = rKey; + pKey->mbIsComment = FALSE; + if ( pPrevKey ) + pPrevKey->mpNext = pKey; + else + pGroup->mpFirstKey = pKey; + bNewValue = TRUE; + } + else + bNewValue = pKey->maValue != rStr; + + if ( bNewValue ) + { + pKey->maValue = rStr; + + if ( !mnLockCount && mbPersistence ) + ImplWriteConfig( mpData ); + else + { + mpData->mbModified = TRUE; + } + } + } +} + +// ----------------------------------------------------------------------- + +void Config::WriteKey( const ByteString& rKey, const UniString& rValue, rtl_TextEncoding eEncoding ) +{ + if ( mpData->mbIsUTF8BOM ) + eEncoding = RTL_TEXTENCODING_UTF8; + WriteKey( rKey, ByteString( rValue, eEncoding ) ); +} + +// ----------------------------------------------------------------------- + +void Config::DeleteKey( const ByteString& rKey ) +{ + // Config-Daten evt. updaten + if ( !mnLockCount || !mpData->mbRead ) + { + ImplUpdateConfig(); + mpData->mbRead = TRUE; + } + + // Key suchen und Value setzen + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pPrevKey = NULL; + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment && pKey->maKey.EqualsIgnoreCaseAscii( rKey ) ) + break; + + pPrevKey = pKey; + pKey = pKey->mpNext; + } + + if ( pKey ) + { + // Gruppe weiterschalten und loeschen + if ( pPrevKey ) + pPrevKey->mpNext = pKey->mpNext; + else + pGroup->mpFirstKey = pKey->mpNext; + delete pKey; + + // Config-Datei neu schreiben + if ( !mnLockCount && mbPersistence ) + ImplWriteConfig( mpData ); + else + { + mpData->mbModified = TRUE; + } + } + } +} + +// ----------------------------------------------------------------------- + +USHORT Config::GetKeyCount() const +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::GetKeyCount()" ); + aTraceStr += " from "; + aTraceStr += GetGroup(); + aTraceStr += " in "; + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + // Key suchen und Value zurueckgeben + USHORT nCount = 0; + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment ) + nCount++; + + pKey = pKey->mpNext; + } + } + + return nCount; +} + +// ----------------------------------------------------------------------- + +ByteString Config::GetKeyName( USHORT nKey ) const +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::GetKeyName( " ); + aTraceStr += ByteString::CreateFromInt32(nKey); + aTraceStr += " ) from "; + aTraceStr += GetGroup(); + aTraceStr += " in "; + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + // Key suchen und Name zurueckgeben + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment ) + { + if ( !nKey ) + return pKey->maKey; + nKey--; + } + + pKey = pKey->mpNext; + } + } + + return getEmptyByteString(); +} + +// ----------------------------------------------------------------------- + +ByteString Config::ReadKey( USHORT nKey ) const +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "Config::ReadKey( " ); + aTraceStr += ByteString::CreateFromInt32( nKey ); + aTraceStr += " ) from "; + aTraceStr += GetGroup(); + aTraceStr += " in "; + aTraceStr += ByteString( maFileName, RTL_TEXTENCODING_UTF8 ); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + // Key suchen und Value zurueckgeben + ImplGroupData* pGroup = ImplGetGroup(); + if ( pGroup ) + { + ImplKeyData* pKey = pGroup->mpFirstKey; + while ( pKey ) + { + if ( !pKey->mbIsComment ) + { + if ( !nKey ) + return pKey->maValue; + nKey--; + } + + pKey = pKey->mpNext; + } + } + + return getEmptyByteString(); +} + +// ----------------------------------------------------------------------- + +void Config::EnterLock() +{ + // Config-Daten evt. updaten + if ( !mnLockCount ) + ImplUpdateConfig(); + + mnLockCount++; +} + +// ----------------------------------------------------------------------- + +void Config::LeaveLock() +{ + DBG_ASSERT( mnLockCount, "Config::LeaveLook() without Config::EnterLook()" ); + mnLockCount--; + + if ( (mnLockCount == 0) && mpData->mbModified && mbPersistence ) + ImplWriteConfig( mpData ); +} + +// ----------------------------------------------------------------------- + +BOOL Config::Update() +{ + return ImplUpdateConfig(); +} + +// ----------------------------------------------------------------------- + +void Config::Flush() +{ + if ( mpData->mbModified && mbPersistence ) + ImplWriteConfig( mpData ); +} + +// ----------------------------------------------------------------------- + +void Config::SetLineEnd( LineEnd eLineEnd ) +{ + mpData->meLineEnd = eLineEnd; +} + +// ----------------------------------------------------------------------- + +LineEnd Config::GetLineEnd() const +{ + return mpData->meLineEnd; +} + diff --git a/tools/source/generic/fract.cxx b/tools/source/generic/fract.cxx new file mode 100644 index 000000000000..353e6c6c5d78 --- /dev/null +++ b/tools/source/generic/fract.cxx @@ -0,0 +1,739 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: fract.cxx,v $ + * $Revision: 1.9 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#ifndef _LIMITS_H +#include <limits.h> +#endif +#include <tools/debug.hxx> +#include <tools/fract.hxx> +#include <tools/stream.hxx> + +#include <tools/bigint.hxx> + +/************************************************************************* +|* +|* GetGGT() +|* +|* Beschreibung Berechnet den groessten gemeinsamen Teiler von +|* nVal1 und nVal2 +|* Parameter long nVal1, long nVal2 +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Die Funktion GetGGT berechnet den groessten gemeinsamen Teiler der +// beiden als Parameter uebergebenen Werte nVal1 und nVal2 nach dem +// Algorithmus von Euklid. Hat einer der beiden Parameter den Wert 0 oder +// 1, so wird als Ergebnis der Wert 1 zurŸckgegeben. Da der Algorithmus +// nur mit positiven Zahlen arbeitet, werden die beiden Parameter +// entsprechend umgewandelt. +// Zum Algorithmus: die beiden Parameter werden solange ducheinander +// geteilt, bis sie beide gleich sind oder bis bei der Division +// kein Rest bleibt. Der kleinere der beiden Werte ist dann der +// GGT. + +static long GetGGT( long nVal1, long nVal2 ) +{ + nVal1 = Abs( nVal1 ); + nVal2 = Abs( nVal2 ); + + if ( nVal1 <= 1 || nVal2 <= 1 ) + return 1; + + while ( nVal1 != nVal2 ) + { + if ( nVal1 > nVal2 ) + { + nVal1 %= nVal2; + if ( nVal1 == 0 ) + return nVal2; + } + else + { + nVal2 %= nVal1; + if ( nVal2 == 0 ) + return nVal1; + } + } + + return nVal1; +} + +static void Reduce( BigInt &rVal1, BigInt &rVal2 ) +{ + BigInt nA( rVal1 ); + BigInt nB( rVal2 ); + nA.Abs(); + nB.Abs(); + + if ( nA.IsOne() || nB.IsOne() || nA.IsZero() || nB.IsZero() ) + return; + + while ( nA != nB ) + { + if ( nA > nB ) + { + nA %= nB; + if ( nA.IsZero() ) + { + rVal1 /= nB; + rVal2 /= nB; + return; + } + } + else + { + nB %= nA; + if ( nB.IsZero() ) + { + rVal1 /= nA; + rVal2 /= nA; + return; + } + } + } + + rVal1 /= nA; + rVal2 /= nB; +} + +/************************************************************************* +|* +|* Fraction::Fraction() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung WP 07.03.97 +|* Letzte Aenderung +|* +*************************************************************************/ + +Fraction::Fraction( long nN1, long nN2, long nD1, long nD2 ) +{ + long n; + int i = 1; + + if( nN1 < 0 ) { i = -i; nN1 = -nN1; } + if( nN2 < 0 ) { i = -i; nN2 = -nN2; } + if( nD1 < 0 ) { i = -i; nD1 = -nD1; } + if( nD2 < 0 ) { i = -i; nD2 = -nD2; } + + n = GetGGT( nN1, nD1 ); if( n > 1 ) { nN1 /= n; nD1 /= n; } + n = GetGGT( nN1, nD2 ); if( n > 1 ) { nN1 /= n; nD2 /= n; } + n = GetGGT( nN2, nD1 ); if( n > 1 ) { nN2 /= n; nD1 /= n; } + n = GetGGT( nN2, nD2 ); if( n > 1 ) { nN2 /= n; nD2 /= n; } + + BigInt nN( nN1 ); + nN *= BigInt( nN2 ); + + BigInt nD( nD1 ); + nD *= BigInt( nD2 ); + + while ( nN.bIsBig || nD.bIsBig ) + { + BigInt n1 = 1; + BigInt n2 = 2; + + nN += n1; + nN /= n2; + nD += n1; + nD /= n2; + + // Kuerzen ueber Groesste Gemeinsame Teiler + Reduce( nN, nD ); + } + + nNumerator = i * (long)nN; + nDenominator = (long)nD; +} + +/************************************************************************* +|* +|* Fraction::Fraction() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Zur Initialisierung eines Bruches wird nNum dem Zaehler und nDen dem +// Nenner zugewiesen. Da negative Werte des Nenners einen Bruch als +// ungueltig kennzeichnen, wird bei der Eingabe eines negativen Nenners +// sowohl das Vorzeichen des Nenners und des Zaehlers invertiert um wieder +// einen gueltigen Wert fuer den Bruch zu erhalten. + +Fraction::Fraction( long nNum, long nDen ) +{ + nNumerator = nNum; + nDenominator = nDen; + if ( nDenominator < 0 ) + { + nDenominator = -nDenominator; + nNumerator = -nNumerator; + } + + // Kuerzen ueber Groesste Gemeinsame Teiler + long n = GetGGT( nNumerator, nDenominator ); + nNumerator /= n; + nDenominator /= n; +} + +/************************************************************************* +|* +|* Fraction::Fraction() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Wenn der Wert von dVal groesser ist als LONG_MAX, dann wird der Bruch +// auf den Wert ungueltig gesetzt, ansonsten werden dVal und der Nenner +// solange mit 10 multipliziert, bis entweder der Zaehler oder der Nenner +// groesser als LONG_MAX / 10 ist. Zum Schluss wird der so entstandene Bruch +// gekuerzt. + +Fraction::Fraction( double dVal ) +{ + long nDen = 1; + long nMAX = LONG_MAX / 10; + + if ( dVal > LONG_MAX || dVal < LONG_MIN ) + { + nNumerator = 0; + nDenominator = -1; + return; + } + + while ( Abs( (long)dVal ) < nMAX && nDen < nMAX ) + { + dVal *= 10; + nDen *= 10; + } + nNumerator = (long)dVal; + nDenominator = nDen; + + // Kuerzen ueber Groesste Gemeinsame Teiler + long n = GetGGT( nNumerator, nDenominator ); + nNumerator /= n; + nDenominator /= n; +} + +/************************************************************************* +|* +|* Fraction::operator double() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 14.05.91 +|* +*************************************************************************/ + +Fraction::operator double() const +{ + if ( nDenominator > 0 ) + return (double)nNumerator / (double)nDenominator; + else + return (double)0; +} + +/************************************************************************* +|* +|* Fraction::operator+=() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Zunaechst werden die beiden Parameter auf ihre Gueltigkeit ueberprueft. +// Ist einer der Parameter ungueltig, dann ist auch des Ergebnis +// ungueltig. Zur Addition werden die beiden Brueche erst durch +// Erweiterung mit den Nenner des jeweils anderen Bruches auf einen +// gemeinsamen Nenner gebracht. Anschliessend werden die beiden Zaehler +// addiert und das Ergebnis gekuerzt (durch Division von Zaehler und +// Nenner mit nGGT). Innerhalb der Funktion wird mit dem Datentyp SLong +// gerechnet, um einen Moeglichen Ueberlauf erkennen zu koennen. Bei +// einem Ueberlauf wird das Ergebnis auf den Wert ungueltig gesetzt. + +Fraction& Fraction::operator += ( const Fraction& rVal ) +{ + if ( !rVal.IsValid() ) + { + nNumerator = 0; + nDenominator = -1; + } + if ( !IsValid() ) + return *this; + + // (a/b) + (c/d) = ( (a*d) + (c*b) ) / (b*d) + BigInt nN( nNumerator ); + nN *= BigInt( rVal.nDenominator ); + BigInt nW1Temp( nDenominator ); + nW1Temp *= BigInt( rVal.nNumerator ); + nN += nW1Temp; + + BigInt nD( nDenominator ); + nD *= BigInt( rVal.nDenominator ); + + Reduce( nN, nD ); + + if ( nN.bIsBig || nD.bIsBig ) + { + nNumerator = 0; + nDenominator = -1; + } + else + { + nNumerator = (long)nN, + nDenominator = (long)nD; + } + + return *this; +} + +/************************************************************************* +|* +|* Fraction::operator-=() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Zunaechst werden die beiden Parameter auf ihre Gueltigkeit ueberprueft. +// Ist einer der Parameter ungueltig, dann ist auch des Ergebnis +// ungueltig. Zur Subtraktion werden die beiden Brueche erst durch +// Erweiterung mit den Nenner des jeweils anderen Bruches auf einen +// gemeinsamen Nenner gebracht. Anschliessend werden die beiden Zaehler +// subtrahiert und das Ergebnis gekuerzt (durch Division von Zaehler und +// Nenner mit nGGT). Innerhalb der Funktion wird mit dem Datentyp BigInt +// gerechnet, um einen Moeglichen Ueberlauf erkennen zu koennen. Bei +// einem Ueberlauf wird das Ergebnis auf den Wert ungueltig gesetzt. + +Fraction& Fraction::operator -= ( const Fraction& rVal ) +{ + if ( !rVal.IsValid() ) + { + nNumerator = 0; + nDenominator = -1; + } + if ( !IsValid() ) + return *this; + + // (a/b) - (c/d) = ( (a*d) - (c*b) ) / (b*d) + BigInt nN( nNumerator ); + nN *= BigInt( rVal.nDenominator ); + BigInt nW1Temp( nDenominator ); + nW1Temp *= BigInt( rVal.nNumerator ); + nN -= nW1Temp; + + BigInt nD( nDenominator ); + nD *= BigInt( rVal.nDenominator ); + + Reduce( nN, nD ); + + if ( nN.bIsBig || nD.bIsBig ) + { + nNumerator = 0; + nDenominator = -1; + } + else + { + nNumerator = (long)nN, + nDenominator = (long)nD; + } + + return *this; +} + +/************************************************************************* +|* +|* Fraction::operator*=() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung TH 19.08.92 +|* +*************************************************************************/ + +// Zunaechst werden die beiden Parameter auf ihre Gueltigkeit ueberprueft. +// Ist einer der Parameter ungueltig, dann ist auch des Ergebnis +// ungueltig. Zur Multiplikation werden jeweils die beiden Zaehler und +// Nenner miteinander multipliziert. Um Ueberlaufe zu vermeiden, werden +// vorher jeweils der GGT zwischen dem Zaehler des einen und dem Nenner +// des anderen Bruches bestimmt und bei der Multiplikation Zaehler und +// Nenner durch die entsprechenden Werte geteilt. +// Innerhalb der Funktion wird mit dem Datentyp BigInt gerechnet, um +// einen Moeglichen Ueberlauf erkennen zu koennen. Bei einem Ueberlauf +// wird das Ergebnis auf den Wert ungueltig gesetzt. + +Fraction& Fraction::operator *= ( const Fraction& rVal ) +{ + if ( !rVal.IsValid() ) + { + nNumerator = 0; + nDenominator = -1; + } + if ( !IsValid() ) + return *this; + + long nGGT1 = GetGGT( nNumerator, rVal.nDenominator ); + long nGGT2 = GetGGT( rVal.nNumerator, nDenominator ); + BigInt nN( nNumerator / nGGT1 ); + nN *= BigInt( rVal.nNumerator / nGGT2 ); + BigInt nD( nDenominator / nGGT2 ); + nD *= BigInt( rVal.nDenominator / nGGT1 ); + + if ( nN.bIsBig || nD.bIsBig ) + { + nNumerator = 0; + nDenominator = -1; + } + else + { + nNumerator = (long)nN, + nDenominator = (long)nD; + } + + return *this; +} + +/************************************************************************* +|* +|* Fraction::operator/=() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Zunaechst werden die beiden Parameter auf ihre Gueltigkeit ueberprueft. +// Ist einer der Parameter ungueltig, dann ist auch des Ergebnis +// ungueltig. +// Um den Bruch a durch b zu teilen, wird a mit dem Kehrwert von b +// multipliziert. Analog zu Multiplikation wird jezt jeweils der Zaehler +// des einen Bruches mit dem Nenner des anderen multipliziert. +// Um Ueberlaufe zu vermeiden, werden vorher jeweils der GGT zwischen den +// beiden Zaehlern und den beiden Nennern bestimmt und bei der +// Multiplikation Zaehler und Nenner durch die entsprechenden Werte +// geteilt. +// Innerhalb der Funktion wird mit dem Datentyp BigInt gerechnet, um +// einen Moeglichen Ueberlauf erkennen zu koennen. Bei einem Ueberlauf +// wird das Ergebnis auf den Wert ungueltig gesetzt. + +Fraction& Fraction::operator /= ( const Fraction& rVal ) +{ + if ( !rVal.IsValid() ) + { + nNumerator = 0; + nDenominator = -1; + } + if ( !IsValid() ) + return *this; + + long nGGT1 = GetGGT( nNumerator, rVal.nNumerator ); + long nGGT2 = GetGGT( rVal.nDenominator, nDenominator ); + BigInt nN( nNumerator / nGGT1 ); + nN *= BigInt( rVal.nDenominator / nGGT2 ); + BigInt nD( nDenominator / nGGT2 ); + nD *= BigInt( rVal.nNumerator / nGGT1 ); + + if ( nN.bIsBig || nD.bIsBig ) + { + nNumerator = 0; + nDenominator = -1; + } + else + { + nNumerator = (long)nN, + nDenominator = (long)nD; + if ( nDenominator < 0 ) + { + nDenominator = -nDenominator; + nNumerator = -nNumerator; + } + } + + return *this; +} + +/************************************************************************* +|* +|* Fraction::ReduceInaccurate() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung JOE 17.09.95 +|* Letzte Aenderung kendy 2007-06-13 +|* +*************************************************************************/ + + +// Similar to clz_table that can be googled +const char nbits_table[32] = +{ + 32, 1, 23, 2, 29, 24, 14, 3, + 30, 27, 25, 18, 20, 15, 10, 4, + 31, 22, 28, 13, 26, 17, 19, 9, + 21, 12, 16, 8, 11, 7, 6, 5 +}; + +static int impl_NumberOfBits( unsigned long nNum ) +{ + // http://en.wikipedia.org/wiki/De_Bruijn_sequence + // + // background paper: Using de Bruijn Sequences to Index a 1 in a + // Computer Word (1998) Charles E. Leiserson, + // Harald Prokop, Keith H. Randall + // (e.g. http://citeseer.ist.psu.edu/leiserson98using.html) + const sal_uInt32 nDeBruijn = 0x7DCD629; + + if ( nNum == 0 ) + return 0; + + // Get it to form like 0000001111111111b + nNum |= ( nNum >> 1 ); + nNum |= ( nNum >> 2 ); + nNum |= ( nNum >> 4 ); + nNum |= ( nNum >> 8 ); + nNum |= ( nNum >> 16 ); + + sal_uInt32 nNumber; + int nBonus = 0; + +#if SAL_TYPES_SIZEOFLONG == 4 + nNumber = nNum; +#elif SAL_TYPES_SIZEOFLONG == 8 + nNum |= ( nNum >> 32 ); + + if ( nNum & 0x80000000 ) + { + nNumber = sal_uInt32( nNum >> 32 ); + nBonus = 32; + + if ( nNumber == 0 ) + return 32; + } + else + nNumber = sal_uInt32( nNum & 0xFFFFFFFF ); +#else +#error "Unknown size of long!" +#endif + + // De facto shift left of nDeBruijn using multiplication (nNumber + // is all ones from topmost bit, thus nDeBruijn + (nDeBruijn * + // nNumber) => nDeBruijn * (nNumber+1) clears all those bits to + // zero, sets the next bit to one, and thus effectively shift-left + // nDeBruijn by lg2(nNumber+1). This generates a distinct 5bit + // sequence in the msb for each distinct position of the last + // leading 0 bit - that's the property of a de Bruijn number. + nNumber = nDeBruijn + ( nDeBruijn * nNumber ); + + // 5-bit window indexes the result + return ( nbits_table[nNumber >> 27] ) + nBonus; +} + +/** Inaccurate cancellation for a fraction. + + Clip both nominator and denominator to said number of bits. If + either of those already have equal or less number of bits used, + this method does nothing. + + @param nSignificantBits denotes, how many significant binary + digits to maintain, in both nominator and denominator. + + @example ReduceInaccurate(8) has an error <1% [1/2^(8-1)] - the + largest error occurs with the following pair of values: + + binary 1000000011111111111111111111111b/1000000000000000000000000000000b + = 1082130431/1073741824 + = approx. 1.007812499 + + A ReduceInaccurate(8) yields 1/1. +*/ +void Fraction::ReduceInaccurate( unsigned nSignificantBits ) +{ + if ( !nNumerator || !nDenominator ) + return; + + // Count with unsigned longs only + const bool bNeg = ( nNumerator < 0 ); + unsigned long nMul = (unsigned long)( bNeg? -nNumerator: nNumerator ); + unsigned long nDiv = (unsigned long)( nDenominator ); + + DBG_ASSERT(nSignificantBits<65, "More than 64 bit of significance is overkill!"); + + // How much bits can we lose? + const int nMulBitsToLose = Max( ( impl_NumberOfBits( nMul ) - int( nSignificantBits ) ), 0 ); + const int nDivBitsToLose = Max( ( impl_NumberOfBits( nDiv ) - int( nSignificantBits ) ), 0 ); + + const int nToLose = Min( nMulBitsToLose, nDivBitsToLose ); + + // Remove the bits + nMul >>= nToLose; + nDiv >>= nToLose; + + if ( !nMul || !nDiv ) + { + // Return without reduction + DBG_ERROR( "Oops, we reduced too much..." ); + return; + } + + // Reduce + long n1 = GetGGT( nMul, nDiv ); + if ( n1 != 1 ) + { + nMul /= n1; + nDiv /= n1; + } + + nNumerator = bNeg? -long( nMul ): long( nMul ); + nDenominator = nDiv; +} + +/************************************************************************* +|* +|* Fraction::operator ==() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung TH 19.08.92 +|* +*************************************************************************/ + +BOOL operator == ( const Fraction& rVal1, const Fraction& rVal2 ) +{ + if ( !rVal1.IsValid() || !rVal2.IsValid() ) + return FALSE; + + return rVal1.nNumerator == rVal2.nNumerator + && rVal1.nDenominator == rVal2.nDenominator; +} + +/************************************************************************* +|* +|* Fraction::operator <() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung DV 21.12.92 +|* +*************************************************************************/ + +// Beide Operanden werden zunaechst auf ihre Gueltigkeit ueberprueft und +// anschliessend zur Sicherheit noch einmal gekuerzt. Um die Brueche +// (a/b) und (c/d) zu vergleichen, werden sie zunaechst auf einen +// gemeinsamen Nenner gebracht (b*d), um dann die beiden Zaehler (a*d) +// und (c*b) zu vergleichen. Das Ergebnis dieses Vergleichs wird +// zurueckgegeben. + +BOOL operator < ( const Fraction& rVal1, const Fraction& rVal2 ) +{ + if ( !rVal1.IsValid() || !rVal2.IsValid() ) + return FALSE; + + BigInt nN( rVal1.nNumerator ); + nN *= BigInt( rVal2.nDenominator ); + BigInt nD( rVal1.nDenominator ); + nD *= BigInt( rVal2.nNumerator ); + + return nN < nD; +} + +/************************************************************************* +|* +|* Fraction::operator >() +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung DV 20.09.90 +|* Letzte Aenderung TH 19.08.92 +|* +*************************************************************************/ + +// Beide Operanden werden zunaechst auf ihre Gueltigkeit ueberprueft und +// anschliessend zur Sicherheit noch einmal gekuerzt. Um die Brueche +// (a/b) und (c/d) zu vergleichen, werden sie zunaechst auf einen +// gemeinsamen Nenner gebracht (b*d), um dann die beiden Zaehler (a*d) +// und (c*b) zu vergleichen. Das Ergebnis dieses Vergleichs wird +// zurueckgegeben. + +BOOL operator > ( const Fraction& rVal1, const Fraction& rVal2 ) +{ + if ( !rVal1.IsValid() || !rVal2.IsValid() ) + return FALSE; + + BigInt nN( rVal1.nNumerator ); + nN *= BigInt( rVal2.nDenominator ); + BigInt nD( rVal1.nDenominator); + nD *= BigInt( rVal2.nNumerator ); + + return nN > nD; +} + +/************************************************************************* +|* +|* SvStream& operator>>( SvStream& rIStream, Fraction& rFract ) +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung MM 08.01.96 +|* Letzte Aenderung MM 08.01.96 +|* +*************************************************************************/ +SvStream& operator >> ( SvStream& rIStream, Fraction& rFract ) +{ + rIStream >> rFract.nNumerator; + rIStream >> rFract.nDenominator; + return rIStream; +} + +/************************************************************************* +|* +|* SvStream& operator<<( SvStream& rIStream, Fraction& rFract ) +|* +|* Beschreibung FRACT.SDW +|* Ersterstellung MM 08.01.96 +|* Letzte Aenderung MM 08.01.96 +|* +*************************************************************************/ +SvStream& operator << ( SvStream& rOStream, const Fraction& rFract ) +{ + rOStream << rFract.nNumerator; + rOStream << rFract.nDenominator; + return rOStream; +} diff --git a/tools/source/generic/gen.cxx b/tools/source/generic/gen.cxx new file mode 100644 index 000000000000..6e82ded34877 --- /dev/null +++ b/tools/source/generic/gen.cxx @@ -0,0 +1,664 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: gen.cxx,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" +#include <tools/debug.hxx> +#include <tools/gen.hxx> +#include <tools/stream.hxx> + +// ======================================================================= + +SvStream& operator>>( SvStream& rIStream, Pair& rPair ) +{ + DBG_ASSERTWARNING( rIStream.GetVersion(), "Pair::>> - Solar-Version not set on rIStream" ); + + if ( rIStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cId; + unsigned char cAry[8]; + int i; + int i1; + int i2; + UINT32 nNum; + + rIStream >> cId; + i1 = (cId & 0x70) >> 4; + i2 = cId & 0x07; + rIStream.Read( cAry, i1+i2 ); + + nNum = 0; + i = i1; + while ( i ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + if ( cId & 0x80 ) + nNum ^= 0xFFFFFFFF; + rPair.nA = (INT32)nNum; + + nNum = 0; + i = i1+i2; + while ( i > i1 ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + if ( cId & 0x08 ) + nNum ^= 0xFFFFFFFF; + rPair.nB = (INT32)nNum; + } + else + { + rIStream >> rPair.nA >> rPair.nB; + } + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const Pair& rPair ) +{ + DBG_ASSERTWARNING( rOStream.GetVersion(), "Pair::<< - Solar-Version not set on rOStream" ); + + if ( rOStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cAry[9]; + int i = 1; + UINT32 nNum; + + cAry[0] = 0; + + nNum = (UINT32)(INT32)rPair.nA; + if ( rPair.nA < 0 ) + { + cAry[0] |= 0x80; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[0] |= 0x40; + } + else + cAry[0] |= 0x30; + } + else + cAry[0] |= 0x20; + } + else + cAry[0] |= 0x10; + } + + nNum = (UINT32)(INT32)rPair.nB; + if ( rPair.nB < 0 ) + { + cAry[0] |= 0x08; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[0] |= 0x04; + } + else + cAry[0] |= 0x03; + } + else + cAry[0] |= 0x02; + } + else + cAry[0] |= 0x01; + } + + rOStream.Write( cAry, i ); + } + else + { + rOStream << rPair.nA << rPair.nB; + } + + return rOStream; +} + +/************************************************************************* +|* +|* Rectangle::SetSize() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung DV 29.10.91 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +void Rectangle::SetSize( const Size& rSize ) +{ + if ( rSize.Width() < 0 ) + nRight = nLeft + rSize.Width() +1; + else if ( rSize.Width() > 0 ) + nRight = nLeft + rSize.Width() -1; + else + nRight = RECT_EMPTY; + + if ( rSize.Height() < 0 ) + nBottom = nTop + rSize.Height() +1; + else if ( rSize.Height() > 0 ) + nBottom = nTop + rSize.Height() -1; + else + nBottom = RECT_EMPTY; +} + +/************************************************************************* +|* +|* Rectangle::Union() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung TH 20.10.92 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +Rectangle& Rectangle::Union( const Rectangle& rRect ) +{ + if ( rRect.IsEmpty() ) + return *this; + + if ( IsEmpty() ) + *this = rRect; + else + { + nLeft = Min( Min( nLeft, rRect.nLeft ), Min( nRight, rRect.nRight ) ); + nRight = Max( Max( nLeft, rRect.nLeft ), Max( nRight, rRect.nRight ) ); + nTop = Min( Min( nTop, rRect.nTop ), Min( nBottom, rRect.nBottom ) ); + nBottom = Max( Max( nTop, rRect.nTop ), Max( nBottom, rRect.nBottom ) ); + } + + return *this; +} + +/************************************************************************* +|* +|* Rectangle::Intersection() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung TH 20.10.92 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +Rectangle& Rectangle::Intersection( const Rectangle& rRect ) +{ + if ( IsEmpty() ) + return *this; + if ( rRect.IsEmpty() ) + { + *this = Rectangle(); + return *this; + } + + // nicht mit umgedrehten Rechtecken arbeiten + Rectangle aTmpRect( rRect ); + Justify(); + aTmpRect.Justify(); + + // Schnitt bilden + nLeft = Max( nLeft, aTmpRect.nLeft ); + nRight = Min( nRight, aTmpRect.nRight ); + nTop = Max( nTop, aTmpRect.nTop ); + nBottom= Min( nBottom, aTmpRect.nBottom ); + + // Feststellen ob Schnitt leer + if ( nRight < nLeft || nBottom < nTop ) + *this = Rectangle(); + + return *this; +} + +/************************************************************************* +|* +|* Rectangle::Justify() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung DV 07.03.91 +|* Letzte Aenderung DV 07.03.91 +|* +*************************************************************************/ + +void Rectangle::Justify() +{ + long nHelp; + + // Abfrage, ob Right kleiner Left + if ( (nRight < nLeft) && (nRight != RECT_EMPTY) ) + { + nHelp = nLeft; + nLeft = nRight; + nRight = nHelp; + } + + // Abfrage, ob Bottom kleiner Top + if ( (nBottom < nTop) && (nBottom != RECT_EMPTY) ) + { + nHelp = nBottom; + nBottom = nTop; + nTop = nHelp; + } +} + +/************************************************************************* +|* +|* Rectangle::IsInside() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung TH 19.03.90 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +BOOL Rectangle::IsInside( const Point& rPoint ) const +{ + if ( IsEmpty() ) + return FALSE; + + BOOL bRet = TRUE; + if ( nLeft <= nRight ) + { + if ( (rPoint.X() < nLeft) || (rPoint.X() > nRight) ) + bRet = FALSE; + } + else + { + if ( (rPoint.X() > nLeft) || (rPoint.X() < nRight) ) + bRet = FALSE; + } + if ( nTop <= nBottom ) + { + if ( (rPoint.Y() < nTop) || (rPoint.Y() > nBottom) ) + bRet = FALSE; + } + else + { + if ( (rPoint.Y() > nTop) || (rPoint.Y() < nBottom) ) + bRet = FALSE; + } + return bRet; +} + +/************************************************************************* +|* +|* Rectangle::IsInside() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung TH 19.03.90 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +BOOL Rectangle::IsInside( const Rectangle& rRect ) const +{ + if ( IsInside( rRect.TopLeft() ) && IsInside( rRect.BottomRight() ) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************* +|* +|* Rectangle::IsOver() +|* +|* Beschreibung GEN.SDW +|* Ersterstellung TH 19.03.90 +|* Letzte Aenderung MM 21.04.94 +|* +*************************************************************************/ + +BOOL Rectangle::IsOver( const Rectangle& rRect ) const +{ + // Wenn sie sich nicht schneiden, ueberlappen sie auch nicht + return !GetIntersection( rRect ).IsEmpty(); +} + +// ======================================================================= + +SvStream& operator>>( SvStream& rIStream, Rectangle& rRect ) +{ + DBG_ASSERTWARNING( rIStream.GetVersion(), "Rectangle::>> - Solar-Version not set on rIStream" ); + + if ( rIStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cIdAry[2]; + unsigned char cAry[16]; + int i; + int iLast; + int i1; + int i2; + int i3; + int i4; + UINT32 nNum; + + rIStream.Read( cIdAry, 2 ); + i1 = (cIdAry[0] & 0x70) >> 4; + i2 = cIdAry[0] & 0x07; + i3 = (cIdAry[1] & 0x70) >> 4; + i4 = cIdAry[1] & 0x07; + rIStream.Read( cAry, i1+i2+i3+i4 ); + + nNum = 0; + i = i1; + iLast = i; + while ( i ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + iLast = i1; + if ( cIdAry[0] & 0x80 ) + nNum ^= 0xFFFFFFFF; + rRect.nLeft = (INT32)nNum; + + nNum = 0; + i = iLast+i2; + while ( i > iLast ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + iLast += i2; + if ( cIdAry[0] & 0x08 ) + nNum ^= 0xFFFFFFFF; + rRect.nTop = (INT32)nNum; + + nNum = 0; + i = iLast+i3; + while ( i > iLast ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + iLast += i3; + if ( cIdAry[1] & 0x80 ) + nNum ^= 0xFFFFFFFF; + rRect.nRight = (INT32)nNum; + + nNum = 0; + i = iLast+i4; + while ( i > iLast ) + { + i--; + nNum <<= 8; + nNum |= cAry[i]; + } + if ( cIdAry[1] & 0x08 ) + nNum ^= 0xFFFFFFFF; + rRect.nBottom = (INT32)nNum; + } + else + { + rIStream >> rRect.nLeft >> rRect.nTop >> rRect.nRight >> rRect.nBottom; + } + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const Rectangle& rRect ) +{ + DBG_ASSERTWARNING( rOStream.GetVersion(), "Rectangle::<< - Solar-Version not set on rOStream" ); + + if ( rOStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + unsigned char cAry[18]; + int i = 2; + UINT32 nNum; + + cAry[0] = 0; + cAry[1] = 0; + + nNum = (UINT32)(INT32)rRect.nLeft; + if ( rRect.nLeft < 0 ) + { + cAry[0] |= 0x80; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[0] |= 0x40; + } + else + cAry[0] |= 0x30; + } + else + cAry[0] |= 0x20; + } + else + cAry[0] |= 0x10; + } + + nNum = (UINT32)(INT32)rRect.nTop; + if ( rRect.nTop < 0 ) + { + cAry[0] |= 0x08; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[0] |= 0x04; + } + else + cAry[0] |= 0x03; + } + else + cAry[0] |= 0x02; + } + else + cAry[0] |= 0x01; + } + + nNum = (UINT32)(INT32)rRect.nRight; + if ( rRect.nRight < 0 ) + { + cAry[1] |= 0x80; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[1] |= 0x40; + } + else + cAry[1] |= 0x30; + } + else + cAry[1] |= 0x20; + } + else + cAry[1] |= 0x10; + } + + nNum = (UINT32)(INT32)rRect.nBottom; + if ( rRect.nBottom < 0 ) + { + cAry[1] |= 0x08; + nNum ^= 0xFFFFFFFF; + } + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + + if ( nNum ) + { + cAry[i] = (unsigned char)(nNum & 0xFF); + nNum >>= 8; + i++; + cAry[1] |= 0x04; + } + else + cAry[1] |= 0x03; + } + else + cAry[1] |= 0x02; + } + else + cAry[1] |= 0x01; + } + + rOStream.Write( cAry, i ); + } + else + { + rOStream << rRect.nLeft << rRect.nTop << rRect.nRight << rRect.nBottom; + } + + return rOStream; +} diff --git a/tools/source/generic/line.cxx b/tools/source/generic/line.cxx new file mode 100644 index 000000000000..5e698d50c006 --- /dev/null +++ b/tools/source/generic/line.cxx @@ -0,0 +1,371 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: line.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _LINE_CXX +#include <tools/link.hxx> +#include <tools/line.hxx> +#include <tools/debug.hxx> + +#include <cstdlib> +#include <math.h> + +inline long FRound( double fVal ) +{ + return( fVal > 0.0 ? (long) ( fVal + 0.5 ) : -(long) ( -fVal + 0.5 ) ); +} + +// -------- +// - Line - +// -------- + +double Line::GetLength() const +{ + return hypot( maStart.X() - maEnd.X(), maStart.Y() - maEnd.Y() ); +} + +// ------------------------------------------------------------------------ + +BOOL Line::Intersection( const Line& rLine, Point& rIntersection ) const +{ + double fX, fY; + BOOL bRet; + + if( Intersection( rLine, fX, fY ) ) + { + rIntersection.X() = FRound( fX ); + rIntersection.Y() = FRound( fY ); + bRet = TRUE; + } + else + bRet = FALSE; + + return bRet; +} + +// ------------------------------------------------------------------------ + +BOOL Line::Intersection( const Line& rLine, double& rIntersectionX, double& rIntersectionY ) const +{ + const double fAx = maEnd.X() - maStart.X(); + const double fAy = maEnd.Y() - maStart.Y(); + const double fBx = rLine.maStart.X() - rLine.maEnd.X(); + const double fBy = rLine.maStart.Y() - rLine.maEnd.Y(); + const double fDen = fAy * fBx - fAx * fBy; + BOOL bOk = FALSE; + + if( fDen != 0. ) + { + const double fCx = maStart.X() - rLine.maStart.X(); + const double fCy = maStart.Y() - rLine.maStart.Y(); + const double fA = fBy * fCx - fBx * fCy; + const BOOL bGreater = ( fDen > 0. ); + + bOk = TRUE; + + if ( bGreater ) + { + if ( ( fA < 0. ) || ( fA > fDen ) ) + bOk = FALSE; + } + else if ( ( fA > 0. ) || ( fA < fDen ) ) + bOk = FALSE; + + if ( bOk ) + { + const double fB = fAx * fCy - fAy * fCx; + + if ( bGreater ) + { + if ( ( fB < 0. ) || ( fB > fDen ) ) + bOk = FALSE; + } + else if ( ( fB > 0. ) || ( fB < fDen ) ) + bOk = FALSE; + + if( bOk ) + { + const double fAlpha = fA / fDen; + + rIntersectionX = ( maStart.X() + fAlpha * fAx ); + rIntersectionY = ( maStart.Y() + fAlpha * fAy ); + } + } + } + + return bOk; +} + +// ------------------------------------------------------------------------ + +BOOL Line::Intersection( const Rectangle& rRect, Line& rIntersection ) const +{ + const BOOL bStartInside = rRect.IsInside( maStart ); + const BOOL bEndInside = rRect.IsInside( maEnd ); + BOOL bRet = TRUE; + + if( bStartInside && bEndInside ) + { + // line completely inside rect + rIntersection.maStart = maStart; + rIntersection.maEnd = maEnd; + } + else + { + // calculate intersections + const Point aTL( rRect.TopLeft() ), aTR( rRect.TopRight() ); + const Point aBR( rRect.BottomRight() ), aBL( rRect.BottomLeft() ); + Point aIntersect1, aIntersect2; + Point* pCurIntersection = &aIntersect1; + + if( Intersection( Line( aTL, aTR ), *pCurIntersection ) ) + pCurIntersection = &aIntersect2; + + if( Intersection( Line( aTR, aBR ), *pCurIntersection ) ) + pCurIntersection = ( pCurIntersection == &aIntersect1 ) ? &aIntersect2 : NULL; + + if( pCurIntersection && Intersection( Line( aBR, aBL ), *pCurIntersection ) ) + pCurIntersection = ( pCurIntersection == &aIntersect1 ) ? &aIntersect2 : NULL; + + if( pCurIntersection && Intersection( Line( aBL, aTL ), *pCurIntersection ) ) + pCurIntersection = ( pCurIntersection == &aIntersect1 ) ? &aIntersect2 : NULL; + + if( !pCurIntersection ) + { + // two intersections + rIntersection.maStart = aIntersect1; + rIntersection.maEnd = aIntersect2; + } + else if( pCurIntersection == &aIntersect2 ) + { + // one intersection + rIntersection.maStart = aIntersect1; + + if( ( maStart != aIntersect1 ) && bStartInside ) + rIntersection.maEnd = maStart; + else if( ( maEnd != aIntersect1 ) && bEndInside ) + rIntersection.maEnd = maEnd; + else + rIntersection.maEnd = rIntersection.maStart; + } + else + bRet = FALSE; + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +Point Line::NearestPoint( const Point& rPoint ) const +{ + Point aRetPt; + + if ( maStart != maEnd ) + { + const double fDistX = maEnd.X() - maStart.X(); + const double fDistY = maStart.Y() - maEnd.Y(); + const double fTau = ( ( maStart.Y() - rPoint.Y() ) * fDistY - + ( maStart.X() - rPoint.X() ) * fDistX ) / + ( fDistX * fDistX + fDistY * fDistY ); + + if( fTau < 0.0 ) + aRetPt = maStart; + else if( fTau <= 1.0 ) + { + aRetPt.X() = FRound( maStart.X() + fTau * fDistX ); + aRetPt.Y() = FRound( maStart.Y() - fTau * fDistY ); + } + else + aRetPt = maEnd; + } + else + aRetPt = maStart; + + return aRetPt; +} + +// ------------------------------------------------------------------------ + +double Line::GetDistance( const double& rPtX, const double& rPtY ) const +{ + double fDist; + + if( maStart != maEnd ) + { + const double fDistX = maEnd.X() - maStart.X(); + const double fDistY = maEnd.Y() - maStart.Y(); + const double fACX = maStart.X() - rPtX; + const double fACY = maStart.Y() - rPtY; + const double fL2 = fDistX * fDistX + fDistY * fDistY; + const double fR = ( fACY * -fDistY - fACX * fDistX ) / fL2; + const double fS = ( fACY * fDistX - fACX * fDistY ) / fL2; + + if( fR < 0.0 ) + { + fDist = hypot( maStart.X() - rPtX, maStart.Y() - rPtY ); + + if( fS < 0.0 ) + fDist *= -1.0; + } + else if( fR <= 1.0 ) + fDist = fS * sqrt( fL2 ); + else + { + fDist = hypot( maEnd.X() - rPtX, maEnd.Y() - rPtY ); + + if( fS < 0.0 ) + fDist *= -1.0; + } + } + else + fDist = hypot( maStart.X() - rPtX, maStart.Y() - rPtY ); + + return fDist; +} + +// ------------------------------------------------------------------------ + +void Line::Enum( const Link& rEnumLink ) +{ + DBG_ASSERT( rEnumLink.IsSet(), "This call doesn't make any sense with !rEnumLink.IsSet()" ); + + Point aEnum; + long nX; + long nY; + + if( maStart.X() == maEnd.X() ) + { + const long nEndY = maEnd.Y(); + + nX = maStart.X(); + nY = maStart.Y(); + + if( nEndY > nY ) + { + while( nY <= nEndY ) + { + aEnum.X() = nX; + aEnum.Y() = nY++; + rEnumLink.Call( &aEnum ); + } + } + else + { + while( nY >= nEndY ) + { + aEnum.X() = nX; + aEnum.Y() = nY--; + rEnumLink.Call( &aEnum ); + } + } + } + else if( maStart.Y() == maEnd.Y() ) + { + const long nEndX = maEnd.X(); + + nX = maStart.X(); + nY = maStart.Y(); + + if( nEndX > nX ) + { + while( nX <= nEndX ) + { + aEnum.X() = nX++; + aEnum.Y() = nY; + rEnumLink.Call( &aEnum ); + } + } + else + { + while( nX >= nEndX ) + { + aEnum.X() = nX--; + aEnum.Y() = nY; + rEnumLink.Call( &aEnum ); + } + } + } + else + { + const long nDX = labs( maEnd.X() - maStart.X() ); + const long nDY = labs( maEnd.Y() - maStart.Y() ); + const long nStartX = maStart.X(); + const long nStartY = maStart.Y(); + const long nEndX = maEnd.X(); + const long nEndY = maEnd.Y(); + const long nXInc = ( nStartX < nEndX ) ? 1L : -1L; + const long nYInc = ( nStartY < nEndY ) ? 1L : -1L; + + if( nDX >= nDY ) + { + const long nDYX = ( nDY - nDX ) << 1; + const long nDY2 = nDY << 1; + long nD = nDY2 - nDX; + + for( nX = nStartX, nY = nStartY; nX != nEndX; nX += nXInc ) + { + aEnum.X() = nX; + aEnum.Y() = nY; + rEnumLink.Call( &aEnum ); + + if( nD < 0L ) + nD += nDY2; + else + nD += nDYX, nY += nYInc; + } + } + else + { + const long nDYX = ( nDX - nDY ) << 1; + const long nDY2 = nDX << 1; + long nD = nDY2 - nDY; + + for( nX = nStartX, nY = nStartY; nY != nEndY; nY += nYInc ) + { + aEnum.X() = nX; + aEnum.Y() = nY; + rEnumLink.Call( &aEnum ); + + if( nD < 0L ) + nD += nDY2; + else + nD += nDYX, nX += nXInc; + } + } + + // last point + aEnum.X() = nEndX; + aEnum.Y() = nEndY; + rEnumLink.Call( &aEnum ); + } +} diff --git a/tools/source/generic/link.cxx b/tools/source/generic/link.cxx new file mode 100644 index 000000000000..9964f58e7cda --- /dev/null +++ b/tools/source/generic/link.cxx @@ -0,0 +1,61 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: link.cxx,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" +#include <tools/link.hxx> + +/************************************************************************* +|* +|* Link::operator==() +|* +|* Beschreibung LINK.SDW +|* Ersterstellung AM 14.02.91 +|* Letzte Aenderung TH 07.11.95 +|* +*************************************************************************/ + +BOOL Link::operator==( const Link& rLink ) const +{ + if ( pFunc == rLink.pFunc ) + { + if ( pFunc ) + { + if ( pInst == rLink.pInst ) + return TRUE; + else + return FALSE; + } + else + return TRUE; + } + else + return FALSE; +} diff --git a/tools/source/generic/makefile.mk b/tools/source/generic/makefile.mk new file mode 100644 index 000000000000..6340e4daae08 --- /dev/null +++ b/tools/source/generic/makefile.mk @@ -0,0 +1,72 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.15 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=gen + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +EXCEPTIONSFILES = $(SLO)$/poly.obj $(OBJ)$/poly.obj + +SLOFILES= $(SLO)$/toolsin.obj \ + $(SLO)$/link.obj \ + $(SLO)$/bigint.obj \ + $(SLO)$/fract.obj \ + $(SLO)$/color.obj \ + $(SLO)$/gen.obj \ + $(SLO)$/config.obj \ + $(SLO)$/poly.obj \ + $(SLO)$/poly2.obj \ + $(SLO)$/svborder.obj \ + $(SLO)$/line.obj + +OBJFILES= $(OBJ)$/toolsin.obj \ + $(OBJ)$/link.obj \ + $(OBJ)$/bigint.obj \ + $(OBJ)$/fract.obj \ + $(OBJ)$/color.obj \ + $(OBJ)$/gen.obj \ + $(OBJ)$/config.obj \ + $(OBJ)$/poly.obj \ + $(OBJ)$/poly2.obj \ + $(OBJ)$/svborder.obj \ + $(OBJ)$/line.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/tools/source/generic/poly.cxx b/tools/source/generic/poly.cxx new file mode 100644 index 000000000000..7f1eb94b646d --- /dev/null +++ b/tools/source/generic/poly.cxx @@ -0,0 +1,2369 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: poly.cxx,v $ + * $Revision: 1.17 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _SV_POLY_CXX +#include <osl/endian.h> +#include <tools/bigint.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <poly.h> +#include <tools/line.hxx> +#ifndef _VECTOR2D_H +#include <tools/vector2d.hxx> +#endif +#ifndef _POLY_HXX +#include <tools/poly.hxx> +#endif +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/curve/b2dcubicbezier.hxx> + +#include <vector> +#include <iterator> +#include <algorithm> +#include <cstring> +#include <limits.h> +#include <cmath> + + +// ======================================================================= + +DBG_NAME( Polygon ) + +// ----------------------------------------------------------------------- + +#define EDGE_LEFT 1 +#define EDGE_TOP 2 +#define EDGE_RIGHT 4 +#define EDGE_BOTTOM 8 +#define EDGE_HORZ (EDGE_RIGHT | EDGE_LEFT) +#define EDGE_VERT (EDGE_TOP | EDGE_BOTTOM) +#define SMALL_DVALUE 0.0000001 +#define FSQRT2 1.4142135623730950488016887242097 + +// ----------------------------------------------------------------------- + +static ImplPolygonData aStaticImplPolygon = +{ + NULL, NULL, 0, 0 +}; + +// ======================================================================= + +ImplPolygon::ImplPolygon( USHORT nInitSize, BOOL bFlags ) +{ + if ( nInitSize ) + { + mpPointAry = (Point*)new char[(ULONG)nInitSize*sizeof(Point)]; + memset( mpPointAry, 0, (ULONG)nInitSize*sizeof(Point) ); + } + else + mpPointAry = NULL; + + if( bFlags ) + { + mpFlagAry = new BYTE[ nInitSize ]; + memset( mpPointAry, 0, nInitSize ); + } + else + mpFlagAry = NULL; + + mnRefCount = 1; + mnPoints = nInitSize; +} + +// ----------------------------------------------------------------------- + +ImplPolygon::ImplPolygon( const ImplPolygon& rImpPoly ) +{ + if ( rImpPoly.mnPoints ) + { + mpPointAry = (Point*)new char[(ULONG)rImpPoly.mnPoints*sizeof(Point)]; + memcpy( mpPointAry, rImpPoly.mpPointAry, (ULONG)rImpPoly.mnPoints*sizeof(Point) ); + + if( rImpPoly.mpFlagAry ) + { + mpFlagAry = new BYTE[ rImpPoly.mnPoints ]; + memcpy( mpFlagAry, rImpPoly.mpFlagAry, rImpPoly.mnPoints ); + } + else + mpFlagAry = NULL; + } + else + { + mpPointAry = NULL; + mpFlagAry = NULL; + } + + mnRefCount = 1; + mnPoints = rImpPoly.mnPoints; +} + +// ----------------------------------------------------------------------- + +ImplPolygon::ImplPolygon( USHORT nInitSize, const Point* pInitAry, const BYTE* pInitFlags ) +{ + if ( nInitSize ) + { + mpPointAry = (Point*)new char[(ULONG)nInitSize*sizeof(Point)]; + memcpy( mpPointAry, pInitAry, (ULONG)nInitSize*sizeof( Point ) ); + + if( pInitFlags ) + { + mpFlagAry = new BYTE[ nInitSize ]; + memcpy( mpFlagAry, pInitFlags, nInitSize ); + } + else + mpFlagAry = NULL; + } + else + { + mpPointAry = NULL; + mpFlagAry = NULL; + } + + mnRefCount = 1; + mnPoints = nInitSize; +} + +// ----------------------------------------------------------------------- + +ImplPolygon::~ImplPolygon() +{ + if ( mpPointAry ) + { + delete[] (char*) mpPointAry; + } + + if( mpFlagAry ) + delete[] mpFlagAry; +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplSetSize( USHORT nNewSize, BOOL bResize ) +{ + if( mnPoints == nNewSize ) + return; + + Point* pNewAry; + + if ( nNewSize ) + { + pNewAry = (Point*)new char[(ULONG)nNewSize*sizeof(Point)]; + + if ( bResize ) + { + // Alte Punkte kopieren + if ( mnPoints < nNewSize ) + { + // Neue Punkte mit 0 initialisieren + memset( pNewAry+mnPoints, 0, (ULONG)(nNewSize-mnPoints)*sizeof(Point) ); + if ( mpPointAry ) + memcpy( pNewAry, mpPointAry, mnPoints*sizeof(Point) ); + } + else + { + if ( mpPointAry ) + memcpy( pNewAry, mpPointAry, (ULONG)nNewSize*sizeof(Point) ); + } + } + } + else + pNewAry = NULL; + + if ( mpPointAry ) + delete[] (char*) mpPointAry; + + // ggf. FlagArray beruecksichtigen + if( mpFlagAry ) + { + BYTE* pNewFlagAry; + + if( nNewSize ) + { + pNewFlagAry = new BYTE[ nNewSize ]; + + if( bResize ) + { + // Alte Flags kopieren + if ( mnPoints < nNewSize ) + { + // Neue Punkte mit 0 initialisieren + memset( pNewFlagAry+mnPoints, 0, nNewSize-mnPoints ); + memcpy( pNewFlagAry, mpFlagAry, mnPoints ); + } + else + memcpy( pNewFlagAry, mpFlagAry, nNewSize ); + } + } + else + pNewFlagAry = NULL; + + delete[] mpFlagAry; + mpFlagAry = pNewFlagAry; + } + + mpPointAry = pNewAry; + mnPoints = nNewSize; +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplSplit( USHORT nPos, USHORT nSpace, ImplPolygon* pInitPoly ) +{ + const ULONG nSpaceSize = nSpace * sizeof( Point ); + const USHORT nNewSize = mnPoints + nSpace; + + if( nPos >= mnPoints ) + { + // Hinten anhaengen + nPos = mnPoints; + ImplSetSize( nNewSize, TRUE ); + + if( pInitPoly ) + { + memcpy( mpPointAry + nPos, pInitPoly->mpPointAry, nSpaceSize ); + + if( pInitPoly->mpFlagAry ) + memcpy( mpFlagAry + nPos, pInitPoly->mpFlagAry, nSpace ); + } + } + else + { + // PointArray ist in diesem Zweig immer vorhanden + const USHORT nSecPos = nPos + nSpace; + const USHORT nRest = mnPoints - nPos; + + Point* pNewAry = (Point*) new char[ (ULONG) nNewSize * sizeof( Point ) ]; + + memcpy( pNewAry, mpPointAry, nPos * sizeof( Point ) ); + + if( pInitPoly ) + memcpy( pNewAry + nPos, pInitPoly->mpPointAry, nSpaceSize ); + else + memset( pNewAry + nPos, 0, nSpaceSize ); + + memcpy( pNewAry + nSecPos, mpPointAry + nPos, nRest * sizeof( Point ) ); + delete[] (char*) mpPointAry; + + // ggf. FlagArray beruecksichtigen + if( mpFlagAry ) + { + BYTE* pNewFlagAry = new BYTE[ nNewSize ]; + + memcpy( pNewFlagAry, mpFlagAry, nPos ); + + if( pInitPoly && pInitPoly->mpFlagAry ) + memcpy( pNewFlagAry + nPos, pInitPoly->mpFlagAry, nSpace ); + else + memset( pNewFlagAry + nPos, 0, nSpace ); + + memcpy( pNewFlagAry + nSecPos, mpFlagAry + nPos, nRest ); + delete[] mpFlagAry; + mpFlagAry = pNewFlagAry; + } + + mpPointAry = pNewAry; + mnPoints = nNewSize; + } +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplRemove( USHORT nPos, USHORT nCount ) +{ + const USHORT nRemoveCount = Min( (USHORT) ( mnPoints - nPos ), (USHORT) nCount ); + + if( nRemoveCount ) + { + const USHORT nNewSize = mnPoints - nRemoveCount; + const USHORT nSecPos = nPos + nRemoveCount; + const USHORT nRest = mnPoints - nSecPos; + + Point* pNewAry = (Point*) new char[ (ULONG) nNewSize * sizeof( Point ) ]; + + memcpy( pNewAry, mpPointAry, nPos * sizeof( Point ) ); + memcpy( pNewAry + nPos, mpPointAry + nSecPos, nRest * sizeof( Point ) ); + + delete[] (char*) mpPointAry; + + // ggf. FlagArray beruecksichtigen + if( mpFlagAry ) + { + BYTE* pNewFlagAry = new BYTE[ nNewSize ]; + + memcpy( pNewFlagAry, mpFlagAry, nPos ); + memcpy( pNewFlagAry + nPos, mpFlagAry + nSecPos, nRest ); + delete[] mpFlagAry; + mpFlagAry = pNewFlagAry; + } + + mpPointAry = pNewAry; + mnPoints = nNewSize; + } +} + +// ----------------------------------------------------------------------- + +void ImplPolygon::ImplCreateFlagArray() +{ + if( !mpFlagAry ) + { + mpFlagAry = new BYTE[ mnPoints ]; + memset( mpFlagAry, 0, mnPoints ); + } +} + +// ======================================================================= + +inline void Polygon::ImplMakeUnique() +{ + // Falls noch andere Referenzen bestehen, dann kopieren + if ( mpImplPolygon->mnRefCount != 1 ) + { + if ( mpImplPolygon->mnRefCount ) + mpImplPolygon->mnRefCount--; + mpImplPolygon = new ImplPolygon( *mpImplPolygon ); + } +} + +// ----------------------------------------------------------------------- + +inline double ImplGetAngle( const Point& rCenter, const Point& rPt ) +{ + const long nDX = rPt.X() - rCenter.X(); + return( atan2( -rPt.Y() + rCenter.Y(), ( ( nDX == 0L ) ? 0.000000001 : nDX ) ) ); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon() +{ + DBG_CTOR( Polygon, NULL ); + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( USHORT nSize ) +{ + DBG_CTOR( Polygon, NULL ); + + if ( nSize ) + mpImplPolygon = new ImplPolygon( nSize ); + else + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( USHORT nPoints, const Point* pPtAry, const BYTE* pFlagAry ) +{ + DBG_CTOR( Polygon, NULL ); + + if( nPoints ) + mpImplPolygon = new ImplPolygon( nPoints, pPtAry, pFlagAry ); + else + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Polygon& rPoly ) +{ + DBG_CTOR( Polygon, NULL ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERT( rPoly.mpImplPolygon->mnRefCount < 0xFFFFFFFE, "Polygon: RefCount overflow" ); + + mpImplPolygon = rPoly.mpImplPolygon; + if ( mpImplPolygon->mnRefCount ) + mpImplPolygon->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Rectangle& rRect ) +{ + DBG_CTOR( Polygon, NULL ); + + if ( rRect.IsEmpty() ) + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); + else + { + mpImplPolygon = new ImplPolygon( 5 ); + mpImplPolygon->mpPointAry[0] = rRect.TopLeft(); + mpImplPolygon->mpPointAry[1] = rRect.TopRight(); + mpImplPolygon->mpPointAry[2] = rRect.BottomRight(); + mpImplPolygon->mpPointAry[3] = rRect.BottomLeft(); + mpImplPolygon->mpPointAry[4] = rRect.TopLeft(); + } +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Rectangle& rRect, ULONG nHorzRound, ULONG nVertRound ) +{ + DBG_CTOR( Polygon, NULL ); + + if ( rRect.IsEmpty() ) + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); + else + { + Rectangle aRect( rRect ); + aRect.Justify(); // SJ: i9140 + + nHorzRound = Min( nHorzRound, (ULONG) labs( aRect.GetWidth() >> 1 ) ); + nVertRound = Min( nVertRound, (ULONG) labs( aRect.GetHeight() >> 1 ) ); + + if( !nHorzRound && !nVertRound ) + { + mpImplPolygon = new ImplPolygon( 5 ); + mpImplPolygon->mpPointAry[0] = aRect.TopLeft(); + mpImplPolygon->mpPointAry[1] = aRect.TopRight(); + mpImplPolygon->mpPointAry[2] = aRect.BottomRight(); + mpImplPolygon->mpPointAry[3] = aRect.BottomLeft(); + mpImplPolygon->mpPointAry[4] = aRect.TopLeft(); + } + else + { + const Point aTL( aRect.Left() + nHorzRound, aRect.Top() + nVertRound ); + const Point aTR( aRect.Right() - nHorzRound, aRect.Top() + nVertRound ); + const Point aBR( aRect.Right() - nHorzRound, aRect.Bottom() - nVertRound ); + const Point aBL( aRect.Left() + nHorzRound, aRect.Bottom() - nVertRound ); + Polygon* pEllipsePoly = new Polygon( Point(), nHorzRound, nVertRound ); + USHORT i, nEnd, nSize4 = pEllipsePoly->GetSize() >> 2; + + mpImplPolygon = new ImplPolygon( pEllipsePoly->GetSize() + 1 ); + + const Point* pSrcAry = pEllipsePoly->GetConstPointAry(); + Point* pDstAry = mpImplPolygon->mpPointAry; + + for( i = 0, nEnd = nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aTR; + + for( nEnd = nEnd + nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aTL; + + for( nEnd = nEnd + nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aBL; + + for( nEnd = nEnd + nSize4; i < nEnd; i++ ) + ( pDstAry[ i ] = pSrcAry[ i ] ) += aBR; + + pDstAry[ nEnd ] = pDstAry[ 0 ]; + delete pEllipsePoly; + } + } +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Point& rCenter, long nRadX, long nRadY, USHORT nPoints ) +{ + DBG_CTOR( Polygon, NULL ); + + if( nRadX && nRadY ) + { + // Default berechnen (abhaengig von Groesse) + if( !nPoints ) + { + nPoints = (USHORT) ( F_PI * ( 1.5 * ( nRadX + nRadY ) - + sqrt( (double) labs( nRadX * nRadY ) ) ) ); + + nPoints = (USHORT) MinMax( nPoints, 32, 256 ); + + if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 ) + nPoints >>= 1; + } + + // Anzahl der Punkte auf durch 4 teilbare Zahl aufrunden + mpImplPolygon = new ImplPolygon( nPoints = (nPoints + 3) & ~3 ); + + Point* pPt; + USHORT i; + USHORT nPoints2 = nPoints >> 1; + USHORT nPoints4 = nPoints >> 2; + double nAngle; + double nAngleStep = F_PI2 / ( nPoints4 - 1 ); + + for( i=0, nAngle = 0.0; i < nPoints4; i++, nAngle += nAngleStep ) + { + long nX = FRound( nRadX * cos( nAngle ) ); + long nY = FRound( -nRadY * sin( nAngle ) ); + + pPt = &(mpImplPolygon->mpPointAry[i]); + pPt->X() = nX + rCenter.X(); + pPt->Y() = nY + rCenter.Y(); + pPt = &(mpImplPolygon->mpPointAry[nPoints2-i-1]); + pPt->X() = -nX + rCenter.X(); + pPt->Y() = nY + rCenter.Y(); + pPt = &(mpImplPolygon->mpPointAry[i+nPoints2]); + pPt->X() = -nX + rCenter.X(); + pPt->Y() = -nY + rCenter.Y(); + pPt = &(mpImplPolygon->mpPointAry[nPoints-i-1]); + pPt->X() = nX + rCenter.X(); + pPt->Y() = -nY + rCenter.Y(); + } + } + else + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Rectangle& rBound, + const Point& rStart, const Point& rEnd, PolyStyle eStyle ) +{ + DBG_CTOR( Polygon, NULL ); + + const long nWidth = rBound.GetWidth(); + const long nHeight = rBound.GetHeight(); + + if( ( nWidth > 1 ) && ( nHeight > 1 ) ) + { + const Point aCenter( rBound.Center() ); + const long nRadX = aCenter.X() - rBound.Left(); + const long nRadY = aCenter.Y() - rBound.Top(); + USHORT nPoints; + + nPoints = (USHORT) ( F_PI * ( 1.5 * ( nRadX + nRadY ) - + sqrt( (double) labs( nRadX * nRadY ) ) ) ); + + nPoints = (USHORT) MinMax( nPoints, 32, 256 ); + + if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 ) + nPoints >>= 1; + + // Winkel berechnen + const double fRadX = nRadX; + const double fRadY = nRadY; + const double fCenterX = aCenter.X(); + const double fCenterY = aCenter.Y(); + double fStart = ImplGetAngle( aCenter, rStart ); + double fEnd = ImplGetAngle( aCenter, rEnd ); + double fDiff = fEnd - fStart; + double fStep; + USHORT nStart; + USHORT nEnd; + + if( fDiff < 0. ) + fDiff += F_2PI; + + // Punktanzahl proportional verkleinern ( fDiff / (2PI) ); + // ist eingentlich nur fuer einen Kreis richtig; wir + // machen es hier aber trotzdem + nPoints = Max( (USHORT) ( ( fDiff * 0.1591549 ) * nPoints ), (USHORT) 16 ); + fStep = fDiff / ( nPoints - 1 ); + + if( POLY_PIE == eStyle ) + { + const Point aCenter2( FRound( fCenterX ), FRound( fCenterY ) ); + + nStart = 1; + nEnd = nPoints + 1; + mpImplPolygon = new ImplPolygon( nPoints + 2 ); + mpImplPolygon->mpPointAry[ 0 ] = aCenter2; + mpImplPolygon->mpPointAry[ nEnd ] = aCenter2; + } + else + { + mpImplPolygon = new ImplPolygon( ( POLY_CHORD == eStyle ) ? ( nPoints + 1 ) : nPoints ); + nStart = 0; + nEnd = nPoints; + } + + for(; nStart < nEnd; nStart++, fStart += fStep ) + { + Point& rPt = mpImplPolygon->mpPointAry[ nStart ]; + + rPt.X() = FRound( fCenterX + fRadX * cos( fStart ) ); + rPt.Y() = FRound( fCenterY - fRadY * sin( fStart ) ); + } + + if( POLY_CHORD == eStyle ) + mpImplPolygon->mpPointAry[ nPoints ] = mpImplPolygon->mpPointAry[ 0 ]; + } + else + mpImplPolygon = (ImplPolygon*) &aStaticImplPolygon; +} + +// ----------------------------------------------------------------------- + +Polygon::Polygon( const Point& rBezPt1, const Point& rCtrlPt1, + const Point& rBezPt2, const Point& rCtrlPt2, + USHORT nPoints ) +{ + DBG_CTOR( Polygon, NULL ); + + nPoints = ( 0 == nPoints ) ? 25 : ( ( nPoints < 2 ) ? 2 : nPoints ); + + const double fInc = 1.0 / ( nPoints - 1 ); + double fK_1 = 0.0, fK1_1 = 1.0; + double fK_2, fK_3, fK1_2, fK1_3, fK12, fK21; + const double fX0 = rBezPt1.X(); + const double fY0 = rBezPt1.Y(); + const double fX1 = 3.0 * rCtrlPt1.X(); + const double fY1 = 3.0 * rCtrlPt1.Y(); + const double fX2 = 3.0 * rCtrlPt2.X();; + const double fY2 = 3.0 * rCtrlPt2.Y();; + const double fX3 = rBezPt2.X(); + const double fY3 = rBezPt2.Y(); + + mpImplPolygon = new ImplPolygon( nPoints ); + + for( USHORT i = 0; i < nPoints; i++, fK_1 += fInc, fK1_1 -= fInc ) + { + Point& rPt = mpImplPolygon->mpPointAry[ i ]; + + fK_2 = fK_1, fK_3 = ( fK_2 *= fK_1 ), fK_3 *= fK_1; + fK1_2 = fK1_1, fK1_3 = ( fK1_2 *= fK1_1 ), fK1_3 *= fK1_1; + fK12 = fK_1 * fK1_2, fK21 = fK_2 * fK1_1; + + rPt.X() = FRound( fK1_3 * fX0 + fK12 * fX1 + fK21 * fX2 + fK_3 * fX3 ); + rPt.Y() = FRound( fK1_3 * fY0 + fK12 * fY1 + fK21 * fY2 + fK_3 * fY3 ); + } +} + +// ----------------------------------------------------------------------- + +Polygon::~Polygon() +{ + DBG_DTOR( Polygon, NULL ); + + // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } +} + +// ----------------------------------------------------------------------- + +Point* Polygon::ImplGetPointAry() +{ + DBG_CHKTHIS( Polygon, NULL ); + + ImplMakeUnique(); + return (Point*)mpImplPolygon->mpPointAry; +} + +// ----------------------------------------------------------------------- + +BYTE* Polygon::ImplGetFlagAry() +{ + DBG_CHKTHIS( Polygon, NULL ); + + ImplMakeUnique(); + mpImplPolygon->ImplCreateFlagArray(); + return mpImplPolygon->mpFlagAry; +} + +// ----------------------------------------------------------------------- + +const Point* Polygon::GetConstPointAry() const +{ + DBG_CHKTHIS( Polygon, NULL ); + return (Point*)mpImplPolygon->mpPointAry; +} + +// ----------------------------------------------------------------------- + +const BYTE* Polygon::GetConstFlagAry() const +{ + DBG_CHKTHIS( Polygon, NULL ); + return mpImplPolygon->mpFlagAry; +} + +// ----------------------------------------------------------------------- + +void Polygon::SetPoint( const Point& rPt, USHORT nPos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::SetPoint(): nPos >= nPoints" ); + + ImplMakeUnique(); + mpImplPolygon->mpPointAry[nPos] = rPt; +} + +// ----------------------------------------------------------------------- + +void Polygon::SetFlags( USHORT nPos, PolyFlags eFlags ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::SetFlags(): nPos >= nPoints" ); + + // we do only want to create the flag array if there + // is at least one flag different to POLY_NORMAL + if ( mpImplPolygon || ( eFlags != POLY_NORMAL ) ) + { + ImplMakeUnique(); + mpImplPolygon->ImplCreateFlagArray(); + mpImplPolygon->mpFlagAry[ nPos ] = (BYTE) eFlags; + } +} + +// ----------------------------------------------------------------------- + +const Point& Polygon::GetPoint( USHORT nPos ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetPoint(): nPos >= nPoints" ); + + return mpImplPolygon->mpPointAry[nPos]; +} + +// ----------------------------------------------------------------------- + +PolyFlags Polygon::GetFlags( USHORT nPos ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetFlags(): nPos >= nPoints" ); + return( mpImplPolygon->mpFlagAry ? + (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : + POLY_NORMAL ); +} + +// ----------------------------------------------------------------------- + +sal_Bool Polygon::HasFlags() const +{ + return mpImplPolygon->mpFlagAry != NULL; +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsControl(USHORT nPos) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetFlags(): nPos >= nPoints" ); + PolyFlags eFlags = mpImplPolygon->mpFlagAry ? + (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : POLY_NORMAL; + + return( POLY_CONTROL == eFlags ); +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsSmooth(USHORT nPos) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, + "Polygon::GetFlags(): nPos >= nPoints" ); + PolyFlags eFlags = mpImplPolygon->mpFlagAry ? + (PolyFlags) mpImplPolygon->mpFlagAry[ nPos ] : POLY_NORMAL; + + return( ( POLY_SMOOTH == eFlags ) || ( POLY_SYMMTR == eFlags ) ); +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsRect() const +{ + BOOL bIsRect = FALSE; + if ( mpImplPolygon->mpFlagAry == NULL ) + { + if ( ( ( mpImplPolygon->mnPoints == 5 ) && ( mpImplPolygon->mpPointAry[ 0 ] == mpImplPolygon->mpPointAry[ 4 ] ) ) || + ( mpImplPolygon->mnPoints == 4 ) ) + { + if ( ( mpImplPolygon->mpPointAry[ 0 ].X() == mpImplPolygon->mpPointAry[ 3 ].X() ) && + ( mpImplPolygon->mpPointAry[ 0 ].Y() == mpImplPolygon->mpPointAry[ 1 ].Y() ) && + ( mpImplPolygon->mpPointAry[ 1 ].X() == mpImplPolygon->mpPointAry[ 2 ].X() ) && + ( mpImplPolygon->mpPointAry[ 2 ].Y() == mpImplPolygon->mpPointAry[ 3 ].Y() ) ) + bIsRect = TRUE; + } + } + return bIsRect; +} + +// ----------------------------------------------------------------------- + +void Polygon::SetSize( USHORT nNewSize ) +{ + DBG_CHKTHIS( Polygon, NULL ); + + if( nNewSize != mpImplPolygon->mnPoints ) + { + ImplMakeUnique(); + mpImplPolygon->ImplSetSize( nNewSize ); + } +} + +// ----------------------------------------------------------------------- + +USHORT Polygon::GetSize() const +{ + DBG_CHKTHIS( Polygon, NULL ); + + return mpImplPolygon->mnPoints; +} + +// ----------------------------------------------------------------------- + +void Polygon::Clear() +{ + DBG_CHKTHIS( Polygon, NULL ); + + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } + + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); +} + +// ----------------------------------------------------------------------- + +double Polygon::CalcDistance( USHORT nP1, USHORT nP2 ) +{ + DBG_ASSERT( nP1 < mpImplPolygon->mnPoints, + "Polygon::CalcDistance(): nPos1 >= nPoints" ); + DBG_ASSERT( nP2 < mpImplPolygon->mnPoints, + "Polygon::CalcDistance(): nPos2 >= nPoints" ); + + const Point& rP1 = mpImplPolygon->mpPointAry[ nP1 ]; + const Point& rP2 = mpImplPolygon->mpPointAry[ nP2 ]; + const double fDx = rP2.X() - rP1.X(); + const double fDy = rP2.Y() - rP1.Y(); + + return sqrt( fDx * fDx + fDy * fDy ); +} + +// ----------------------------------------------------------------------- + +void Polygon::Optimize( ULONG nOptimizeFlags, const PolyOptimizeData* pData ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( !mpImplPolygon->mpFlagAry, "Optimizing could fail with beziers!" ); + + USHORT nSize = mpImplPolygon->mnPoints; + + if( nOptimizeFlags && nSize ) + { + if( nOptimizeFlags & POLY_OPTIMIZE_EDGES ) + { + const Rectangle aBound( GetBoundRect() ); + const double fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5; + const USHORT nPercent = pData ? pData->GetPercentValue() : 50; + + Optimize( POLY_OPTIMIZE_NO_SAME ); + ImplReduceEdges( *this, fArea, nPercent ); + } + else if( nOptimizeFlags & ( POLY_OPTIMIZE_REDUCE | POLY_OPTIMIZE_NO_SAME ) ) + { + Polygon aNewPoly; + const Point& rFirst = mpImplPolygon->mpPointAry[ 0 ]; + ULONG nReduce; + + if( nOptimizeFlags & ( POLY_OPTIMIZE_REDUCE ) ) + nReduce = pData ? pData->GetAbsValue() : 4UL; + else + nReduce = 0UL; + + while( nSize && ( mpImplPolygon->mpPointAry[ nSize - 1 ] == rFirst ) ) + nSize--; + + if( nSize > 1 ) + { + USHORT nLast = 0, nNewCount = 1; + + aNewPoly.SetSize( nSize ); + aNewPoly[ 0 ] = rFirst; + + for( USHORT i = 1; i < nSize; i++ ) + { + if( ( mpImplPolygon->mpPointAry[ i ] != mpImplPolygon->mpPointAry[ nLast ] ) && + ( !nReduce || ( nReduce < (ULONG) FRound( CalcDistance( nLast, i ) ) ) ) ) + { + aNewPoly[ nNewCount++ ] = mpImplPolygon->mpPointAry[ nLast = i ]; + } + } + + if( nNewCount == 1 ) + aNewPoly.Clear(); + else + aNewPoly.SetSize( nNewCount ); + } + + *this = aNewPoly; + } + + nSize = mpImplPolygon->mnPoints; + + if( nSize > 1 ) + { + if( ( nOptimizeFlags & POLY_OPTIMIZE_CLOSE ) && + ( mpImplPolygon->mpPointAry[ 0 ] != mpImplPolygon->mpPointAry[ nSize - 1 ] ) ) + { + SetSize( mpImplPolygon->mnPoints + 1 ); + mpImplPolygon->mpPointAry[ mpImplPolygon->mnPoints - 1 ] = mpImplPolygon->mpPointAry[ 0 ]; + } + else if( ( nOptimizeFlags & POLY_OPTIMIZE_OPEN ) && + ( mpImplPolygon->mpPointAry[ 0 ] == mpImplPolygon->mpPointAry[ nSize - 1 ] ) ) + { + const Point& rFirst = mpImplPolygon->mpPointAry[ 0 ]; + + while( nSize && ( mpImplPolygon->mpPointAry[ nSize - 1 ] == rFirst ) ) + nSize--; + + SetSize( nSize ); + } + } + } +} + +// ======================================================================= + +/* Recursively subdivide cubic bezier curve via deCasteljau. + + @param rPointIter + Output iterator, where the subdivided polylines are written to. + + @param d + Squared difference of curve to a straight line + + @param P* + Exactly four points, interpreted as support and control points of + a cubic bezier curve. Must be in device coordinates, since stop + criterion is based on the following assumption: the device has a + finite resolution, it is thus sufficient to stop subdivision if the + curve does not deviate more than one pixel from a straight line. + +*/ +static void ImplAdaptiveSubdivide( ::std::back_insert_iterator< ::std::vector< Point > >& rPointIter, + const double old_d2, + int recursionDepth, + const double d2, + const double P1x, const double P1y, + const double P2x, const double P2y, + const double P3x, const double P3y, + const double P4x, const double P4y ) +{ + // Hard limit on recursion depth, empiric number. + enum {maxRecursionDepth=128}; + + // Perform bezier flatness test (lecture notes from R. Schaback, + // Mathematics of Computer-Aided Design, Uni Goettingen, 2000) + // + // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)|| + // 0<=j<=n + // + // What is calculated here is an upper bound to the distance from + // a line through b_0 and b_3 (P1 and P4 in our notation) and the + // curve. We can drop 0 and n from the running indices, since the + // argument of max becomes zero for those cases. + const double fJ1x( P2x - P1x - 1.0/3.0*(P4x - P1x) ); + const double fJ1y( P2y - P1y - 1.0/3.0*(P4y - P1y) ); + const double fJ2x( P3x - P1x - 2.0/3.0*(P4x - P1x) ); + const double fJ2y( P3y - P1y - 2.0/3.0*(P4y - P1y) ); + const double distance2( ::std::max( fJ1x*fJ1x + fJ1y*fJ1y, + fJ2x*fJ2x + fJ2y*fJ2y) ); + + // stop if error measure does not improve anymore. This is a + // safety guard against floating point inaccuracies. + // stop at recursion level 128. This is a safety guard against + // floating point inaccuracies. + // stop if distance from line is guaranteed to be bounded by d + if( old_d2 > d2 && + recursionDepth < maxRecursionDepth && + distance2 >= d2 ) + { + // deCasteljau bezier arc, split at t=0.5 + // Foley/vanDam, p. 508 + const double L1x( P1x ), L1y( P1y ); + const double L2x( (P1x + P2x)*0.5 ), L2y( (P1y + P2y)*0.5 ); + const double Hx ( (P2x + P3x)*0.5 ), Hy ( (P2y + P3y)*0.5 ); + const double L3x( (L2x + Hx)*0.5 ), L3y( (L2y + Hy)*0.5 ); + const double R4x( P4x ), R4y( P4y ); + const double R3x( (P3x + P4x)*0.5 ), R3y( (P3y + P4y)*0.5 ); + const double R2x( (Hx + R3x)*0.5 ), R2y( (Hy + R3y)*0.5 ); + const double R1x( (L3x + R2x)*0.5 ), R1y( (L3y + R2y)*0.5 ); + const double L4x( R1x ), L4y( R1y ); + + // subdivide further + ++recursionDepth; + ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, L1x, L1y, L2x, L2y, L3x, L3y, L4x, L4y); + ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, R1x, R1y, R2x, R2y, R3x, R3y, R4x, R4y); + } + else + { + // requested resolution reached. + // Add end points to output iterator. + // order is preserved, since this is so to say depth first traversal. + *rPointIter++ = Point( FRound(P1x), FRound(P1y) ); + } +} + +// ======================================================================= + +void Polygon::AdaptiveSubdivide( Polygon& rResult, const double d ) const +{ + if( !mpImplPolygon->mpFlagAry ) + { + rResult = *this; + } + else + { + USHORT i; + USHORT nPts( GetSize() ); + ::std::vector< Point > aPoints; + aPoints.reserve( nPts ); + ::std::back_insert_iterator< ::std::vector< Point > > aPointIter( aPoints ); + + for(i=0; i<nPts;) + { + if( ( i + 3 ) < nPts ) + { + BYTE P1( mpImplPolygon->mpFlagAry[ i ] ); + BYTE P4( mpImplPolygon->mpFlagAry[ i + 3 ] ); + + if( ( POLY_NORMAL == P1 || POLY_SMOOTH == P1 || POLY_SYMMTR == P1 ) && + ( POLY_CONTROL == mpImplPolygon->mpFlagAry[ i + 1 ] ) && + ( POLY_CONTROL == mpImplPolygon->mpFlagAry[ i + 2 ] ) && + ( POLY_NORMAL == P4 || POLY_SMOOTH == P4 || POLY_SYMMTR == P4 ) ) + { + ImplAdaptiveSubdivide( aPointIter, d*d+1.0, 0, d*d, + mpImplPolygon->mpPointAry[ i ].X(), mpImplPolygon->mpPointAry[ i ].Y(), + mpImplPolygon->mpPointAry[ i+1 ].X(), mpImplPolygon->mpPointAry[ i+1 ].Y(), + mpImplPolygon->mpPointAry[ i+2 ].X(), mpImplPolygon->mpPointAry[ i+2 ].Y(), + mpImplPolygon->mpPointAry[ i+3 ].X(), mpImplPolygon->mpPointAry[ i+3 ].Y() ); + i += 3; + continue; + } + } + + *aPointIter++ = mpImplPolygon->mpPointAry[ i++ ]; + } + + // fill result polygon + rResult = Polygon( (USHORT)aPoints.size() ); // ensure sufficient size for copy + ::std::copy(aPoints.begin(), aPoints.end(), rResult.mpImplPolygon->mpPointAry); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::GetIntersection( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetIntersection( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::GetUnion( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetUnion( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::GetDifference( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetDifference( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::GetXOR( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + const PolyPolygon aTmp( *this ); + aTmp.GetXOR( rPolyPoly, rResult ); +} + +// ----------------------------------------------------------------------- + +void Polygon::ImplReduceEdges( Polygon& rPoly, const double& rArea, USHORT nPercent ) +{ + const double fBound = 2000.0 * ( 100 - nPercent ) * 0.01; + USHORT nNumNoChange = 0, nNumRuns = 0; + + while( nNumNoChange < 2 ) + { + USHORT nPntCnt = rPoly.GetSize(), nNewPos = 0; + Polygon aNewPoly( nPntCnt ); + BOOL bChangeInThisRun = FALSE; + + for( USHORT n = 0; n < nPntCnt; n++ ) + { + BOOL bDeletePoint = FALSE; + + if( ( n + nNumRuns ) % 2 ) + { + USHORT nIndPrev = !n ? nPntCnt - 1 : n - 1; + USHORT nIndPrevPrev = !nIndPrev ? nPntCnt - 1 : nIndPrev - 1; + USHORT nIndNext = ( n == nPntCnt-1 ) ? 0 : n + 1; + USHORT nIndNextNext = ( nIndNext == nPntCnt - 1 ) ? 0 : nIndNext + 1; + Vector2D aVec1( rPoly[ nIndPrev ] ); aVec1 -= rPoly[ nIndPrevPrev ]; + Vector2D aVec2( rPoly[ n ] ); aVec2 -= rPoly[ nIndPrev ]; + Vector2D aVec3( rPoly[ nIndNext ] ); aVec3 -= rPoly[ n ]; + Vector2D aVec4( rPoly[ nIndNextNext ] ); aVec4 -= rPoly[ nIndNext ]; + double fDist1 = aVec1.GetLength(), fDist2 = aVec2.GetLength(); + double fDist3 = aVec3.GetLength(), fDist4 = aVec4.GetLength(); + double fTurnB = aVec2.Normalize().Scalar( aVec3.Normalize() ); + + if( fabs( fTurnB ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnB ) > ( 1.0 - SMALL_DVALUE ) ) + bDeletePoint = TRUE; + else + { + Vector2D aVecB( rPoly[ nIndNext ] ); + double fDistB = ( aVecB -= rPoly[ nIndPrev ] ).GetLength(); + double fLenWithB = fDist2 + fDist3; + double fLenFact = ( fDistB != 0.0 ) ? fLenWithB / fDistB : 1.0; + double fTurnPrev = aVec1.Normalize().Scalar( aVec2 ); + double fTurnNext = aVec3.Scalar( aVec4.Normalize() ); + double fGradPrev, fGradB, fGradNext; + + if( fabs( fTurnPrev ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnPrev ) > ( 1.0 - SMALL_DVALUE ) ) + fGradPrev = 0.0; + else + fGradPrev = acos( fTurnPrev ) / ( aVec1.IsNegative( aVec2 ) ? -F_PI180 : F_PI180 ); + + fGradB = acos( fTurnB ) / ( aVec2.IsNegative( aVec3 ) ? -F_PI180 : F_PI180 ); + + if( fabs( fTurnNext ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnNext ) > ( 1.0 - SMALL_DVALUE ) ) + fGradNext = 0.0; + else + fGradNext = acos( fTurnNext ) / ( aVec3.IsNegative( aVec4 ) ? -F_PI180 : F_PI180 ); + + if( ( fGradPrev > 0.0 && fGradB < 0.0 && fGradNext > 0.0 ) || + ( fGradPrev < 0.0 && fGradB > 0.0 && fGradNext < 0.0 ) ) + { + if( ( fLenFact < ( FSQRT2 + SMALL_DVALUE ) ) && + ( ( ( fDist1 + fDist4 ) / ( fDist2 + fDist3 ) ) * 2000.0 ) > fBound ) + { + bDeletePoint = TRUE; + } + } + else + { + double fRelLen = 1.0 - sqrt( fDistB / rArea ); + + if( fRelLen < 0.0 ) + fRelLen = 0.0; + else if( fRelLen > 1.0 ) + fRelLen = 1.0; + + if( ( (UINT32) ( ( ( fLenFact - 1.0 ) * 1000000.0 ) + 0.5 ) < fBound ) && + ( fabs( fGradB ) <= ( fRelLen * fBound * 0.01 ) ) ) + { + bDeletePoint = TRUE; + } + } + } + } + + if( !bDeletePoint ) + aNewPoly[ nNewPos++ ] = rPoly[ n ]; + else + bChangeInThisRun = TRUE; + } + + if( bChangeInThisRun && nNewPos ) + { + aNewPoly.SetSize( nNewPos ); + rPoly = aNewPoly; + nNumNoChange = 0; + } + else + nNumNoChange++; + + nNumRuns++; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Move( long nHorzMove, long nVertMove ) +{ + DBG_CHKTHIS( Polygon, NULL ); + + // Diese Abfrage sollte man fuer die DrawEngine durchfuehren + if ( !nHorzMove && !nVertMove ) + return; + + ImplMakeUnique(); + + // Punkte verschieben + USHORT nCount = mpImplPolygon->mnPoints; + for ( USHORT i = 0; i < nCount; i++ ) + { + Point* pPt = &(mpImplPolygon->mpPointAry[i]); + pPt->X() += nHorzMove; + pPt->Y() += nVertMove; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Translate(const Point& rTrans) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for ( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + mpImplPolygon->mpPointAry[ i ] += rTrans; +} + +// ----------------------------------------------------------------------- + +void Polygon::Scale( double fScaleX, double fScaleY ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for ( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[i]; + rPnt.X() = (long) ( fScaleX * rPnt.X() ); + rPnt.Y() = (long) ( fScaleY * rPnt.Y() ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Rotate( const Point& rCenter, USHORT nAngle10 ) +{ + DBG_CHKTHIS( Polygon, NULL ); + nAngle10 %= 3600; + + if( nAngle10 ) + { + const double fAngle = F_PI1800 * nAngle10; + Rotate( rCenter, sin( fAngle ), cos( fAngle ) ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Rotate( const Point& rCenter, double fSin, double fCos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + long nX, nY; + long nCenterX = rCenter.X(); + long nCenterY = rCenter.Y(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPt = mpImplPolygon->mpPointAry[ i ]; + + nX = rPt.X() - nCenterX; + nY = rPt.Y() - nCenterY; + rPt.X() = (long) FRound( fCos * nX + fSin * nY ) + nCenterX; + rPt.Y() = -(long) FRound( fSin * nX - fCos * nY ) + nCenterY; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::SlantX( long nYRef, double fSin, double fCos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[ i ]; + const long nDy = rPnt.Y() - nYRef; + + rPnt.X() += (long)( fSin * nDy ); + rPnt.Y() = nYRef + (long)( fCos * nDy ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::SlantY( long nXRef, double fSin, double fCos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[ i ]; + const long nDx = rPnt.X() - nXRef; + + rPnt.X() = nXRef + (long)( fCos * nDx ); + rPnt.Y() -= (long)( fSin * nDx ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Distort( const Rectangle& rRefRect, const Polygon& rDistortedRect ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + long Xr, Wr, X1, X2, X3, X4; + long Yr, Hr, Y1, Y2, Y3, Y4; + double fTx, fTy, fUx, fUy; + + Xr = rRefRect.Left(); + Yr = rRefRect.Top(); + Wr = rRefRect.GetWidth(); + Hr = rRefRect.GetHeight(); + + if( Wr && Hr ) + { + DBG_ASSERT( rDistortedRect.mpImplPolygon->mnPoints >= 4, "Distort rect too small!" ); + + X1 = rDistortedRect[0].X(); + Y1 = rDistortedRect[0].Y(); + X2 = rDistortedRect[1].X(); + Y2 = rDistortedRect[1].Y(); + X3 = rDistortedRect[3].X(); + Y3 = rDistortedRect[3].Y(); + X4 = rDistortedRect[2].X(); + Y4 = rDistortedRect[2].Y(); + + for( USHORT i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ ) + { + Point& rPnt = mpImplPolygon->mpPointAry[ i ]; + + fTx = (double)( rPnt.X() - Xr) / Wr; + fTy = (double)( rPnt.Y() - Yr) / Hr; + fUx = 1.0 - fTx; + fUy = 1.0 - fTy; + + rPnt.X() = (long) ( fUy * (fUx * X1 + fTx * X2) + fTy * (fUx * X3 + fTx * X4) ); + rPnt.Y() = (long) ( fUx * (fUy * Y1 + fTy * Y3) + fTx * (fUy * Y2 + fTy * Y4) ); + } + } +} + +// ----------------------------------------------------------------------- + +class ImplPointFilter +{ +public: + virtual void LastPoint() = 0; + virtual void Input( const Point& rPoint ) = 0; +}; + +class ImplPolygonPointFilter : public ImplPointFilter +{ +public: + ImplPolygon* mpPoly; // Nicht loeschen, wird dem Polygon zugewiesen + USHORT mnSize; + + ImplPolygonPointFilter( USHORT nDestSize ) : + mnSize( 0 ) + { + mpPoly = new ImplPolygon( nDestSize ); + } + + virtual void LastPoint(); + virtual void Input( const Point& rPoint ); +}; + +void ImplPolygonPointFilter::Input( const Point& rPoint ) +{ + if ( !mnSize || (rPoint != mpPoly->mpPointAry[mnSize-1]) ) + { + mnSize++; + if ( mnSize > mpPoly->mnPoints ) + mpPoly->ImplSetSize( mnSize ); + mpPoly->mpPointAry[mnSize-1] = rPoint; + } +} + +void ImplPolygonPointFilter::LastPoint() +{ + if ( mnSize < mpPoly->mnPoints ) + mpPoly->ImplSetSize( mnSize ); +}; + +class ImplEdgePointFilter : public ImplPointFilter +{ + Point maFirstPoint; + Point maLastPoint; + ImplPointFilter& mrNextFilter; + const long mnLow; + const long mnHigh; + const int mnEdge; + int mnLastOutside; + BOOL mbFirst; + +public: + ImplEdgePointFilter( int nEdge, long nLow, long nHigh, + ImplPointFilter& rNextFilter ) : + mrNextFilter( rNextFilter ), + mnLow( nLow ), + mnHigh( nHigh ), + mnEdge( nEdge ), + mbFirst( TRUE ) + { + } + + Point EdgeSection( const Point& rPoint, int nEdge ) const; + int VisibleSide( const Point& rPoint ) const; + int IsPolygon() const + { return maFirstPoint == maLastPoint; } + + virtual void Input( const Point& rPoint ); + virtual void LastPoint(); +}; + +inline int ImplEdgePointFilter::VisibleSide( const Point& rPoint ) const +{ + if ( mnEdge & EDGE_HORZ ) + { + return rPoint.X() < mnLow ? EDGE_LEFT : + rPoint.X() > mnHigh ? EDGE_RIGHT : 0; + } + else + { + return rPoint.Y() < mnLow ? EDGE_TOP : + rPoint.Y() > mnHigh ? EDGE_BOTTOM : 0; + } +} + +Point ImplEdgePointFilter::EdgeSection( const Point& rPoint, int nEdge ) const +{ + long lx = maLastPoint.X(); + long ly = maLastPoint.Y(); + long md = rPoint.X() - lx; + long mn = rPoint.Y() - ly; + long nNewX; + long nNewY; + + if ( nEdge & EDGE_VERT ) + { + nNewY = (nEdge == EDGE_TOP) ? mnLow : mnHigh; + long dy = nNewY - ly; + if ( !md ) + nNewX = lx; + else if ( (LONG_MAX / Abs(md)) >= Abs(dy) ) + nNewX = (dy * md) / mn + lx; + else + { + BigInt ady = dy; + ady *= md; + if( ady.IsNeg() ) + if( mn < 0 ) + ady += mn/2; + else + ady -= (mn-1)/2; + else + if( mn < 0 ) + ady -= (mn+1)/2; + else + ady += mn/2; + ady /= mn; + nNewX = (long)ady + lx; + } + } + else + { + nNewX = (nEdge == EDGE_LEFT) ? mnLow : mnHigh; + long dx = nNewX - lx; + if ( !mn ) + nNewY = ly; + else if ( (LONG_MAX / Abs(mn)) >= Abs(dx) ) + nNewY = (dx * mn) / md + ly; + else + { + BigInt adx = dx; + adx *= mn; + if( adx.IsNeg() ) + if( md < 0 ) + adx += md/2; + else + adx -= (md-1)/2; + else + if( md < 0 ) + adx -= (md+1)/2; + else + adx += md/2; + adx /= md; + nNewY = (long)adx + ly; + } + } + + return Point( nNewX, nNewY ); +} + +void ImplEdgePointFilter::Input( const Point& rPoint ) +{ + int nOutside = VisibleSide( rPoint ); + + if ( mbFirst ) + { + maFirstPoint = rPoint; + mbFirst = FALSE; + if ( !nOutside ) + mrNextFilter.Input( rPoint ); + } + else if ( rPoint == maLastPoint ) + return; + else if ( !nOutside ) + { + if ( mnLastOutside ) + mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) ); + mrNextFilter.Input( rPoint ); + } + else if ( !mnLastOutside ) + mrNextFilter.Input( EdgeSection( rPoint, nOutside ) ); + else if ( nOutside != mnLastOutside ) + { + mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) ); + mrNextFilter.Input( EdgeSection( rPoint, nOutside ) ); + } + + maLastPoint = rPoint; + mnLastOutside = nOutside; +} + +void ImplEdgePointFilter::LastPoint() +{ + if ( !mbFirst ) + { + int nOutside = VisibleSide( maFirstPoint ); + + if ( nOutside != mnLastOutside ) + Input( maFirstPoint ); + mrNextFilter.LastPoint(); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Clip( const Rectangle& rRect, BOOL bPolygon ) +{ + // #105251# Justify rect befor edge filtering + Rectangle aJustifiedRect( rRect ); + aJustifiedRect.Justify(); + + USHORT nSourceSize = mpImplPolygon->mnPoints; + ImplPolygonPointFilter aPolygon( nSourceSize ); + ImplEdgePointFilter aHorzFilter( EDGE_HORZ, aJustifiedRect.Left(), aJustifiedRect.Right(), + aPolygon ); + ImplEdgePointFilter aVertFilter( EDGE_VERT, aJustifiedRect.Top(), aJustifiedRect.Bottom(), + aHorzFilter ); + + for ( USHORT i = 0; i < nSourceSize; i++ ) + aVertFilter.Input( mpImplPolygon->mpPointAry[i] ); + if ( bPolygon || aVertFilter.IsPolygon() ) + aVertFilter.LastPoint(); + else + aPolygon.LastPoint(); + + // Alte ImpPolygon-Daten loeschen und die vom ImpPolygonPointFilter + // zuweisen + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } + mpImplPolygon = aPolygon.mpPoly; +} + +// ----------------------------------------------------------------------- + +Rectangle Polygon::GetBoundRect() const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( !mpImplPolygon->mpFlagAry, "GetBoundRect could fail with beziers!" ); + + USHORT nCount = mpImplPolygon->mnPoints; + if( ! nCount ) + return Rectangle(); + + long nXMin, nXMax, nYMin, nYMax; + + const Point* pPt = &(mpImplPolygon->mpPointAry[0]); + nXMin = nXMax = pPt->X(); + nYMin = nYMax = pPt->Y(); + + for ( USHORT i = 0; i < nCount; i++ ) + { + pPt = &(mpImplPolygon->mpPointAry[i]); + + if ( pPt->X() < nXMin ) + nXMin = pPt->X(); + if ( pPt->X() > nXMax ) + nXMax = pPt->X(); + if ( pPt->Y() < nYMin ) + nYMin = pPt->Y(); + if ( pPt->Y() > nYMax ) + nYMax = pPt->Y(); + } + + return Rectangle( nXMin, nYMin, nXMax, nYMax ); +} + +// ----------------------------------------------------------------------- + +double Polygon::GetArea() const +{ + const double fArea = GetSignedArea(); + return( ( fArea < 0.0 ) ? -fArea : fArea ); +} + +// ----------------------------------------------------------------------- + +double Polygon::GetSignedArea() const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( !mpImplPolygon->mpFlagAry, "GetArea could fail with beziers!" ); + + double fArea = 0.0; + + if( mpImplPolygon->mnPoints > 2 ) + { + const USHORT nCount1 = mpImplPolygon->mnPoints - 1; + + for( USHORT i = 0; i < nCount1; ) + { + const Point& rPt = mpImplPolygon->mpPointAry[ i ]; + const Point& rPt1 = mpImplPolygon->mpPointAry[ ++i ]; + fArea += ( rPt.X() - rPt1.X() ) * ( rPt.Y() + rPt1.Y() ); + } + + const Point& rPt = mpImplPolygon->mpPointAry[ nCount1 ]; + const Point& rPt0 = mpImplPolygon->mpPointAry[ 0 ]; + fArea += ( rPt.X() - rPt0.X() ) * ( rPt.Y() + rPt0.Y() ); + } + + return fArea; +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsInside( const Point& rPoint ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( !mpImplPolygon->mpFlagAry, "IsInside could fail with beziers!" ); + + const Rectangle aBound( GetBoundRect() ); + const Line aLine( rPoint, Point( aBound.Right() + 100L, rPoint.Y() ) ); + USHORT nCount = mpImplPolygon->mnPoints; + USHORT nPCounter = 0; + + if ( ( nCount > 2 ) && aBound.IsInside( rPoint ) ) + { + Point aPt1( mpImplPolygon->mpPointAry[ 0 ] ); + Point aIntersection; + Point aLastIntersection; + + while ( ( aPt1 == mpImplPolygon->mpPointAry[ nCount - 1 ] ) && ( nCount > 3 ) ) + nCount--; + + for ( USHORT i = 1; i <= nCount; i++ ) + { + const Point& rPt2 = mpImplPolygon->mpPointAry[ ( i < nCount ) ? i : 0 ]; + + if ( aLine.Intersection( Line( aPt1, rPt2 ), aIntersection ) ) + { + // Hiermit verhindern wir das Einfuegen von + // doppelten Intersections, die gleich hintereinander folgen + if ( nPCounter ) + { + if ( aIntersection != aLastIntersection ) + { + aLastIntersection = aIntersection; + nPCounter++; + } + } + else + { + aLastIntersection = aIntersection; + nPCounter++; + } + } + + aPt1 = rPt2; + } + } + + // innerhalb, wenn die Anzahl der Schnittpunkte ungerade ist + return ( ( nPCounter & 1 ) == 1 ); +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::IsRightOrientated() const +{ + DBG_CHKTHIS( Polygon, NULL ); + return GetSignedArea() >= 0.0; +} + +// ----------------------------------------------------------------------- + +void Polygon::Insert( USHORT nPos, const Point& rPt, PolyFlags eFlags ) +{ + DBG_CHKTHIS( Polygon, NULL ); + ImplMakeUnique(); + + if( nPos >= mpImplPolygon->mnPoints ) + nPos = mpImplPolygon->mnPoints; + + mpImplPolygon->ImplSplit( nPos, 1 ); + mpImplPolygon->mpPointAry[ nPos ] = rPt; + + if( POLY_NORMAL != eFlags ) + { + mpImplPolygon->ImplCreateFlagArray(); + mpImplPolygon->mpFlagAry[ nPos ] = (BYTE) eFlags; + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Insert( USHORT nPos, const Polygon& rPoly ) +{ + DBG_CHKTHIS( Polygon, NULL ); + const USHORT nInsertCount = rPoly.mpImplPolygon->mnPoints; + + if( nInsertCount ) + { + ImplMakeUnique(); + + if( nPos >= mpImplPolygon->mnPoints ) + nPos = mpImplPolygon->mnPoints; + + if( rPoly.mpImplPolygon->mpFlagAry ) + mpImplPolygon->ImplCreateFlagArray(); + + mpImplPolygon->ImplSplit( nPos, nInsertCount, rPoly.mpImplPolygon ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Remove( USHORT nPos, USHORT nCount ) +{ + DBG_CHKTHIS( Polygon, NULL ); + if( nCount && ( nPos < mpImplPolygon->mnPoints ) ) + { + ImplMakeUnique(); + mpImplPolygon->ImplRemove( nPos, nCount ); + } +} + +// ----------------------------------------------------------------------- + +Point& Polygon::operator[]( USHORT nPos ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_ASSERT( nPos < mpImplPolygon->mnPoints, "Polygon::[]: nPos >= nPoints" ); + + ImplMakeUnique(); + return mpImplPolygon->mpPointAry[nPos]; +} + +// ----------------------------------------------------------------------- + +Polygon& Polygon::operator=( const Polygon& rPoly ) +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERT( rPoly.mpImplPolygon->mnRefCount < 0xFFFFFFFE, "Polygon: RefCount overflow" ); + + // Zuerst Referenzcounter erhoehen, damit man sich selbst zuweisen kann + // RefCount == 0 fuer statische Objekte + if ( rPoly.mpImplPolygon->mnRefCount ) + rPoly.mpImplPolygon->mnRefCount++; + + // Wenn es keine statischen ImpDaten sind, dann loeschen, wenn es + // die letzte Referenz ist, sonst Referenzcounter decrementieren + if ( mpImplPolygon->mnRefCount ) + { + if ( mpImplPolygon->mnRefCount > 1 ) + mpImplPolygon->mnRefCount--; + else + delete mpImplPolygon; + } + + mpImplPolygon = rPoly.mpImplPolygon; + return *this; +} + +// ----------------------------------------------------------------------- + +BOOL Polygon::operator==( const Polygon& rPoly ) const +{ + DBG_CHKTHIS( Polygon, NULL ); + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + + if ( (rPoly.mpImplPolygon == mpImplPolygon) ) + return TRUE; + else + return FALSE; +} + +// ----------------------------------------------------------------------- + +sal_Bool Polygon::IsEqual( const Polygon& rPoly ) const +{ + sal_Bool bIsEqual = sal_True;; + sal_uInt16 i; + if ( GetSize() != rPoly.GetSize() ) + bIsEqual = sal_False; + else + { + for ( i = 0; i < GetSize(); i++ ) + { + if ( ( GetPoint( i ) != rPoly.GetPoint( i ) ) || + ( GetFlags( i ) != rPoly.GetFlags( i ) ) ) + { + bIsEqual = sal_False; + break; + } + } + } + return bIsEqual; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStream, Polygon& rPoly ) +{ + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERTWARNING( rIStream.GetVersion(), "Polygon::>> - Solar-Version not set on rIStream" ); + + USHORT i; + USHORT nStart; + USHORT nCurPoints; + USHORT nPoints; + unsigned char bShort; + short nShortX; + short nShortY; + long nLongX; + long nLongY; + + // Anzahl der Punkte einlesen und Array erzeugen + rIStream >> nPoints; + if ( rPoly.mpImplPolygon->mnRefCount != 1 ) + { + if ( rPoly.mpImplPolygon->mnRefCount ) + rPoly.mpImplPolygon->mnRefCount--; + rPoly.mpImplPolygon = new ImplPolygon( nPoints ); + } + else + rPoly.mpImplPolygon->ImplSetSize( nPoints, FALSE ); + + // Je nach CompressMode das Polygon einlesen + if ( rIStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + i = 0; + while ( i < nPoints ) + { + rIStream >> bShort >> nCurPoints; + + if ( bShort ) + { + for ( nStart = i; i < nStart+nCurPoints; i++ ) + { + rIStream >> nShortX >> nShortY; + rPoly.mpImplPolygon->mpPointAry[i].X() = nShortX; + rPoly.mpImplPolygon->mpPointAry[i].Y() = nShortY; + } + } + else + { + for ( nStart = i; i < nStart+nCurPoints; i++ ) + { + rIStream >> nLongX >> nLongY; + rPoly.mpImplPolygon->mpPointAry[i].X() = nLongX; + rPoly.mpImplPolygon->mpPointAry[i].Y() = nLongY; + } + } + } + } + else + { + // Feststellen, ob ueber die Operatoren geschrieben werden muss +#if (SAL_TYPES_SIZEOFLONG) != 4 + if ( 1 ) +#else +#ifdef OSL_BIGENDIAN + if ( rIStream.GetNumberFormatInt() != NUMBERFORMAT_INT_BIGENDIAN ) +#else + if ( rIStream.GetNumberFormatInt() != NUMBERFORMAT_INT_LITTLEENDIAN ) +#endif +#endif + { + for( i = 0; i < nPoints; i++ ) + { + rIStream >> rPoly.mpImplPolygon->mpPointAry[i].X() + >> rPoly.mpImplPolygon->mpPointAry[i].Y(); + } + } + else + rIStream.Read( rPoly.mpImplPolygon->mpPointAry, nPoints*sizeof(Point) ); + } + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const Polygon& rPoly ) +{ + DBG_CHKOBJ( &rPoly, Polygon, NULL ); + DBG_ASSERTWARNING( rOStream.GetVersion(), "Polygon::<< - Solar-Version not set on rOStream" ); + + unsigned char bShort; + unsigned char bCurShort; + USHORT nStart; + USHORT i; + USHORT nPoints = rPoly.GetSize(); + + // Anzahl der Punkte rausschreiben + rOStream << nPoints; + + // Je nach CompressMode das Polygon rausschreiben + if ( rOStream.GetCompressMode() == COMPRESSMODE_FULL ) + { + i = 0; + while ( i < nPoints ) + { + nStart = i; + + // Feststellen, welcher Typ geschrieben werden soll + if ( ((rPoly.mpImplPolygon->mpPointAry[nStart].X() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].X() <= SHRT_MAX)) && + ((rPoly.mpImplPolygon->mpPointAry[nStart].Y() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].Y() <= SHRT_MAX)) ) + bShort = TRUE; + else + bShort = FALSE; + while ( i < nPoints ) + { + // Feststellen, welcher Typ geschrieben werden soll + if ( ((rPoly.mpImplPolygon->mpPointAry[nStart].X() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].X() <= SHRT_MAX)) && + ((rPoly.mpImplPolygon->mpPointAry[nStart].Y() >= SHRT_MIN) && + (rPoly.mpImplPolygon->mpPointAry[nStart].Y() <= SHRT_MAX)) ) + bCurShort = TRUE; + else + bCurShort = FALSE; + + // Wenn sich die Werte in einen anderen Bereich begeben, + // muessen wir neu rausschreiben + if ( bCurShort != bShort ) + { + bShort = bCurShort; + break; + } + + i++; + } + + rOStream << bShort << (USHORT)(i-nStart); + + if ( bShort ) + { + for( ; nStart < i; nStart++ ) + { + rOStream << (short)rPoly.mpImplPolygon->mpPointAry[nStart].X() + << (short)rPoly.mpImplPolygon->mpPointAry[nStart].Y(); + } + } + else + { + for( ; nStart < i; nStart++ ) + { + rOStream << rPoly.mpImplPolygon->mpPointAry[nStart].X() + << rPoly.mpImplPolygon->mpPointAry[nStart].Y(); + } + } + } + } + else + { + // Feststellen, ob ueber die Operatoren geschrieben werden muss +#if (SAL_TYPES_SIZEOFLONG) != 4 + if ( 1 ) +#else +#ifdef OSL_BIGENDIAN + if ( rOStream.GetNumberFormatInt() != NUMBERFORMAT_INT_BIGENDIAN ) +#else + if ( rOStream.GetNumberFormatInt() != NUMBERFORMAT_INT_LITTLEENDIAN ) +#endif +#endif + { + for( i = 0; i < nPoints; i++ ) + { + rOStream << rPoly.mpImplPolygon->mpPointAry[i].X() + << rPoly.mpImplPolygon->mpPointAry[i].Y(); + } + } + else + { + if ( nPoints ) + rOStream.Write( rPoly.mpImplPolygon->mpPointAry, nPoints*sizeof(Point) ); + } + } + + return rOStream; +} + +// ----------------------------------------------------------------------- + +void Polygon::ImplRead( SvStream& rIStream ) +{ + sal_uInt8 bHasPolyFlags; + + rIStream >> *this + >> bHasPolyFlags; + + if ( bHasPolyFlags ) + { + mpImplPolygon->mpFlagAry = new sal_uInt8[ mpImplPolygon->mnPoints ]; + rIStream.Read( mpImplPolygon->mpFlagAry, mpImplPolygon->mnPoints ); + } +} + +// ----------------------------------------------------------------------- + +void Polygon::Read( SvStream& rIStream ) +{ + VersionCompat aCompat( rIStream, STREAM_READ ); + + ImplRead( rIStream ); +} + +// ----------------------------------------------------------------------- + +void Polygon::ImplWrite( SvStream& rOStream ) const +{ + sal_uInt8 bHasPolyFlags = mpImplPolygon->mpFlagAry != NULL; + rOStream << *this + << bHasPolyFlags; + + if ( bHasPolyFlags ) + rOStream.Write( mpImplPolygon->mpFlagAry, mpImplPolygon->mnPoints ); +} + +// ----------------------------------------------------------------------- + +void Polygon::Write( SvStream& rOStream ) const +{ + VersionCompat aCompat( rOStream, STREAM_WRITE, 1 ); + + ImplWrite( rOStream ); +} + +// ----------------------------------------------------------------------- +// #i74631# numerical correction method for B2DPolygon +void impCorrectContinuity(basegfx::B2DPolygon& roPolygon, sal_uInt32 nIndex, BYTE nCFlag) +{ + const sal_uInt32 nPointCount(roPolygon.count()); + OSL_ENSURE(nIndex < nPointCount, "impCorrectContinuity: index access out of range (!)"); + + if(nIndex < nPointCount && (POLY_SMOOTH == nCFlag || POLY_SYMMTR == nCFlag)) + { + if(roPolygon.isPrevControlPointUsed(nIndex) && roPolygon.isNextControlPointUsed(nIndex)) + { + const basegfx::B2DPoint aPoint(roPolygon.getB2DPoint(nIndex)); + + if(POLY_SMOOTH == nCFlag) + { + // C1: apply inverse direction of prev to next, keep length of next + const basegfx::B2DVector aOriginalNext(roPolygon.getNextControlPoint(nIndex) - aPoint); + basegfx::B2DVector aNewNext(aPoint - roPolygon.getPrevControlPoint(nIndex)); + + aNewNext.setLength(aOriginalNext.getLength()); + roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + aNewNext)); + } + else // POLY_SYMMTR + { + // C2: apply inverse control point to next + roPolygon.setNextControlPoint(nIndex, (2.0 * aPoint) - roPolygon.getPrevControlPoint(nIndex)); + } + } + } +} + +// ----------------------------------------------------------------------- +// convert to basegfx::B2DPolygon and return +basegfx::B2DPolygon Polygon::getB2DPolygon() const +{ + basegfx::B2DPolygon aRetval; + const sal_uInt16 nCount(mpImplPolygon->mnPoints); + + if(nCount) + { + if(mpImplPolygon->mpFlagAry) + { + // handling for curves. Add start point + const Point aStartPoint(mpImplPolygon->mpPointAry[0]); + BYTE nPointFlag(mpImplPolygon->mpFlagAry[0]); + aRetval.append(basegfx::B2DPoint(aStartPoint.X(), aStartPoint.Y())); + Point aControlA, aControlB; + + for(sal_uInt16 a(1); a < nCount;) + { + bool bControlA(false); + bool bControlB(false); + + if(POLY_CONTROL == mpImplPolygon->mpFlagAry[a]) + { + aControlA = mpImplPolygon->mpPointAry[a++]; + bControlA = true; + } + + if(a < nCount && POLY_CONTROL == mpImplPolygon->mpFlagAry[a]) + { + aControlB = mpImplPolygon->mpPointAry[a++]; + bControlB = true; + } + + // assert invalid polygons + OSL_ENSURE(bControlA == bControlB, "Polygon::getB2DPolygon: Invalid source polygon (!)"); + + if(a < nCount) + { + const Point aEndPoint(mpImplPolygon->mpPointAry[a]); + + if(bControlA) + { + // bezier edge, add + aRetval.appendBezierSegment( + basegfx::B2DPoint(aControlA.X(), aControlA.Y()), + basegfx::B2DPoint(aControlB.X(), aControlB.Y()), + basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y())); + + impCorrectContinuity(aRetval, aRetval.count() - 2, nPointFlag); + } + else + { + // no bezier edge, add end point + aRetval.append(basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y())); + } + + nPointFlag = mpImplPolygon->mpFlagAry[a++]; + } + } + + // if exist, remove double first/last points, set closed and correct control points + basegfx::tools::checkClosed(aRetval); + + if(aRetval.isClosed()) + { + // closeWithGeometryChange did really close, so last point(s) were removed. + // Correct the continuity in the changed point + impCorrectContinuity(aRetval, 0, mpImplPolygon->mpFlagAry[0]); + } + } + else + { + // extra handling for non-curves (most-used case) for speedup + for(sal_uInt16 a(0); a < nCount; a++) + { + // get point and add + const Point aPoint(mpImplPolygon->mpPointAry[a]); + aRetval.append(basegfx::B2DPoint(aPoint.X(), aPoint.Y())); + } + + // set closed flag + basegfx::tools::checkClosed(aRetval); + } + } + + return aRetval; +} + +// ----------------------------------------------------------------------- +// constructor to convert from basegfx::B2DPolygon +// #i76891# Needed to change from adding all control points (even for unused +// edges) and creating a fixed-size Polygon in the first run to creating the +// minimal Polygon. This requires a temporary Point- and Flag-Array for curves +// and a memcopy at ImplPolygon creation, but contains no zero-controlpoints +// for straight edges. +Polygon::Polygon(const basegfx::B2DPolygon& rPolygon) +: mpImplPolygon(0) +{ + DBG_CTOR( Polygon, NULL ); + + const bool bCurve(rPolygon.areControlPointsUsed()); + const bool bClosed(rPolygon.isClosed()); + sal_uInt32 nB2DLocalCount(rPolygon.count()); + + if(bCurve) + { + // #127979# Reduce source point count hard to the limit of the tools Polygon + if(nB2DLocalCount > ((0x0000ffff / 3L) - 1L)) + { + DBG_ERROR("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)"); + nB2DLocalCount = ((0x0000ffff / 3L) - 1L); + } + + // calculate target point count + const sal_uInt32 nLoopCount(bClosed ? nB2DLocalCount : (nB2DLocalCount ? nB2DLocalCount - 1L : 0L )); + + if(nLoopCount) + { + // calculate maximum array size and allocate; prepare insert index + const sal_uInt32 nMaxTargetCount((nLoopCount * 3) + 1); + mpImplPolygon = new ImplPolygon(static_cast< sal_uInt16 >(nMaxTargetCount), true); + + // prepare insert index and current point + sal_uInt32 nArrayInsert(0); + basegfx::B2DCubicBezier aBezier; + aBezier.setStartPoint(rPolygon.getB2DPoint(0)); + + for(sal_uInt32 a(0L); a < nLoopCount; a++) + { + // add current point (always) and remember StartPointIndex for evtl. later corrections + const Point aStartPoint(FRound(aBezier.getStartPoint().getX()), FRound(aBezier.getStartPoint().getY())); + const sal_uInt32 nStartPointIndex(nArrayInsert); + mpImplPolygon->mpPointAry[nStartPointIndex] = aStartPoint; + mpImplPolygon->mpFlagAry[nStartPointIndex] = (BYTE)POLY_NORMAL; + nArrayInsert++; + + // prepare next segment + const sal_uInt32 nNextIndex((a + 1) % nB2DLocalCount); + aBezier.setEndPoint(rPolygon.getB2DPoint(nNextIndex)); + aBezier.setControlPointA(rPolygon.getNextControlPoint(a)); + aBezier.setControlPointB(rPolygon.getPrevControlPoint(nNextIndex)); + + if(aBezier.isBezier()) + { + // if one is used, add always two control points due to the old schema + mpImplPolygon->mpPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointA().getX()), FRound(aBezier.getControlPointA().getY())); + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_CONTROL; + nArrayInsert++; + + mpImplPolygon->mpPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointB().getX()), FRound(aBezier.getControlPointB().getY())); + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_CONTROL; + nArrayInsert++; + } + + // test continuity with previous control point to set flag value + if(aBezier.getControlPointA() != aBezier.getStartPoint() && (bClosed || a)) + { + const basegfx::B2VectorContinuity eCont(rPolygon.getContinuityInPoint(a)); + + if(basegfx::CONTINUITY_C1 == eCont) + { + mpImplPolygon->mpFlagAry[nStartPointIndex] = (BYTE)POLY_SMOOTH; + } + else if(basegfx::CONTINUITY_C2 == eCont) + { + mpImplPolygon->mpFlagAry[nStartPointIndex] = (BYTE)POLY_SYMMTR; + } + } + + // prepare next polygon step + aBezier.setStartPoint(aBezier.getEndPoint()); + } + + if(bClosed) + { + // add first point again as closing point due to old definition + mpImplPolygon->mpPointAry[nArrayInsert] = mpImplPolygon->mpPointAry[0]; + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_NORMAL; + nArrayInsert++; + } + else + { + // add last point as closing point + const basegfx::B2DPoint aClosingPoint(rPolygon.getB2DPoint(nB2DLocalCount - 1L)); + const Point aEnd(FRound(aClosingPoint.getX()), FRound(aClosingPoint.getY())); + mpImplPolygon->mpPointAry[nArrayInsert] = aEnd; + mpImplPolygon->mpFlagAry[nArrayInsert] = (BYTE)POLY_NORMAL; + nArrayInsert++; + } + + DBG_ASSERT(nArrayInsert <= nMaxTargetCount, "Polygon::Polygon from basegfx::B2DPolygon: wrong max point count estimation (!)"); + + if(nArrayInsert != nMaxTargetCount) + { + mpImplPolygon->ImplSetSize(static_cast< sal_uInt16 >(nArrayInsert), true); + } + } + } + else + { + // #127979# Reduce source point count hard to the limit of the tools Polygon + if(nB2DLocalCount > (0x0000ffff - 1L)) + { + DBG_ERROR("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)"); + nB2DLocalCount = (0x0000ffff - 1L); + } + + if(nB2DLocalCount) + { + // point list creation + const sal_uInt32 nTargetCount(nB2DLocalCount + (bClosed ? 1L : 0L)); + mpImplPolygon = new ImplPolygon( static_cast< sal_uInt16 >(nTargetCount) ); + sal_uInt16 nIndex(0); + + for(sal_uInt32 a(0L); a < nB2DLocalCount; a++) + { + basegfx::B2DPoint aB2DPoint(rPolygon.getB2DPoint(a)); + Point aPoint(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY())); + mpImplPolygon->mpPointAry[nIndex++] = aPoint; + } + + if(bClosed) + { + // add first point as closing point + mpImplPolygon->mpPointAry[nIndex] = mpImplPolygon->mpPointAry[0]; + } + } + } + + if(!mpImplPolygon) + { + // no content yet, create empty polygon + mpImplPolygon = (ImplPolygon*)(&aStaticImplPolygon); + } +} + +// eof diff --git a/tools/source/generic/poly2.cxx b/tools/source/generic/poly2.cxx new file mode 100644 index 000000000000..692e47a9d1d2 --- /dev/null +++ b/tools/source/generic/poly2.cxx @@ -0,0 +1,894 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: poly2.cxx,v $ + * $Revision: 1.23 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _SV_POLY2_CXX + +#define POLY_CLIP_INT 0 +#define POLY_CLIP_UNION 1 +#define POLY_CLIP_DIFF 2 +#define POLY_CLIP_XOR 3 + +#include <rtl/math.hxx> +#include <poly.h> +#include <tools/poly.hxx> +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygoncutter.hxx> + +// --------------- +// - PolyPolygon - +// --------------- + +DBG_NAME( PolyPolygon ) + +// ----------------------------------------------------------------------- + +ImplPolyPolygon::ImplPolyPolygon( USHORT nInitSize ) +{ + mnRefCount = 1; + mnCount = nInitSize; + mnSize = nInitSize; + mnResize = 16; + mpPolyAry = new SVPPOLYGON[ nInitSize ]; +} + +// ----------------------------------------------------------------------- + +ImplPolyPolygon::ImplPolyPolygon( const ImplPolyPolygon& rImplPolyPoly ) +{ + mnRefCount = 1; + mnCount = rImplPolyPoly.mnCount; + mnSize = rImplPolyPoly.mnSize; + mnResize = rImplPolyPoly.mnResize; + + if ( rImplPolyPoly.mpPolyAry ) + { + mpPolyAry = new SVPPOLYGON[mnSize]; + for ( USHORT i = 0; i < mnCount; i++ ) + mpPolyAry[i] = new Polygon( *rImplPolyPoly.mpPolyAry[i] ); + } + else + mpPolyAry = NULL; +} + +// ----------------------------------------------------------------------- + +ImplPolyPolygon::~ImplPolyPolygon() +{ + if ( mpPolyAry ) + { + for ( USHORT i = 0; i < mnCount; i++ ) + delete mpPolyAry[i]; + delete[] mpPolyAry; + } +} + +// ======================================================================= + +PolyPolygon::PolyPolygon( USHORT nInitSize, USHORT nResize ) +{ + DBG_CTOR( PolyPolygon, NULL ); + + if ( nInitSize > MAX_POLYGONS ) + nInitSize = MAX_POLYGONS; + else if ( !nInitSize ) + nInitSize = 1; + if ( nResize > MAX_POLYGONS ) + nResize = MAX_POLYGONS; + else if ( !nResize ) + nResize = 1; + mpImplPolyPolygon = new ImplPolyPolygon( nInitSize, nResize ); +} + +// ----------------------------------------------------------------------- + +PolyPolygon::PolyPolygon( const Polygon& rPoly ) +{ + DBG_CTOR( PolyPolygon, NULL ); + + if ( rPoly.GetSize() ) + { + mpImplPolyPolygon = new ImplPolyPolygon( 1 ); + mpImplPolyPolygon->mpPolyAry[0] = new Polygon( rPoly ); + } + else + mpImplPolyPolygon = new ImplPolyPolygon( 16, 16 ); +} + +// ----------------------------------------------------------------------- + +PolyPolygon::PolyPolygon( USHORT nPoly, const USHORT* pPointCountAry, + const Point* pPtAry ) +{ + DBG_CTOR( PolyPolygon, NULL ); + + if ( nPoly > MAX_POLYGONS ) + nPoly = MAX_POLYGONS; + + mpImplPolyPolygon = new ImplPolyPolygon( nPoly ); + for ( USHORT i = 0; i < nPoly; i++ ) + { + mpImplPolyPolygon->mpPolyAry[i] = new Polygon( *pPointCountAry, pPtAry ); + pPtAry += *pPointCountAry; + pPointCountAry++; + } +} + +// ----------------------------------------------------------------------- + +PolyPolygon::PolyPolygon( const PolyPolygon& rPolyPoly ) +{ + DBG_CTOR( PolyPolygon, NULL ); + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + DBG_ASSERT( rPolyPoly.mpImplPolyPolygon->mnRefCount < 0xFFFFFFFE, "PolyPolygon: RefCount overflow" ); + + mpImplPolyPolygon = rPolyPoly.mpImplPolyPolygon; + mpImplPolyPolygon->mnRefCount++; +} + +// ----------------------------------------------------------------------- + +PolyPolygon::~PolyPolygon() +{ + DBG_DTOR( PolyPolygon, NULL ); + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + mpImplPolyPolygon->mnRefCount--; + else + delete mpImplPolyPolygon; +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Insert( const Polygon& rPoly, USHORT nPos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + if ( mpImplPolyPolygon->mnCount >= MAX_POLYGONS ) + return; + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + if ( nPos > mpImplPolyPolygon->mnCount ) + nPos = mpImplPolyPolygon->mnCount; + + if ( !mpImplPolyPolygon->mpPolyAry ) + mpImplPolyPolygon->mpPolyAry = new SVPPOLYGON[mpImplPolyPolygon->mnSize]; + else if ( mpImplPolyPolygon->mnCount == mpImplPolyPolygon->mnSize ) + { + USHORT nOldSize = mpImplPolyPolygon->mnSize; + USHORT nNewSize = nOldSize + mpImplPolyPolygon->mnResize; + SVPPOLYGON* pNewAry; + + if ( nNewSize >= MAX_POLYGONS ) + nNewSize = MAX_POLYGONS; + pNewAry = new SVPPOLYGON[nNewSize]; + memcpy( pNewAry, mpImplPolyPolygon->mpPolyAry, nPos*sizeof(SVPPOLYGON) ); + memcpy( pNewAry+nPos+1, mpImplPolyPolygon->mpPolyAry+nPos, + (nOldSize-nPos)*sizeof(SVPPOLYGON) ); + delete[] mpImplPolyPolygon->mpPolyAry; + mpImplPolyPolygon->mpPolyAry = pNewAry; + mpImplPolyPolygon->mnSize = nNewSize; + } + else if ( nPos < mpImplPolyPolygon->mnCount ) + { + memmove( mpImplPolyPolygon->mpPolyAry+nPos+1, + mpImplPolyPolygon->mpPolyAry+nPos, + (mpImplPolyPolygon->mnCount-nPos)*sizeof(SVPPOLYGON) ); + } + + mpImplPolyPolygon->mpPolyAry[nPos] = new Polygon( rPoly ); + mpImplPolyPolygon->mnCount++; +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Remove( USHORT nPos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERT( nPos < Count(), "PolyPolygon::Remove(): nPos >= nSize" ); + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + delete mpImplPolyPolygon->mpPolyAry[nPos]; + mpImplPolyPolygon->mnCount--; + memmove( mpImplPolyPolygon->mpPolyAry+nPos, + mpImplPolyPolygon->mpPolyAry+nPos+1, + (mpImplPolyPolygon->mnCount-nPos)*sizeof(SVPPOLYGON) ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Replace( const Polygon& rPoly, USHORT nPos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERT( nPos < Count(), "PolyPolygon::Replace(): nPos >= nSize" ); + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + delete mpImplPolyPolygon->mpPolyAry[nPos]; + mpImplPolyPolygon->mpPolyAry[nPos] = new Polygon( rPoly ); +} + +// ----------------------------------------------------------------------- + +const Polygon& PolyPolygon::GetObject( USHORT nPos ) const +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERT( nPos < Count(), "PolyPolygon::GetObject(): nPos >= nSize" ); + + return *(mpImplPolyPolygon->mpPolyAry[nPos]); +} + +// ----------------------------------------------------------------------- + +BOOL PolyPolygon::IsRect() const +{ + BOOL bIsRect = FALSE; + if ( Count() == 1 ) + bIsRect = mpImplPolyPolygon->mpPolyAry[ 0 ]->IsRect(); + return bIsRect; +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Clear() +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( mpImplPolyPolygon->mnResize, + mpImplPolyPolygon->mnResize ); + } + else + { + if ( mpImplPolyPolygon->mpPolyAry ) + { + for ( USHORT i = 0; i < mpImplPolyPolygon->mnCount; i++ ) + delete mpImplPolyPolygon->mpPolyAry[i]; + delete[] mpImplPolyPolygon->mpPolyAry; + mpImplPolyPolygon->mpPolyAry = NULL; + mpImplPolyPolygon->mnCount = 0; + mpImplPolyPolygon->mnSize = mpImplPolyPolygon->mnResize; + } + } +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Optimize( ULONG nOptimizeFlags, const PolyOptimizeData* pData ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + if( nOptimizeFlags ) + { + double fArea; + const BOOL bEdges = ( nOptimizeFlags & POLY_OPTIMIZE_EDGES ) == POLY_OPTIMIZE_EDGES; + USHORT nPercent = 0; + + if( bEdges ) + { + const Rectangle aBound( GetBoundRect() ); + + fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5; + nPercent = pData ? pData->GetPercentValue() : 50; + nOptimizeFlags &= ~POLY_OPTIMIZE_EDGES; + } + + // watch for ref counter + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Optimize polygons + for( USHORT i = 0, nPolyCount = mpImplPolyPolygon->mnCount; i < nPolyCount; i++ ) + { + if( bEdges ) + { + mpImplPolyPolygon->mpPolyAry[ i ]->Optimize( POLY_OPTIMIZE_NO_SAME ); + Polygon::ImplReduceEdges( *( mpImplPolyPolygon->mpPolyAry[ i ] ), fArea, nPercent ); + } + + if( nOptimizeFlags ) + mpImplPolyPolygon->mpPolyAry[ i ]->Optimize( nOptimizeFlags, pData ); + } + } +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::AdaptiveSubdivide( PolyPolygon& rResult, const double d ) const +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + rResult.Clear(); + + Polygon aPolygon; + + for( USHORT i = 0; i < mpImplPolyPolygon->mnCount; i++ ) + { + mpImplPolyPolygon->mpPolyAry[ i ]->AdaptiveSubdivide( aPolygon, d ); + rResult.Insert( aPolygon ); + } +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::GetIntersection( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + ImplDoOperation( rPolyPoly, rResult, POLY_CLIP_INT ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::GetUnion( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + ImplDoOperation( rPolyPoly, rResult, POLY_CLIP_UNION ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::GetDifference( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + ImplDoOperation( rPolyPoly, rResult, POLY_CLIP_DIFF ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::GetXOR( const PolyPolygon& rPolyPoly, PolyPolygon& rResult ) const +{ + ImplDoOperation( rPolyPoly, rResult, POLY_CLIP_XOR ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::ImplDoOperation( const PolyPolygon& rPolyPoly, PolyPolygon& rResult, ULONG nOperation ) const +{ + // Convert to B2DPolyPolygon, temporarily. It might be + // advantageous in the future, to have a PolyPolygon adaptor that + // just simulates a B2DPolyPolygon here... + basegfx::B2DPolyPolygon aMergePolyPolygonA( getB2DPolyPolygon() ); + basegfx::B2DPolyPolygon aMergePolyPolygonB( rPolyPoly.getB2DPolyPolygon() ); + + // normalize the two polypolygons before. Force properly oriented + // polygons. + aMergePolyPolygonA = basegfx::tools::prepareForPolygonOperation( aMergePolyPolygonA ); + aMergePolyPolygonB = basegfx::tools::prepareForPolygonOperation( aMergePolyPolygonB ); + + switch( nOperation ) + { + // All code extracted from svx/source/svdraw/svedtv2.cxx + // ----------------------------------------------------- + + case POLY_CLIP_UNION: + { + // merge A and B (OR) + aMergePolyPolygonA = basegfx::tools::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB); + break; + } + + case POLY_CLIP_DIFF: + { + // substract B from A (DIFF) + aMergePolyPolygonA = basegfx::tools::solvePolygonOperationDiff(aMergePolyPolygonA, aMergePolyPolygonB); + break; + } + + case POLY_CLIP_XOR: + { + // compute XOR between poly A and B + aMergePolyPolygonA = basegfx::tools::solvePolygonOperationXor(aMergePolyPolygonA, aMergePolyPolygonB); + break; + } + + default: + case POLY_CLIP_INT: + { + // cut poly 1 against polys 2..n (AND) + aMergePolyPolygonA = basegfx::tools::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB); + break; + } + } + + rResult = PolyPolygon( aMergePolyPolygonA ); +} + +// ----------------------------------------------------------------------- + +USHORT PolyPolygon::Count() const +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + return mpImplPolyPolygon->mnCount; +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Move( long nHorzMove, long nVertMove ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Diese Abfrage sollte man fuer die DrawEngine durchfuehren + if( nHorzMove || nVertMove ) + { + // Referenzcounter beruecksichtigen + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + USHORT nPolyCount = mpImplPolyPolygon->mnCount; + for ( USHORT i = 0; i < nPolyCount; i++ ) + mpImplPolyPolygon->mpPolyAry[i]->Move( nHorzMove, nVertMove ); + } +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Translate( const Point& rTrans ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->Translate( rTrans ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Scale( double fScaleX, double fScaleY ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->Scale( fScaleX, fScaleY ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Rotate( const Point& rCenter, USHORT nAngle10 ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + nAngle10 %= 3600; + + if( nAngle10 ) + { + const double fAngle = F_PI1800 * nAngle10; + Rotate( rCenter, sin( fAngle ), cos( fAngle ) ); + } +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Rotate( const Point& rCenter, double fSin, double fCos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->Rotate( rCenter, fSin, fCos ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::SlantX( long nYRef, double fSin, double fCos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->SlantX( nYRef, fSin, fCos ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::SlantY( long nXRef, double fSin, double fCos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->SlantY( nXRef, fSin, fCos ); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Distort( const Rectangle& rRefRect, const Polygon& rDistortedRect ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + // Referenzcounter beruecksichtigen + if( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Punkte verschieben + for ( USHORT i = 0, nCount = mpImplPolyPolygon->mnCount; i < nCount; i++ ) + mpImplPolyPolygon->mpPolyAry[ i ]->Distort( rRefRect, rDistortedRect ); +} + + +// ----------------------------------------------------------------------- + +void PolyPolygon::Clip( const Rectangle& rRect ) +{ + // Polygon-Clippen + USHORT nPolyCount = mpImplPolyPolygon->mnCount; + USHORT i; + + if ( !nPolyCount ) + return; + + // Referenzcounter beruecksichtigen + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + // Erst jedes Polygon Clippen und dann die leeren entfernen + for ( i = 0; i < nPolyCount; i++ ) + mpImplPolyPolygon->mpPolyAry[i]->Clip( rRect ); + while ( nPolyCount ) + { + if ( GetObject( nPolyCount-1 ).GetSize() <= 2 ) + Remove( nPolyCount-1 ); + nPolyCount--; + } +} + +// ----------------------------------------------------------------------- + +Rectangle PolyPolygon::GetBoundRect() const +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + + long nXMin=0, nXMax=0, nYMin=0, nYMax=0; + BOOL bFirst = TRUE; + USHORT nPolyCount = mpImplPolyPolygon->mnCount; + + for ( USHORT n = 0; n < nPolyCount; n++ ) + { + const Polygon* pPoly = mpImplPolyPolygon->mpPolyAry[n]; + const Point* pAry = pPoly->GetConstPointAry(); + USHORT nPointCount = pPoly->GetSize(); + + for ( USHORT i = 0; i < nPointCount; i++ ) + { + const Point* pPt = &pAry[ i ]; + + if ( bFirst ) + { + nXMin = nXMax = pPt->X(); + nYMin = nYMax = pPt->Y(); + bFirst = FALSE; + } + else + { + if ( pPt->X() < nXMin ) + nXMin = pPt->X(); + if ( pPt->X() > nXMax ) + nXMax = pPt->X(); + if ( pPt->Y() < nYMin ) + nYMin = pPt->Y(); + if ( pPt->Y() > nYMax ) + nYMax = pPt->Y(); + } + } + } + + if ( !bFirst ) + return Rectangle( nXMin, nYMin, nXMax, nYMax ); + else + return Rectangle(); +} + +// ----------------------------------------------------------------------- + +Polygon& PolyPolygon::operator[]( USHORT nPos ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERT( nPos < Count(), "PolyPolygon::[](): nPos >= nSize" ); + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + { + mpImplPolyPolygon->mnRefCount--; + mpImplPolyPolygon = new ImplPolyPolygon( *mpImplPolyPolygon ); + } + + return *(mpImplPolyPolygon->mpPolyAry[nPos]); +} + +// ----------------------------------------------------------------------- + +PolyPolygon& PolyPolygon::operator=( const PolyPolygon& rPolyPoly ) +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + DBG_ASSERT( rPolyPoly.mpImplPolyPolygon->mnRefCount < 0xFFFFFFFE, "PolyPolygon: RefCount overflow" ); + + rPolyPoly.mpImplPolyPolygon->mnRefCount++; + + if ( mpImplPolyPolygon->mnRefCount > 1 ) + mpImplPolyPolygon->mnRefCount--; + else + delete mpImplPolyPolygon; + + mpImplPolyPolygon = rPolyPoly.mpImplPolyPolygon; + return *this; +} + +// ----------------------------------------------------------------------- + +BOOL PolyPolygon::operator==( const PolyPolygon& rPolyPoly ) const +{ + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + + if ( rPolyPoly.mpImplPolyPolygon == mpImplPolyPolygon ) + return TRUE; + else + return FALSE; +} + +// ----------------------------------------------------------------------- + +sal_Bool PolyPolygon::IsEqual( const PolyPolygon& rPolyPoly ) const +{ + sal_Bool bIsEqual = sal_True; + if ( Count() != rPolyPoly.Count() ) + bIsEqual = sal_False; + else + { + sal_uInt16 i; + for ( i = 0; i < Count(); i++ ) + { + if (!GetObject( i ).IsEqual( rPolyPoly.GetObject( i ) ) ) + { + bIsEqual = sal_False; + break; + } + } + } + return bIsEqual; +} + +// ----------------------------------------------------------------------- + +SvStream& operator>>( SvStream& rIStream, PolyPolygon& rPolyPoly ) +{ + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + DBG_ASSERTWARNING( rIStream.GetVersion(), "PolyPolygon::>> - Solar-Version not set on rIStream" ); + + Polygon* pPoly; + USHORT nPolyCount; + + // Anzahl der Polygone einlesen + rIStream >> nPolyCount; + + // Daten anlegen + if( nPolyCount ) + { + // Referenzcounter beruecksichtigen + if ( rPolyPoly.mpImplPolyPolygon->mnRefCount > 1 ) + rPolyPoly.mpImplPolyPolygon->mnRefCount--; + else + delete rPolyPoly.mpImplPolyPolygon; + + rPolyPoly.mpImplPolyPolygon = new ImplPolyPolygon( nPolyCount ); + + for ( USHORT i = 0; i < nPolyCount; i++ ) + { + pPoly = new Polygon; + rIStream >> *pPoly; + rPolyPoly.mpImplPolyPolygon->mpPolyAry[i] = pPoly; + } + } + else + rPolyPoly = PolyPolygon(); + + return rIStream; +} + +// ----------------------------------------------------------------------- + +SvStream& operator<<( SvStream& rOStream, const PolyPolygon& rPolyPoly ) +{ + DBG_CHKOBJ( &rPolyPoly, PolyPolygon, NULL ); + DBG_ASSERTWARNING( rOStream.GetVersion(), "PolyPolygon::<< - Solar-Version not set on rOStream" ); + + // Anzahl der Polygone rausschreiben + USHORT nPolyCount = rPolyPoly.mpImplPolyPolygon->mnCount; + rOStream << nPolyCount; + + // Die einzelnen Polygone ausgeben + for ( USHORT i = 0; i < nPolyCount; i++ ) + rOStream << *(rPolyPoly.mpImplPolyPolygon->mpPolyAry[i]); + + return rOStream; +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Read( SvStream& rIStream ) +{ + VersionCompat aCompat( rIStream, STREAM_READ ); + + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERTWARNING( rIStream.GetVersion(), "PolyPolygon::>> - Solar-Version not set on rIStream" ); + + Polygon* pPoly; + USHORT nPolyCount; + + // Anzahl der Polygone einlesen + rIStream >> nPolyCount; + + // Daten anlegen + if( nPolyCount ) + { + // Referenzcounter beruecksichtigen + if ( mpImplPolyPolygon->mnRefCount > 1 ) + mpImplPolyPolygon->mnRefCount--; + else + delete mpImplPolyPolygon; + + mpImplPolyPolygon = new ImplPolyPolygon( nPolyCount ); + + for ( USHORT i = 0; i < nPolyCount; i++ ) + { + pPoly = new Polygon; + pPoly->ImplRead( rIStream ); + mpImplPolyPolygon->mpPolyAry[i] = pPoly; + } + } + else + *this = PolyPolygon(); +} + +// ----------------------------------------------------------------------- + +void PolyPolygon::Write( SvStream& rOStream ) const +{ + VersionCompat aCompat( rOStream, STREAM_WRITE, 1 ); + + DBG_CHKTHIS( PolyPolygon, NULL ); + DBG_ASSERTWARNING( rOStream.GetVersion(), "PolyPolygon::<< - Solar-Version not set on rOStream" ); + + // Anzahl der Polygone rausschreiben + USHORT nPolyCount = mpImplPolyPolygon->mnCount; + rOStream << nPolyCount; + + // Die einzelnen Polygone ausgeben + for ( USHORT i = 0; i < nPolyCount; i++ ) + mpImplPolyPolygon->mpPolyAry[i]->ImplWrite( rOStream );; +} + +// ----------------------------------------------------------------------- +// convert to basegfx::B2DPolyPolygon and return +basegfx::B2DPolyPolygon PolyPolygon::getB2DPolyPolygon() const +{ + basegfx::B2DPolyPolygon aRetval; + + for(sal_uInt16 a(0); a < mpImplPolyPolygon->mnCount; a++) + { + Polygon* pCandidate = mpImplPolyPolygon->mpPolyAry[a]; + aRetval.append(pCandidate->getB2DPolygon()); + } + + return aRetval; +} + +// ----------------------------------------------------------------------- +// constructor to convert from basegfx::B2DPolyPolygon +PolyPolygon::PolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon) +{ + DBG_CTOR( PolyPolygon, NULL ); + const sal_uInt16 nCount(sal_uInt16(rPolyPolygon.count())); + DBG_ASSERT(sal_uInt32(nCount) == rPolyPolygon.count(), + "PolyPolygon::PolyPolygon: Too many sub-polygons in given basegfx::B2DPolyPolygon (!)"); + + if ( nCount ) + { + mpImplPolyPolygon = new ImplPolyPolygon( nCount ); + + for(sal_uInt16 a(0); a < nCount; a++) + { + basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(sal_uInt32(a))); + mpImplPolyPolygon->mpPolyAry[a] = new Polygon( aCandidate ); + } + } + else + { + mpImplPolyPolygon = new ImplPolyPolygon( 16, 16 ); + } +} + +// eof diff --git a/tools/source/generic/svborder.cxx b/tools/source/generic/svborder.cxx new file mode 100644 index 000000000000..6057eb6a9d3e --- /dev/null +++ b/tools/source/generic/svborder.cxx @@ -0,0 +1,80 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: svborder.cxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <tools/svborder.hxx> +#include <osl/diagnose.h> + +SvBorder::SvBorder( const Rectangle & rOuter, const Rectangle & rInner ) +{ + Rectangle aOuter( rOuter ); + aOuter.Justify(); + Rectangle aInner( rInner ); + if( aInner.IsEmpty() ) + aInner = Rectangle( aOuter.Center(), aOuter.Center() ); + else + aInner.Justify(); + + OSL_ENSURE( aOuter.IsInside( aInner ), + "SvBorder::SvBorder: FALSE == aOuter.IsInside( aInner )" ); + nTop = aInner.Top() - aOuter.Top(); + nRight = aOuter.Right() - aInner.Right(); + nBottom = aOuter.Bottom() - aInner.Bottom(); + nLeft = aInner.Left() - aOuter.Left(); +} + +Rectangle & operator += ( Rectangle & rRect, const SvBorder & rBorder ) +{ + // wegen Empty-Rect, GetSize muss als erstes gerufen werden + Size aS( rRect.GetSize() ); + aS.Width() += rBorder.Left() + rBorder.Right(); + aS.Height() += rBorder.Top() + rBorder.Bottom(); + + rRect.Left() -= rBorder.Left(); + rRect.Top() -= rBorder.Top(); + rRect.SetSize( aS ); + return rRect; +} + +Rectangle & operator -= ( Rectangle & rRect, const SvBorder & rBorder ) +{ + // wegen Empty-Rect, GetSize muss als erstes gerufen werden + Size aS( rRect.GetSize() ); + aS.Width() -= rBorder.Left() + rBorder.Right(); + aS.Height() -= rBorder.Top() + rBorder.Bottom(); + + rRect.Left() += rBorder.Left(); + rRect.Top() += rBorder.Top(); + rRect.SetSize( aS ); + return rRect; +} + diff --git a/tools/source/generic/toolsin.cxx b/tools/source/generic/toolsin.cxx new file mode 100644 index 000000000000..a2cc6759e9c1 --- /dev/null +++ b/tools/source/generic/toolsin.cxx @@ -0,0 +1,98 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: toolsin.cxx,v $ + * $Revision: 1.9 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _TOOLS_TOOLSIN_CXX + +#include <string.h> +#include <tools/shl.hxx> +#include <tools/debug.hxx> +#include <toolsin.hxx> + +#if defined WNT || defined OS2 +#include <dll.hxx> +#endif + +void ImplDeleteCharTabData(); + +// ======================================================================= + +TOOLSINDATA* ImplGetToolsInData() +{ + TOOLSINDATA** ppData = (TOOLSINDATA**)GetAppData( SHL_TOOLS ); + if ( !(*ppData) ) + { + TOOLSINDATA* pData = new TOOLSINDATA; + memset( pData, 0, sizeof( TOOLSINDATA ) ); + *ppData = pData; + } + + return *ppData; +} + +// ======================================================================= + +void InitTools() +{ + DBG_DEBUGSTART(); +} + +// ----------------------------------------------------------------------- + +void DeInitTools() +{ + TOOLSINDATA** ppData = (TOOLSINDATA**)GetAppData( SHL_TOOLS ); + TOOLSINDATA* pData = *ppData; + + if ( pData ) + { + ImplDeleteCharTabData(); + delete pData; + *ppData = NULL; + } + + DBG_DEBUGEND(); +} + +// ----------------------------------------------------------------------- + +void GlobalDeInitTools() +{ + DBG_GLOBALDEBUGEND(); + +#if defined WNT + ImpDeInitWinTools(); +#endif +#ifdef OS2 + ImpDeInitOS2Tools(); +#endif +} diff --git a/tools/source/inet/inetmime.cxx b/tools/source/inet/inetmime.cxx new file mode 100644 index 000000000000..9b94464d7949 --- /dev/null +++ b/tools/source/inet/inetmime.cxx @@ -0,0 +1,4566 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: inetmime.cxx,v $ + * $Revision: 1.14 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <cstddef> +#include <limits> + +#include "rtl/tencinfo.h" +#include <tools/datetime.hxx> +#include <tools/inetmime.hxx> + +namespace unnamed_tools_inetmime {} using namespace unnamed_tools_inetmime; + // unnamed namespaces don't work well yet + +//============================================================================ +namespace unnamed_tools_inetmime { + +class Charset +{ + rtl_TextEncoding m_eEncoding; + const sal_uInt32 * m_pRanges; + +public: + inline Charset(rtl_TextEncoding eTheEncoding, + const sal_uInt32 * pTheRanges); + + rtl_TextEncoding getEncoding() const { return m_eEncoding; } + + bool contains(sal_uInt32 nChar) const; +}; + +inline Charset::Charset(rtl_TextEncoding eTheEncoding, + const sal_uInt32 * pTheRanges): + m_eEncoding(eTheEncoding), + m_pRanges(pTheRanges) +{ + DBG_ASSERT(m_pRanges, "Charset::Charset(): Bad ranges"); +} + +//============================================================================ +void appendISO88591(UniString & rText, sal_Char const * pBegin, + sal_Char const * pEnd); + +} + +//============================================================================ +class INetMIMECharsetList_Impl +{ + struct Node + { + Charset m_aCharset; + bool m_bDisabled; + Node * m_pNext; + + inline Node(const Charset & rTheCharset, bool bTheDisabled, + Node * pTheNext); + }; + + Node * m_pFirst; + +public: + INetMIMECharsetList_Impl(): m_pFirst(0) {} + + ~INetMIMECharsetList_Impl(); + + void prepend(const Charset & rCharset) + { m_pFirst = new Node(rCharset, false, m_pFirst); } + + void includes(sal_uInt32 nChar); + + rtl_TextEncoding getPreferredEncoding(rtl_TextEncoding eDefault + = RTL_TEXTENCODING_DONTKNOW) + const; + + void reset(); +}; + +inline INetMIMECharsetList_Impl::Node::Node(const Charset & rTheCharset, + bool bTheDisabled, + Node * pTheNext): + m_aCharset(rTheCharset), + m_bDisabled(bTheDisabled), + m_pNext(pTheNext) +{} + +//============================================================================ +namespace unnamed_tools_inetmime { + +struct Parameter +{ + Parameter * m_pNext; + ByteString m_aAttribute; + ByteString m_aCharset; + ByteString m_aLanguage; + ByteString m_aValue; + sal_uInt32 m_nSection; + bool m_bExtended; + + inline Parameter(Parameter * pTheNext, ByteString const & rTheAttribute, + ByteString const & rTheCharset, + ByteString const & rTheLanguage, + ByteString const & rTheValue, sal_uInt32 nTheSection, + bool bTheExtended); +}; + +inline Parameter::Parameter(Parameter * pTheNext, + ByteString const & rTheAttribute, + ByteString const & rTheCharset, + ByteString const & rTheLanguage, + ByteString const & rTheValue, + sal_uInt32 nTheSection, bool bTheExtended): + m_pNext(pTheNext), + m_aAttribute(rTheAttribute), + m_aCharset(rTheCharset), + m_aLanguage(rTheLanguage), + m_aValue(rTheValue), + m_nSection(nTheSection), + m_bExtended(bTheExtended) +{} + +//============================================================================ +struct ParameterList +{ + Parameter * m_pList; + + ParameterList(): m_pList(0) {} + + inline ~ParameterList(); + + Parameter ** find(ByteString const & rAttribute, sal_uInt32 nSection, + bool & rPresent); +}; + +inline ParameterList::~ParameterList() +{ + while (m_pList) + { + Parameter * pNext = m_pList->m_pNext; + delete m_pList; + m_pList = pNext; + } +} + +//============================================================================ +bool parseParameters(ParameterList const & rInput, + INetContentTypeParameterList * pOutput); + +} + +//============================================================================ +// +// Charset +// +//============================================================================ + +bool Charset::contains(sal_uInt32 nChar) const +{ + for (const sal_uInt32 * p = m_pRanges;;) + { + if (nChar < *p++) + return false; + if (nChar <= *p++) + return true; + } +} + +//============================================================================ +// +// appendISO88591 +// +//============================================================================ + +namespace unnamed_tools_inetmime { + +void appendISO88591(UniString & rText, sal_Char const * pBegin, + sal_Char const * pEnd) +{ + xub_StrLen nLength = static_cast< xub_StrLen >(pEnd - pBegin); + sal_Unicode * pBuffer = new sal_Unicode[nLength]; + for (sal_Unicode * p = pBuffer; pBegin != pEnd;) + *p++ = sal_uChar(*pBegin++); + rText.Append(pBuffer, nLength); + delete[] pBuffer; +} + +} + +//============================================================================ +// +// INetMIMECharsetList_Impl +// +//============================================================================ + +INetMIMECharsetList_Impl::~INetMIMECharsetList_Impl() +{ + while (m_pFirst) + { + Node * pRemove = m_pFirst; + m_pFirst = m_pFirst->m_pNext; + delete pRemove; + } +} + +//============================================================================ +void INetMIMECharsetList_Impl::includes(sal_uInt32 nChar) +{ + for (Node * p = m_pFirst; p; p = p->m_pNext) + if (!(p->m_bDisabled || p->m_aCharset.contains(nChar))) + p->m_bDisabled = true; +} + +//============================================================================ +rtl_TextEncoding +INetMIMECharsetList_Impl::getPreferredEncoding(rtl_TextEncoding eDefault) + const +{ + for (Node * p = m_pFirst; p; p = p->m_pNext) + if (!p->m_bDisabled) + return p->m_aCharset.getEncoding(); + return eDefault; +} + +//============================================================================ +void INetMIMECharsetList_Impl::reset() +{ + for (Node * p = m_pFirst; p; p = p->m_pNext) + p->m_bDisabled = false; +} + +//============================================================================ +// +// ParameterList +// +//============================================================================ + +Parameter ** ParameterList::find(ByteString const & rAttribute, + sal_uInt32 nSection, bool & rPresent) +{ + Parameter ** p = &m_pList; + for (; *p; p = &(*p)->m_pNext) + { + StringCompare eCompare = rAttribute.CompareTo((*p)->m_aAttribute); + if (eCompare == COMPARE_GREATER) + break; + else if (eCompare == COMPARE_EQUAL) + { + if (nSection > (*p)->m_nSection) + break; + else if (nSection == (*p)->m_nSection) + { + rPresent = true; + return p; + } + } + } + rPresent = false; + return p; +} + +//============================================================================ +// +// parseParameters +// +//============================================================================ + +namespace unnamed_tools_inetmime { + +bool parseParameters(ParameterList const & rInput, + INetContentTypeParameterList * pOutput) +{ + if (pOutput) + pOutput->Clear(); + + Parameter * pPrev = 0; + for (Parameter * p = rInput.m_pList; p; p = p->m_pNext) + { + if (p->m_nSection > 0 + && (!pPrev + || pPrev->m_nSection != p->m_nSection - 1 + || pPrev->m_aAttribute != p->m_aAttribute)) + return false; + pPrev = p; + } + + if (pOutput) + for (Parameter * p = rInput.m_pList; p;) + { + bool bCharset = p->m_aCharset.Len() != 0; + rtl_TextEncoding eEncoding = RTL_TEXTENCODING_DONTKNOW; + if (bCharset) + eEncoding + = INetMIME::getCharsetEncoding(p->m_aCharset.GetBuffer(), + p->m_aCharset.GetBuffer() + + rInput.m_pList-> + m_aCharset. + Len()); + UniString aValue; + bool bBadEncoding = false; + Parameter * pNext = p; + do + { + sal_Size nSize; + sal_Unicode * pUnicode + = INetMIME::convertToUnicode(pNext->m_aValue.GetBuffer(), + pNext->m_aValue.GetBuffer() + + pNext->m_aValue.Len(), + bCharset && p->m_bExtended ? + eEncoding : + RTL_TEXTENCODING_UTF8, + nSize); + if (!pUnicode && !(bCharset && p->m_bExtended)) + pUnicode = INetMIME::convertToUnicode( + pNext->m_aValue.GetBuffer(), + pNext->m_aValue.GetBuffer() + + pNext->m_aValue.Len(), + RTL_TEXTENCODING_ISO_8859_1, nSize); + if (!pUnicode) + { + bBadEncoding = true; + break; + } + aValue += UniString(pUnicode, static_cast< xub_StrLen >(nSize)); + delete[] pUnicode; + pNext = pNext->m_pNext; + } + while (pNext && pNext->m_nSection > 0); + if (bBadEncoding) + { + aValue.Erase(); + for (pNext = p;;) + { + if (pNext->m_bExtended) + for (xub_StrLen i = 0; i < pNext->m_aValue.Len(); ++i) + aValue += sal_Unicode( + sal_Unicode( + sal_uChar(pNext->m_aValue.GetChar(i))) + | 0xF800); + else + for (xub_StrLen i = 0; i < pNext->m_aValue.Len(); ++i) + aValue + += sal_Unicode(sal_uChar + (pNext-> + m_aValue.GetChar(i))); + pNext = pNext->m_pNext; + if (!pNext || pNext->m_nSection == 0) + break; + }; + } + pOutput->Insert(new INetContentTypeParameter(p->m_aAttribute, + p->m_aCharset, + p->m_aLanguage, + aValue, + !bBadEncoding), + LIST_APPEND); + p = pNext; + } + return true; +} + +} + +//============================================================================ +// +// INetMIME +// +//============================================================================ + +// static +bool INetMIME::isAtomChar(sal_uInt32 nChar) +{ + static const bool aMap[128] + = { false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, true, true, true, true, true, // !"#$%&' + false, false, true, true, false, true, false, true, //()*+,-./ + true, true, true, true, true, true, true, true, //01234567 + true, true, false, false, false, true, false, true, //89:;<=>? + false, true, true, true, true, true, true, true, //@ABCDEFG + true, true, true, true, true, true, true, true, //HIJKLMNO + true, true, true, true, true, true, true, true, //PQRSTUVW + true, true, true, false, false, false, true, true, //XYZ[\]^_ + true, true, true, true, true, true, true, true, //`abcdefg + true, true, true, true, true, true, true, true, //hijklmno + true, true, true, true, true, true, true, true, //pqrstuvw + true, true, true, true, true, true, true, false //xyz{|}~ + }; + return isUSASCII(nChar) && aMap[nChar]; +} + +//============================================================================ +// static +bool INetMIME::isTokenChar(sal_uInt32 nChar) +{ + static const sal_Char aMap[128] + = { false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, true, true, true, true, true, // !"#$%&' + false, false, true, true, false, true, true, false, //()*+,-./ + true, true, true, true, true, true, true, true, //01234567 + true, true, false, false, false, false, false, false, //89:;<=>? + false, true, true, true, true, true, true, true, //@ABCDEFG + true, true, true, true, true, true, true, true, //HIJKLMNO + true, true, true, true, true, true, true, true, //PQRSTUVW + true, true, true, false, false, false, true, true, //XYZ[\]^_ + true, true, true, true, true, true, true, true, //`abcdefg + true, true, true, true, true, true, true, true, //hijklmno + true, true, true, true, true, true, true, true, //pqrstuvw + true, true, true, true, true, true, true, false //xyz{|}~ + }; + return isUSASCII(nChar) && aMap[nChar]; +} + +//============================================================================ +// static +bool INetMIME::isEncodedWordTokenChar(sal_uInt32 nChar) +{ + static const sal_Char aMap[128] + = { false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, true, true, true, true, true, // !"#$%&' + false, false, true, true, false, true, false, false, //()*+,-./ + true, true, true, true, true, true, true, true, //01234567 + true, true, false, false, false, false, false, false, //89:;<=>? + false, true, true, true, true, true, true, true, //@ABCDEFG + true, true, true, true, true, true, true, true, //HIJKLMNO + true, true, true, true, true, true, true, true, //PQRSTUVW + true, true, true, false, false, false, true, true, //XYZ[\]^_ + true, true, true, true, true, true, true, true, //`abcdefg + true, true, true, true, true, true, true, true, //hijklmno + true, true, true, true, true, true, true, true, //pqrstuvw + true, true, true, true, true, true, true, false //xyz{|}~ + }; + return isUSASCII(nChar) && aMap[nChar]; +} + +//============================================================================ +// static +bool INetMIME::isIMAPAtomChar(sal_uInt32 nChar) +{ + static const sal_Char aMap[128] + = { false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, true, true, false, true, true, // !"#$%&' + false, false, false, true, true, true, true, true, //()*+,-./ + true, true, true, true, true, true, true, true, //01234567 + true, true, true, true, true, true, true, true, //89:;<=>? + true, true, true, true, true, true, true, true, //@ABCDEFG + true, true, true, true, true, true, true, true, //HIJKLMNO + true, true, true, true, true, true, true, true, //PQRSTUVW + true, true, true, true, false, true, true, true, //XYZ[\]^_ + true, true, true, true, true, true, true, true, //`abcdefg + true, true, true, true, true, true, true, true, //hijklmno + true, true, true, true, true, true, true, true, //pqrstuvw + true, true, true, false, true, true, true, false //xyz{|}~ + }; + return isUSASCII(nChar) && aMap[nChar]; +} + +//============================================================================ +// static +sal_uInt32 INetMIME::getDigit(int nWeight) +{ + DBG_ASSERT(nWeight >= 0 && nWeight < 10, + "INetMIME::getDigit(): Bad weight"); + + static const sal_Char aDigits[16] + = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + return aDigits[nWeight]; +} + +//============================================================================ +// static +sal_uInt32 INetMIME::getHexDigit(int nWeight) +{ + DBG_ASSERT(nWeight >= 0 && nWeight < 16, + "INetMIME::getHexDigit(): Bad weight"); + + static const sal_Char aDigits[16] + = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', + 'D', 'E', 'F' }; + return aDigits[nWeight]; +} + +//============================================================================ +// static +sal_uInt32 INetMIME::getBase64Digit(int nWeight) +{ + DBG_ASSERT(nWeight >= 0 && nWeight < 64, + "INetMIME::getBase64Digit(): Bad weight"); + + static const sal_Char aDigits[64] + = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; + return aDigits[nWeight]; +} + +//============================================================================ +// static +bool INetMIME::equalIgnoreCase(const sal_Char * pBegin1, + const sal_Char * pEnd1, + const sal_Char * pBegin2, + const sal_Char * pEnd2) +{ + DBG_ASSERT(pBegin1 && pBegin1 <= pEnd1 && pBegin2 && pBegin2 <= pEnd2, + "INetMIME::equalIgnoreCase(): Bad sequences"); + + if (pEnd1 - pBegin1 != pEnd2 - pBegin2) + return false; + while (pBegin1 != pEnd1) + if (toUpperCase(*pBegin1++) != toUpperCase(*pBegin2++)) + return false; + return true; +} + +//============================================================================ +// static +bool INetMIME::equalIgnoreCase(const sal_Char * pBegin1, + const sal_Char * pEnd1, + const sal_Char * pString2) +{ + DBG_ASSERT(pBegin1 && pBegin1 <= pEnd1 && pString2, + "INetMIME::equalIgnoreCase(): Bad sequences"); + + while (*pString2 != 0) + if (pBegin1 == pEnd1 + || toUpperCase(*pBegin1++) != toUpperCase(*pString2++)) + return false; + return pBegin1 == pEnd1; +} + +//============================================================================ +// static +bool INetMIME::equalIgnoreCase(const sal_Unicode * pBegin1, + const sal_Unicode * pEnd1, + const sal_Char * pString2) +{ + DBG_ASSERT(pBegin1 && pBegin1 <= pEnd1 && pString2, + "INetMIME::equalIgnoreCase(): Bad sequences"); + + while (*pString2 != 0) + if (pBegin1 == pEnd1 + || toUpperCase(*pBegin1++) != toUpperCase(*pString2++)) + return false; + return pBegin1 == pEnd1; +} + +//============================================================================ +// static +const sal_Char * INetMIME::skipLinearWhiteSpace(const sal_Char * pBegin, + const sal_Char * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIME::skipLinearWhiteSpace(): Bad sequence"); + + while (pBegin != pEnd) + switch (*pBegin) + { + case '\t': + case ' ': + ++pBegin; + break; + + case 0x0D: // CR + if (startsWithLineFolding(pBegin, pEnd)) + pBegin += 3; + else + return pBegin; + break; + + default: + return pBegin; + } + return pBegin; +} + +//============================================================================ +// static +const sal_Unicode * INetMIME::skipLinearWhiteSpace(const sal_Unicode * pBegin, + const sal_Unicode * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIME::skipLinearWhiteSpace(): Bad sequence"); + + while (pBegin != pEnd) + switch (*pBegin) + { + case '\t': + case ' ': + ++pBegin; + break; + + case 0x0D: // CR + if (startsWithLineFolding(pBegin, pEnd)) + pBegin += 3; + else + return pBegin; + break; + + default: + return pBegin; + } + return pBegin; +} + +//============================================================================ +// static +const sal_Char * INetMIME::skipComment(const sal_Char * pBegin, + const sal_Char * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIME::skipComment(): Bad sequence"); + + if (pBegin != pEnd && *pBegin == '(') + { + sal_uInt32 nLevel = 0; + for (const sal_Char * p = pBegin; p != pEnd;) + switch (*p++) + { + case '(': + ++nLevel; + break; + + case ')': + if (--nLevel == 0) + return p; + break; + + case '\\': + if (p != pEnd) + ++p; + break; + } + } + return pBegin; +} + +//============================================================================ +// static +const sal_Unicode * INetMIME::skipComment(const sal_Unicode * pBegin, + const sal_Unicode * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIME::skipComment(): Bad sequence"); + + if (pBegin != pEnd && *pBegin == '(') + { + sal_uInt32 nLevel = 0; + for (const sal_Unicode * p = pBegin; p != pEnd;) + switch (*p++) + { + case '(': + ++nLevel; + break; + + case ')': + if (--nLevel == 0) + return p; + break; + + case '\\': + if (p != pEnd) + ++p; + break; + } + } + return pBegin; +} + +//============================================================================ +// static +const sal_Char * INetMIME::skipLinearWhiteSpaceComment(const sal_Char * + pBegin, + const sal_Char * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIME::skipLinearWhiteSpaceComment(): Bad sequence"); + + while (pBegin != pEnd) + switch (*pBegin) + { + case '\t': + case ' ': + ++pBegin; + break; + + case 0x0D: // CR + if (startsWithLineFolding(pBegin, pEnd)) + pBegin += 3; + else + return pBegin; + break; + + case '(': + { + const sal_Char * p = skipComment(pBegin, pEnd); + if (p == pBegin) + return pBegin; + pBegin = p; + break; + } + + default: + return pBegin; + } + return pBegin; +} + +//============================================================================ +// static +const sal_Unicode * INetMIME::skipLinearWhiteSpaceComment(const sal_Unicode * + pBegin, + const sal_Unicode * + pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIME::skipLinearWhiteSpaceComment(): Bad sequence"); + + while (pBegin != pEnd) + switch (*pBegin) + { + case '\t': + case ' ': + ++pBegin; + break; + + case 0x0D: // CR + if (startsWithLineFolding(pBegin, pEnd)) + pBegin += 3; + else + return pBegin; + break; + + case '(': + { + const sal_Unicode * p = skipComment(pBegin, pEnd); + if (p == pBegin) + return pBegin; + pBegin = p; + break; + } + + default: + return pBegin; + } + return pBegin; +} + +//============================================================================ +// static +const sal_Char * INetMIME::skipQuotedString(const sal_Char * pBegin, + const sal_Char * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIME::skipQuotedString(): Bad sequence"); + + if (pBegin != pEnd && *pBegin == '"') + for (const sal_Char * p = pBegin + 1; p != pEnd;) + switch (*p++) + { + case 0x0D: // CR + if (pEnd - p < 2 || *p++ != 0x0A // LF + || !isWhiteSpace(*p++)) + return pBegin; + break; + + case '"': + return p; + + case '\\': + if (p != pEnd) + ++p; + break; + } + return pBegin; +} + +//============================================================================ +// static +const sal_Unicode * INetMIME::skipQuotedString(const sal_Unicode * pBegin, + const sal_Unicode * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIME::skipQuotedString(): Bad sequence"); + + if (pBegin != pEnd && *pBegin == '"') + for (const sal_Unicode * p = pBegin + 1; p != pEnd;) + switch (*p++) + { + case 0x0D: // CR + if (pEnd - p < 2 || *p++ != 0x0A // LF + || !isWhiteSpace(*p++)) + return pBegin; + break; + + case '"': + return p; + + case '\\': + if (p != pEnd) + ++p; + break; + } + return pBegin; +} + +//============================================================================ +// static +const sal_Char * INetMIME::scanAtom(const sal_Char * pBegin, + const sal_Char * pEnd) +{ + while (pBegin != pEnd && isAtomChar(*pBegin)) + ++pBegin; + return pBegin; +} + +//============================================================================ +// static +const sal_Unicode * INetMIME::scanAtom(const sal_Unicode * pBegin, + const sal_Unicode * pEnd) +{ + while (pBegin != pEnd && isAtomChar(*pBegin)) + ++pBegin; + return pBegin; +} + +//============================================================================ +// static +bool INetMIME::scanUnsigned(const sal_Char *& rBegin, const sal_Char * pEnd, + bool bLeadingZeroes, sal_uInt32 & rValue) +{ + sal_uInt64 nTheValue = 0; + const sal_Char * p = rBegin; + for ( ; p != pEnd; ++p) + { + int nWeight = getWeight(*p); + if (nWeight < 0) + break; + nTheValue = 10 * nTheValue + nWeight; + if (nTheValue > std::numeric_limits< sal_uInt32 >::max()) + return false; + } + if (nTheValue == 0 && (p == rBegin || (!bLeadingZeroes && p - rBegin != 1))) + return false; + rBegin = p; + rValue = sal_uInt32(nTheValue); + return true; +} + +//============================================================================ +// static +bool INetMIME::scanUnsigned(const sal_Unicode *& rBegin, + const sal_Unicode * pEnd, bool bLeadingZeroes, + sal_uInt32 & rValue) +{ + sal_uInt64 nTheValue = 0; + const sal_Unicode * p = rBegin; + for ( ; p != pEnd; ++p) + { + int nWeight = getWeight(*p); + if (nWeight < 0) + break; + nTheValue = 10 * nTheValue + nWeight; + if (nTheValue > std::numeric_limits< sal_uInt32 >::max()) + return false; + } + if (nTheValue == 0 && (p == rBegin || (!bLeadingZeroes && p - rBegin != 1))) + return false; + rBegin = p; + rValue = sal_uInt32(nTheValue); + return true; +} + +//============================================================================ +// static +bool INetMIME::scanUnsignedHex(const sal_Char *& rBegin, + const sal_Char * pEnd, bool bLeadingZeroes, + sal_uInt32 & rValue) +{ + sal_uInt64 nTheValue = 0; + const sal_Char * p = rBegin; + for ( p = rBegin; p != pEnd; ++p) + { + int nWeight = getHexWeight(*p); + if (nWeight < 0) + break; + nTheValue = nTheValue << 4 | nWeight; + if (nTheValue > std::numeric_limits< sal_uInt32 >::max()) + return false; + } + if (nTheValue == 0 && (p == rBegin || (!bLeadingZeroes && p - rBegin != 1))) + return false; + rBegin = p; + rValue = sal_uInt32(nTheValue); + return true; +} + +//============================================================================ +// static +bool INetMIME::scanUnsignedHex(const sal_Unicode *& rBegin, + const sal_Unicode * pEnd, bool bLeadingZeroes, + sal_uInt32 & rValue) +{ + sal_uInt64 nTheValue = 0; + const sal_Unicode * p = rBegin; + for ( ; p != pEnd; ++p) + { + int nWeight = getHexWeight(*p); + if (nWeight < 0) + break; + nTheValue = nTheValue << 4 | nWeight; + if (nTheValue > std::numeric_limits< sal_uInt32 >::max()) + return false; + } + if (nTheValue == 0 && (p == rBegin || (!bLeadingZeroes && p - rBegin != 1))) + return false; + rBegin = p; + rValue = sal_uInt32(nTheValue); + return true; +} + +//============================================================================ +// static +const sal_Char * INetMIME::scanQuotedBlock(const sal_Char * pBegin, + const sal_Char * pEnd, + sal_uInt32 nOpening, + sal_uInt32 nClosing, + sal_Size & rLength, + bool & rModify) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIME::scanQuotedBlock(): Bad sequence"); + + if (pBegin != pEnd && static_cast< unsigned char >(*pBegin) == nOpening) + { + ++rLength; + ++pBegin; + while (pBegin != pEnd) + if (static_cast< unsigned char >(*pBegin) == nClosing) + { + ++rLength; + return ++pBegin; + } + else + { + sal_uInt32 c = *pBegin++; + switch (c) + { + case 0x0D: // CR + if (pBegin != pEnd && *pBegin == 0x0A) // LF + if (pEnd - pBegin >= 2 && isWhiteSpace(pBegin[1])) + { + ++rLength; + rModify = true; + pBegin += 2; + } + else + { + rLength += 3; + rModify = true; + ++pBegin; + } + else + ++rLength; + break; + + case '\\': + ++rLength; + if (pBegin != pEnd) + { + if (startsWithLineBreak(pBegin, pEnd) + && (pEnd - pBegin < 3 + || !isWhiteSpace(pBegin[2]))) + { + rLength += 3; + rModify = true; + pBegin += 2; + } + else + ++pBegin; + } + break; + + default: + ++rLength; + if (!isUSASCII(c)) + rModify = true; + break; + } + } + } + return pBegin; +} + +//============================================================================ +// static +const sal_Unicode * INetMIME::scanQuotedBlock(const sal_Unicode * pBegin, + const sal_Unicode * pEnd, + sal_uInt32 nOpening, + sal_uInt32 nClosing, + sal_Size & rLength, + bool & rModify) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIME::scanQuotedBlock(): Bad sequence"); + + if (pBegin != pEnd && *pBegin == nOpening) + { + ++rLength; + ++pBegin; + while (pBegin != pEnd) + if (*pBegin == nClosing) + { + ++rLength; + return ++pBegin; + } + else + { + sal_uInt32 c = *pBegin++; + switch (c) + { + case 0x0D: // CR + if (pBegin != pEnd && *pBegin == 0x0A) // LF + if (pEnd - pBegin >= 2 && isWhiteSpace(pBegin[1])) + { + ++rLength; + rModify = true; + pBegin += 2; + } + else + { + rLength += 3; + rModify = true; + ++pBegin; + } + else + ++rLength; + break; + + case '\\': + ++rLength; + if (pBegin != pEnd) + { + if (startsWithLineBreak(pBegin, pEnd) + && (pEnd - pBegin < 3 + || !isWhiteSpace(pBegin[2]))) + { + rLength += 3; + rModify = true; + pBegin += 2; + } + else + ++pBegin; + } + break; + + default: + ++rLength; + if (!isUSASCII(c)) + rModify = true; + break; + } + } + } + return pBegin; +} + +//============================================================================ +// static +sal_Char const * INetMIME::scanParameters(sal_Char const * pBegin, + sal_Char const * pEnd, + INetContentTypeParameterList * + pParameters) +{ + ParameterList aList; + sal_Char const * pParameterBegin = pBegin; + for (sal_Char const * p = pParameterBegin;; pParameterBegin = p) + { + pParameterBegin = skipLinearWhiteSpaceComment(p, pEnd); + if (pParameterBegin == pEnd || *pParameterBegin != ';') + break; + p = pParameterBegin + 1; + + sal_Char const * pAttributeBegin = skipLinearWhiteSpaceComment(p, + pEnd); + p = pAttributeBegin; + bool bDowncaseAttribute = false; + while (p != pEnd && isTokenChar(*p) && *p != '*') + { + bDowncaseAttribute = bDowncaseAttribute || isUpperCase(*p); + ++p; + } + if (p == pAttributeBegin) + break; + ByteString aAttribute( + pAttributeBegin, static_cast< xub_StrLen >(p - pAttributeBegin)); + if (bDowncaseAttribute) + aAttribute.ToLowerAscii(); + + sal_uInt32 nSection = 0; + if (p != pEnd && *p == '*') + { + ++p; + if (p != pEnd && isDigit(*p) + && !scanUnsigned(p, pEnd, false, nSection)) + break; + } + + bool bPresent; + Parameter ** pPos = aList.find(aAttribute, nSection, bPresent); + if (bPresent) + break; + + bool bExtended = false; + if (p != pEnd && *p == '*') + { + ++p; + bExtended = true; + } + + p = skipLinearWhiteSpaceComment(p, pEnd); + + if (p == pEnd || *p != '=') + break; + + p = skipLinearWhiteSpaceComment(p + 1, pEnd); + + ByteString aCharset; + ByteString aLanguage; + ByteString aValue; + if (bExtended) + { + if (nSection == 0) + { + sal_Char const * pCharsetBegin = p; + bool bDowncaseCharset = false; + while (p != pEnd && isTokenChar(*p) && *p != '\'') + { + bDowncaseCharset = bDowncaseCharset || isUpperCase(*p); + ++p; + } + if (p == pCharsetBegin) + break; + if (pParameters) + { + aCharset = ByteString( + pCharsetBegin, + static_cast< xub_StrLen >(p - pCharsetBegin)); + if (bDowncaseCharset) + aCharset.ToLowerAscii(); + } + + if (p == pEnd || *p != '\'') + break; + ++p; + + sal_Char const * pLanguageBegin = p; + bool bDowncaseLanguage = false; + int nLetters = 0; + for (; p != pEnd; ++p) + if (isAlpha(*p)) + { + if (++nLetters > 8) + break; + bDowncaseLanguage = bDowncaseLanguage + || isUpperCase(*p); + } + else if (*p == '-') + { + if (nLetters == 0) + break; + nLetters = 0; + } + else + break; + if (nLetters == 0 || nLetters > 8) + break; + if (pParameters) + { + aLanguage = ByteString( + pLanguageBegin, + static_cast< xub_StrLen >(p - pLanguageBegin)); + if (bDowncaseLanguage) + aLanguage.ToLowerAscii(); + } + + if (p == pEnd || *p != '\'') + break; + ++p; + } + if (pParameters) + while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p))) + { + if (*p == '%') + { + if (p + 2 < pEnd) + { + int nWeight1 = getHexWeight(p[1]); + int nWeight2 = getHexWeight(p[2]); + if (nWeight1 >= 0 && nWeight2 >= 0) + { + aValue += sal_Char(nWeight1 << 4 | nWeight2); + p += 3; + continue; + } + } + } + aValue += *p++; + } + else + while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p))) + ++p; + } + else if (p != pEnd && *p == '"') + if (pParameters) + { + bool bInvalid = false; + for (++p;;) + { + if (p == pEnd) + { + bInvalid = true; + break; + } + else if (*p == '"') + { + ++p; + break; + } + else if (*p == 0x0D) // CR + { + if (pEnd - p < 3 || p[1] != 0x0A // LF + || !isWhiteSpace(p[2])) + { + bInvalid = true; + break; + } + p += 2; + } + else if (*p == '\\' && ++p == pEnd) + { + bInvalid = true; + break; + } + aValue += *p++; + } + if (bInvalid) + break; + } + else + { + sal_Char const * pStringEnd = skipQuotedString(p, pEnd); + if (p == pStringEnd) + break; + p = pStringEnd; + } + else + { + sal_Char const * pTokenBegin = p; + while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p))) + ++p; + if (p == pTokenBegin) + break; + if (pParameters) + aValue = ByteString( + pTokenBegin, static_cast< xub_StrLen >(p - pTokenBegin)); + } + + *pPos = new Parameter(*pPos, aAttribute, aCharset, aLanguage, aValue, + nSection, bExtended); + } + return parseParameters(aList, pParameters) ? pParameterBegin : pBegin; +} + +//============================================================================ +// static +sal_Unicode const * INetMIME::scanParameters(sal_Unicode const * pBegin, + sal_Unicode const * pEnd, + INetContentTypeParameterList * + pParameters) +{ + ParameterList aList; + sal_Unicode const * pParameterBegin = pBegin; + for (sal_Unicode const * p = pParameterBegin;; pParameterBegin = p) + { + pParameterBegin = skipLinearWhiteSpaceComment(p, pEnd); + if (pParameterBegin == pEnd || *pParameterBegin != ';') + break; + p = pParameterBegin + 1; + + sal_Unicode const * pAttributeBegin + = skipLinearWhiteSpaceComment(p, pEnd); + p = pAttributeBegin; + bool bDowncaseAttribute = false; + while (p != pEnd && isTokenChar(*p) && *p != '*') + { + bDowncaseAttribute = bDowncaseAttribute || isUpperCase(*p); + ++p; + } + if (p == pAttributeBegin) + break; + ByteString aAttribute = ByteString( + pAttributeBegin, static_cast< xub_StrLen >(p - pAttributeBegin), + RTL_TEXTENCODING_ASCII_US); + if (bDowncaseAttribute) + aAttribute.ToLowerAscii(); + + sal_uInt32 nSection = 0; + if (p != pEnd && *p == '*') + { + ++p; + if (p != pEnd && isDigit(*p) + && !scanUnsigned(p, pEnd, false, nSection)) + break; + } + + bool bPresent; + Parameter ** pPos = aList.find(aAttribute, nSection, bPresent); + if (bPresent) + break; + + bool bExtended = false; + if (p != pEnd && *p == '*') + { + ++p; + bExtended = true; + } + + p = skipLinearWhiteSpaceComment(p, pEnd); + + if (p == pEnd || *p != '=') + break; + + p = skipLinearWhiteSpaceComment(p + 1, pEnd); + + ByteString aCharset; + ByteString aLanguage; + ByteString aValue; + if (bExtended) + { + if (nSection == 0) + { + sal_Unicode const * pCharsetBegin = p; + bool bDowncaseCharset = false; + while (p != pEnd && isTokenChar(*p) && *p != '\'') + { + bDowncaseCharset = bDowncaseCharset || isUpperCase(*p); + ++p; + } + if (p == pCharsetBegin) + break; + if (pParameters) + { + aCharset = ByteString( + pCharsetBegin, + static_cast< xub_StrLen >(p - pCharsetBegin), + RTL_TEXTENCODING_ASCII_US); + if (bDowncaseCharset) + aCharset.ToLowerAscii(); + } + + if (p == pEnd || *p != '\'') + break; + ++p; + + sal_Unicode const * pLanguageBegin = p; + bool bDowncaseLanguage = false; + int nLetters = 0; + for (; p != pEnd; ++p) + if (isAlpha(*p)) + { + if (++nLetters > 8) + break; + bDowncaseLanguage = bDowncaseLanguage + || isUpperCase(*p); + } + else if (*p == '-') + { + if (nLetters == 0) + break; + nLetters = 0; + } + else + break; + if (nLetters == 0 || nLetters > 8) + break; + if (pParameters) + { + aLanguage = ByteString( + pLanguageBegin, + static_cast< xub_StrLen >(p - pLanguageBegin), + RTL_TEXTENCODING_ASCII_US); + if (bDowncaseLanguage) + aLanguage.ToLowerAscii(); + } + + if (p == pEnd || *p != '\'') + break; + ++p; + } + if (pParameters) + { + INetMIMEStringOutputSink + aSink(0, INetMIMEOutputSink::NO_LINE_LENGTH_LIMIT); + while (p != pEnd) + { + sal_uInt32 nChar = INetMIME::getUTF32Character(p, pEnd); + if (isUSASCII(nChar) && !isTokenChar(nChar)) + break; + if (nChar == '%' && p + 1 < pEnd) + { + int nWeight1 = getHexWeight(p[0]); + int nWeight2 = getHexWeight(p[1]); + if (nWeight1 >= 0 && nWeight2 >= 0) + { + aSink << sal_Char(nWeight1 << 4 | nWeight2); + p += 2; + continue; + } + } + INetMIME::writeUTF8(aSink, nChar); + } + aValue = aSink.takeBuffer(); + } + else + while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p))) + ++p; + } + else if (p != pEnd && *p == '"') + if (pParameters) + { + INetMIMEStringOutputSink + aSink(0, INetMIMEOutputSink::NO_LINE_LENGTH_LIMIT); + bool bInvalid = false; + for (++p;;) + { + if (p == pEnd) + { + bInvalid = true; + break; + } + sal_uInt32 nChar = INetMIME::getUTF32Character(p, pEnd); + if (nChar == '"') + break; + else if (nChar == 0x0D) // CR + { + if (pEnd - p < 2 || *p++ != 0x0A // LF + || !isWhiteSpace(*p)) + { + bInvalid = true; + break; + } + nChar = sal_uChar(*p++); + } + else if (nChar == '\\') + { + if (p == pEnd) + { + bInvalid = true; + break; + } + nChar = INetMIME::getUTF32Character(p, pEnd); + } + INetMIME::writeUTF8(aSink, nChar); + } + if (bInvalid) + break; + aValue = aSink.takeBuffer(); + } + else + { + sal_Unicode const * pStringEnd = skipQuotedString(p, pEnd); + if (p == pStringEnd) + break; + p = pStringEnd; + } + else + { + sal_Unicode const * pTokenBegin = p; + while (p != pEnd && (isTokenChar(*p) || !isUSASCII(*p))) + ++p; + if (p == pTokenBegin) + break; + if (pParameters) + aValue = ByteString( + pTokenBegin, static_cast< xub_StrLen >(p - pTokenBegin), + RTL_TEXTENCODING_UTF8); + } + + *pPos = new Parameter(*pPos, aAttribute, aCharset, aLanguage, aValue, + nSection, bExtended); + } + return parseParameters(aList, pParameters) ? pParameterBegin : pBegin; +} + +//============================================================================ +// static +const sal_Char * INetMIME::getCharsetName(rtl_TextEncoding eEncoding) +{ + if (rtl_isOctetTextEncoding(eEncoding)) + { + char const * p = rtl_getMimeCharsetFromTextEncoding(eEncoding); + DBG_ASSERT(p, "INetMIME::getCharsetName(): Unsupported encoding"); + return p; + } + else + switch (eEncoding) + { + case RTL_TEXTENCODING_UCS4: + return "ISO-10646-UCS-4"; + + case RTL_TEXTENCODING_UCS2: + return "ISO-10646-UCS-2"; + + default: + DBG_ERROR("INetMIME::getCharsetName(): Unsupported encoding"); + return 0; + } +} + +//============================================================================ +namespace unnamed_tools_inetmime { + +struct EncodingEntry +{ + sal_Char const * m_aName; + rtl_TextEncoding m_eEncoding; +}; + +//============================================================================ +// The source for the following table is <ftp://ftp.iana.org/in-notes/iana/ +// assignments/character-sets> as of Jan, 21 2000 12:46:00, unless otherwise +// noted: +EncodingEntry const aEncodingMap[] + = { { "US-ASCII", RTL_TEXTENCODING_ASCII_US }, + { "ANSI_X3.4-1968", RTL_TEXTENCODING_ASCII_US }, + { "ISO-IR-6", RTL_TEXTENCODING_ASCII_US }, + { "ANSI_X3.4-1986", RTL_TEXTENCODING_ASCII_US }, + { "ISO_646.IRV:1991", RTL_TEXTENCODING_ASCII_US }, + { "ASCII", RTL_TEXTENCODING_ASCII_US }, + { "ISO646-US", RTL_TEXTENCODING_ASCII_US }, + { "US", RTL_TEXTENCODING_ASCII_US }, + { "IBM367", RTL_TEXTENCODING_ASCII_US }, + { "CP367", RTL_TEXTENCODING_ASCII_US }, + { "CSASCII", RTL_TEXTENCODING_ASCII_US }, + { "ISO-8859-1", RTL_TEXTENCODING_ISO_8859_1 }, + { "ISO_8859-1:1987", RTL_TEXTENCODING_ISO_8859_1 }, + { "ISO-IR-100", RTL_TEXTENCODING_ISO_8859_1 }, + { "ISO_8859-1", RTL_TEXTENCODING_ISO_8859_1 }, + { "LATIN1", RTL_TEXTENCODING_ISO_8859_1 }, + { "L1", RTL_TEXTENCODING_ISO_8859_1 }, + { "IBM819", RTL_TEXTENCODING_ISO_8859_1 }, + { "CP819", RTL_TEXTENCODING_ISO_8859_1 }, + { "CSISOLATIN1", RTL_TEXTENCODING_ISO_8859_1 }, + { "ISO-8859-2", RTL_TEXTENCODING_ISO_8859_2 }, + { "ISO_8859-2:1987", RTL_TEXTENCODING_ISO_8859_2 }, + { "ISO-IR-101", RTL_TEXTENCODING_ISO_8859_2 }, + { "ISO_8859-2", RTL_TEXTENCODING_ISO_8859_2 }, + { "LATIN2", RTL_TEXTENCODING_ISO_8859_2 }, + { "L2", RTL_TEXTENCODING_ISO_8859_2 }, + { "CSISOLATIN2", RTL_TEXTENCODING_ISO_8859_2 }, + { "ISO-8859-3", RTL_TEXTENCODING_ISO_8859_3 }, + { "ISO_8859-3:1988", RTL_TEXTENCODING_ISO_8859_3 }, + { "ISO-IR-109", RTL_TEXTENCODING_ISO_8859_3 }, + { "ISO_8859-3", RTL_TEXTENCODING_ISO_8859_3 }, + { "LATIN3", RTL_TEXTENCODING_ISO_8859_3 }, + { "L3", RTL_TEXTENCODING_ISO_8859_3 }, + { "CSISOLATIN3", RTL_TEXTENCODING_ISO_8859_3 }, + { "ISO-8859-4", RTL_TEXTENCODING_ISO_8859_4 }, + { "ISO_8859-4:1988", RTL_TEXTENCODING_ISO_8859_4 }, + { "ISO-IR-110", RTL_TEXTENCODING_ISO_8859_4 }, + { "ISO_8859-4", RTL_TEXTENCODING_ISO_8859_4 }, + { "LATIN4", RTL_TEXTENCODING_ISO_8859_4 }, + { "L4", RTL_TEXTENCODING_ISO_8859_4 }, + { "CSISOLATIN4", RTL_TEXTENCODING_ISO_8859_4 }, + { "ISO-8859-5", RTL_TEXTENCODING_ISO_8859_5 }, + { "ISO_8859-5:1988", RTL_TEXTENCODING_ISO_8859_5 }, + { "ISO-IR-144", RTL_TEXTENCODING_ISO_8859_5 }, + { "ISO_8859-5", RTL_TEXTENCODING_ISO_8859_5 }, + { "CYRILLIC", RTL_TEXTENCODING_ISO_8859_5 }, + { "CSISOLATINCYRILLIC", RTL_TEXTENCODING_ISO_8859_5 }, + { "ISO-8859-6", RTL_TEXTENCODING_ISO_8859_6 }, + { "ISO_8859-6:1987", RTL_TEXTENCODING_ISO_8859_6 }, + { "ISO-IR-127", RTL_TEXTENCODING_ISO_8859_6 }, + { "ISO_8859-6", RTL_TEXTENCODING_ISO_8859_6 }, + { "ECMA-114", RTL_TEXTENCODING_ISO_8859_6 }, + { "ASMO-708", RTL_TEXTENCODING_ISO_8859_6 }, + { "ARABIC", RTL_TEXTENCODING_ISO_8859_6 }, + { "CSISOLATINARABIC", RTL_TEXTENCODING_ISO_8859_6 }, + { "ISO-8859-7", RTL_TEXTENCODING_ISO_8859_7 }, + { "ISO_8859-7:1987", RTL_TEXTENCODING_ISO_8859_7 }, + { "ISO-IR-126", RTL_TEXTENCODING_ISO_8859_7 }, + { "ISO_8859-7", RTL_TEXTENCODING_ISO_8859_7 }, + { "ELOT_928", RTL_TEXTENCODING_ISO_8859_7 }, + { "ECMA-118", RTL_TEXTENCODING_ISO_8859_7 }, + { "GREEK", RTL_TEXTENCODING_ISO_8859_7 }, + { "GREEK8", RTL_TEXTENCODING_ISO_8859_7 }, + { "CSISOLATINGREEK", RTL_TEXTENCODING_ISO_8859_7 }, + { "ISO-8859-8", RTL_TEXTENCODING_ISO_8859_8 }, + { "ISO_8859-8:1988", RTL_TEXTENCODING_ISO_8859_8 }, + { "ISO-IR-138", RTL_TEXTENCODING_ISO_8859_8 }, + { "ISO_8859-8", RTL_TEXTENCODING_ISO_8859_8 }, + { "HEBREW", RTL_TEXTENCODING_ISO_8859_8 }, + { "CSISOLATINHEBREW", RTL_TEXTENCODING_ISO_8859_8 }, + { "ISO-8859-9", RTL_TEXTENCODING_ISO_8859_9 }, + { "ISO_8859-9:1989", RTL_TEXTENCODING_ISO_8859_9 }, + { "ISO-IR-148", RTL_TEXTENCODING_ISO_8859_9 }, + { "ISO_8859-9", RTL_TEXTENCODING_ISO_8859_9 }, + { "LATIN5", RTL_TEXTENCODING_ISO_8859_9 }, + { "L5", RTL_TEXTENCODING_ISO_8859_9 }, + { "CSISOLATIN5", RTL_TEXTENCODING_ISO_8859_9 }, + { "ISO-8859-14", RTL_TEXTENCODING_ISO_8859_14 }, // RFC 2047 + { "ISO_8859-15", RTL_TEXTENCODING_ISO_8859_15 }, + { "ISO-8859-15", RTL_TEXTENCODING_ISO_8859_15 }, // RFC 2047 + { "MACINTOSH", RTL_TEXTENCODING_APPLE_ROMAN }, + { "MAC", RTL_TEXTENCODING_APPLE_ROMAN }, + { "CSMACINTOSH", RTL_TEXTENCODING_APPLE_ROMAN }, + { "IBM437", RTL_TEXTENCODING_IBM_437 }, + { "CP437", RTL_TEXTENCODING_IBM_437 }, + { "437", RTL_TEXTENCODING_IBM_437 }, + { "CSPC8CODEPAGE437", RTL_TEXTENCODING_IBM_437 }, + { "IBM850", RTL_TEXTENCODING_IBM_850 }, + { "CP850", RTL_TEXTENCODING_IBM_850 }, + { "850", RTL_TEXTENCODING_IBM_850 }, + { "CSPC850MULTILINGUAL", RTL_TEXTENCODING_IBM_850 }, + { "IBM860", RTL_TEXTENCODING_IBM_860 }, + { "CP860", RTL_TEXTENCODING_IBM_860 }, + { "860", RTL_TEXTENCODING_IBM_860 }, + { "CSIBM860", RTL_TEXTENCODING_IBM_860 }, + { "IBM861", RTL_TEXTENCODING_IBM_861 }, + { "CP861", RTL_TEXTENCODING_IBM_861 }, + { "861", RTL_TEXTENCODING_IBM_861 }, + { "CP-IS", RTL_TEXTENCODING_IBM_861 }, + { "CSIBM861", RTL_TEXTENCODING_IBM_861 }, + { "IBM863", RTL_TEXTENCODING_IBM_863 }, + { "CP863", RTL_TEXTENCODING_IBM_863 }, + { "863", RTL_TEXTENCODING_IBM_863 }, + { "CSIBM863", RTL_TEXTENCODING_IBM_863 }, + { "IBM865", RTL_TEXTENCODING_IBM_865 }, + { "CP865", RTL_TEXTENCODING_IBM_865 }, + { "865", RTL_TEXTENCODING_IBM_865 }, + { "CSIBM865", RTL_TEXTENCODING_IBM_865 }, + { "IBM775", RTL_TEXTENCODING_IBM_775 }, + { "CP775", RTL_TEXTENCODING_IBM_775 }, + { "CSPC775BALTIC", RTL_TEXTENCODING_IBM_775 }, + { "IBM852", RTL_TEXTENCODING_IBM_852 }, + { "CP852", RTL_TEXTENCODING_IBM_852 }, + { "852", RTL_TEXTENCODING_IBM_852 }, + { "CSPCP852", RTL_TEXTENCODING_IBM_852 }, + { "IBM855", RTL_TEXTENCODING_IBM_855 }, + { "CP855", RTL_TEXTENCODING_IBM_855 }, + { "855", RTL_TEXTENCODING_IBM_855 }, + { "CSIBM855", RTL_TEXTENCODING_IBM_855 }, + { "IBM857", RTL_TEXTENCODING_IBM_857 }, + { "CP857", RTL_TEXTENCODING_IBM_857 }, + { "857", RTL_TEXTENCODING_IBM_857 }, + { "CSIBM857", RTL_TEXTENCODING_IBM_857 }, + { "IBM862", RTL_TEXTENCODING_IBM_862 }, + { "CP862", RTL_TEXTENCODING_IBM_862 }, + { "862", RTL_TEXTENCODING_IBM_862 }, + { "CSPC862LATINHEBREW", RTL_TEXTENCODING_IBM_862 }, + { "IBM864", RTL_TEXTENCODING_IBM_864 }, + { "CP864", RTL_TEXTENCODING_IBM_864 }, + { "CSIBM864", RTL_TEXTENCODING_IBM_864 }, + { "IBM866", RTL_TEXTENCODING_IBM_866 }, + { "CP866", RTL_TEXTENCODING_IBM_866 }, + { "866", RTL_TEXTENCODING_IBM_866 }, + { "CSIBM866", RTL_TEXTENCODING_IBM_866 }, + { "IBM869", RTL_TEXTENCODING_IBM_869 }, + { "CP869", RTL_TEXTENCODING_IBM_869 }, + { "869", RTL_TEXTENCODING_IBM_869 }, + { "CP-GR", RTL_TEXTENCODING_IBM_869 }, + { "CSIBM869", RTL_TEXTENCODING_IBM_869 }, + { "WINDOWS-1250", RTL_TEXTENCODING_MS_1250 }, + { "WINDOWS-1251", RTL_TEXTENCODING_MS_1251 }, + { "WINDOWS-1253", RTL_TEXTENCODING_MS_1253 }, + { "WINDOWS-1254", RTL_TEXTENCODING_MS_1254 }, + { "WINDOWS-1255", RTL_TEXTENCODING_MS_1255 }, + { "WINDOWS-1256", RTL_TEXTENCODING_MS_1256 }, + { "WINDOWS-1257", RTL_TEXTENCODING_MS_1257 }, + { "WINDOWS-1258", RTL_TEXTENCODING_MS_1258 }, + { "SHIFT_JIS", RTL_TEXTENCODING_SHIFT_JIS }, + { "MS_KANJI", RTL_TEXTENCODING_SHIFT_JIS }, + { "CSSHIFTJIS", RTL_TEXTENCODING_SHIFT_JIS }, + { "GB2312", RTL_TEXTENCODING_GB_2312 }, + { "CSGB2312", RTL_TEXTENCODING_GB_2312 }, + { "BIG5", RTL_TEXTENCODING_BIG5 }, + { "CSBIG5", RTL_TEXTENCODING_BIG5 }, + { "EUC-JP", RTL_TEXTENCODING_EUC_JP }, + { "EXTENDED_UNIX_CODE_PACKED_FORMAT_FOR_JAPANESE", + RTL_TEXTENCODING_EUC_JP }, + { "CSEUCPKDFMTJAPANESE", RTL_TEXTENCODING_EUC_JP }, + { "ISO-2022-JP", RTL_TEXTENCODING_ISO_2022_JP }, + { "CSISO2022JP", RTL_TEXTENCODING_ISO_2022_JP }, + { "ISO-2022-CN", RTL_TEXTENCODING_ISO_2022_CN }, + { "KOI8-R", RTL_TEXTENCODING_KOI8_R }, + { "CSKOI8R", RTL_TEXTENCODING_KOI8_R }, + { "UTF-7", RTL_TEXTENCODING_UTF7 }, + { "UTF-8", RTL_TEXTENCODING_UTF8 }, + { "ISO-8859-10", RTL_TEXTENCODING_ISO_8859_10 }, // RFC 2047 + { "ISO-8859-13", RTL_TEXTENCODING_ISO_8859_13 }, // RFC 2047 + { "EUC-KR", RTL_TEXTENCODING_EUC_KR }, + { "CSEUCKR", RTL_TEXTENCODING_EUC_KR }, + { "ISO-2022-KR", RTL_TEXTENCODING_ISO_2022_KR }, + { "CSISO2022KR", RTL_TEXTENCODING_ISO_2022_KR }, + { "ISO-10646-UCS-4", RTL_TEXTENCODING_UCS4 }, + { "CSUCS4", RTL_TEXTENCODING_UCS4 }, + { "ISO-10646-UCS-2", RTL_TEXTENCODING_UCS2 }, + { "CSUNICODE", RTL_TEXTENCODING_UCS2 } }; + +//============================================================================ +template< typename T > +inline rtl_TextEncoding getCharsetEncoding_Impl(T const * pBegin, + T const * pEnd) +{ + for (sal_Size i = 0; i < sizeof aEncodingMap / sizeof (EncodingEntry); + ++i) + if (INetMIME::equalIgnoreCase(pBegin, pEnd, aEncodingMap[i].m_aName)) + return aEncodingMap[i].m_eEncoding; + return RTL_TEXTENCODING_DONTKNOW; +} + +} + +//============================================================================ +// static +rtl_TextEncoding INetMIME::getCharsetEncoding(sal_Char const * pBegin, + sal_Char const * pEnd) +{ + return getCharsetEncoding_Impl(pBegin, pEnd); +} + +//============================================================================ +// static +rtl_TextEncoding INetMIME::getCharsetEncoding(sal_Unicode const * pBegin, + sal_Unicode const * pEnd) +{ + return getCharsetEncoding_Impl(pBegin, pEnd); +} + +//============================================================================ +// static +INetMIMECharsetList_Impl * +INetMIME::createPreferredCharsetList(rtl_TextEncoding eEncoding) +{ + static const sal_uInt32 aUSASCIIRanges[] = { 0, 0x7F, sal_uInt32(-1) }; + + static const sal_uInt32 aISO88591Ranges[] = { 0, 0xFF, sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT> version + // 1.0 of 1999 July 27 + + static const sal_uInt32 aISO88592Ranges[] + = { 0, 0xA0, 0xA4, 0xA4, 0xA7, 0xA8, 0xAD, 0xAD, 0xB0, 0xB0, + 0xB4, 0xB4, 0xB8, 0xB8, 0xC1, 0xC2, 0xC4, 0xC4, 0xC7, 0xC7, + 0xC9, 0xC9, 0xCB, 0xCB, 0xCD, 0xCE, 0xD3, 0xD4, 0xD6, 0xD7, + 0xDA, 0xDA, 0xDC, 0xDD, 0xDF, 0xDF, 0xE1, 0xE2, 0xE4, 0xE4, + 0xE7, 0xE7, 0xE9, 0xE9, 0xEB, 0xEB, 0xED, 0xEE, 0xF3, 0xF4, + 0xF6, 0xF7, 0xFA, 0xFA, 0xFC, 0xFD, 0x102, 0x107, 0x10C, 0x111, + 0x118, 0x11B, 0x139, 0x13A, 0x13D, 0x13E, 0x141, 0x144, + 0x147, 0x148, 0x150, 0x151, 0x154, 0x155, 0x158, 0x15B, + 0x15E, 0x165, 0x16E, 0x171, 0x179, 0x17E, 0x2C7, 0x2C7, + 0x2D8, 0x2D9, 0x2DB, 0x2DB, 0x2DD, 0x2DD, sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-2.TXT> version + // 1.0 of 1999 July 27 + + static const sal_uInt32 aISO88593Ranges[] + = { 0, 0xA0, 0xA3, 0xA4, 0xA7, 0xA8, 0xAD, 0xAD, 0xB0, 0xB0, + 0xB2, 0xB5, 0xB7, 0xB8, 0xBD, 0xBD, 0xC0, 0xC2, 0xC4, 0xC4, + 0xC7, 0xCF, 0xD1, 0xD4, 0xD6, 0xD7, 0xD9, 0xDC, 0xDF, 0xE2, + 0xE4, 0xE4, 0xE7, 0xEF, 0xF1, 0xF4, 0xF6, 0xF7, 0xF9, 0xFC, + 0x108, 0x10B, 0x11C, 0x121, 0x124, 0x127, 0x130, 0x131, + 0x134, 0x135, 0x15C, 0x15F, 0x16C, 0x16D, 0x17B, 0x17C, + 0x2D8, 0x2D9, sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-3.TXT> version + // 1.0 of 1999 July 27 + + static const sal_uInt32 aISO88594Ranges[] + = { 0, 0xA0, 0xA4, 0xA4, 0xA7, 0xA8, 0xAD, 0xAD, 0xAF, 0xB0, + 0xB4, 0xB4, 0xB8, 0xB8, 0xC1, 0xC6, 0xC9, 0xC9, 0xCB, 0xCB, + 0xCD, 0xCE, 0xD4, 0xD8, 0xDA, 0xDC, 0xDF, 0xDF, 0xE1, 0xE6, + 0xE9, 0xE9, 0xEB, 0xEB, 0xED, 0xEE, 0xF4, 0xF8, 0xFA, 0xFC, + 0x100, 0x101, 0x104, 0x105, 0x10C, 0x10D, 0x110, 0x113, + 0x116, 0x119, 0x122, 0x123, 0x128, 0x12B, 0x12E, 0x12F, + 0x136, 0x138, 0x13B, 0x13C, 0x145, 0x146, 0x14A, 0x14D, + 0x156, 0x157, 0x160, 0x161, 0x166, 0x16B, 0x172, 0x173, + 0x17D, 0x17E, 0x2C7, 0x2C7, 0x2D9, 0x2D9, 0x2DB, 0x2DB, + sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-4.TXT> version + // 1.0 of 1999 July 27 + + static const sal_uInt32 aISO88595Ranges[] + = { 0, 0xA0, 0xA7, 0xA7, 0xAD, 0xAD, 0x401, 0x40C, 0x40E, 0x44F, + 0x451, 0x45C, 0x45E, 0x45F, 0x2116, 0x2116, sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-5.TXT> version + // 1.0 of 1999 July 27 + + static const sal_uInt32 aISO88596Ranges[] + = { 0, 0xA0, 0xA4, 0xA4, 0xAD, 0xAD, 0x60C, 0x60C, 0x61B, 0x61B, + 0x61F, 0x61F, 0x621, 0x63A, 0x640, 0x652, sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-6.TXT> version + // 1.0 of 1999 July 27 + + static const sal_uInt32 aISO88597Ranges[] + = { 0, 0xA0, 0xA3, 0xA3, 0xA6, 0xA9, 0xAB, 0xAD, 0xB0, 0xB3, + 0xB7, 0xB7, 0xBB, 0xBB, 0xBD, 0xBD, 0x384, 0x386, 0x388, 0x38A, + 0x38C, 0x38C, 0x38E, 0x3A1, 0x3A3, 0x3CE, 0x2015, 0x2015, + 0x2018, 0x2019, sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-7.TXT> version + // 1.0 of 1999 July 27 + + static const sal_uInt32 aISO88598Ranges[] + = { 0, 0xA0, 0xA2, 0xA9, 0xAB, 0xB9, 0xBB, 0xBE, 0xD7, 0xD7, + 0xF7, 0xF7, 0x5D0, 0x5EA, 0x200E, 0x200F, 0x2017, 0x2017, + sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-8.TXT> version + // 1.1 of 2000-Jan-03 + + static const sal_uInt32 aISO88599Ranges[] + = { 0, 0xCF, 0xD1, 0xDC, 0xDF, 0xEF, 0xF1, 0xFC, 0xFF, 0xFF, + 0x11E, 0x11F, 0x130, 0x131, 0x15E, 0x15F, sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-9.TXT> version + // 1.0 of 1999 July 27 + + static const sal_uInt32 aISO885910Ranges[] + = { 0, 0xA0, 0xA7, 0xA7, 0xAD, 0xAD, 0xB0, 0xB0, 0xB7, 0xB7, + 0xC1, 0xC6, 0xC9, 0xC9, 0xCB, 0xCB, 0xCD, 0xD0, 0xD3, 0xD6, + 0xD8, 0xD8, 0xDA, 0xDF, 0xE1, 0xE6, 0xE9, 0xE9, 0xEB, 0xEB, + 0xED, 0xF0, 0xF3, 0xF6, 0xF8, 0xF8, 0xFA, 0xFE, 0x100, 0x101, + 0x104, 0x105, 0x10C, 0x10D, 0x110, 0x113, 0x116, 0x119, + 0x122, 0x123, 0x128, 0x12B, 0x12E, 0x12F, 0x136, 0x138, + 0x13B, 0x13C, 0x145, 0x146, 0x14A, 0x14D, 0x160, 0x161, + 0x166, 0x16B, 0x172, 0x173, 0x17D, 0x17E, 0x2015, 0x2015, + sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-10.TXT> version + // 1.1 of 1999 October 11 + + static const sal_uInt32 aISO885913Ranges[] + = { 0, 0xA0, 0xA2, 0xA4, 0xA6, 0xA7, 0xA9, 0xA9, 0xAB, 0xAE, + 0xB0, 0xB3, 0xB5, 0xB7, 0xB9, 0xB9, 0xBB, 0xBE, 0xC4, 0xC6, + 0xC9, 0xC9, 0xD3, 0xD3, 0xD5, 0xD8, 0xDC, 0xDC, 0xDF, 0xDF, + 0xE4, 0xE6, 0xE9, 0xE9, 0xF3, 0xF3, 0xF5, 0xF8, 0xFC, 0xFC, + 0x100, 0x101, 0x104, 0x107, 0x10C, 0x10D, 0x112, 0x113, + 0x116, 0x119, 0x122, 0x123, 0x12A, 0x12B, 0x12E, 0x12F, + 0x136, 0x137, 0x13B, 0x13C, 0x141, 0x146, 0x14C, 0x14D, + 0x156, 0x157, 0x15A, 0x15B, 0x160, 0x161, 0x16A, 0x16B, + 0x172, 0x173, 0x179, 0x17E, 0x2019, 0x2019, 0x201C, 0x201E, + sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-13.TXT> version + // 1.0 of 1999 July 27 + + static const sal_uInt32 aISO885914Ranges[] + = { 0, 0xA0, 0xA3, 0xA3, 0xA7, 0xA7, 0xA9, 0xA9, 0xAD, 0xAE, + 0xB6, 0xB6, 0xC0, 0xCF, 0xD1, 0xD6, 0xD8, 0xDD, 0xDF, 0xEF, + 0xF1, 0xF6, 0xF8, 0xFD, 0xFF, 0xFF, 0x10A, 0x10B, 0x120, 0x121, + 0x174, 0x178, 0x1E02, 0x1E03, 0x1E0A, 0x1E0B, 0x1E1E, 0x1E1F, + 0x1E40, 0x1E41, 0x1E56, 0x1E57, 0x1E60, 0x1E61, 0x1E6A, 0x1E6B, + 0x1E80, 0x1E85, 0x1EF2, 0x1EF3, sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-14.TXT> version + // 1.0 of 1999 July 27 + + static const sal_uInt32 aISO885915Ranges[] + = { 0, 0xA3, 0xA5, 0xA5, 0xA7, 0xA7, 0xA9, 0xB3, 0xB5, 0xB7, + 0xB9, 0xBB, 0xBF, 0xFF, 0x152, 0x153, 0x160, 0x161, 0x178, 0x178, + 0x17D, 0x17E, 0x20AC, 0x20AC, sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/ISO8859/8859-15.TXT> version + // 1.0 of 1999 July 27 + + static const sal_uInt32 aKOI8RRanges[] + = { 0, 0x7F, 0xA0, 0xA0, 0xA9, 0xA9, 0xB0, 0xB0, 0xB2, 0xB2, + 0xB7, 0xB7, 0xF7, 0xF7, 0x401, 0x401, 0x410, 0x44F, 0x451, 0x451, + 0x2219, 0x221A, 0x2248, 0x2248, 0x2264, 0x2265, 0x2320, 0x2321, + 0x2500, 0x2500, 0x2502, 0x2502, 0x250C, 0x250C, 0x2510, 0x2510, + 0x2514, 0x2514, 0x2518, 0x2518, 0x251C, 0x251C, 0x2524, 0x2524, + 0x252C, 0x252C, 0x2534, 0x2534, 0x253C, 0x253C, 0x2550, 0x256C, + 0x2580, 0x2580, 0x2584, 0x2584, 0x2588, 0x2588, 0x258C, 0x258C, + 0x2590, 0x2593, 0x25A0, 0x25A0, sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MISC/KOI8-R.TXT> + // version 1.0 of 18 August 1999 + +#if defined WNT + static const sal_uInt32 aWindows1252Ranges[] + = { 0, 0x7F, 0xA0, 0xFF, 0x152, 0x153, 0x160, 0x161, 0x178, 0x178, + 0x17D, 0x17E, 0x192, 0x192, 0x2C6, 0x2C6, 0x2DC, 0x2DC, + 0x2013, 0x2014, 0x2018, 0x201A, 0x201C, 0x201E, 0x2020, 0x2022, + 0x2026, 0x2026, 0x2030, 0x2030, 0x2039, 0x203A, 0x20AC, 0x20AC, + 0x2122, 0x2122, sal_uInt32(-1) }; + // <ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/ + // CP1252.TXT> version 2.01 of 04/15/98 +#endif // WNT + + INetMIMECharsetList_Impl * pList = new INetMIMECharsetList_Impl; + switch (eEncoding) + { + case RTL_TEXTENCODING_MS_1252: +#if defined WNT + pList->prepend(Charset(RTL_TEXTENCODING_MS_1252, + aWindows1252Ranges)); +#endif // WNT + case RTL_TEXTENCODING_ISO_8859_1: + case RTL_TEXTENCODING_UTF7: + case RTL_TEXTENCODING_UTF8: + break; + + case RTL_TEXTENCODING_ISO_8859_2: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_2, + aISO88592Ranges)); + break; + + case RTL_TEXTENCODING_ISO_8859_3: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_3, + aISO88593Ranges)); + break; + + case RTL_TEXTENCODING_ISO_8859_4: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_4, + aISO88594Ranges)); + break; + + case RTL_TEXTENCODING_ISO_8859_5: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_5, + aISO88595Ranges)); + break; + + case RTL_TEXTENCODING_ISO_8859_6: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_6, + aISO88596Ranges)); + break; + + case RTL_TEXTENCODING_ISO_8859_7: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_7, + aISO88597Ranges)); + break; + + case RTL_TEXTENCODING_ISO_8859_8: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_8, + aISO88598Ranges)); + break; + + case RTL_TEXTENCODING_ISO_8859_9: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_9, + aISO88599Ranges)); + break; + + case RTL_TEXTENCODING_ISO_8859_10: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_10, + aISO885910Ranges)); + break; + + case RTL_TEXTENCODING_ISO_8859_13: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_13, + aISO885913Ranges)); + break; + + case RTL_TEXTENCODING_ISO_8859_14: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_14, + aISO885914Ranges)); + break; + + case RTL_TEXTENCODING_ISO_8859_15: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_15, + aISO885915Ranges)); + break; + + case RTL_TEXTENCODING_MS_1250: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_2, + aISO88592Ranges)); + break; + + case RTL_TEXTENCODING_MS_1251: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_5, + aISO88595Ranges)); + break; + + case RTL_TEXTENCODING_MS_1253: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_7, + aISO88597Ranges)); + break; + + case RTL_TEXTENCODING_MS_1254: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_9, + aISO88599Ranges)); + break; + + case RTL_TEXTENCODING_MS_1255: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_8, + aISO88598Ranges)); + break; + + case RTL_TEXTENCODING_MS_1256: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_6, + aISO88596Ranges)); + break; + + case RTL_TEXTENCODING_MS_1257: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_4, + aISO88594Ranges)); + break; + + case RTL_TEXTENCODING_KOI8_R: + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_5, + aISO88595Ranges)); + pList->prepend(Charset(RTL_TEXTENCODING_KOI8_R, aKOI8RRanges)); + break; + + default: //@@@ more cases are missing! + DBG_ERROR("INetMIME::createPreferredCharsetList():" + " Unsupported encoding"); + break; + } + pList->prepend(Charset(RTL_TEXTENCODING_ISO_8859_1, aISO88591Ranges)); + pList->prepend(Charset(RTL_TEXTENCODING_ASCII_US, aUSASCIIRanges)); + return pList; +} + +//============================================================================ +// static +sal_Unicode * INetMIME::convertToUnicode(const sal_Char * pBegin, + const sal_Char * pEnd, + rtl_TextEncoding eEncoding, + sal_Size & rSize) +{ + if (eEncoding == RTL_TEXTENCODING_DONTKNOW) + return 0; + rtl_TextToUnicodeConverter hConverter + = rtl_createTextToUnicodeConverter(eEncoding); + rtl_TextToUnicodeContext hContext + = rtl_createTextToUnicodeContext(hConverter); + sal_Unicode * pBuffer; + sal_uInt32 nInfo; + for (sal_Size nBufferSize = pEnd - pBegin;; + nBufferSize += nBufferSize / 3 + 1) + { + pBuffer = new sal_Unicode[nBufferSize]; + sal_Size nSrcCvtBytes; + rSize = rtl_convertTextToUnicode( + hConverter, hContext, pBegin, pEnd - pBegin, pBuffer, + nBufferSize, + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR + | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR + | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR, + &nInfo, &nSrcCvtBytes); + if (nInfo != RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOSMALL) + break; + delete[] pBuffer; + rtl_resetTextToUnicodeContext(hConverter, hContext); + } + rtl_destroyTextToUnicodeContext(hConverter, hContext); + rtl_destroyTextToUnicodeConverter(hConverter); + if (nInfo != 0) + { + delete[] pBuffer; + pBuffer = 0; + } + return pBuffer; +} + +//============================================================================ +// static +sal_Char * INetMIME::convertFromUnicode(const sal_Unicode * pBegin, + const sal_Unicode * pEnd, + rtl_TextEncoding eEncoding, + sal_Size & rSize) +{ + if (eEncoding == RTL_TEXTENCODING_DONTKNOW) + return 0; + rtl_UnicodeToTextConverter hConverter + = rtl_createUnicodeToTextConverter(eEncoding); + rtl_UnicodeToTextContext hContext + = rtl_createUnicodeToTextContext(hConverter); + sal_Char * pBuffer; + sal_uInt32 nInfo; + for (sal_Size nBufferSize = pEnd - pBegin;; + nBufferSize += nBufferSize / 3 + 1) + { + pBuffer = new sal_Char[nBufferSize]; + sal_Size nSrcCvtBytes; + rSize = rtl_convertUnicodeToText( + hConverter, hContext, pBegin, pEnd - pBegin, pBuffer, + nBufferSize, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR + | RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE + | RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACESTR, + &nInfo, &nSrcCvtBytes); + if (nInfo != RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) + break; + delete[] pBuffer; + rtl_resetUnicodeToTextContext(hConverter, hContext); + } + rtl_destroyUnicodeToTextContext(hConverter, hContext); + rtl_destroyUnicodeToTextConverter(hConverter); + if (nInfo != 0) + { + delete[] pBuffer; + pBuffer = 0; + } + return pBuffer; +} + +//============================================================================ +// static +void INetMIME::writeUTF8(INetMIMEOutputSink & rSink, sal_uInt32 nChar) +{ + // See RFC 2279 for a discussion of UTF-8. + DBG_ASSERT(nChar < 0x80000000, "INetMIME::writeUTF8(): Bad char"); + + if (nChar < 0x80) + rSink << sal_Char(nChar); + else if (nChar < 0x800) + rSink << sal_Char(nChar >> 6 | 0xC0) + << sal_Char((nChar & 0x3F) | 0x80); + else if (nChar < 0x10000) + rSink << sal_Char(nChar >> 12 | 0xE0) + << sal_Char((nChar >> 6 & 0x3F) | 0x80) + << sal_Char((nChar & 0x3F) | 0x80); + else if (nChar < 0x200000) + rSink << sal_Char(nChar >> 18 | 0xF0) + << sal_Char((nChar >> 12 & 0x3F) | 0x80) + << sal_Char((nChar >> 6 & 0x3F) | 0x80) + << sal_Char((nChar & 0x3F) | 0x80); + else if (nChar < 0x4000000) + rSink << sal_Char(nChar >> 24 | 0xF8) + << sal_Char((nChar >> 18 & 0x3F) | 0x80) + << sal_Char((nChar >> 12 & 0x3F) | 0x80) + << sal_Char((nChar >> 6 & 0x3F) | 0x80) + << sal_Char((nChar & 0x3F) | 0x80); + else + rSink << sal_Char(nChar >> 30 | 0xFC) + << sal_Char((nChar >> 24 & 0x3F) | 0x80) + << sal_Char((nChar >> 18 & 0x3F) | 0x80) + << sal_Char((nChar >> 12 & 0x3F) | 0x80) + << sal_Char((nChar >> 6 & 0x3F) | 0x80) + << sal_Char((nChar & 0x3F) | 0x80); +} + +//============================================================================ +// static +void INetMIME::writeUnsigned(INetMIMEOutputSink & rSink, sal_uInt32 nValue, + int nMinDigits) +{ + sal_Char aBuffer[10]; + // max unsigned 32 bit value (4294967295) has 10 places + sal_Char * p = aBuffer; + for (; nValue > 0; nValue /= 10) + *p++ = sal_Char(getDigit(nValue % 10)); + nMinDigits -= p - aBuffer; + while (nMinDigits-- > 0) + rSink << '0'; + while (p != aBuffer) + rSink << *--p; +} + +//============================================================================ +// static +void INetMIME::writeDateTime(INetMIMEOutputSink & rSink, + const DateTime & rUTC) +{ + static const sal_Char aDay[7][3] + = { { 'M', 'o', 'n' }, + { 'T', 'u', 'e' }, + { 'W', 'e', 'd' }, + { 'T', 'h', 'u' }, + { 'F', 'r', 'i' }, + { 'S', 'a', 't' }, + { 'S', 'u', 'n' } }; + const sal_Char * pTheDay = aDay[rUTC.GetDayOfWeek()]; + rSink.write(pTheDay, pTheDay + 3); + rSink << ", "; + writeUnsigned(rSink, rUTC.GetDay()); + rSink << ' '; + static const sal_Char aMonth[12][3] + = { { 'J', 'a', 'n' }, + { 'F', 'e', 'b' }, + { 'M', 'a', 'r' }, + { 'A', 'p', 'r' }, + { 'M', 'a', 'y' }, + { 'J', 'u', 'n' }, + { 'J', 'u', 'l' }, + { 'A', 'u', 'g' }, + { 'S', 'e', 'p' }, + { 'O', 'c', 't' }, + { 'N', 'o', 'v' }, + { 'D', 'e', 'c' } }; + const sal_Char * pTheMonth = aMonth[rUTC.GetMonth() - 1]; + rSink.write(pTheMonth, pTheMonth + 3); + rSink << ' '; + writeUnsigned(rSink, rUTC.GetYear()); + rSink << ' '; + writeUnsigned(rSink, rUTC.GetHour(), 2); + rSink << ':'; + writeUnsigned(rSink, rUTC.GetMin(), 2); + rSink << ':'; + writeUnsigned(rSink, rUTC.GetSec(), 2); + rSink << " +0000"; +} + +//============================================================================ +// static +void INetMIME::writeHeaderFieldBody(INetMIMEOutputSink & rSink, + HeaderFieldType eType, + const ByteString & rBody, + rtl_TextEncoding ePreferredEncoding, + bool bInitialSpace) +{ + writeHeaderFieldBody(rSink, eType, + UniString(rBody, RTL_TEXTENCODING_UTF8), + ePreferredEncoding, bInitialSpace); +} + +//============================================================================ +// static +void INetMIME::writeHeaderFieldBody(INetMIMEOutputSink & rSink, + HeaderFieldType eType, + const UniString & rBody, + rtl_TextEncoding ePreferredEncoding, + bool bInitialSpace) +{ + if (eType == HEADER_FIELD_TEXT) + { + INetMIMEEncodedWordOutputSink + aOutput(rSink, INetMIMEEncodedWordOutputSink::CONTEXT_TEXT, + bInitialSpace ? + INetMIMEEncodedWordOutputSink::SPACE_ALWAYS : + INetMIMEEncodedWordOutputSink::SPACE_NO, + ePreferredEncoding); + aOutput.write(rBody.GetBuffer(), rBody.GetBuffer() + rBody.Len()); + aOutput.flush(); + } + else + { + enum Brackets { BRACKETS_OUTSIDE, BRACKETS_OPENING, BRACKETS_INSIDE }; + Brackets eBrackets = BRACKETS_OUTSIDE; + + const sal_Unicode * pBodyPtr = rBody.GetBuffer(); + const sal_Unicode * pBodyEnd = pBodyPtr + rBody.Len(); + while (pBodyPtr != pBodyEnd) + switch (*pBodyPtr) + { + case '\t': + case ' ': + // A WSP adds to accumulated space: + bInitialSpace = true; + ++pBodyPtr; + break; + + case '(': + { + // Write a pending '<' if necessary: + if (eBrackets == BRACKETS_OPENING) + { + if (rSink.getColumn() + (bInitialSpace ? 1 : 0) + >= rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + rSink << '<'; + bInitialSpace = false; + eBrackets = BRACKETS_INSIDE; + } + + // Write the comment, introducing encoded-words where + // necessary: + int nLevel = 0; + INetMIMEEncodedWordOutputSink + aOutput( + rSink, + INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT, + INetMIMEEncodedWordOutputSink::SPACE_NO, + ePreferredEncoding); + while (pBodyPtr != pBodyEnd) + switch (*pBodyPtr) + { + case '(': + aOutput.flush(); + if (rSink.getColumn() + + (bInitialSpace ? 1 : 0) + >= rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + rSink << '('; + bInitialSpace = false; + ++nLevel; + ++pBodyPtr; + break; + + case ')': + aOutput.flush(); + if (rSink.getColumn() + >= rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + rSink << ')'; + ++pBodyPtr; + if (--nLevel == 0) + goto comment_done; + break; + + case '\\': + if (++pBodyPtr == pBodyEnd) + break; + default: + aOutput << *pBodyPtr++; + break; + } + comment_done: + break; + } + + case '<': + // Write an already pending '<' if necessary: + if (eBrackets == BRACKETS_OPENING) + { + if (rSink.getColumn() + (bInitialSpace ? 1 : 0) + >= rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + rSink << '<'; + bInitialSpace = false; + } + + // Remember this '<' as pending, and open a bracketed + // block: + eBrackets = BRACKETS_OPENING; + ++pBodyPtr; + break; + + case '>': + // Write a pending '<' if necessary: + if (eBrackets == BRACKETS_OPENING) + { + if (rSink.getColumn() + (bInitialSpace ? 1 : 0) + >= rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + rSink << '<'; + bInitialSpace = false; + } + + // Write this '>', and close any bracketed block: + if (rSink.getColumn() + (bInitialSpace ? 1 : 0) + >= rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + rSink << '>'; + bInitialSpace = false; + eBrackets = BRACKETS_OUTSIDE; + ++pBodyPtr; + break; + + case ',': + case ':': + case ';': + case '\\': + case ']': + // Write a pending '<' if necessary: + if (eBrackets == BRACKETS_OPENING) + { + if (rSink.getColumn() + (bInitialSpace ? 1 : 0) + >= rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + rSink << '<'; + bInitialSpace = false; + eBrackets = BRACKETS_INSIDE; + } + + // Write this specials: + if (rSink.getColumn() + (bInitialSpace ? 1 : 0) + >= rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + rSink << sal_Char(*pBodyPtr++); + bInitialSpace = false; + break; + + case '\x0D': // CR + // A <CRLF WSP> adds to accumulated space, a <CR> not + // followed by <LF WSP> starts 'junk': + if (startsWithLineFolding(pBodyPtr, pBodyEnd)) + { + bInitialSpace = true; + pBodyPtr += 3; + break; + } + default: + { + // The next token is either one of <"." / "@" / atom / + // quoted-string / domain-literal>, or it's 'junk'; if it + // is not 'junk', it is either a 'phrase' (i.e., it may + // contain encoded-words) or a 'non-phrase' (i.e., it may + // not contain encoded-words): + enum Entity { ENTITY_JUNK, ENTITY_NON_PHRASE, + ENTITY_PHRASE }; + Entity eEntity = ENTITY_JUNK; + switch (*pBodyPtr) + { + case '.': + case '@': + case '[': + // A token of <"." / "@" / domain-literal> always + // starts a 'non-phrase': + eEntity = ENTITY_NON_PHRASE; + break; + + default: + if (isUSASCII(*pBodyPtr) + && !isAtomChar(*pBodyPtr)) + { + eEntity = ENTITY_JUNK; + break; + } + case '"': + // A token of <atom / quoted-string> can either be + // a 'phrase' or a 'non-phrase': + switch (eType) + { + case HEADER_FIELD_STRUCTURED: + eEntity = ENTITY_NON_PHRASE; + break; + + case HEADER_FIELD_PHRASE: + eEntity = ENTITY_PHRASE; + break; + + case HEADER_FIELD_MESSAGE_ID: + // A 'phrase' if and only if outside any + // bracketed block: + eEntity + = eBrackets == BRACKETS_OUTSIDE ? + ENTITY_PHRASE : + ENTITY_NON_PHRASE; + break; + + case HEADER_FIELD_ADDRESS: + { + // A 'non-phrase' if and only if, after + // skipping this token and any following + // <linear-white-space> and <comment>s, + // there is no token left, or the next + // token is any of <"." / "@" / ">" / "," + // / ";">, or the next token is <":"> and + // is within a bracketed block: + const sal_Unicode * pLookAhead = pBodyPtr; + if (*pLookAhead == '"') + { + pLookAhead + = skipQuotedString(pLookAhead, + pBodyEnd); + if (pLookAhead == pBodyPtr) + pLookAhead = pBodyEnd; + } + else + while (pLookAhead != pBodyEnd + && (isAtomChar(*pLookAhead) + || !isUSASCII( + *pLookAhead))) + ++pLookAhead; + while (pLookAhead != pBodyEnd) + switch (*pLookAhead) + { + case '\t': + case ' ': + ++pLookAhead; + break; + + case '(': + { + const sal_Unicode * pPast + = skipComment(pLookAhead, + pBodyEnd); + pLookAhead + = pPast == pLookAhead ? + pBodyEnd : pPast; + break; + } + + case ',': + case '.': + case ';': + case '>': + case '@': + eEntity = ENTITY_NON_PHRASE; + goto entity_determined; + + case ':': + eEntity + = eBrackets + == BRACKETS_OUTSIDE ? + ENTITY_PHRASE : + ENTITY_NON_PHRASE; + goto entity_determined; + + case '\x0D': // CR + if (startsWithLineFolding( + pLookAhead, pBodyEnd)) + { + pLookAhead += 3; + break; + } + default: + eEntity = ENTITY_PHRASE; + goto entity_determined; + } + eEntity = ENTITY_NON_PHRASE; + entity_determined: + break; + } + + case HEADER_FIELD_TEXT: + OSL_ASSERT(false); + break; + } + + // In a 'non-phrase', a non-US-ASCII character + // cannot be part of an <atom>, but instead the + // whole entity is 'junk' rather than 'non- + // phrase': + if (eEntity == ENTITY_NON_PHRASE + && !isUSASCII(*pBodyPtr)) + eEntity = ENTITY_JUNK; + break; + } + + switch (eEntity) + { + case ENTITY_JUNK: + { + // Write a pending '<' if necessary: + if (eBrackets == BRACKETS_OPENING) + { + if (rSink.getColumn() + + (bInitialSpace ? 1 : 0) + >= rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + rSink << '<'; + bInitialSpace = false; + eBrackets = BRACKETS_INSIDE; + } + + // Calculate the length of in- and output: + const sal_Unicode * pStart = pBodyPtr; + sal_Size nLength = 0; + bool bModify = false; + bool bEnd = false; + while (pBodyPtr != pBodyEnd && !bEnd) + switch (*pBodyPtr) + { + case '\x0D': // CR + if (startsWithLineFolding(pBodyPtr, + pBodyEnd)) + bEnd = true; + else if (startsWithLineBreak( + pBodyPtr, pBodyEnd)) + { + nLength += 3; + bModify = true; + pBodyPtr += 2; + } + else + { + ++nLength; + ++pBodyPtr; + } + break; + + case '\t': + case ' ': + bEnd = true; + break; + + default: + if (isVisible(*pBodyPtr)) + bEnd = true; + else if (isUSASCII(*pBodyPtr)) + { + ++nLength; + ++pBodyPtr; + } + else + { + nLength += getUTF8OctetCount( + *pBodyPtr++); + bModify = true; + } + break; + } + + // Write the output: + if (rSink.getColumn() + (bInitialSpace ? 1 : 0) + + nLength + > rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + bInitialSpace = false; + if (bModify) + while (pStart != pBodyPtr) + if (startsWithLineBreak(pStart, pBodyPtr)) + { + rSink << "\x0D\\\x0A"; // CR, '\', LF + pStart += 2; + } + else + writeUTF8(rSink, *pStart++); + else + rSink.write(pStart, pBodyPtr); + break; + } + + case ENTITY_NON_PHRASE: + { + // Calculate the length of in- and output: + const sal_Unicode * pStart = pBodyPtr; + sal_Size nLength = 0; + bool bBracketedBlock = false; + bool bSymbol = *pStart != '.' && *pStart != '@'; + bool bModify = false; + bool bEnd = false; + while (pBodyPtr != pBodyEnd && !bEnd) + switch (*pBodyPtr) + { + case '\t': + case ' ': + case '\x0D': // CR + { + const sal_Unicode * pLookAhead + = skipLinearWhiteSpace(pBodyPtr, + pBodyEnd); + if (pLookAhead < pBodyEnd + && (bSymbol ? + isAtomChar(*pLookAhead) + || *pLookAhead == '"' + || *pLookAhead == '[' : + *pLookAhead == '.' + || *pLookAhead == '@' + || (*pLookAhead == '>' + && eType + >= HEADER_FIELD_MESSAGE_ID + && eBrackets + == BRACKETS_OPENING))) + { + bModify = true; + pBodyPtr = pLookAhead; + } + else + bEnd = true; + break; + } + + case '"': + if (bSymbol) + { + pBodyPtr + = scanQuotedBlock(pBodyPtr, + pBodyEnd, + '"', '"', + nLength, + bModify); + bSymbol = false; + } + else + bEnd = true; + break; + + case '[': + if (bSymbol) + { + pBodyPtr + = scanQuotedBlock(pBodyPtr, + pBodyEnd, + '[', ']', + nLength, + bModify); + bSymbol = false; + } + else + bEnd = true; + break; + + case '.': + case '@': + if (bSymbol) + bEnd = true; + else + { + ++nLength; + bSymbol = true; + ++pBodyPtr; + } + break; + + case '>': + if (eBrackets == BRACKETS_OPENING + && eType + >= HEADER_FIELD_MESSAGE_ID) + { + ++nLength; + bBracketedBlock = true; + ++pBodyPtr; + } + bEnd = true; + break; + + default: + if (isAtomChar(*pBodyPtr) && bSymbol) + { + while (pBodyPtr != pBodyEnd + && isAtomChar(*pBodyPtr)) + { + ++nLength; + ++pBodyPtr; + } + bSymbol = false; + } + else + { + if (!isUSASCII(*pBodyPtr)) + bModify = true; + bEnd = true; + } + break; + } + + // Write a pending '<' if necessary: + if (eBrackets == BRACKETS_OPENING + && !bBracketedBlock) + { + if (rSink.getColumn() + + (bInitialSpace ? 1 : 0) + >= rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + rSink << '<'; + bInitialSpace = false; + eBrackets = BRACKETS_INSIDE; + } + + // Write the output: + if (rSink.getColumn() + (bInitialSpace ? 1 : 0) + + nLength + > rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + bInitialSpace = false; + if (bBracketedBlock) + { + rSink << '<'; + eBrackets = BRACKETS_OUTSIDE; + } + if (bModify) + { + enum Mode { MODE_PLAIN, MODE_QUOTED_STRING, + MODE_DOMAIN_LITERAL }; + Mode eMode = MODE_PLAIN; + while (pStart != pBodyPtr) + switch (*pStart) + { + case '\x0D': // CR + if (startsWithLineFolding( + pStart, pBodyPtr)) + { + if (eMode != MODE_PLAIN) + rSink << sal_Char( + pStart[2]); + pStart += 3; + } + else if (startsWithLineBreak( + pStart, pBodyPtr)) + { + rSink << "\x0D\\\x0A"; + // CR, '\', LF + pStart += 2; + } + else + { + rSink << '\x0D'; // CR + ++pStart; + } + break; + + case '\t': + case ' ': + if (eMode != MODE_PLAIN) + rSink << sal_Char(*pStart); + ++pStart; + break; + + case '"': + if (eMode == MODE_PLAIN) + eMode = MODE_QUOTED_STRING; + else if (eMode + == MODE_QUOTED_STRING) + eMode = MODE_PLAIN; + rSink << '"'; + ++pStart; + break; + + case '[': + if (eMode == MODE_PLAIN) + eMode = MODE_DOMAIN_LITERAL; + rSink << '['; + ++pStart; + break; + + case ']': + if (eMode == MODE_DOMAIN_LITERAL) + eMode = MODE_PLAIN; + rSink << ']'; + ++pStart; + break; + + case '\\': + rSink << '\\'; + if (++pStart < pBodyPtr) + writeUTF8(rSink, *pStart++); + break; + + default: + writeUTF8(rSink, *pStart++); + break; + } + } + else + rSink.write(pStart, pBodyPtr); + break; + } + + case ENTITY_PHRASE: + { + // Write a pending '<' if necessary: + if (eBrackets == BRACKETS_OPENING) + { + if (rSink.getColumn() + + (bInitialSpace ? 1 : 0) + >= rSink.getLineLengthLimit()) + rSink << INetMIMEOutputSink::endl << ' '; + else if (bInitialSpace) + rSink << ' '; + rSink << '<'; + bInitialSpace = false; + eBrackets = BRACKETS_INSIDE; + } + + // Calculate the length of in- and output: + const sal_Unicode * pStart = pBodyPtr; + bool bQuotedString = false; + bool bEnd = false; + while (pBodyPtr != pBodyEnd && !bEnd) + switch (*pBodyPtr) + { + case '\t': + case ' ': + case '\x0D': // CR + if (bQuotedString) + ++pBodyPtr; + else + { + const sal_Unicode * pLookAhead + = skipLinearWhiteSpace( + pBodyPtr, pBodyEnd); + if (pLookAhead != pBodyEnd + && (isAtomChar(*pLookAhead) + || !isUSASCII(*pLookAhead) + || *pLookAhead == '"')) + pBodyPtr = pLookAhead; + else + bEnd = true; + } + break; + + case '"': + bQuotedString = !bQuotedString; + ++pBodyPtr; + break; + + case '\\': + if (bQuotedString) + { + if (++pBodyPtr != pBodyEnd) + ++pBodyPtr; + } + else + bEnd = true; + break; + + default: + if (bQuotedString + || isAtomChar(*pBodyPtr) + || !isUSASCII(*pBodyPtr)) + ++pBodyPtr; + else + bEnd = true; + break; + } + + // Write the phrase, introducing encoded-words + // where necessary: + INetMIMEEncodedWordOutputSink + aOutput( + rSink, + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, + bInitialSpace ? + INetMIMEEncodedWordOutputSink::SPACE_ALWAYS : + INetMIMEEncodedWordOutputSink::SPACE_ENCODED, + ePreferredEncoding); + while (pStart != pBodyPtr) + switch (*pStart) + { + case '"': + ++pStart; + break; + + case '\\': + if (++pStart != pBodyPtr) + aOutput << *pStart++; + break; + + case '\x0D': // CR + pStart += 2; + aOutput << *pStart++; + break; + + default: + aOutput << *pStart++; + break; + } + bInitialSpace = aOutput.flush(); + break; + } + } + break; + } + } + } +} + +//============================================================================ +// static +bool INetMIME::translateUTF8Char(const sal_Char *& rBegin, + const sal_Char * pEnd, + rtl_TextEncoding eEncoding, + sal_uInt32 & rCharacter) +{ + if (rBegin == pEnd || static_cast< unsigned char >(*rBegin) < 0x80 + || static_cast< unsigned char >(*rBegin) >= 0xFE) + return false; + + int nCount; + sal_uInt32 nMin; + sal_uInt32 nUCS4; + const sal_Char * p = rBegin; + if (static_cast< unsigned char >(*p) < 0xE0) + { + nCount = 1; + nMin = 0x80; + nUCS4 = static_cast< unsigned char >(*p) & 0x1F; + } + else if (static_cast< unsigned char >(*p) < 0xF0) + { + nCount = 2; + nMin = 0x800; + nUCS4 = static_cast< unsigned char >(*p) & 0xF; + } + else if (static_cast< unsigned char >(*p) < 0xF8) + { + nCount = 3; + nMin = 0x10000; + nUCS4 = static_cast< unsigned char >(*p) & 7; + } + else if (static_cast< unsigned char >(*p) < 0xFC) + { + nCount = 4; + nMin = 0x200000; + nUCS4 = static_cast< unsigned char >(*p) & 3; + } + else + { + nCount = 5; + nMin = 0x4000000; + nUCS4 = static_cast< unsigned char >(*p) & 1; + } + ++p; + + for (; nCount-- > 0; ++p) + if ((static_cast< unsigned char >(*p) & 0xC0) == 0x80) + nUCS4 = (nUCS4 << 6) | (static_cast< unsigned char >(*p) & 0x3F); + else + return false; + + if (nUCS4 < nMin || nUCS4 > 0x10FFFF) + return false; + + if (eEncoding >= RTL_TEXTENCODING_UCS4) + rCharacter = nUCS4; + else + { + sal_Unicode aUTF16[2]; + const sal_Unicode * pUTF16End = putUTF32Character(aUTF16, nUCS4); + sal_Size nSize; + sal_Char * pBuffer = convertFromUnicode(aUTF16, pUTF16End, eEncoding, + nSize); + if (!pBuffer) + return false; + DBG_ASSERT(nSize == 1, + "INetMIME::translateUTF8Char(): Bad conversion"); + rCharacter = *pBuffer; + delete[] pBuffer; + } + rBegin = p; + return true; +} + +//============================================================================ +// static +ByteString INetMIME::decodeUTF8(const ByteString & rText, + rtl_TextEncoding eEncoding) +{ + const sal_Char * p = rText.GetBuffer(); + const sal_Char * pEnd = p + rText.Len(); + ByteString sDecoded; + while (p != pEnd) + { + sal_uInt32 nCharacter; + if (translateUTF8Char(p, pEnd, eEncoding, nCharacter)) + sDecoded += sal_Char(nCharacter); + else + sDecoded += sal_Char(*p++); + } + return sDecoded; +} + +//============================================================================ +// static +UniString INetMIME::decodeHeaderFieldBody(HeaderFieldType eType, + const ByteString & rBody) +{ + // Due to a bug in INetCoreRFC822MessageStream::ConvertTo7Bit(), old + // versions of StarOffice send mails with header fields where encoded + // words can be preceded by '=', ',', '.', '"', or '(', and followed by + // '=', ',', '.', '"', ')', without any required white space in between. + // And there appear to exist some broken mailers that only encode single + // letters within words, like "Appel + // =?iso-8859-1?Q?=E0?=t=?iso-8859-1?Q?=E9?=moin", so it seems best to + // detect encoded words even when not propperly surrounded by white space. + // + // Non US-ASCII characters in rBody are treated as ISO-8859-1. + // + // encoded-word = "=?" + // 1*(%x21 / %x23-27 / %x2A-2B / %x2D / %30-39 / %x41-5A / %x5E-7E) + // ["*" 1*8ALPHA *("-" 1*8ALPHA)] "?" + // ("B?" *(4base64) (4base64 / 3base64 "=" / 2base64 "==") + // / "Q?" 1*(%x21-3C / %x3E / %x40-7E / "=" 2HEXDIG)) + // "?=" + // + // base64 = ALPHA / DIGIT / "+" / "/" + + const sal_Char * pBegin = rBody.GetBuffer(); + const sal_Char * pEnd = pBegin + rBody.Len(); + + UniString sDecoded; + const sal_Char * pCopyBegin = pBegin; + + /* bool bStartEncodedWord = true; */ + const sal_Char * pWSPBegin = pBegin; + UniString sEncodedText; + bool bQuotedEncodedText = false; + sal_uInt32 nCommentLevel = 0; + + for (const sal_Char * p = pBegin; p != pEnd;) + { + if (p != pEnd && *p == '=' /* && bStartEncodedWord */) + { + const sal_Char * q = p + 1; + bool bEncodedWord = q != pEnd && *q++ == '?'; + + rtl_TextEncoding eCharsetEncoding = RTL_TEXTENCODING_DONTKNOW; + if (bEncodedWord) + { + const sal_Char * pCharsetBegin = q; + const sal_Char * pLanguageBegin = 0; + int nAlphaCount = 0; + for (bool bDone = false; !bDone;) + if (q == pEnd) + { + bEncodedWord = false; + bDone = true; + } + else + { + sal_Char cChar = *q++; + switch (cChar) + { + case '*': + pLanguageBegin = q - 1; + nAlphaCount = 0; + break; + + case '-': + if (pLanguageBegin != 0) + { + if (nAlphaCount == 0) + pLanguageBegin = 0; + else + nAlphaCount = 0; + } + break; + + case '?': + if (pCharsetBegin == q - 1) + bEncodedWord = false; + else + { + eCharsetEncoding + = getCharsetEncoding( + pCharsetBegin, + pLanguageBegin == 0 + || nAlphaCount == 0 ? + q - 1 : pLanguageBegin); + bEncodedWord = isMIMECharsetEncoding( + eCharsetEncoding); + eCharsetEncoding + = translateFromMIME(eCharsetEncoding); + } + bDone = true; + break; + + default: + if (pLanguageBegin != 0 + && (!isAlpha(cChar) || ++nAlphaCount > 8)) + pLanguageBegin = 0; + break; + } + } + } + + bool bEncodingB = false; + if (bEncodedWord) + { + if (q == pEnd) + bEncodedWord = false; + else + { + switch (*q++) + { + case 'B': + case 'b': + bEncodingB = true; + break; + + case 'Q': + case 'q': + bEncodingB = false; + break; + + default: + bEncodedWord = false; + break; + } + } + } + + bEncodedWord = bEncodedWord && q != pEnd && *q++ == '?'; + + ByteString sText; + if (bEncodedWord) + { + if (bEncodingB) + { + for (bool bDone = false; !bDone;) + { + if (pEnd - q < 4) + { + bEncodedWord = false; + bDone = true; + } + else + { + bool bFinal = false; + int nCount = 3; + sal_uInt32 nValue = 0; + for (int nShift = 18; nShift >= 0; nShift -= 6) + { + int nWeight = getBase64Weight(*q++); + if (nWeight == -2) + { + bEncodedWord = false; + bDone = true; + break; + } + if (nWeight == -1) + { + if (!bFinal) + { + if (nShift >= 12) + { + bEncodedWord = false; + bDone = true; + break; + } + bFinal = true; + nCount = nShift == 6 ? 1 : 2; + } + } + else + nValue |= nWeight << nShift; + } + if (bEncodedWord) + { + for (int nShift = 16; nCount-- > 0; + nShift -= 8) + sText += sal_Char(nValue >> nShift + & 0xFF); + if (*q == '?') + { + ++q; + bDone = true; + } + if (bFinal && !bDone) + { + bEncodedWord = false; + bDone = true; + } + } + } + } + } + else + { + const sal_Char * pEncodedTextBegin = q; + const sal_Char * pEncodedTextCopyBegin = q; + for (bool bDone = false; !bDone;) + if (q == pEnd) + { + bEncodedWord = false; + bDone = true; + } + else + { + sal_uInt32 nChar = *q++; + switch (nChar) + { + case '=': + { + if (pEnd - q < 2) + { + bEncodedWord = false; + bDone = true; + break; + } + int nDigit1 = getHexWeight(q[0]); + int nDigit2 = getHexWeight(q[1]); + if (nDigit1 < 0 || nDigit2 < 0) + { + bEncodedWord = false; + bDone = true; + break; + } + sText += rBody.Copy( + static_cast< xub_StrLen >( + pEncodedTextCopyBegin - pBegin), + static_cast< xub_StrLen >( + q - 1 - pEncodedTextCopyBegin)); + sText += sal_Char(nDigit1 << 4 | nDigit2); + q += 2; + pEncodedTextCopyBegin = q; + break; + } + + case '?': + if (q - pEncodedTextBegin > 1) + sText += rBody.Copy( + static_cast< xub_StrLen >( + pEncodedTextCopyBegin - pBegin), + static_cast< xub_StrLen >( + q - 1 - pEncodedTextCopyBegin)); + else + bEncodedWord = false; + bDone = true; + break; + + case '_': + sText += rBody.Copy( + static_cast< xub_StrLen >( + pEncodedTextCopyBegin - pBegin), + static_cast< xub_StrLen >( + q - 1 - pEncodedTextCopyBegin)); + sText += ' '; + pEncodedTextCopyBegin = q; + break; + + default: + if (!isVisible(nChar)) + { + bEncodedWord = false; + bDone = true; + } + break; + } + } + } + } + + bEncodedWord = bEncodedWord && q != pEnd && *q++ == '='; + +// if (bEncodedWord && q != pEnd) +// switch (*q) +// { +// case '\t': +// case ' ': +// case '"': +// case ')': +// case ',': +// case '.': +// case '=': +// break; +// +// default: +// bEncodedWord = false; +// break; +// } + + sal_Unicode * pUnicodeBuffer = 0; + sal_Size nUnicodeSize = 0; + if (bEncodedWord) + { + pUnicodeBuffer + = convertToUnicode(sText.GetBuffer(), + sText.GetBuffer() + sText.Len(), + eCharsetEncoding, nUnicodeSize); + if (pUnicodeBuffer == 0) + bEncodedWord = false; + } + + if (bEncodedWord) + { + appendISO88591(sDecoded, pCopyBegin, pWSPBegin); + if (eType == HEADER_FIELD_TEXT) + sDecoded.Append( + pUnicodeBuffer, + static_cast< xub_StrLen >(nUnicodeSize)); + else if (nCommentLevel == 0) + { + sEncodedText.Append( + pUnicodeBuffer, + static_cast< xub_StrLen >(nUnicodeSize)); + if (!bQuotedEncodedText) + { + const sal_Unicode * pTextPtr = pUnicodeBuffer; + const sal_Unicode * pTextEnd = pTextPtr + + nUnicodeSize; + for (; pTextPtr != pTextEnd; ++pTextPtr) + if (!isEncodedWordTokenChar(*pTextPtr)) + { + bQuotedEncodedText = true; + break; + } + } + } + else + { + const sal_Unicode * pTextPtr = pUnicodeBuffer; + const sal_Unicode * pTextEnd = pTextPtr + nUnicodeSize; + for (; pTextPtr != pTextEnd; ++pTextPtr) + { + switch (*pTextPtr) + { + case '(': + case ')': + case '\\': + case '\x0D': + case '=': + sDecoded += '\\'; + break; + } + sDecoded += *pTextPtr; + } + } + delete[] pUnicodeBuffer; + p = q; + pCopyBegin = p; + + pWSPBegin = p; + while (p != pEnd && isWhiteSpace(*p)) + ++p; + /* bStartEncodedWord = p != pWSPBegin; */ + continue; + } + } + + if (sEncodedText.Len() != 0) + { + if (bQuotedEncodedText) + { + sDecoded += '"'; + const sal_Unicode * pTextPtr = sEncodedText.GetBuffer(); + const sal_Unicode * pTextEnd = pTextPtr + sEncodedText.Len(); + for (;pTextPtr != pTextEnd; ++pTextPtr) + { + switch (*pTextPtr) + { + case '"': + case '\\': + case '\x0D': + sDecoded += '\\'; + break; + } + sDecoded += *pTextPtr; + } + sDecoded += '"'; + } + else + sDecoded += sEncodedText; + sEncodedText.Erase(); + bQuotedEncodedText = false; + } + + if (p == pEnd) + break; + + switch (*p++) + { +// case '\t': +// case ' ': +// case ',': +// case '.': +// case '=': +// bStartEncodedWord = true; +// break; + + case '"': + if (eType != HEADER_FIELD_TEXT && nCommentLevel == 0) + { + const sal_Char * pQuotedStringEnd + = skipQuotedString(p - 1, pEnd); + p = pQuotedStringEnd == p - 1 ? pEnd : pQuotedStringEnd; + } + /* bStartEncodedWord = true; */ + break; + + case '(': + if (eType != HEADER_FIELD_TEXT) + ++nCommentLevel; + /* bStartEncodedWord = true; */ + break; + + case ')': + if (nCommentLevel > 0) + --nCommentLevel; + /* bStartEncodedWord = false; */ + break; + + default: + { + const sal_Char * pUTF8Begin = p - 1; + const sal_Char * pUTF8End = pUTF8Begin; + sal_uInt32 nCharacter; + if (translateUTF8Char(pUTF8End, pEnd, RTL_TEXTENCODING_UCS4, + nCharacter)) + { + appendISO88591(sDecoded, pCopyBegin, p - 1); + sal_Unicode aUTF16Buf[2]; + xub_StrLen nUTF16Len = static_cast< xub_StrLen >( + putUTF32Character(aUTF16Buf, nCharacter) - aUTF16Buf); + sDecoded.Append(aUTF16Buf, nUTF16Len); + p = pUTF8End; + pCopyBegin = p; + } + /* bStartEncodedWord = false; */ + break; + } + } + pWSPBegin = p; + } + + appendISO88591(sDecoded, pCopyBegin, pEnd); + return sDecoded; +} + +//============================================================================ +// +// INetMIMEOutputSink +// +//============================================================================ + +// virtual +sal_Size INetMIMEOutputSink::writeSequence(const sal_Char * pSequence) +{ + sal_Size nLength = rtl_str_getLength(pSequence); + writeSequence(pSequence, pSequence + nLength); + return nLength; +} + +//============================================================================ +// virtual +void INetMIMEOutputSink::writeSequence(const sal_uInt32 * pBegin, + const sal_uInt32 * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIMEOutputSink::writeSequence(): Bad sequence"); + + sal_Char * pBufferBegin = new sal_Char[pEnd - pBegin]; + sal_Char * pBufferEnd = pBufferBegin; + while (pBegin != pEnd) + { + DBG_ASSERT(*pBegin < 256, + "INetMIMEOutputSink::writeSequence(): Bad octet"); + *pBufferEnd++ = sal_Char(*pBegin++); + } + writeSequence(pBufferBegin, pBufferEnd); + delete[] pBufferBegin; +} + +//============================================================================ +// virtual +void INetMIMEOutputSink::writeSequence(const sal_Unicode * pBegin, + const sal_Unicode * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIMEOutputSink::writeSequence(): Bad sequence"); + + sal_Char * pBufferBegin = new sal_Char[pEnd - pBegin]; + sal_Char * pBufferEnd = pBufferBegin; + while (pBegin != pEnd) + { + DBG_ASSERT(*pBegin < 256, + "INetMIMEOutputSink::writeSequence(): Bad octet"); + *pBufferEnd++ = sal_Char(*pBegin++); + } + writeSequence(pBufferBegin, pBufferEnd); + delete[] pBufferBegin; +} + +//============================================================================ +// virtual +ErrCode INetMIMEOutputSink::getError() const +{ + return ERRCODE_NONE; +} + +//============================================================================ +void INetMIMEOutputSink::writeLineEnd() +{ + static const sal_Char aCRLF[2] = { 0x0D, 0x0A }; + writeSequence(aCRLF, aCRLF + 2); + m_nColumn = 0; +} + +//============================================================================ +// +// INetMIMEStringOutputSink +// +//============================================================================ + +// virtual +void INetMIMEStringOutputSink::writeSequence(const sal_Char * pBegin, + const sal_Char * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIMEStringOutputSink::writeSequence(): Bad sequence"); + + m_bOverflow = m_bOverflow + || pEnd - pBegin > STRING_MAXLEN - m_aBuffer.Len(); + if (!m_bOverflow) + m_aBuffer.Append(pBegin, static_cast< xub_StrLen >(pEnd - pBegin)); +} + +//============================================================================ +// virtual +ErrCode INetMIMEStringOutputSink::getError() const +{ + return m_bOverflow ? ERRCODE_IO_OUTOFMEMORY : ERRCODE_NONE; +} + +//============================================================================ +// +// INetMIMEUnicodeOutputSink +// +//============================================================================ + +// virtual +void INetMIMEUnicodeOutputSink::writeSequence(const sal_Char * pBegin, + const sal_Char * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIMEUnicodeOutputSink::writeSequence(): Bad sequence"); + + sal_Unicode * pBufferBegin = new sal_Unicode[pEnd - pBegin]; + sal_Unicode * pBufferEnd = pBufferBegin; + while (pBegin != pEnd) + *pBufferEnd++ = sal_uChar(*pBegin++); + writeSequence(pBufferBegin, pBufferEnd); + delete[] pBufferBegin; +} + +//============================================================================ +// virtual +void INetMIMEUnicodeOutputSink::writeSequence(const sal_uInt32 * pBegin, + const sal_uInt32 * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIMEUnicodeOutputSink::writeSequence(): Bad sequence"); + + sal_Unicode * pBufferBegin = new sal_Unicode[pEnd - pBegin]; + sal_Unicode * pBufferEnd = pBufferBegin; + while (pBegin != pEnd) + { + DBG_ASSERT(*pBegin < 256, + "INetMIMEOutputSink::writeSequence(): Bad octet"); + *pBufferEnd++ = sal_Unicode(*pBegin++); + } + writeSequence(pBufferBegin, pBufferEnd); + delete[] pBufferBegin; +} + +//============================================================================ +// virtual +void INetMIMEUnicodeOutputSink::writeSequence(const sal_Unicode * pBegin, + const sal_Unicode * pEnd) +{ + DBG_ASSERT(pBegin && pBegin <= pEnd, + "INetMIMEUnicodeOutputSink::writeSequence(): Bad sequence"); + + m_bOverflow = m_bOverflow + || pEnd - pBegin > STRING_MAXLEN - m_aBuffer.Len(); + if (!m_bOverflow) + m_aBuffer.Append(pBegin, static_cast< xub_StrLen >(pEnd - pBegin)); +} + +//============================================================================ +// virtual +ErrCode INetMIMEUnicodeOutputSink::getError() const +{ + return m_bOverflow ? ERRCODE_IO_OUTOFMEMORY : ERRCODE_NONE; +} + +//============================================================================ +// +// INetMIMEEncodedWordOutputSink +// +//============================================================================ + +static const sal_Char aEscape[128] + = { INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x00 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x01 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x02 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x03 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x04 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x05 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x06 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x07 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x08 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x09 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0A + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0B + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0C + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0D + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0E + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x0F + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x10 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x11 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x12 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x13 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x14 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x15 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x16 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x17 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x18 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x19 + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1A + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1B + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1C + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1D + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1E + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // 0x1F + 0, // ' ' + 0, // '!' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '"' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '#' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '$' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '%' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '&' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // ''' + INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '(' + INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // ')' + 0, // '*' + 0, // '+' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // ',' + 0, // '-' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '.' + 0, // '/' + 0, // '0' + 0, // '1' + 0, // '2' + 0, // '3' + 0, // '4' + 0, // '5' + 0, // '6' + 0, // '7' + 0, // '8' + 0, // '9' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // ':' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // ';' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '<' + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '=' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '>' + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '?' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '@' + 0, // 'A' + 0, // 'B' + 0, // 'C' + 0, // 'D' + 0, // 'E' + 0, // 'F' + 0, // 'G' + 0, // 'H' + 0, // 'I' + 0, // 'J' + 0, // 'K' + 0, // 'L' + 0, // 'M' + 0, // 'N' + 0, // 'O' + 0, // 'P' + 0, // 'Q' + 0, // 'R' + 0, // 'S' + 0, // 'T' + 0, // 'U' + 0, // 'V' + 0, // 'W' + 0, // 'X' + 0, // 'Y' + 0, // 'Z' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '[' + INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '\' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // ']' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '^' + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '_' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '`' + 0, // 'a' + 0, // 'b' + 0, // 'c' + 0, // 'd' + 0, // 'e' + 0, // 'f' + 0, // 'g' + 0, // 'h' + 0, // 'i' + 0, // 'j' + 0, // 'k' + 0, // 'l' + 0, // 'm' + 0, // 'n' + 0, // 'o' + 0, // 'p' + 0, // 'q' + 0, // 'r' + 0, // 's' + 0, // 't' + 0, // 'u' + 0, // 'v' + 0, // 'w' + 0, // 'x' + 0, // 'y' + 0, // 'z' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '{' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '|' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '}' + INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE, // '~' + INetMIMEEncodedWordOutputSink::CONTEXT_TEXT | INetMIMEEncodedWordOutputSink::CONTEXT_COMMENT | INetMIMEEncodedWordOutputSink::CONTEXT_PHRASE }; // DEL + +inline bool +INetMIMEEncodedWordOutputSink::needsEncodedWordEscape(sal_uInt32 nChar) const +{ + return !INetMIME::isUSASCII(nChar) || aEscape[nChar] & m_eContext; +} + +//============================================================================ +void INetMIMEEncodedWordOutputSink::finish(bool bWriteTrailer) +{ + if (m_eInitialSpace == SPACE_ALWAYS && m_nExtraSpaces == 0) + m_nExtraSpaces = 1; + + if (m_eEncodedWordState == STATE_SECOND_EQUALS) + { + // If the text is already an encoded word, copy it verbatim: + sal_uInt32 nSize = m_pBufferEnd - m_pBuffer; + switch (m_ePrevCoding) + { + case CODING_QUOTED: + m_rSink << '"'; + case CODING_NONE: + if (m_eInitialSpace == SPACE_ENCODED && m_nExtraSpaces == 0) + m_nExtraSpaces = 1; + for (; m_nExtraSpaces > 1; --m_nExtraSpaces) + { + if (m_rSink.getColumn() >= m_rSink.getLineLengthLimit()) + m_rSink << INetMIMEOutputSink::endl; + m_rSink << ' '; + } + if (m_nExtraSpaces == 1) + { + if (m_rSink.getColumn() + nSize + >= m_rSink.getLineLengthLimit()) + m_rSink << INetMIMEOutputSink::endl; + m_rSink << ' '; + } + break; + + case CODING_ENCODED: + { + const sal_Char * pCharsetName + = INetMIME::getCharsetName(m_ePrevMIMEEncoding); + while (m_nExtraSpaces-- > 0) + { + if (m_rSink.getColumn() + > m_rSink.getLineLengthLimit() - 3) + m_rSink << "?=" << INetMIMEOutputSink::endl << " =?" + << pCharsetName << "?Q?"; + m_rSink << '_'; + } + m_rSink << "?="; + } + case CODING_ENCODED_TERMINATED: + if (m_rSink.getColumn() + nSize + > m_rSink.getLineLengthLimit() - 1) + m_rSink << INetMIMEOutputSink::endl; + m_rSink << ' '; + break; + } + m_rSink.write(m_pBuffer, m_pBufferEnd); + m_eCoding = CODING_ENCODED_TERMINATED; + } + else + { + // If the text itself is too long to fit into a single line, make it + // into multiple encoded words: + switch (m_eCoding) + { + case CODING_NONE: + if (m_nExtraSpaces == 0) + { + DBG_ASSERT(m_ePrevCoding == CODING_NONE + || m_pBuffer == m_pBufferEnd, + "INetMIMEEncodedWordOutputSink::finish():" + " Bad state"); + if (m_rSink.getColumn() + (m_pBufferEnd - m_pBuffer) + > m_rSink.getLineLengthLimit()) + m_eCoding = CODING_ENCODED; + } + else + { + OSL_ASSERT(m_pBufferEnd >= m_pBuffer); + if (static_cast< std::size_t >(m_pBufferEnd - m_pBuffer) + > m_rSink.getLineLengthLimit() - 1) + { + m_eCoding = CODING_ENCODED; + } + } + break; + + case CODING_QUOTED: + if (m_nExtraSpaces == 0) + { + DBG_ASSERT(m_ePrevCoding == CODING_NONE, + "INetMIMEEncodedWordOutputSink::finish():" + " Bad state"); + if (m_rSink.getColumn() + (m_pBufferEnd - m_pBuffer) + + m_nQuotedEscaped + > m_rSink.getLineLengthLimit() - 2) + m_eCoding = CODING_ENCODED; + } + else if ((m_pBufferEnd - m_pBuffer) + m_nQuotedEscaped + > m_rSink.getLineLengthLimit() - 3) + m_eCoding = CODING_ENCODED; + break; + + default: + break; + } + + switch (m_eCoding) + { + case CODING_NONE: + switch (m_ePrevCoding) + { + case CODING_QUOTED: + if (m_rSink.getColumn() + m_nExtraSpaces + + (m_pBufferEnd - m_pBuffer) + < m_rSink.getLineLengthLimit()) + m_eCoding = CODING_QUOTED; + else + m_rSink << '"'; + break; + + case CODING_ENCODED: + m_rSink << "?="; + break; + + default: + break; + } + for (; m_nExtraSpaces > 1; --m_nExtraSpaces) + { + if (m_rSink.getColumn() >= m_rSink.getLineLengthLimit()) + m_rSink << INetMIMEOutputSink::endl; + m_rSink << ' '; + } + if (m_nExtraSpaces == 1) + { + if (m_rSink.getColumn() + (m_pBufferEnd - m_pBuffer) + >= m_rSink.getLineLengthLimit()) + m_rSink << INetMIMEOutputSink::endl; + m_rSink << ' '; + } + m_rSink.write(m_pBuffer, m_pBufferEnd); + if (m_eCoding == CODING_QUOTED && bWriteTrailer) + { + m_rSink << '"'; + m_eCoding = CODING_NONE; + } + break; + + case CODING_QUOTED: + { + bool bInsertLeadingQuote = true; + sal_uInt32 nSize = (m_pBufferEnd - m_pBuffer) + + m_nQuotedEscaped + 2; + switch (m_ePrevCoding) + { + case CODING_QUOTED: + if (m_rSink.getColumn() + m_nExtraSpaces + nSize - 1 + < m_rSink.getLineLengthLimit()) + { + bInsertLeadingQuote = false; + --nSize; + } + else + m_rSink << '"'; + break; + + case CODING_ENCODED: + m_rSink << "?="; + break; + + default: + break; + } + for (; m_nExtraSpaces > 1; --m_nExtraSpaces) + { + if (m_rSink.getColumn() >= m_rSink.getLineLengthLimit()) + m_rSink << INetMIMEOutputSink::endl; + m_rSink << ' '; + } + if (m_nExtraSpaces == 1) + { + if (m_rSink.getColumn() + nSize + >= m_rSink.getLineLengthLimit()) + m_rSink << INetMIMEOutputSink::endl; + m_rSink << ' '; + } + if (bInsertLeadingQuote) + m_rSink << '"'; + for (const sal_Unicode * p = m_pBuffer; p != m_pBufferEnd; + ++p) + { + if (INetMIME::needsQuotedStringEscape(*p)) + m_rSink << '\\'; + m_rSink << sal_Char(*p); + } + if (bWriteTrailer) + { + m_rSink << '"'; + m_eCoding = CODING_NONE; + } + break; + } + + case CODING_ENCODED: + { + rtl_TextEncoding eCharsetEncoding + = m_pEncodingList-> + getPreferredEncoding(RTL_TEXTENCODING_UTF8); + rtl_TextEncoding eMIMEEncoding + = INetMIME::translateToMIME(eCharsetEncoding); + + // The non UTF-8 code will only work for stateless single byte + // character encodings (see also below): + sal_Char * pTargetBuffer = NULL; + sal_Size nTargetSize = 0; + sal_uInt32 nSize; + if (eMIMEEncoding == RTL_TEXTENCODING_UTF8) + { + nSize = 0; + for (sal_Unicode const * p = m_pBuffer; + p != m_pBufferEnd;) + { + sal_uInt32 nUTF32 + = INetMIME::getUTF32Character(p, m_pBufferEnd); + nSize += needsEncodedWordEscape(nUTF32) ? + 3 * INetMIME::getUTF8OctetCount(nUTF32) : + 1; + // only US-ASCII characters (that are converted to + // a single byte by UTF-8) need no encoded word + // escapes... + } + } + else + { + rtl_UnicodeToTextConverter hConverter + = rtl_createUnicodeToTextConverter(eCharsetEncoding); + rtl_UnicodeToTextContext hContext + = rtl_createUnicodeToTextContext(hConverter); + for (sal_Size nBufferSize = m_pBufferEnd - m_pBuffer;; + nBufferSize += nBufferSize / 3 + 1) + { + pTargetBuffer = new sal_Char[nBufferSize]; + sal_uInt32 nInfo; + sal_Size nSrcCvtBytes; + nTargetSize + = rtl_convertUnicodeToText( + hConverter, hContext, m_pBuffer, + m_pBufferEnd - m_pBuffer, pTargetBuffer, + nBufferSize, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_IGNORE + | RTL_UNICODETOTEXT_FLAGS_INVALID_IGNORE, + &nInfo, &nSrcCvtBytes); + if (!(nInfo + & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL)) + break; + delete[] pTargetBuffer; + pTargetBuffer = NULL; + rtl_resetUnicodeToTextContext(hConverter, hContext); + } + rtl_destroyUnicodeToTextContext(hConverter, hContext); + rtl_destroyUnicodeToTextConverter(hConverter); + + nSize = nTargetSize; + for (sal_Size k = 0; k < nTargetSize; ++k) + if (needsEncodedWordEscape(sal_uChar( + pTargetBuffer[k]))) + nSize += 2; + } + + const sal_Char * pCharsetName + = INetMIME::getCharsetName(eMIMEEncoding); + sal_uInt32 nWrapperSize = rtl_str_getLength(pCharsetName) + 7; + // '=?', '?Q?', '?=' + + switch (m_ePrevCoding) + { + case CODING_QUOTED: + m_rSink << '"'; + case CODING_NONE: + if (m_eInitialSpace == SPACE_ENCODED + && m_nExtraSpaces == 0) + m_nExtraSpaces = 1; + nSize += nWrapperSize; + for (; m_nExtraSpaces > 1; --m_nExtraSpaces) + { + if (m_rSink.getColumn() + >= m_rSink.getLineLengthLimit()) + m_rSink << INetMIMEOutputSink::endl; + m_rSink << ' '; + } + if (m_nExtraSpaces == 1) + { + if (m_rSink.getColumn() + nSize + >= m_rSink.getLineLengthLimit()) + m_rSink << INetMIMEOutputSink::endl; + m_rSink << ' '; + } + m_rSink << "=?" << pCharsetName << "?Q?"; + break; + + case CODING_ENCODED: + if (m_ePrevMIMEEncoding != eMIMEEncoding + || m_rSink.getColumn() + m_nExtraSpaces + nSize + > m_rSink.getLineLengthLimit() - 2) + { + m_rSink << "?="; + if (m_rSink.getColumn() + nWrapperSize + + m_nExtraSpaces + nSize + > m_rSink.getLineLengthLimit() - 1) + m_rSink << INetMIMEOutputSink::endl; + m_rSink << " =?" << pCharsetName << "?Q?"; + } + while (m_nExtraSpaces-- > 0) + { + if (m_rSink.getColumn() + > m_rSink.getLineLengthLimit() - 3) + m_rSink << "?=" << INetMIMEOutputSink::endl + << " =?" << pCharsetName << "?Q?"; + m_rSink << '_'; + } + break; + + case CODING_ENCODED_TERMINATED: + if (m_rSink.getColumn() + nWrapperSize + + m_nExtraSpaces + nSize + > m_rSink.getLineLengthLimit() - 1) + m_rSink << INetMIMEOutputSink::endl; + m_rSink << " =?" << pCharsetName << "?Q?"; + while (m_nExtraSpaces-- > 0) + { + if (m_rSink.getColumn() + > m_rSink.getLineLengthLimit() - 3) + m_rSink << "?=" << INetMIMEOutputSink::endl + << " =?" << pCharsetName << "?Q?"; + m_rSink << '_'; + } + break; + } + + // The non UTF-8 code will only work for stateless single byte + // character encodings (see also above): + if (eMIMEEncoding == RTL_TEXTENCODING_UTF8) + { + bool bInitial = true; + for (sal_Unicode const * p = m_pBuffer; + p != m_pBufferEnd;) + { + sal_uInt32 nUTF32 + = INetMIME::getUTF32Character(p, m_pBufferEnd); + bool bEscape = needsEncodedWordEscape(nUTF32); + sal_uInt32 nWidth + = bEscape ? + 3 * INetMIME::getUTF8OctetCount(nUTF32) : 1; + // only US-ASCII characters (that are converted to + // a single byte by UTF-8) need no encoded word + // escapes... + if (!bInitial + && m_rSink.getColumn() + nWidth + 2 + > m_rSink.getLineLengthLimit()) + m_rSink << "?=" << INetMIMEOutputSink::endl + << " =?" << pCharsetName << "?Q?"; + if (bEscape) + { + DBG_ASSERT( + nUTF32 < 0x10FFFF, + "INetMIMEEncodedWordOutputSink::finish():" + " Bad char"); + if (nUTF32 < 0x80) + INetMIME::writeEscapeSequence(m_rSink, + nUTF32); + else if (nUTF32 < 0x800) + { + INetMIME::writeEscapeSequence(m_rSink, + (nUTF32 >> 6) + | 0xC0); + INetMIME::writeEscapeSequence(m_rSink, + (nUTF32 & 0x3F) + | 0x80); + } + else if (nUTF32 < 0x10000) + { + INetMIME::writeEscapeSequence(m_rSink, + (nUTF32 >> 12) + | 0xE0); + INetMIME::writeEscapeSequence(m_rSink, + ((nUTF32 >> 6) + & 0x3F) + | 0x80); + INetMIME::writeEscapeSequence(m_rSink, + (nUTF32 & 0x3F) + | 0x80); + } + else + { + INetMIME::writeEscapeSequence(m_rSink, + (nUTF32 >> 18) + | 0xF0); + INetMIME::writeEscapeSequence(m_rSink, + ((nUTF32 >> 12) + & 0x3F) + | 0x80); + INetMIME::writeEscapeSequence(m_rSink, + ((nUTF32 >> 6) + & 0x3F) + | 0x80); + INetMIME::writeEscapeSequence(m_rSink, + (nUTF32 & 0x3F) + | 0x80); + } + } + else + m_rSink << sal_Char(nUTF32); + bInitial = false; + } + } + else + { + for (sal_Size k = 0; k < nTargetSize; ++k) + { + sal_uInt32 nUCS4 = sal_uChar(pTargetBuffer[k]); + bool bEscape = needsEncodedWordEscape(nUCS4); + if (k > 0 + && m_rSink.getColumn() + (bEscape ? 5 : 3) + > m_rSink.getLineLengthLimit()) + m_rSink << "?=" << INetMIMEOutputSink::endl + << " =?" << pCharsetName << "?Q?"; + if (bEscape) + INetMIME::writeEscapeSequence(m_rSink, nUCS4); + else + m_rSink << sal_Char(nUCS4); + } + delete[] pTargetBuffer; + } + + if (bWriteTrailer) + { + m_rSink << "?="; + m_eCoding = CODING_ENCODED_TERMINATED; + } + + m_ePrevMIMEEncoding = eMIMEEncoding; + break; + } + + default: + OSL_ASSERT(false); + break; + } + } + + m_eInitialSpace = SPACE_NO; + m_nExtraSpaces = 0; + m_pEncodingList->reset(); + m_pBufferEnd = m_pBuffer; + m_ePrevCoding = m_eCoding; + m_eCoding = CODING_NONE; + m_nQuotedEscaped = 0; + m_eEncodedWordState = STATE_INITIAL; +} + +//============================================================================ +INetMIMEEncodedWordOutputSink::~INetMIMEEncodedWordOutputSink() +{ + rtl_freeMemory(m_pBuffer); + delete m_pEncodingList; +} + +//============================================================================ +INetMIMEEncodedWordOutputSink & +INetMIMEEncodedWordOutputSink::operator <<(sal_uInt32 nChar) +{ + if (nChar == ' ') + { + if (m_pBufferEnd != m_pBuffer) + finish(false); + ++m_nExtraSpaces; + } + else + { + // Check for an already encoded word: + switch (m_eEncodedWordState) + { + case STATE_INITIAL: + if (nChar == '=') + m_eEncodedWordState = STATE_FIRST_EQUALS; + else + m_eEncodedWordState = STATE_BAD; + break; + + case STATE_FIRST_EQUALS: + if (nChar == '?') + m_eEncodedWordState = STATE_FIRST_EQUALS; + else + m_eEncodedWordState = STATE_BAD; + break; + + case STATE_FIRST_QUESTION: + if (INetMIME::isEncodedWordTokenChar(nChar)) + m_eEncodedWordState = STATE_CHARSET; + else + m_eEncodedWordState = STATE_BAD; + break; + + case STATE_CHARSET: + if (nChar == '?') + m_eEncodedWordState = STATE_SECOND_QUESTION; + else if (!INetMIME::isEncodedWordTokenChar(nChar)) + m_eEncodedWordState = STATE_BAD; + break; + + case STATE_SECOND_QUESTION: + if (nChar == 'B' || nChar == 'Q' + || nChar == 'b' || nChar == 'q') + m_eEncodedWordState = STATE_ENCODING; + else + m_eEncodedWordState = STATE_BAD; + break; + + case STATE_ENCODING: + if (nChar == '?') + m_eEncodedWordState = STATE_THIRD_QUESTION; + else + m_eEncodedWordState = STATE_BAD; + break; + + case STATE_THIRD_QUESTION: + if (INetMIME::isVisible(nChar) && nChar != '?') + m_eEncodedWordState = STATE_ENCODED_TEXT; + else + m_eEncodedWordState = STATE_BAD; + break; + + case STATE_ENCODED_TEXT: + if (nChar == '?') + m_eEncodedWordState = STATE_FOURTH_QUESTION; + else if (!INetMIME::isVisible(nChar)) + m_eEncodedWordState = STATE_BAD; + break; + + case STATE_FOURTH_QUESTION: + if (nChar == '=') + m_eEncodedWordState = STATE_SECOND_EQUALS; + else + m_eEncodedWordState = STATE_BAD; + break; + + case STATE_SECOND_EQUALS: + m_eEncodedWordState = STATE_BAD; + break; + + case STATE_BAD: + break; + } + + // Update encoding: + m_pEncodingList->includes(nChar); + + // Update coding: + enum { TENQ = 1, // CONTEXT_TEXT, CODING_ENCODED + CENQ = 2, // CONTEXT_COMMENT, CODING_ENCODED + PQTD = 4, // CONTEXT_PHRASE, CODING_QUOTED + PENQ = 8 }; // CONTEXT_PHRASE, CODING_ENCODED + static const sal_Char aMinimal[128] + = { TENQ | CENQ | PENQ, // 0x00 + TENQ | CENQ | PENQ, // 0x01 + TENQ | CENQ | PENQ, // 0x02 + TENQ | CENQ | PENQ, // 0x03 + TENQ | CENQ | PENQ, // 0x04 + TENQ | CENQ | PENQ, // 0x05 + TENQ | CENQ | PENQ, // 0x06 + TENQ | CENQ | PENQ, // 0x07 + TENQ | CENQ | PENQ, // 0x08 + TENQ | CENQ | PENQ, // 0x09 + TENQ | CENQ | PENQ, // 0x0A + TENQ | CENQ | PENQ, // 0x0B + TENQ | CENQ | PENQ, // 0x0C + TENQ | CENQ | PENQ, // 0x0D + TENQ | CENQ | PENQ, // 0x0E + TENQ | CENQ | PENQ, // 0x0F + TENQ | CENQ | PENQ, // 0x10 + TENQ | CENQ | PENQ, // 0x11 + TENQ | CENQ | PENQ, // 0x12 + TENQ | CENQ | PENQ, // 0x13 + TENQ | CENQ | PENQ, // 0x14 + TENQ | CENQ | PENQ, // 0x15 + TENQ | CENQ | PENQ, // 0x16 + TENQ | CENQ | PENQ, // 0x17 + TENQ | CENQ | PENQ, // 0x18 + TENQ | CENQ | PENQ, // 0x19 + TENQ | CENQ | PENQ, // 0x1A + TENQ | CENQ | PENQ, // 0x1B + TENQ | CENQ | PENQ, // 0x1C + TENQ | CENQ | PENQ, // 0x1D + TENQ | CENQ | PENQ, // 0x1E + TENQ | CENQ | PENQ, // 0x1F + 0, // ' ' + 0, // '!' + PQTD , // '"' + 0, // '#' + 0, // '$' + 0, // '%' + 0, // '&' + 0, // ''' + CENQ | PQTD , // '(' + CENQ | PQTD , // ')' + 0, // '*' + 0, // '+' + PQTD , // ',' + 0, // '-' + PQTD , // '.' + 0, // '/' + 0, // '0' + 0, // '1' + 0, // '2' + 0, // '3' + 0, // '4' + 0, // '5' + 0, // '6' + 0, // '7' + 0, // '8' + 0, // '9' + PQTD , // ':' + PQTD , // ';' + PQTD , // '<' + 0, // '=' + PQTD , // '>' + 0, // '?' + PQTD , // '@' + 0, // 'A' + 0, // 'B' + 0, // 'C' + 0, // 'D' + 0, // 'E' + 0, // 'F' + 0, // 'G' + 0, // 'H' + 0, // 'I' + 0, // 'J' + 0, // 'K' + 0, // 'L' + 0, // 'M' + 0, // 'N' + 0, // 'O' + 0, // 'P' + 0, // 'Q' + 0, // 'R' + 0, // 'S' + 0, // 'T' + 0, // 'U' + 0, // 'V' + 0, // 'W' + 0, // 'X' + 0, // 'Y' + 0, // 'Z' + PQTD , // '[' + CENQ | PQTD , // '\' + PQTD , // ']' + 0, // '^' + 0, // '_' + 0, // '`' + 0, // 'a' + 0, // 'b' + 0, // 'c' + 0, // 'd' + 0, // 'e' + 0, // 'f' + 0, // 'g' + 0, // 'h' + 0, // 'i' + 0, // 'j' + 0, // 'k' + 0, // 'l' + 0, // 'm' + 0, // 'n' + 0, // 'o' + 0, // 'p' + 0, // 'q' + 0, // 'r' + 0, // 's' + 0, // 't' + 0, // 'u' + 0, // 'v' + 0, // 'w' + 0, // 'x' + 0, // 'y' + 0, // 'z' + 0, // '{' + 0, // '|' + 0, // '}' + 0, // '~' + TENQ | CENQ | PENQ }; // DEL + Coding eNewCoding = !INetMIME::isUSASCII(nChar) ? CODING_ENCODED : + m_eContext == CONTEXT_PHRASE ? + Coding(aMinimal[nChar] >> 2) : + aMinimal[nChar] & m_eContext ? CODING_ENCODED : + CODING_NONE; + if (eNewCoding > m_eCoding) + m_eCoding = eNewCoding; + if (m_eCoding == CODING_QUOTED + && INetMIME::needsQuotedStringEscape(nChar)) + ++m_nQuotedEscaped; + + // Append to buffer: + if (sal_uInt32(m_pBufferEnd - m_pBuffer) == m_nBufferSize) + { + m_pBuffer + = static_cast< sal_Unicode * >( + rtl_reallocateMemory(m_pBuffer, + (m_nBufferSize + BUFFER_SIZE) + * sizeof (sal_Unicode))); + m_pBufferEnd = m_pBuffer + m_nBufferSize; + m_nBufferSize += BUFFER_SIZE; + } + *m_pBufferEnd++ = sal_Unicode(nChar); + } + return *this; +} + +//============================================================================ +// +// INetContentTypeParameterList +// +//============================================================================ + +void INetContentTypeParameterList::Clear() +{ + while (Count() > 0) + delete static_cast< INetContentTypeParameter * >(Remove(Count() - 1)); +} + +//============================================================================ +const INetContentTypeParameter * +INetContentTypeParameterList::find(const ByteString & rAttribute) const +{ + for (ULONG i = 0; i < Count(); ++i) + { + const INetContentTypeParameter * pParameter = GetObject(i); + if (pParameter->m_sAttribute.EqualsIgnoreCaseAscii(rAttribute)) + return pParameter; + } + return 0; +} + diff --git a/tools/source/inet/inetmsg.cxx b/tools/source/inet/inetmsg.cxx new file mode 100644 index 000000000000..1acef4719c85 --- /dev/null +++ b/tools/source/inet/inetmsg.cxx @@ -0,0 +1,1656 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: inetmsg.cxx,v $ + * $Revision: 1.12 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" +#include <sal/types.h> +#include <tools/datetime.hxx> +#ifndef _TOOLS_INETMIME_HXX +#include <tools/inetmime.hxx> +#endif +#include <tools/inetmsg.hxx> +#include <tools/inetstrm.hxx> +#include <rtl/instance.hxx> + +#include <stdio.h> + +//======================================================================= + +inline sal_Bool ascii_isDigit( sal_Unicode ch ) +{ + return ((ch >= 0x0030) && (ch <= 0x0039)); +} + +inline sal_Bool ascii_isLetter( sal_Unicode ch ) +{ + return (( (ch >= 0x0041) && (ch <= 0x005A)) || ((ch >= 0x0061) && (ch <= 0x007A))); +} + +inline sal_Unicode ascii_toLowerCase( sal_Unicode ch ) +{ + if ( (ch >= 0x0041) && (ch <= 0x005A) ) + return ch + 0x20; + else + return ch; +} + +/*======================================================================= + * + * INetMessage Implementation. + * + *=====================================================================*/ +#define CONSTASCII_STRINGPARAM(a) (a), RTL_TEXTENCODING_ASCII_US +#define HEADERFIELD INetMessageHeader + +/* + * ~INetMessage. + */ +INetMessage::~INetMessage (void) +{ + ListCleanup_Impl(); +} + +/* + * ListCleanup_Impl. + */ +void INetMessage::ListCleanup_Impl (void) +{ + // Cleanup. + ULONG i, n = m_aHeaderList.Count(); + for (i = 0; i < n; i++) + delete ((HEADERFIELD*)(m_aHeaderList.GetObject(i))); + m_aHeaderList.Clear(); +} + +/* + * ListCopy. + */ +void INetMessage::ListCopy (const INetMessage &rMsg) +{ + if (!(this == &rMsg)) + { + // Cleanup. + ListCleanup_Impl(); + + // Copy. + ULONG i, n = rMsg.GetHeaderCount(); + for (i = 0; i < n; i++) + { + HEADERFIELD *p = (HEADERFIELD*)(rMsg.m_aHeaderList.GetObject(i)); + m_aHeaderList.Insert (new HEADERFIELD(*p), LIST_APPEND); + } + } +} + +/* + * SetHeaderField_Impl. + */ +void INetMessage::SetHeaderField_Impl ( + INetMIME::HeaderFieldType eType, + const ByteString &rName, + const UniString &rValue, + ULONG &rnIndex) +{ + INetMIMEStringOutputSink aSink (0, STRING_MAXLEN); + INetMIME::writeHeaderFieldBody ( + aSink, eType, rValue, gsl_getSystemTextEncoding(), false); + SetHeaderField_Impl ( + INetMessageHeader (rName, aSink.takeBuffer()), rnIndex); +} + +/* + * SetHeaderField. + */ +ULONG INetMessage::SetHeaderField ( + const UniString& rName, const UniString& rValue, ULONG nIndex) +{ + ULONG nResult = nIndex; + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_TEXT, + ByteString (rName, RTL_TEXTENCODING_ASCII_US), rValue, + nResult); + return nResult; +} + +/* + * SetHeaderField. + */ +ULONG INetMessage::SetHeaderField ( + const INetMessageHeader &rHeader, ULONG nIndex) +{ + ULONG nResult = nIndex; + SetHeaderField_Impl (rHeader, nResult); + return nResult; +} + + +/* + * operator<< + */ +SvStream& INetMessage::operator<< (SvStream& rStrm) const +{ + rStrm << static_cast<sal_uInt32>(m_nDocSize); + rStrm.WriteByteString (m_aDocName, RTL_TEXTENCODING_UTF8); + + ULONG i, n = m_aHeaderList.Count(); + rStrm << static_cast<sal_uInt32>(n); + + for (i = 0; i < n; i++) + rStrm << *((HEADERFIELD *)(m_aHeaderList.GetObject(i))); + + return rStrm; +} + +/* + * operator>> + */ +SvStream& INetMessage::operator>> (SvStream& rStrm) +{ + // Cleanup. + m_nDocSize = 0; + m_xDocLB.Clear(); + ListCleanup_Impl(); + + sal_uInt32 nTemp; + + // Copy. + rStrm >> nTemp; + m_nDocSize = nTemp; + rStrm.ReadByteString (m_aDocName, RTL_TEXTENCODING_UTF8); + + ULONG i, n = 0; + rStrm >> nTemp; + n = nTemp; + + for (i = 0; i < n; i++) + { + HEADERFIELD *p = new HEADERFIELD(); + rStrm >> *p; + m_aHeaderList.Insert (p, LIST_APPEND); + } + + // Done. + return rStrm; +} + +/*======================================================================= + * + * INetMessageHeaderIterator Implementation. + * + *=====================================================================*/ +INetMessageHeaderIterator::INetMessageHeaderIterator ( + const INetMessage& rMsg, const UniString& rHdrName) +{ + ULONG i, n = rMsg.GetHeaderCount(); + for (i = 0; i < n; i++) + { + if (rHdrName.CompareIgnoreCaseToAscii (rMsg.GetHeaderName(i)) == 0) + { + UniString *pValue = new UniString (rMsg.GetHeaderValue(i)); + aValueList.Insert (pValue, LIST_APPEND); + } + } + nValueCount = aValueList.Count(); +} + +INetMessageHeaderIterator::~INetMessageHeaderIterator (void) +{ + ULONG i, n = aValueList.Count(); + for (i = 0; i < n; i++) + delete ((UniString*)(aValueList.GetObject(i))); + aValueList.Clear(); +} + +/*======================================================================= + * + * INetRFC822Message Implementation. + * + *=====================================================================*/ +/* + * ImplINetRFC822MessageHeaderData. + */ +namespace +{ + struct ImplINetRFC822MessageHeaderDataImpl + { + const ByteString* operator()() + { + static const ByteString _ImplINetRFC822MessageHeaderData[] = + { + ByteString ("BCC"), + ByteString ("CC"), + ByteString ("Comments"), + ByteString ("Date"), + ByteString ("From"), + ByteString ("In-Reply-To"), + ByteString ("Keywords"), + ByteString ("Message-ID"), + ByteString ("References"), + ByteString ("Reply-To"), + ByteString ("Return-Path"), + ByteString ("Subject"), + ByteString ("Sender"), + ByteString ("To"), + ByteString ("X-Mailer"), + ByteString ("Return-Receipt-To") + }; + return &_ImplINetRFC822MessageHeaderData[0]; + } + }; + + struct ImplINetRFC822MessageHeaderData + : public rtl::StaticAggregate< const ByteString, ImplINetRFC822MessageHeaderDataImpl > {}; +} + +#define HDR(n) ImplINetRFC822MessageHeaderData::get()[(n)] + +/* + * _ImplINetRFC822MessageHeaderState. + */ +enum _ImplINetRFC822MessageHeaderState +{ + INETMSG_RFC822_BEGIN, + INETMSG_RFC822_CHECK, + INETMSG_RFC822_OK, + INETMSG_RFC822_JUNK, + + INETMSG_RFC822_TOKEN_RE, + INETMSG_RFC822_TOKEN_RETURNMINUS, + INETMSG_RFC822_TOKEN_XMINUS, + INETMSG_RFC822_LETTER_C, + INETMSG_RFC822_LETTER_S +}; + +/* + * INetRFC822Message. + */ +INetRFC822Message::INetRFC822Message (void) + : INetMessage() +{ + for (USHORT i = 0; i < INETMSG_RFC822_NUMHDR; i++) + m_nIndex[i] = LIST_ENTRY_NOTFOUND; +} + +INetRFC822Message::INetRFC822Message (const INetRFC822Message& rMsg) + : INetMessage (rMsg) +{ + for (USHORT i = 0; i < INETMSG_RFC822_NUMHDR; i++) + m_nIndex[i] = rMsg.m_nIndex[i]; +} + +/* + * operator= + */ +INetRFC822Message& INetRFC822Message::operator= (const INetRFC822Message& rMsg) +{ + if (this != &rMsg) + { + INetMessage::operator= (rMsg); + + for (USHORT i = 0; i < INETMSG_RFC822_NUMHDR; i++) + m_nIndex[i] = rMsg.m_nIndex[i]; + } + return *this; +} + +/* + * ~INetRFC822Message. + */ +INetRFC822Message::~INetRFC822Message (void) +{ +} + +/* + * <Generate|Parse>DateField and local helper functions. + * + * GenerateDateField. + * Generates a String from Date and Time objects in format: + * Wkd, 00 Mon 0000 00:00:00 [GMT] (rfc822, rfc1123) + * + * ParseDateField. + * Parses a String in (implied) GMT format into class Date and Time objects. + * Four formats are accepted: + * + * [Wkd,] 1*2DIGIT Mon 2*4DIGIT 00:00:00 [GMT] (rfc1123) + * [Wkd,] 00 Mon 0000 00:00:00 [GMT]) (rfc822, rfc1123) + * Weekday, 00-Mon-00 00:00:00 [GMT] (rfc850, rfc1036) + * Wkd Mon 00 00:00:00 0000 [GMT] (ctime) + * 1*DIGIT (delta seconds) + * + */ + +// Months and Weekdays. +static const sal_Char *months[12] = +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static const sal_Char *wkdays[7] = +{ + "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" +}; + +/* + * GenerateDateField. + */ +BOOL INetRFC822Message::GenerateDateField ( + const DateTime& rDateTime, UniString& rDateFieldW) +{ + // Check arguments. + if (!rDateTime.IsValid() || + (rDateTime.GetSec() > 59) || + (rDateTime.GetMin() > 59) || + (rDateTime.GetHour() > 23) ) return FALSE; + + // Prepare output string. + ByteString rDateField; + + // Insert Date. + rDateField += wkdays[(USHORT)(rDateTime.GetDayOfWeek())]; + rDateField += ", "; + + USHORT nNum = rDateTime.GetDay(); + if (nNum < 10) rDateField += '0'; + rDateField += ByteString::CreateFromInt32(nNum); + rDateField += ' '; + + rDateField += months[(USHORT)(rDateTime.GetMonth() - 1)]; + rDateField += ' '; + + rDateField += ByteString::CreateFromInt32(rDateTime.GetYear()); + rDateField += ' '; + + // Insert Time. + nNum = rDateTime.GetHour(); + if (nNum < 10) rDateField += '0'; + rDateField += ByteString::CreateFromInt32(nNum); + rDateField += ':'; + + nNum = rDateTime.GetMin(); + if (nNum < 10) rDateField += '0'; + rDateField += ByteString::CreateFromInt32(nNum); + rDateField += ':'; + + nNum = rDateTime.GetSec(); + if (nNum < 10) rDateField += '0'; + rDateField += ByteString::CreateFromInt32(nNum); + rDateField += " GMT"; + + // Done. + rDateFieldW = UniString (rDateField, RTL_TEXTENCODING_ASCII_US); + return TRUE; +} + +/* + * ParseDateField and local helper functions. + */ +static USHORT ParseNumber (const ByteString& rStr, USHORT& nIndex) +{ + USHORT n = nIndex; + while ((n < rStr.Len()) && ascii_isDigit(rStr.GetChar(n))) n++; + + ByteString aNum (rStr.Copy (nIndex, (n - nIndex))); + nIndex = n; + + return (USHORT)(aNum.ToInt32()); +} + +static USHORT ParseMonth (const ByteString& rStr, USHORT& nIndex) +{ + USHORT n = nIndex; + while ((n < rStr.Len()) && ascii_isLetter(rStr.GetChar(n))) n++; + + ByteString aMonth (rStr.Copy (nIndex, 3)); + nIndex = n; + + USHORT i; + for (i = 0; i < 12; i++) + if (aMonth.CompareIgnoreCaseToAscii (months[i]) == 0) break; + return (i + 1); +} + +BOOL INetRFC822Message::ParseDateField ( + const UniString& rDateFieldW, DateTime& rDateTime) +{ + ByteString rDateField (rDateFieldW, RTL_TEXTENCODING_ASCII_US); + if (rDateField.Len() == 0) return FALSE; + + if (rDateField.Search (':') != STRING_NOTFOUND) + { + // Some DateTime format. + USHORT nIndex = 0; + + // Skip over <Wkd> or <Weekday>, leading and trailing space. + while ((nIndex < rDateField.Len()) && + (rDateField.GetChar(nIndex) == ' ')) + nIndex++; + + while ( + (nIndex < rDateField.Len()) && + (ascii_isLetter (rDateField.GetChar(nIndex)) || + (rDateField.GetChar(nIndex) == ',') )) + nIndex++; + + while ((nIndex < rDateField.Len()) && + (rDateField.GetChar(nIndex) == ' ')) + nIndex++; + + if (ascii_isLetter (rDateField.GetChar(nIndex))) + { + // Format: ctime(). + if ((rDateField.Len() - nIndex) < 20) return FALSE; + + rDateTime.SetMonth (ParseMonth (rDateField, nIndex)); nIndex++; + rDateTime.SetDay (ParseNumber (rDateField, nIndex)); nIndex++; + + rDateTime.SetHour (ParseNumber (rDateField, nIndex)); nIndex++; + rDateTime.SetMin (ParseNumber (rDateField, nIndex)); nIndex++; + rDateTime.SetSec (ParseNumber (rDateField, nIndex)); nIndex++; + rDateTime.Set100Sec (0); + + USHORT nYear = ParseNumber (rDateField, nIndex); + if (nYear < 100) nYear += 1900; + rDateTime.SetYear (nYear); + } + else + { + // Format: RFC1036 or RFC1123. + if ((rDateField.Len() - nIndex) < 17) return FALSE; + + rDateTime.SetDay (ParseNumber (rDateField, nIndex)); nIndex++; + rDateTime.SetMonth (ParseMonth (rDateField, nIndex)); nIndex++; + + USHORT nYear = ParseNumber (rDateField, nIndex); nIndex++; + if (nYear < 100) nYear += 1900; + rDateTime.SetYear (nYear); + + rDateTime.SetHour (ParseNumber (rDateField, nIndex)); nIndex++; + rDateTime.SetMin (ParseNumber (rDateField, nIndex)); nIndex++; + rDateTime.SetSec (ParseNumber (rDateField, nIndex)); nIndex++; + rDateTime.Set100Sec (0); + + if ((rDateField.GetChar(nIndex) == '+') || + (rDateField.GetChar(nIndex) == '-') ) + { + // Offset from GMT: "(+|-)HHMM". + BOOL bEast = (rDateField.GetChar(nIndex++) == '+'); + USHORT nOffset = ParseNumber (rDateField, nIndex); + if (nOffset > 0) + { + Time aDiff; + aDiff.SetHour (nOffset / 100); + aDiff.SetMin (nOffset % 100); + aDiff.SetSec (0); + aDiff.Set100Sec (0); + + if (bEast) + rDateTime -= aDiff; + else + rDateTime += aDiff; + } + } + } + } + else if (rDateField.IsNumericAscii()) + { + // Format: delta seconds. + Time aDelta (0); + aDelta.SetTime (rDateField.ToInt32() * 100); + + DateTime aNow; + aNow += aDelta; + aNow.ConvertToUTC(); + + rDateTime.SetDate (aNow.GetDate()); + rDateTime.SetTime (aNow.GetTime()); + } + else + { + // Junk. + return FALSE; + } + + return (rDateTime.IsValid() && + !((rDateTime.GetSec() > 59) || + (rDateTime.GetMin() > 59) || + (rDateTime.GetHour() > 23) )); +} + +/* + * SetHeaderField. + * (Header Field Parser). + */ +ULONG INetRFC822Message::SetHeaderField ( + const INetMessageHeader &rHeader, ULONG nNewIndex) +{ + ByteString aName (rHeader.GetName()); + const sal_Char *pData = aName.GetBuffer(); + const sal_Char *pStop = pData + aName.Len() + 1; + const sal_Char *check = ""; + + ULONG nIdx = LIST_APPEND; + int eState = INETMSG_RFC822_BEGIN; + int eOkState = INETMSG_RFC822_OK; + + while (pData < pStop) + { + switch (eState) + { + case INETMSG_RFC822_BEGIN: + eState = INETMSG_RFC822_CHECK; + eOkState = INETMSG_RFC822_OK; + + switch (ascii_toLowerCase (*pData)) + { + case 'b': + check = "cc"; + nIdx = INETMSG_RFC822_BCC; + break; + + case 'c': + eState = INETMSG_RFC822_LETTER_C; + break; + + case 'd': + check = "ate"; + nIdx = INETMSG_RFC822_DATE; + break; + + case 'f': + check = "rom"; + nIdx = INETMSG_RFC822_FROM; + break; + + case 'i': + check = "n-reply-to"; + nIdx = INETMSG_RFC822_IN_REPLY_TO; + break; + + case 'k': + check = "eywords"; + nIdx = INETMSG_RFC822_KEYWORDS; + break; + + case 'm': + check = "essage-id"; + nIdx = INETMSG_RFC822_MESSAGE_ID; + break; + + case 'r': + check = "e"; + eOkState = INETMSG_RFC822_TOKEN_RE; + break; + + case 's': + eState = INETMSG_RFC822_LETTER_S; + break; + + case 't': + check = "o"; + nIdx = INETMSG_RFC822_TO; + break; + + case 'x': + check = "-"; + eOkState = INETMSG_RFC822_TOKEN_XMINUS; + break; + + default: + eState = INETMSG_RFC822_JUNK; + break; + } + pData++; + break; + + case INETMSG_RFC822_TOKEN_RE: + eState = INETMSG_RFC822_CHECK; + eOkState = INETMSG_RFC822_OK; + + switch (ascii_toLowerCase (*pData)) + { + case 'f': + check = "erences"; + nIdx = INETMSG_RFC822_REFERENCES; + break; + + case 'p': + check = "ly-to"; + nIdx = INETMSG_RFC822_REPLY_TO; + break; + + case 't': + check = "urn-"; + eOkState = INETMSG_RFC822_TOKEN_RETURNMINUS; + break; + + default: + eState = INETMSG_RFC822_JUNK; + break; + } + pData++; + break; + + case INETMSG_RFC822_TOKEN_RETURNMINUS: + eState = INETMSG_RFC822_CHECK; + eOkState = INETMSG_RFC822_OK; + + switch (ascii_toLowerCase (*pData)) + { + case 'p': + check = "ath"; + nIdx = INETMSG_RFC822_RETURN_PATH; + break; + + case 'r': + check = "eceipt-to"; + nIdx = INETMSG_RFC822_RETURN_RECEIPT_TO; + break; + + default: + eState = INETMSG_RFC822_JUNK; + break; + } + pData++; + break; + + case INETMSG_RFC822_TOKEN_XMINUS: + eState = INETMSG_RFC822_CHECK; + eOkState = INETMSG_RFC822_OK; + + switch (ascii_toLowerCase (*pData)) + { + case 'm': + check = "ailer"; + nIdx = INETMSG_RFC822_X_MAILER; + break; + +#if 0 /* NYI */ + case 'p': + check = "riority"; + eOkState = INETMSG_RFC822_X_PRIORITY; + break; +#endif /* NYI */ + + default: + eState = INETMSG_RFC822_JUNK; + break; + } + pData++; + break; + + case INETMSG_RFC822_LETTER_C: + eState = INETMSG_RFC822_CHECK; + eOkState = INETMSG_RFC822_OK; + + switch (ascii_toLowerCase (*pData)) + { + case 'c': + check = ""; + nIdx = INETMSG_RFC822_CC; + break; + + case 'o': + check = "mments"; + nIdx = INETMSG_RFC822_COMMENTS; + break; + + default: + eState = INETMSG_RFC822_JUNK; + break; + } + pData++; + break; + + case INETMSG_RFC822_LETTER_S: + eState = INETMSG_RFC822_CHECK; + eOkState = INETMSG_RFC822_OK; + + switch (ascii_toLowerCase (*pData)) + { + case 'e': + check = "nder"; + nIdx = INETMSG_RFC822_SENDER; + break; + + case 'u': + check = "bject"; + nIdx = INETMSG_RFC822_SUBJECT; + break; + + default: + eState = INETMSG_RFC822_JUNK; + break; + } + pData++; + break; + + case INETMSG_RFC822_CHECK: + if (*check) + { + while (*pData && *check && + (ascii_toLowerCase (*pData) == *check)) + { + pData++; + check++; + } + } + else + { + check = pData; + } + eState = (*check == '\0') ? eOkState : INETMSG_RFC822_JUNK; + break; + + case INETMSG_RFC822_OK: + pData = pStop; + SetHeaderField_Impl ( + HEADERFIELD (HDR(nIdx), rHeader.GetValue()), + m_nIndex[nIdx]); + nNewIndex = m_nIndex[nIdx]; + break; + + default: // INETMSG_RFC822_JUNK + pData = pStop; + nNewIndex = INetMessage::SetHeaderField (rHeader, nNewIndex); + break; + } + } + return nNewIndex; +} + +/* + * Specific Set-Methods. + */ +void INetRFC822Message::SetBCC (const UniString& rBCC) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_ADDRESS, + HDR(INETMSG_RFC822_BCC), rBCC, + m_nIndex[INETMSG_RFC822_BCC]); +} + +void INetRFC822Message::SetCC (const UniString& rCC) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_ADDRESS, + HDR(INETMSG_RFC822_CC), rCC, + m_nIndex[INETMSG_RFC822_CC]); +} + +void INetRFC822Message::SetComments (const UniString& rComments) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_TEXT, + HDR(INETMSG_RFC822_COMMENTS), rComments, + m_nIndex[INETMSG_RFC822_COMMENTS]); +} + +void INetRFC822Message::SetDate (const UniString& rDate) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_STRUCTURED, + HDR(INETMSG_RFC822_DATE), rDate, + m_nIndex[INETMSG_RFC822_DATE]); +} + +void INetRFC822Message::SetFrom (const UniString& rFrom) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_ADDRESS, + HDR(INETMSG_RFC822_FROM), rFrom, + m_nIndex[INETMSG_RFC822_FROM]); +} + +void INetRFC822Message::SetInReplyTo (const UniString& rInReplyTo) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_ADDRESS, // ??? MESSAGE_ID ??? + HDR(INETMSG_RFC822_IN_REPLY_TO), rInReplyTo, + m_nIndex[INETMSG_RFC822_IN_REPLY_TO]); +} + +void INetRFC822Message::SetKeywords (const UniString& rKeywords) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_PHRASE, + HDR(INETMSG_RFC822_KEYWORDS), rKeywords, + m_nIndex[INETMSG_RFC822_KEYWORDS]); +} + +void INetRFC822Message::SetMessageID (const UniString& rMessageID) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_MESSAGE_ID, + HDR(INETMSG_RFC822_MESSAGE_ID), rMessageID, + m_nIndex[INETMSG_RFC822_MESSAGE_ID]); +} + +void INetRFC822Message::SetReferences (const UniString& rReferences) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_MESSAGE_ID, + HDR(INETMSG_RFC822_REFERENCES), rReferences, + m_nIndex[INETMSG_RFC822_REFERENCES]); +} + +void INetRFC822Message::SetReplyTo (const UniString& rReplyTo) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_ADDRESS, + HDR(INETMSG_RFC822_REPLY_TO), rReplyTo, + m_nIndex[INETMSG_RFC822_REPLY_TO]); +} + +void INetRFC822Message::SetReturnPath (const UniString& rReturnPath) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_ADDRESS, + HDR(INETMSG_RFC822_RETURN_PATH), rReturnPath, + m_nIndex[INETMSG_RFC822_RETURN_PATH]); +} + +void INetRFC822Message::SetReturnReceiptTo (const UniString& rValue) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_ADDRESS, + HDR(INETMSG_RFC822_RETURN_RECEIPT_TO), rValue, + m_nIndex[INETMSG_RFC822_RETURN_RECEIPT_TO]); +} + +void INetRFC822Message::SetSender (const UniString& rSender) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_ADDRESS, + HDR(INETMSG_RFC822_SENDER), rSender, + m_nIndex[INETMSG_RFC822_SENDER]); +} + +void INetRFC822Message::SetSubject (const UniString& rSubject) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_TEXT, + HDR(INETMSG_RFC822_SUBJECT), rSubject, + m_nIndex[INETMSG_RFC822_SUBJECT]); +} + +void INetRFC822Message::SetTo (const UniString& rTo) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_ADDRESS, + HDR(INETMSG_RFC822_TO), rTo, + m_nIndex[INETMSG_RFC822_TO]); +} + +void INetRFC822Message::SetXMailer (const UniString& rXMailer) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_TEXT, + HDR(INETMSG_RFC822_X_MAILER), rXMailer, + m_nIndex[INETMSG_RFC822_X_MAILER]); +} + +/* + * operator<< + */ +SvStream& INetRFC822Message::operator<< (SvStream& rStrm) const +{ + INetMessage::operator<< (rStrm); + + for (USHORT i = 0; i < INETMSG_RFC822_NUMHDR; i++) + rStrm << static_cast<sal_uInt32>(m_nIndex[i]); + + return rStrm; +} + +/* + * operator>> + */ +SvStream& INetRFC822Message::operator>> (SvStream& rStrm) +{ + INetMessage::operator>> (rStrm); + + sal_uInt32 nTemp; + for (USHORT i = 0; i < INETMSG_RFC822_NUMHDR; i++) + { + rStrm >> nTemp; + m_nIndex[i] = nTemp; + } + + return rStrm; +} + +/*======================================================================= + * + * INetMIMEMessage Implementation. + * + *=====================================================================*/ +/* + * _ImplINetMIMEMessageHeaderData. + */ +namespace +{ + struct ImplINetMIMEMessageHeaderDataImpl + { + const ByteString* operator()() + { + static const ByteString _ImplINetMIMEMessageHeaderData[] = + { + ByteString ("MIME-Version"), + ByteString ("Content-Description"), + ByteString ("Content-Disposition"), + ByteString ("Content-ID"), + ByteString ("Content-Type"), + ByteString ("Content-Transfer-Encoding") + }; + return &_ImplINetMIMEMessageHeaderData[0]; + } + }; + + struct ImplINetMIMEMessageHeaderData + : public rtl::StaticAggregate< const ByteString, ImplINetMIMEMessageHeaderDataImpl > {}; +} + +#define MIMEHDR(n) ImplINetMIMEMessageHeaderData::get()[(n)] + +/* + * _ImplINetMIMEMessageHeaderState. + */ +enum _ImplINetMIMEMessageHeaderState +{ + INETMSG_MIME_BEGIN, + INETMSG_MIME_CHECK, + INETMSG_MIME_OK, + INETMSG_MIME_JUNK, + + INETMSG_MIME_TOKEN_CONTENT, + INETMSG_MIME_TOKEN_CONTENT_D, + INETMSG_MIME_TOKEN_CONTENT_T +}; + +/* + * INetMIMEMessage. + */ +INetMIMEMessage::INetMIMEMessage (void) + : INetRFC822Message (), + pParent (NULL), + nNumChildren (0), + bHeaderParsed (FALSE) +{ + for (USHORT i = 0; i < INETMSG_MIME_NUMHDR; i++) + m_nIndex[i] = LIST_ENTRY_NOTFOUND; +} + +INetMIMEMessage::INetMIMEMessage (const INetMIMEMessage& rMsg) + : INetRFC822Message (rMsg) +{ + // Copy. + CopyImp (rMsg); +} + +/* + * operator= + */ +INetMIMEMessage& INetMIMEMessage::operator= ( + const INetMIMEMessage& rMsg) +{ + if (this != &rMsg) + { + // Assign base. + INetRFC822Message::operator= (rMsg); + + // Cleanup. + CleanupImp(); + + // Copy. + CopyImp (rMsg); + } + return *this; +} + +/* + * ~INetMIMEMessage. + */ +INetMIMEMessage::~INetMIMEMessage (void) +{ + // Cleanup. + CleanupImp(); +} + +/* + * CleanupImp. + */ +void INetMIMEMessage::CleanupImp (void) +{ + INetMIMEMessage *pChild = NULL; + while ((pChild = (INetMIMEMessage *)(aChildren.Remove())) != NULL) + if (pChild->pParent == this) delete pChild; +} + +/* + * CopyImp. + */ +void INetMIMEMessage::CopyImp (const INetMIMEMessage& rMsg) +{ + bHeaderParsed = rMsg.bHeaderParsed; + + USHORT i; + for (i = 0; i < INETMSG_MIME_NUMHDR; i++) + m_nIndex[i] = rMsg.m_nIndex[i]; + + m_aBoundary = rMsg.m_aBoundary; + nNumChildren = rMsg.nNumChildren; + + for (i = 0; i < rMsg.aChildren.Count(); i++) + { + INetMIMEMessage *pChild = + (INetMIMEMessage *)(rMsg.aChildren.GetObject (i)); + + if (pChild->pParent == &rMsg) + { + pChild = pChild->CreateMessage (*pChild); + pChild->pParent = this; + } + aChildren.Insert (pChild, LIST_APPEND); + } +} + +/* + * CreateMessage. + */ +INetMIMEMessage *INetMIMEMessage::CreateMessage ( + const INetMIMEMessage& rMsg) const +{ + return (new INetMIMEMessage (rMsg)); +} + +/* + * SetHeaderField. + * (Header Field Parser). + */ +ULONG INetMIMEMessage::SetHeaderField ( + const INetMessageHeader &rHeader, ULONG nNewIndex) +{ + ByteString aName (rHeader.GetName()); + const sal_Char *pData = aName.GetBuffer(); + const sal_Char *pStop = pData + aName.Len() + 1; + const sal_Char *check = ""; + + ULONG nIdx = LIST_APPEND; + int eState = INETMSG_MIME_BEGIN; + int eOkState = INETMSG_MIME_OK; + + while (pData < pStop) + { + switch (eState) + { + case INETMSG_MIME_BEGIN: + eState = INETMSG_MIME_CHECK; + eOkState = INETMSG_MIME_OK; + + switch (ascii_toLowerCase (*pData)) + { + case 'c': + check = "ontent-"; + eOkState = INETMSG_MIME_TOKEN_CONTENT; + break; + + case 'm': + check = "ime-version"; + nIdx = INETMSG_MIME_VERSION; + break; + + default: + eState = INETMSG_MIME_JUNK; + break; + } + pData++; + break; + + case INETMSG_MIME_TOKEN_CONTENT: + eState = INETMSG_MIME_CHECK; + eOkState = INETMSG_MIME_OK; + + switch (ascii_toLowerCase (*pData)) + { + case 'd': + eState = INETMSG_MIME_TOKEN_CONTENT_D; + break; + + case 'i': + check = "d"; + nIdx = INETMSG_MIME_CONTENT_ID; + break; + + case 't': + eState = INETMSG_MIME_TOKEN_CONTENT_T; + break; + + default: + eState = INETMSG_MIME_JUNK; + break; + } + pData++; + break; + + case INETMSG_MIME_TOKEN_CONTENT_D: + eState = INETMSG_MIME_CHECK; + eOkState = INETMSG_MIME_OK; + + switch (ascii_toLowerCase (*pData)) + { + case 'e': + check = "scription"; + nIdx = INETMSG_MIME_CONTENT_DESCRIPTION; + break; + + case 'i': + check = "sposition"; + nIdx = INETMSG_MIME_CONTENT_DISPOSITION; + break; + + default: + eState = INETMSG_MIME_JUNK; + break; + } + pData++; + break; + + case INETMSG_MIME_TOKEN_CONTENT_T: + eState = INETMSG_MIME_CHECK; + eOkState = INETMSG_MIME_OK; + + switch (ascii_toLowerCase (*pData)) + { + case 'r': + check = "ansfer-encoding"; + nIdx = INETMSG_MIME_CONTENT_TRANSFER_ENCODING; + break; + + case 'y': + check = "pe"; + nIdx = INETMSG_MIME_CONTENT_TYPE; + break; + + default: + eState = INETMSG_MIME_JUNK; + break; + } + pData++; + break; + + case INETMSG_MIME_CHECK: + if (*check) + { + while (*pData && *check && + (ascii_toLowerCase (*pData) == *check)) + { + pData++; + check++; + } + } + else + { + check = pData; + } + eState = (*check == '\0') ? eOkState : INETMSG_MIME_JUNK; + break; + + case INETMSG_MIME_OK: + pData = pStop; + SetHeaderField_Impl ( + HEADERFIELD (MIMEHDR(nIdx), rHeader.GetValue()), + m_nIndex[nIdx]); + nNewIndex = m_nIndex[nIdx]; + break; + + default: // INETMSG_MIME_JUNK + pData = pStop; + nNewIndex = INetRFC822Message::SetHeaderField ( + rHeader, nNewIndex); + break; + } + } + return nNewIndex; +} + +/* + * Specific Set-Methods. + */ +void INetMIMEMessage::SetMIMEVersion (const UniString& rVersion) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_TEXT, + MIMEHDR(INETMSG_MIME_VERSION), rVersion, + m_nIndex[INETMSG_MIME_VERSION]); +} + +void INetMIMEMessage::SetContentDescription (const String& rDescription) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_TEXT, + MIMEHDR(INETMSG_MIME_CONTENT_DESCRIPTION), rDescription, + m_nIndex[INETMSG_MIME_CONTENT_DESCRIPTION]); +} + +void INetMIMEMessage::SetContentDisposition (const String& rDisposition) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_TEXT, + MIMEHDR(INETMSG_MIME_CONTENT_DISPOSITION), rDisposition, + m_nIndex[INETMSG_MIME_CONTENT_DISPOSITION]); +} + +void INetMIMEMessage::SetContentID (const String& rID) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_TEXT, + MIMEHDR(INETMSG_MIME_CONTENT_ID), rID, + m_nIndex[INETMSG_MIME_CONTENT_ID]); +} + +void INetMIMEMessage::SetContentType (const String& rType) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_TEXT, + MIMEHDR(INETMSG_MIME_CONTENT_TYPE), rType, + m_nIndex[INETMSG_MIME_CONTENT_TYPE]); +} + +void INetMIMEMessage::SetContentTransferEncoding ( + const String& rEncoding) +{ + SetHeaderField_Impl ( + INetMIME::HEADER_FIELD_TEXT, + MIMEHDR(INETMSG_MIME_CONTENT_TRANSFER_ENCODING), rEncoding, + m_nIndex[INETMSG_MIME_CONTENT_TRANSFER_ENCODING]); +} + +/* + * GetDefaultContentType. + */ +void INetMIMEMessage::GetDefaultContentType (String& rContentType) +{ + String aDefaultCT ( + "text/plain; charset=us-ascii", RTL_TEXTENCODING_ASCII_US); + if (pParent == NULL) + { + rContentType = aDefaultCT; + } + else + { + String aParentCT (pParent->GetContentType()); + if (aParentCT.Len() == 0) + pParent->GetDefaultContentType (aParentCT); + + if (aParentCT.CompareIgnoreCaseToAscii ("message/", 8) == 0) + { + rContentType = aDefaultCT; + } + else if (aParentCT.CompareIgnoreCaseToAscii ("multipart/", 10) == 0) + { + if (aParentCT.CompareIgnoreCaseToAscii ("multipart/digest") == 0) + rContentType.AssignAscii ("message/rfc822"); + else + rContentType = aDefaultCT; + } + else + { + rContentType = aDefaultCT; + } + } +} + +/* + * EnableAttachChild. + */ +BOOL INetMIMEMessage::EnableAttachChild (INetMessageContainerType eType) +{ + // Check context. + if (IsContainer()) + return FALSE; + + // Setup Content-Type header field. + ByteString aContentType; + switch (eType) + { + case INETMSG_MESSAGE_RFC822: + aContentType = "message/rfc822"; + break; + + case INETMSG_MULTIPART_ALTERNATIVE: + aContentType = "multipart/alternative"; + break; + + case INETMSG_MULTIPART_DIGEST: + aContentType = "multipart/digest"; + break; + + case INETMSG_MULTIPART_PARALLEL: + aContentType = "multipart/parallel"; + break; + + case INETMSG_MULTIPART_RELATED: + aContentType = "multipart/related"; + break; + + case INETMSG_MULTIPART_FORM_DATA: + aContentType = "multipart/form-data"; + break; + + default: + aContentType = "multipart/mixed"; + break; + } + + // Setup boundary for multipart types. + if (aContentType.CompareIgnoreCaseToAscii ("multipart/", 10) == 0) + { + // Generate a unique boundary from current time. + sal_Char sTail[16 + 1]; + Time aCurTime; + sal_uInt64 nThis = reinterpret_cast< sal_uIntPtr >( this ); // we can be on a 64bit architecture + nThis = ( ( nThis >> 32 ) ^ nThis ) & SAL_MAX_UINT32; + sprintf (sTail, "%08X%08X", + static_cast< unsigned int >(aCurTime.GetTime()), + static_cast< unsigned int >(nThis)); + m_aBoundary = "------------_4D48"; + m_aBoundary += sTail; + + // Append boundary as ContentType parameter. + aContentType += "; boundary="; + aContentType += m_aBoundary; + } + + // Set header fields. + SetMIMEVersion (String (CONSTASCII_STRINGPARAM("1.0"))); + SetContentType (String (aContentType, RTL_TEXTENCODING_ASCII_US)); + SetContentTransferEncoding (String (CONSTASCII_STRINGPARAM("7bit"))); + + // Done. + return TRUE; +} + +/* + * AttachChild. + */ +BOOL INetMIMEMessage::AttachChild ( + INetMIMEMessage& rChildMsg, BOOL bOwner) +{ + if (IsContainer() /*&& rChildMsg.GetContentType().Len() */) + { + if (bOwner) rChildMsg.pParent = this; + aChildren.Insert (&rChildMsg, LIST_APPEND); + nNumChildren = aChildren.Count(); + + return TRUE; + } + return FALSE; +} + +/* + * DetachChild. + */ +BOOL INetMIMEMessage::DetachChild ( + ULONG nIndex, INetMIMEMessage& rChildMsg) const +{ + if (IsContainer()) + { + // Check document stream. + if (GetDocumentLB() == NULL) return FALSE; + SvStream *pDocStrm = new SvStream (GetDocumentLB()); + + // Initialize message buffer. + char pMsgBuffer[1024]; + char *pMsgRead, *pMsgWrite; + pMsgRead = pMsgWrite = pMsgBuffer; + + // Initialize message parser stream. + INetMIMEMessageStream *pMsgStrm = NULL; + + // Check for "multipart/uvw" or "message/xyz". + if (IsMultipart()) + { + // Multipart message body. Initialize multipart delimiters. + ByteString aDelim ("--"); + aDelim += GetMultipartBoundary(); + ByteString aClose = aDelim; + aClose += "--"; + + // Initialize line buffer. + SvMemoryStream aLineBuf; + + // Initialize control variables. + INetMessageStreamState eState = INETMSG_EOL_SCR; + int nCurIndex = -1; + + // Go! + while (nCurIndex < (int)(nIndex + 1)) + { + if ((pMsgRead - pMsgWrite) > 0) + { + // Bytes still in buffer. + if (eState == INETMSG_EOL_FCR) + { + // Check for 2nd line break character. + if ((*pMsgWrite == '\r') || (*pMsgWrite == '\n')) + aLineBuf << *pMsgWrite++; + + // Check current index. + if (nCurIndex == (int)nIndex) + { + // Found requested part. + if (pMsgStrm == NULL) + { + // Create message parser stream. + pMsgStrm = new INetMIMEMessageStream; + pMsgStrm->SetTargetMessage (&rChildMsg); + } + + // Put message down-stream. + int status = pMsgStrm->Write ( + (const sal_Char *) aLineBuf.GetData(), aLineBuf.Tell()); + if (status != INETSTREAM_STATUS_OK) + { + // Cleanup. + delete pDocStrm; + delete pMsgStrm; + + // Finish. + return (!(status == INETSTREAM_STATUS_OK)); + } + } + + // Reset to <Begin-of-Line>. + aLineBuf.Seek (STREAM_SEEK_TO_BEGIN); + eState = INETMSG_EOL_SCR; + } + else if ((*pMsgWrite == '\r') || (*pMsgWrite == '\n')) + { + /* + * Found any line break character. + * Compare buffered line with part/close delimiter. + * Increment current part index upon match. + */ + USHORT nLen = (USHORT)(aLineBuf.Tell() & 0xffff); + if (nLen == aDelim.Len()) + { + if (aDelim.CompareTo ((const sal_Char *) aLineBuf.GetData(), nLen) + == COMPARE_EQUAL) nCurIndex++; + } + else if (nLen == aClose.Len()) + { + if (aClose.CompareTo ((const sal_Char *) aLineBuf.GetData(), nLen) + == COMPARE_EQUAL) nCurIndex++; + } + aLineBuf << *pMsgWrite++; + eState = INETMSG_EOL_FCR; + } + else + { + // Insert into line buffer. + aLineBuf << *pMsgWrite; + } + } + else + { + // Buffer empty. Reset to <Begin-of-Buffer>. + pMsgRead = pMsgWrite = pMsgBuffer; + + // Read document stream. + ULONG nRead = pDocStrm->Read ( + pMsgBuffer, sizeof (pMsgBuffer)); + if (nRead > 0) + { + // Set read pointer. + pMsgRead += nRead; + } + else + { + // Premature end. + if (pMsgStrm) + { + // Assume end of requested part. + nCurIndex++; + } + else + { + // Requested part not found. + delete pDocStrm; + return FALSE; + } + } + } + } // while (nCurIndex < (nIndex + 1)) + } + else + { + // Encapsulated message body. Create message parser stream. + pMsgStrm = new INetMIMEMessageStream; + pMsgStrm->SetTargetMessage (&rChildMsg); + + // Initialize control variables. + INetMessageStreamState eState = INETMSG_EOL_BEGIN; + + // Go. + while (eState == INETMSG_EOL_BEGIN) + { + if ((pMsgRead - pMsgWrite) > 0) + { + // Bytes still in buffer. Put message down-stream. + int status = pMsgStrm->Write ( + pMsgBuffer, (pMsgRead - pMsgWrite)); + if (status != INETSTREAM_STATUS_OK) + { + // Cleanup. + delete pDocStrm; + delete pMsgStrm; + + // Finish. + return (!(status == INETSTREAM_STATUS_ERROR)); + } + pMsgWrite = pMsgBuffer + (pMsgRead - pMsgWrite); + } + else + { + // Buffer empty. Reset to <Begin-of-Buffer>. + pMsgRead = pMsgWrite = pMsgBuffer; + + // Read document stream. + ULONG nRead = pDocStrm->Read ( + pMsgBuffer, sizeof (pMsgBuffer)); + if (nRead > 0) + { + // Set read pointer. + pMsgRead += nRead; + } + else + { + // Mark we're done. + eState = INETMSG_EOL_DONE; + } + } + } // while (eState == INETMSG_EOL_BEGIN) + } + + // Done. + if (pDocStrm) delete pDocStrm; + if (pMsgStrm) delete pMsgStrm; + return TRUE; + } + return FALSE; +} + +/* + * operator<< + */ +SvStream& INetMIMEMessage::operator<< (SvStream& rStrm) const +{ + INetRFC822Message::operator<< (rStrm); + + for (USHORT i = 0; i < INETMSG_MIME_NUMHDR; i++) + rStrm << static_cast<sal_uInt32>(m_nIndex[i]); + +#ifdef ENABLE_BYTESTRING_STREAM_OPERATORS + rStrm << m_aBoundary; +#else + rStrm.WriteByteString (m_aBoundary); +#endif + rStrm << static_cast<sal_uInt32>(nNumChildren); + + return rStrm; +} + +/* + * operator>> + */ +SvStream& INetMIMEMessage::operator>> (SvStream& rStrm) +{ + INetRFC822Message::operator>> (rStrm); + + sal_uInt32 nTemp; + for (USHORT i = 0; i < INETMSG_MIME_NUMHDR; i++) + { + rStrm >> nTemp; + m_nIndex[i] = nTemp; + } + +#ifdef ENABLE_BYTESTRING_STREAM_OPERATORS + rStrm >> m_aBoundary; +#else + rStrm.ReadByteString (m_aBoundary); +#endif + rStrm >> nTemp; + nNumChildren = nTemp; + + return rStrm; +} + + diff --git a/tools/source/inet/inetstrm.cxx b/tools/source/inet/inetstrm.cxx new file mode 100644 index 000000000000..ec0cea10d43e --- /dev/null +++ b/tools/source/inet/inetstrm.cxx @@ -0,0 +1,1824 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: inetstrm.cxx,v $ + * $Revision: 1.11 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" +#include <sal/types.h> +#include <rtl/memory.h> +#include <tools/cachestr.hxx> +#include <tools/debug.hxx> +#include <tools/inetmsg.hxx> +#include <tools/inetstrm.hxx> + +#include <ctype.h> // toupper + +inline sal_Bool SAL_CALL ascii_isWhitespace( sal_Unicode ch ) +{ + return ((ch <= 0x20) && ch); +} + +#define CONSTASCII_STRINGPARAM(a) (a), RTL_TEXTENCODING_ASCII_US + +/*======================================================================= + * + * INetMessageEncodeQPStream Interface. + * (Quoted-Printable Encoding) + * + *=====================================================================*/ +class INetMessageEncodeQPStream_Impl : public INetMessageIStream +{ + SvStream *pMsgStrm; + + ULONG nMsgBufSiz; + sal_Char *pMsgBuffer; + sal_Char *pMsgRead; + sal_Char *pMsgWrite; + + ULONG nTokBufSiz; + sal_Char *pTokBuffer; + sal_Char *pTokRead; + sal_Char *pTokWrite; + + INetMessageStreamState eState; + BOOL bDone; + + virtual int GetMsgLine (sal_Char *pData, ULONG nSize); + +public: + INetMessageEncodeQPStream_Impl (ULONG nMsgBufferSize = 1024); + virtual ~INetMessageEncodeQPStream_Impl (void); +}; + +/*===================================================================== + * + * INetMessageDecodeQPStream Interface. + * (Quoted-Printable Decoding) + * + *====================================================================*/ +class INetMessageDecodeQPStream_Impl : public INetMessageOStream +{ + INetMessageStreamState eState; + SvMemoryStream *pMsgBuffer; + + ULONG nTokBufLen; + sal_Char pTokBuffer[4]; + + virtual int PutMsgLine (const sal_Char *pData, ULONG nSize); + +public: + INetMessageDecodeQPStream_Impl (void); + virtual ~INetMessageDecodeQPStream_Impl (void); +}; + +/*====================================================================== + * + * INetMessageEncode64Stream Interface. + * (Base64 Encoding) + * + *====================================================================*/ +class INetMessageEncode64Stream_Impl : public INetMessageIStream +{ + SvStream *pMsgStrm; + + ULONG nMsgBufSiz; + sal_uInt8 *pMsgBuffer; + sal_uInt8 *pMsgRead; + sal_uInt8 *pMsgWrite; + + ULONG nTokBufSiz; + sal_Char *pTokBuffer; + sal_Char *pTokRead; + sal_Char *pTokWrite; + + BOOL bDone; + + virtual int GetMsgLine (sal_Char *pData, ULONG nSize); + +public: + INetMessageEncode64Stream_Impl (ULONG nMsgBufferSize = 2048); + virtual ~INetMessageEncode64Stream_Impl (void); +}; + +/*====================================================================== + * + * INetMessageDecode64Stream Interface. + * (Base64 Decoding) + * + *====================================================================*/ +class INetMessageDecode64Stream_Impl : public INetMessageOStream +{ + INetMessageStreamState eState; + + ULONG nMsgBufSiz; + sal_Char *pMsgBuffer; + sal_Char *pMsgRead; + sal_Char *pMsgWrite; + + virtual int PutMsgLine (const sal_Char *pData, ULONG nSize); + +public: + INetMessageDecode64Stream_Impl (ULONG nMsgBufferSize = 128); + virtual ~INetMessageDecode64Stream_Impl (void); +}; + +/*========================================================================= + * + * INetIStream Implementation. + * + *=======================================================================*/ +/* + * INetIStream. + */ +INetIStream::INetIStream () +{ +} + +/* + * ~INetIStream. + */ +INetIStream::~INetIStream (void) +{ +} + +/* + * Read. + */ +int INetIStream::Read (sal_Char *pData, ULONG nSize) +{ + return GetData (pData, nSize); +} + +/* + * Decode64. + */ +void INetIStream::Decode64 (SvStream& rIn, SvStream& rOut) +{ + INetMessage aMsg; + aMsg.SetDocumentLB(new SvAsyncLockBytes(&rOut, FALSE)); + + INetMessageDecode64Stream_Impl aStream (8192); + aStream.SetTargetMessage (&aMsg); + + sal_Char* pBuf = new sal_Char[8192]; + + int nRead = 0; + while ((nRead = rIn.Read (pBuf, 8192)) > 0) + aStream.Write( pBuf, nRead ); + aStream.Write ("\r\n", 2); + + delete[] pBuf; +} + +/* + * Encode64. + */ +void INetIStream::Encode64 (SvStream& rIn, SvStream& rOut) +{ + INetMessage aMsg; + aMsg.SetDocumentLB ( + new SvLockBytes (&rIn, FALSE)); + + INetMessageEncode64Stream_Impl aStream (8192); + aStream.SetSourceMessage (&aMsg); + + sal_Char* pBuf = new sal_Char[8192]; + + int nRead = 0; + while ((nRead = aStream.Read (pBuf, 8192)) > 0) + rOut.Write( pBuf, nRead ); + + delete[] pBuf; +} + +/*========================================================================= + * + * INetOStream Implementation. + * + *=======================================================================*/ +/* + * INetOStream. + */ +INetOStream::INetOStream () +{ +} + +/* + * ~INetOStream. + */ +INetOStream::~INetOStream (void) +{ +} + +/* + * Write. + */ +int INetOStream::Write (const sal_Char *pData, ULONG nSize) +{ + return PutData (pData, nSize); +} + +/*========================================================================= + * + * INetMessageIStream Implementation. + * + *=======================================================================*/ +/* + * INetMessageIStream. + */ +INetMessageIStream::INetMessageIStream (ULONG nBufferSize) + : pSourceMsg (NULL), + bHeaderGenerated (FALSE), + nBufSiz (nBufferSize), + pMsgStrm (NULL), + pMsgBuffer (new SvMemoryStream) +{ + pMsgBuffer->SetStreamCharSet (RTL_TEXTENCODING_ASCII_US); + pBuffer = new sal_Char[nBufSiz]; + pRead = pWrite = pBuffer; +} + +/* + * ~INetMessageIStream. + */ +INetMessageIStream::~INetMessageIStream (void) +{ + delete [] pBuffer; + delete pMsgBuffer; + delete pMsgStrm; +} + +/* + * GetData. + */ +int INetMessageIStream::GetData (sal_Char *pData, ULONG nSize) +{ + if (pSourceMsg == NULL) return INETSTREAM_STATUS_ERROR; + + sal_Char *pWBuf = pData; + sal_Char *pWEnd = pData + nSize; + + while (pWBuf < pWEnd) + { + // Caller's buffer not yet filled. + ULONG n = pRead - pWrite; + if (n > 0) + { + // Bytes still in buffer. + ULONG m = pWEnd - pWBuf; + if (m < n) n = m; + for (ULONG i = 0; i < n; i++) *pWBuf++ = *pWrite++; + } + else + { + // Buffer empty. Reset to <Begin-of-Buffer>. + pRead = pWrite = pBuffer; + + // Read next message line. + int nRead = GetMsgLine (pBuffer, nBufSiz); + if (nRead > 0) + { + // Set read pointer. + pRead = pBuffer + nRead; + } + else + { + if (!bHeaderGenerated) + { + // Header generated. Insert empty line. + bHeaderGenerated = TRUE; + *pRead++ = '\r'; + *pRead++ = '\n'; + } + else + { + // Body generated. + return (pWBuf - pData); + } + } + } + } + return (pWBuf - pData); +} + +/* + * GetMsgLine. + */ +int INetMessageIStream::GetMsgLine (sal_Char *pData, ULONG nSize) +{ + if (pSourceMsg == NULL) return INETSTREAM_STATUS_ERROR; + + sal_Char *pWBuf = pData; + sal_Char *pWEnd = pData + nSize; + + if (!bHeaderGenerated) + { + ULONG i, n; + + if (pMsgBuffer->Tell() == 0) + { + // Insert formatted header into buffer. + n = pSourceMsg->GetHeaderCount(); + for (i = 0; i < n; i++) + { + INetMessageHeader aHeader (pSourceMsg->GetHeaderField(i)); + if (aHeader.GetValue().Len()) + { + // NYI: Folding long lines. + *pMsgBuffer << (sal_Char*)(aHeader.GetName().GetBuffer()); + *pMsgBuffer << ": "; + *pMsgBuffer << (sal_Char*)(aHeader.GetValue().GetBuffer()); + *pMsgBuffer << "\r\n"; + } + } + + pMsgWrite = (sal_Char *)(pMsgBuffer->GetData()); + pMsgRead = pMsgWrite + pMsgBuffer->Tell(); + } + + n = pMsgRead - pMsgWrite; + if (n > 0) + { + // Move to caller. + if (nSize < n) n = nSize; + for (i = 0; i < n; i++) *pWBuf++ = *pMsgWrite++; + } + else + { + // Reset buffer. + pMsgBuffer->Seek (STREAM_SEEK_TO_BEGIN); + } + } + else + { + if (pSourceMsg->GetDocumentLB()) + { + if (pMsgStrm == NULL) + pMsgStrm = new SvStream (pSourceMsg->GetDocumentLB()); + + ULONG nRead = pMsgStrm->Read (pWBuf, (pWEnd - pWBuf)); + pWBuf += nRead; + } + } + return (pWBuf - pData); +} + +/*========================================================================= + * + * INetMessageOStream Implementation. + * + *=======================================================================*/ +/* + * INetMessageOStream. + */ +INetMessageOStream::INetMessageOStream (void) + : pTargetMsg (NULL), + bHeaderParsed (FALSE), + eOState (INETMSG_EOL_BEGIN), + pMsgBuffer (new SvMemoryStream) +{ +} + +/* + * ~INetMessageOStream. + */ +INetMessageOStream::~INetMessageOStream (void) +{ + if (pMsgBuffer->Tell() > 0) + PutMsgLine ((const sal_Char *) pMsgBuffer->GetData(), pMsgBuffer->Tell()); + delete pMsgBuffer; + + if (pTargetMsg) + { + SvOpenLockBytes *pLB = + PTR_CAST (SvOpenLockBytes, pTargetMsg->GetDocumentLB()); + if (pLB) + { + pLB->Flush(); + pLB->Terminate(); + } + } +} + +/* + * PutData. + * (Simple Field Parsing (RFC822, Appendix B)). + */ +int INetMessageOStream::PutData (const sal_Char *pData, ULONG nSize) +{ + if (pTargetMsg == NULL) return INETSTREAM_STATUS_ERROR; + + const sal_Char *pStop = (pData + nSize); + + while (!bHeaderParsed && (pData < pStop)) + { + if (eOState == INETMSG_EOL_BEGIN) + { + if ((*pData == '\r') || (*pData == '\n')) + { + /* + * Empty Line. Separates header fields from message body. + * Skip this and any 2nd line break character (if any). + */ + pData++; + if ((pData < pStop) && ((*pData == '\r') || (*pData == '\n'))) + pData++; + + // Emit any buffered last header field. + if (pMsgBuffer->Tell() > 0) + { + *pMsgBuffer << '\0'; + int status = PutMsgLine ( + (const sal_Char *) pMsgBuffer->GetData(), + pMsgBuffer->Tell()); + if (status != INETSTREAM_STATUS_OK) return status; + } + + // Reset to begin. + eOState = INETMSG_EOL_BEGIN; + pMsgBuffer->Seek (STREAM_SEEK_TO_BEGIN); + + // Mark header parsed. + bHeaderParsed = TRUE; + } + else if ((*pData == ' ') || (*pData == '\t')) + { + // Continuation line. Unfold multi-line field-body. + *pMsgBuffer << ' '; + pData++; + } + else + { + // Begin of new header field. + if (pMsgBuffer->Tell() > 0) + { + // Emit buffered header field now. + *pMsgBuffer << '\0'; + int status = PutMsgLine ( + (const sal_Char *) pMsgBuffer->GetData(), + pMsgBuffer->Tell()); + if (status != INETSTREAM_STATUS_OK) return status; + } + + // Reset to begin of buffer. + pMsgBuffer->Seek (STREAM_SEEK_TO_BEGIN); + + // Insert current character into buffer. + *pMsgBuffer << *pData++; + } + + // Search for next line break character. + if (!bHeaderParsed) eOState = INETMSG_EOL_SCR; + } + else if (eOState == INETMSG_EOL_FCR) + { + // Skip line break character. + pData++; + + // Mark begin of line. + eOState = INETMSG_EOL_BEGIN; + } + else if ((*pData == '\r') || (*pData == '\n')) + { + if (*pData == '\r') pData++; + eOState = INETMSG_EOL_FCR; + } + else if (ascii_isWhitespace (*pData & 0x7f)) + { + // Any <LWS> is folded into a single <SP> character. + sal_Char c = *((const sal_Char *) pMsgBuffer->GetData() + pMsgBuffer->Tell() - 1); + if (!ascii_isWhitespace (c & 0x7f)) *pMsgBuffer << ' '; + + // Skip over this <LWS> character. + pData++; + } + else + { + // Any other character is inserted into line buffer. + *pMsgBuffer << *pData++; + } + } + + if (bHeaderParsed && (pData < pStop)) + { + // Put message body down-stream. + return PutMsgLine (pData, (pStop - pData)); + } + + return INETSTREAM_STATUS_OK; +} + +/* + * PutMsgLine. + */ +int INetMessageOStream::PutMsgLine (const sal_Char *pData, ULONG nSize) +{ + // Check for message container. + if (pTargetMsg == NULL) return INETSTREAM_STATUS_ERROR; + + // Check for header or body. + if (!IsHeaderParsed()) + { + ByteString aField (pData); + USHORT nPos = aField.Search (':'); + if (nPos != STRING_NOTFOUND) + { + ByteString aName ( + aField.Copy (0, nPos)); + ByteString aValue ( + aField.Copy (nPos + 1, aField.Len() - nPos + 1)); + aValue.EraseLeadingChars (' '); + + pTargetMsg->SetHeaderField ( + INetMessageHeader (aName, aValue)); + } + } + else + { + SvOpenLockBytes *pLB = + PTR_CAST(SvOpenLockBytes, pTargetMsg->GetDocumentLB()); + if (pLB == NULL) + return INETSTREAM_STATUS_WOULDBLOCK; + + sal_Size nDocSiz = pTargetMsg->GetDocumentSize(); + sal_Size nWrite = 0; + + pLB->FillAppend ((sal_Char *)pData, nSize, &nWrite); + pTargetMsg->SetDocumentSize (nDocSiz + nWrite); + + if (nWrite < nSize) return INETSTREAM_STATUS_ERROR; + } + return INETSTREAM_STATUS_OK; +} + +/*========================================================================= + * + * INetMessageIOStream Implementation. + * + *=======================================================================*/ +/* + * INetMessageIOStream. + */ +INetMessageIOStream::INetMessageIOStream (ULONG nBufferSize) + : INetMessageIStream (nBufferSize), + INetMessageOStream () +{ +} + +/* + * ~INetMessageIOStream. + */ +INetMessageIOStream::~INetMessageIOStream (void) +{ +} + +/*======================================================================= + * + * INetMessageEncodeQPStream_Impl Implementation. + * (Quoted-Printable Encoding) + * + *=====================================================================*/ +static const sal_Char hex2pr[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static const sal_Char ebcdic[] = { + '!', '"', '#', '$', '@', '[', '\\', ']', '^', '`', '{', '|', '}', '~' +}; + +/* + * INetMessageEncodeQPStream_Impl. + */ +INetMessageEncodeQPStream_Impl::INetMessageEncodeQPStream_Impl ( + ULONG nMsgBufferSize) + : INetMessageIStream (), + pMsgStrm (NULL), + nMsgBufSiz (nMsgBufferSize), + nTokBufSiz (80), + eState (INETMSG_EOL_SCR), + bDone (FALSE) +{ + GenerateHeader (FALSE); + + pMsgBuffer = new sal_Char[nMsgBufSiz]; + pMsgRead = pMsgWrite = pMsgBuffer; + + pTokBuffer = new sal_Char[nTokBufSiz]; + pTokRead = pTokWrite = pTokBuffer; +} + +/* + * ~INetMessageEncodeQPStream_Impl. + */ +INetMessageEncodeQPStream_Impl::~INetMessageEncodeQPStream_Impl (void) +{ + delete pMsgStrm; + delete [] pMsgBuffer; + delete [] pTokBuffer; +} + +/* + * GetMsgLine. + */ +int INetMessageEncodeQPStream_Impl::GetMsgLine (sal_Char *pData, ULONG nSize) +{ + INetMessage *pMsg = GetSourceMessage (); + if (pMsg == NULL) return INETSTREAM_STATUS_ERROR; + + if (pMsg->GetDocumentLB() == NULL) return 0; + if (pMsgStrm == NULL) pMsgStrm = new SvStream (pMsg->GetDocumentLB()); + + sal_Char *pWBuf = pData; + while (pWBuf < (pData + nSize)) + { + // Caller's buffer not yet filled. + if ((pMsgRead - pMsgWrite) > 0) + { + // Bytes still in message buffer. + if ((eState != INETMSG_EOL_BEGIN) && + ((pTokRead - pTokBuffer) < 72)) + { + // Token buffer not yet filled. + if (eState == INETMSG_EOL_FCR) + { + eState = INETMSG_EOL_BEGIN; + if (*pMsgWrite != '\n') + { + // Convert orphant <CR> into <CR><LF> sequence. + *pTokRead++ = '\n'; + } + *pTokRead++ = *pMsgWrite++; + } + else if ((*pMsgWrite == ' ') || (*pMsgWrite == '\t')) + { + eState = INETMSG_EOL_FSP; + *pTokRead++ = *pMsgWrite++; + } + else if (*pMsgWrite == '\r') + { + // Found <CR>. + if (eState == INETMSG_EOL_FSP) + { + // Encode last (trailing space) character. + sal_uInt8 c = (sal_uInt8)(*(--pTokRead)); + *pTokRead++ = '='; + *pTokRead++ = hex2pr[((c & 0xf0) >> 4)]; + *pTokRead++ = hex2pr[((c & 0x0f) )]; + } + eState = INETMSG_EOL_FCR; + *pTokRead++ = *pMsgWrite++; + } + else if (*pMsgWrite == '\n') + { + // Found <LF> only. + if (eState == INETMSG_EOL_FSP) + { + // Encode last (trailing space) character. + sal_uInt8 c = (sal_uInt8)(*(--pTokRead)); + *pTokRead++ = '='; + *pTokRead++ = hex2pr[((c & 0xf0) >> 4)]; + *pTokRead++ = hex2pr[((c & 0x0f) )]; + } + eState = INETMSG_EOL_BEGIN; + + // Convert orphant <LF> into <CR><LF> sequence. + *pTokRead++ = '\r'; + *pTokRead++ = *pMsgWrite++; + } + else if (*pMsgWrite == '=') + { + // Escape character itself MUST be encoded, of course. + sal_uInt8 c = (sal_uInt8)(*pMsgWrite++); + *pTokRead++ = '='; + *pTokRead++ = hex2pr[((c & 0xf0) >> 4)]; + *pTokRead++ = hex2pr[((c & 0x0f) )]; + + eState = INETMSG_EOL_SCR; + } + else if (((sal_uInt8)(*pMsgWrite) > 0x20) && + ((sal_uInt8)(*pMsgWrite) < 0x7f) ) + { + /* + * Some printable ASCII character. + * (Encode EBCDIC special characters (NYI)). + */ + *pTokRead++ = *pMsgWrite++; + eState = INETMSG_EOL_SCR; + } + else + { + // Encode any other character. + sal_uInt8 c = (sal_uInt8)(*pMsgWrite++); + *pTokRead++ = '='; + *pTokRead++ = hex2pr[((c & 0xf0) >> 4)]; + *pTokRead++ = hex2pr[((c & 0x0f) )]; + + eState = INETMSG_EOL_SCR; + } + } + else + { + // Check for maximum line length. + if (eState != INETMSG_EOL_BEGIN) + { + // Insert soft line break. + *pTokRead++ = '='; + *pTokRead++ = '\r'; + *pTokRead++ = '\n'; + + eState = INETMSG_EOL_BEGIN; + } + + // Copy to caller's buffer. + if ((pTokRead - pTokWrite) > 0) + { + // Bytes still in token buffer. + *pWBuf++ = *pTokWrite++; + } + else + { + // Token buffer empty. Reset to <Begin-of-Buffer>. + pTokRead = pTokWrite = pTokBuffer; + eState = INETMSG_EOL_SCR; + } + } + } + else + { + // Message buffer empty. Reset to <Begin-of-Buffer>. + pMsgRead = pMsgWrite = pMsgBuffer; + + // Read next message block. + ULONG nRead = pMsgStrm->Read (pMsgBuffer, nMsgBufSiz); + if (nRead > 0) + { + // Set read pointer. + pMsgRead = (pMsgBuffer + nRead); + } + else + { + // Nothing more ro read. + if (!bDone) + { + // Append final <CR><LF> and mark we're done. + *pTokRead++ = '\r'; + *pTokRead++ = '\n'; + + bDone = TRUE; + } + else + { + // Already done all encoding. + if ((pTokRead - pTokWrite) > 0) + { + // Bytes still in token buffer. + *pWBuf++ = *pTokWrite++; + } + else + { + // Token buffer empty. Reset to <Begin-of-Buffer>. + pTokRead = pTokWrite = pTokBuffer; + + // Return. + return (pWBuf - pData); + } + } + } + } + } + return (pWBuf - pData); +} + +/*===================================================================== + * + * INetMessageDecodeQPStream_Impl Implementation. + * (Quoted-Printable Decoding) + * + *====================================================================*/ +static const sal_uInt8 pr2hex[128] = { + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + + 0x10, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 +}; + +/* + * INetMessageDecodeQPStream_Impl. + */ +INetMessageDecodeQPStream_Impl::INetMessageDecodeQPStream_Impl (void) + : INetMessageOStream (), + eState (INETMSG_EOL_BEGIN), + pMsgBuffer (new SvMemoryStream), + nTokBufLen (0) +{ + ParseHeader (FALSE); +} + +/* + * ~INetMessageDecodeQPStream_Impl. + */ +INetMessageDecodeQPStream_Impl::~INetMessageDecodeQPStream_Impl (void) +{ + delete pMsgBuffer; +} + +/* + * PutMsgLine. + */ +int INetMessageDecodeQPStream_Impl::PutMsgLine ( + const sal_Char *pData, ULONG nSize) +{ + INetMessage *pMsg = GetTargetMessage(); + if (pMsg == NULL) return INETSTREAM_STATUS_ERROR; + + SvOpenLockBytes * pLB = PTR_CAST(SvOpenLockBytes, pMsg->GetDocumentLB()); + if (pLB == NULL) return INETSTREAM_STATUS_WOULDBLOCK; + + const sal_Char *pStop = pData + nSize; + while (pData < pStop) + { + if (eState == INETMSG_EOL_FESC) + { + *(pTokBuffer + nTokBufLen++) = static_cast< char >(toupper(*pData)); + pData++; + if (nTokBufLen == 2) + { + if ((*pTokBuffer == '\r') || (*pTokBuffer == '\n')) + { + // Soft line break (=<CR><LF>). Emit buffer now. + eState = INETMSG_EOL_BEGIN; + } + else + { + // Decode token. + *pMsgBuffer << sal_uInt8 ( + (pr2hex[(int)(pTokBuffer[0] & 0x7f)] << 4) | + (pr2hex[(int)(pTokBuffer[1] & 0x7f)] & 15) ); + + // Search for next <CR>. + eState = INETMSG_EOL_SCR; + } + + // Reset token buffer. + nTokBufLen = 0; + } + } + else if (*pData == '=') + { + // Found escape character. + pData++; + eState = INETMSG_EOL_FESC; + } + else if (eState == INETMSG_EOL_FCR) + { + *pMsgBuffer << *pData++; + eState = INETMSG_EOL_BEGIN; + } + else if (*pData == '\r') + { + *pMsgBuffer << *pData++; + eState = INETMSG_EOL_FCR; + } + else + { + *pMsgBuffer << *pData++; + } + + if (eState == INETMSG_EOL_BEGIN) + { + sal_Size nRead = pMsgBuffer->Tell(); + if (nRead > 0) + { + // Emit buffer. + sal_Size nDocSiz = pMsg->GetDocumentSize(); + sal_Size nWrite = 0; + + pLB->FillAppend ( + (sal_Char *)(pMsgBuffer->GetData()), nRead, &nWrite); + pMsg->SetDocumentSize (nDocSiz + nWrite); + + if (nWrite < nRead) return INETSTREAM_STATUS_ERROR; + + pMsgBuffer->Seek (STREAM_SEEK_TO_BEGIN); + } + eState = INETMSG_EOL_SCR; + } + } + return INETSTREAM_STATUS_OK; +} + +/*====================================================================== + * + * INetMessageEncode64Stream_Impl Implementation. + * (Base64 Encoding) + * + *====================================================================*/ +static const sal_Char six2pr[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +/* + * INetMessageEncode64Stream_Impl. + */ +INetMessageEncode64Stream_Impl::INetMessageEncode64Stream_Impl ( + ULONG nMsgBufferSize) + : INetMessageIStream (), + pMsgStrm (NULL), + nMsgBufSiz (nMsgBufferSize), + nTokBufSiz (80), + bDone (FALSE) +{ + GenerateHeader (FALSE); + + pMsgBuffer = new sal_uInt8[nMsgBufSiz]; + pMsgRead = pMsgWrite = pMsgBuffer; + + pTokBuffer = new sal_Char[nTokBufSiz]; + pTokRead = pTokWrite = pTokBuffer; +} + +/* + * ~INetMessageEncode64Stream_Impl. + */ +INetMessageEncode64Stream_Impl::~INetMessageEncode64Stream_Impl (void) +{ + delete pMsgStrm; + delete [] pMsgBuffer; + delete [] pTokBuffer; +} + +/* + * GetMsgLine. + */ +int INetMessageEncode64Stream_Impl::GetMsgLine (sal_Char *pData, ULONG nSize) +{ + INetMessage *pMsg = GetSourceMessage (); + if (pMsg == NULL) return INETSTREAM_STATUS_ERROR; + + if (pMsg->GetDocumentLB() == NULL) return 0; + if (pMsgStrm == NULL) pMsgStrm = new SvStream (pMsg->GetDocumentLB()); + + sal_Char *pWBuf = pData; + while (pWBuf < (pData + nSize)) + { + // Caller's buffer not yet filled. + if ((pMsgRead - pMsgWrite) > 0) + { + // Bytes still in message buffer. + if ((pTokRead - pTokBuffer) < 72) + { + // Token buffer not yet filled. + switch ((pTokRead - pTokBuffer) % 4) + { + case 0: + *pTokRead++ = six2pr[(int)(*pMsgWrite >> 2)]; + break; + + case 1: + *pTokRead++ = six2pr[ + (int)(((*pMsgWrite << 4) & 060) | + (((*(pMsgWrite + 1)) >> 4) & 017))]; + pMsgWrite++; + break; + + case 2: + *pTokRead++ = six2pr[ + (int)(((*pMsgWrite << 2) & 074) | + (((*(pMsgWrite + 1)) >> 6) & 003))]; + pMsgWrite++; + break; + + default: // == case 3 + *pTokRead++ = six2pr[(int)(*pMsgWrite & 077)]; + pMsgWrite++; + break; + } + } + else if ((pTokRead - pTokBuffer) == 72) + { + // Maximum line length. Append <CR><LF>. + *pTokRead++ = '\r'; + *pTokRead++ = '\n'; + } + else + { + if ((pTokRead - pTokWrite) > 0) + { + // Bytes still in token buffer. + *pWBuf++ = *pTokWrite++; + } + else + { + // Token buffer empty. Reset to <Begin-of-Buffer>. + pTokRead = pTokWrite = pTokBuffer; + } + } + } + else + { + // Message buffer empty. Reset to <Begin-of-Buffer>. + pMsgRead = pMsgWrite = pMsgBuffer; + + // Read next message block. + ULONG nRead = pMsgStrm->Read (pMsgBuffer, nMsgBufSiz); + if (nRead > 0) + { + // Set read pointer. + pMsgRead = (pMsgBuffer + nRead); + } + else + { + // Nothing more to read. + if (!bDone) + { + // Append pad character(s) and final <CR><LF>. + switch ((pTokRead - pTokBuffer) % 4) + { + case 2: + *pTokRead++ = '='; + // Fall through for 2nd pad character. + + case 3: + *pTokRead++ = '='; + break; + + default: + break; + } + *pTokRead++ = '\r'; + *pTokRead++ = '\n'; + + // Mark we're done. + bDone = TRUE; + } + else + { + // Already done all encoding. + if ((pTokRead - pTokWrite) > 0) + { + // Bytes still in token buffer. + *pWBuf++ = *pTokWrite++; + } + else + { + // Token buffer empty. Reset to <Begin-of-Buffer>. + pTokRead = pTokWrite = pTokBuffer; + + // Reset done flag, if everything has been done. + // if (pWBuf == pData) bDone = FALSE; + + // Return. + return (pWBuf - pData); + } + } + } + } + } // while (pWBuf < (pData + nSize)) + return (pWBuf - pData); +} + +/*====================================================================== + * + * INetMessageDecode64Stream_Impl Implementation. + * (Base64 Decoding) + * + *====================================================================*/ +static const sal_uInt8 pr2six[256] = { + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x3E, 0x40, 0x40, 0x40, 0x3F, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, + 0x3C, 0x3D, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + + 0x40, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0x40, 0x40, 0x40, 0x40, 0x40, + + 0x40, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, + 0x31, 0x32, 0x33, 0x40, 0x40, 0x40, 0x40, 0x40, + + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 +}; + +/* + * INetMessageDecode64Stream_Impl. + */ +INetMessageDecode64Stream_Impl::INetMessageDecode64Stream_Impl ( + ULONG nMsgBufferSize) + : INetMessageOStream (), + eState (INETMSG_EOL_SCR), + nMsgBufSiz (nMsgBufferSize) +{ + ParseHeader (FALSE); + + pMsgBuffer = new sal_Char[nMsgBufSiz]; + pMsgRead = pMsgWrite = pMsgBuffer; +} + +/* + * ~INetMessageDecode64Stream_Impl. + */ +INetMessageDecode64Stream_Impl::~INetMessageDecode64Stream_Impl (void) +{ + delete [] pMsgBuffer; +} + +/* + * PutMsgLine. + */ +int INetMessageDecode64Stream_Impl::PutMsgLine ( + const sal_Char *pData, ULONG nSize) +{ + INetMessage *pMsg = GetTargetMessage (); + if (pMsg == NULL) return INETSTREAM_STATUS_ERROR; + + SvOpenLockBytes * pLB = PTR_CAST(SvOpenLockBytes, pMsg->GetDocumentLB()); + if (pLB == NULL) return INETSTREAM_STATUS_WOULDBLOCK; + + const sal_Char *pStop = (pData + nSize); + while (pData < pStop) + { + if (pr2six[(int)(*pData)] > 63) + { + /* + * Character not in base64 alphabet. + * Check for <End-of-Stream> or Junk. + */ + if (*pData == '=') + { + // Final pad character -> Done. + sal_Size nDocSiz = pMsg->GetDocumentSize(); + sal_Size nRead = pMsgWrite - pMsgBuffer; + sal_Size nWrite = 0; + + pLB->FillAppend (pMsgBuffer, nRead, &nWrite); + pMsg->SetDocumentSize (nDocSiz + nWrite); + + if (nWrite < nRead) + return INETSTREAM_STATUS_ERROR; + else + return INETSTREAM_STATUS_LOADED; + } + else if (eState == INETMSG_EOL_FCR) + { + // Skip any line break character. + if ((*pData == '\r') || (*pData == '\n')) pData++; + + // Store decoded message buffer contents. + sal_Size nDocSiz = pMsg->GetDocumentSize(); + sal_Size nRead = pMsgWrite - pMsgBuffer; + sal_Size nWrite = 0; + + pLB->FillAppend (pMsgBuffer, nRead, &nWrite); + pMsg->SetDocumentSize (nDocSiz + nWrite); + + if (nWrite < nRead) return INETSTREAM_STATUS_ERROR; + + // Reset to <Begin-of-Buffer>. + pMsgWrite = pMsgBuffer; + eState = INETMSG_EOL_SCR; + } + else if ((*pData == '\r') || (*pData == '\n')) + { + // Skip any line break character. + pData++; + eState = INETMSG_EOL_FCR; + } + else + { + // Skip any junk character (may be transmission error). + pData++; + } + } + else + { + // Decode any other character into message buffer. + switch ((pMsgRead - pMsgBuffer) % 4) + { + case 0: + *pMsgWrite = (pr2six[(int)(*pData++)] << 2); + pMsgRead++; + break; + + case 1: + *pMsgWrite++ |= (pr2six[(int)(*pData )] >> 4); + *pMsgWrite = (pr2six[(int)(*pData++)] << 4); + pMsgRead++; + break; + + case 2: + *pMsgWrite++ |= (pr2six[(int)(*pData )] >> 2); + *pMsgWrite = (pr2six[(int)(*pData++)] << 6); + pMsgRead++; + break; + + default: // == case 3 + *pMsgWrite++ |= (pr2six[(int)(*pData++)]); + pMsgRead = pMsgBuffer; + break; + } // switch ((pMsgRead - pMsgBuffer) % 4) + } + } // while (pData < pStop) + return INETSTREAM_STATUS_OK; +} + +/*========================================================================= + * + * INetMIMEMessageStream Implementation. + * + *=======================================================================*/ +/* + * INetMIMEMessageStream. + */ +INetMIMEMessageStream::INetMIMEMessageStream (ULONG nBufferSize) + : INetMessageIOStream (nBufferSize), + eState (INETMSG_EOL_BEGIN), + nChildIndex (0), + pChildStrm (NULL), + eEncoding (INETMSG_ENCODING_BINARY), + pEncodeStrm (NULL), + pDecodeStrm (NULL), + pMsgBuffer (NULL) +{ +} + +/* + * ~INetMIMEMessageStream. + */ +INetMIMEMessageStream::~INetMIMEMessageStream (void) +{ + delete pChildStrm; + delete pEncodeStrm; + delete pDecodeStrm; + delete pMsgBuffer; +} + +/* + * GetMsgEncoding. + */ +INetMessageEncoding +INetMIMEMessageStream::GetMsgEncoding (const String& rContentType) +{ + if ((rContentType.CompareIgnoreCaseToAscii ("message" , 7) == 0) || + (rContentType.CompareIgnoreCaseToAscii ("multipart", 9) == 0) ) + return INETMSG_ENCODING_7BIT; + + if (rContentType.CompareIgnoreCaseToAscii ("text", 4) == 0) + { + if (rContentType.CompareIgnoreCaseToAscii ("text/plain", 10) == 0) + { + if (rContentType.GetTokenCount ('=') > 1) + { + String aCharset (rContentType.GetToken (1, '=')); + aCharset.EraseLeadingChars (' '); + aCharset.EraseLeadingChars ('"'); + + if (aCharset.CompareIgnoreCaseToAscii ("us-ascii", 8) == 0) + return INETMSG_ENCODING_7BIT; + else + return INETMSG_ENCODING_QUOTED; + } + else + return INETMSG_ENCODING_7BIT; + } + else + return INETMSG_ENCODING_QUOTED; + } + + return INETMSG_ENCODING_BASE64; +} + +/* + * GetMsgLine. + * (Message Generator). + */ +int INetMIMEMessageStream::GetMsgLine (sal_Char *pData, ULONG nSize) +{ + // Check for message container. + INetMIMEMessage *pMsg = GetSourceMessage(); + if (pMsg == NULL) return INETSTREAM_STATUS_ERROR; + + // Check for header or body. + if (!IsHeaderGenerated()) + { + if (eState == INETMSG_EOL_BEGIN) + { + // Prepare special header fields. + if (pMsg->GetParent()) + { + String aPCT (pMsg->GetParent()->GetContentType()); + if (aPCT.CompareIgnoreCaseToAscii ("message/rfc822", 14) == 0) + pMsg->SetMIMEVersion ( + String(CONSTASCII_STRINGPARAM("1.0"))); + else + pMsg->SetMIMEVersion (String()); + } + else + { + pMsg->SetMIMEVersion (String(CONSTASCII_STRINGPARAM("1.0"))); + } + + // Check ContentType. + String aContentType (pMsg->GetContentType()); + if (aContentType.Len()) + { + // Determine default Content-Type. + String aDefaultType; + pMsg->GetDefaultContentType (aDefaultType); + + if (aDefaultType.CompareIgnoreCaseToAscii ( + aContentType, aContentType.Len()) == 0) + { + // No need to specify default. + pMsg->SetContentType (String()); + } + } + + // Check Encoding. + String aEncoding (pMsg->GetContentTransferEncoding()); + if (aEncoding.Len()) + { + // Use given Encoding. + if (aEncoding.CompareIgnoreCaseToAscii ( + "base64", 6) == 0) + eEncoding = INETMSG_ENCODING_BASE64; + else if (aEncoding.CompareIgnoreCaseToAscii ( + "quoted-printable", 16) == 0) + eEncoding = INETMSG_ENCODING_QUOTED; + else + eEncoding = INETMSG_ENCODING_7BIT; + } + else + { + // Use default Encoding for (given|default) Content-Type. + if (aContentType.Len() == 0) + { + // Determine default Content-Type. + pMsg->GetDefaultContentType (aContentType); + } + eEncoding = GetMsgEncoding (aContentType); + } + + // Set Content-Transfer-Encoding header. + if (eEncoding == INETMSG_ENCODING_BASE64) + { + // Base64. + pMsg->SetContentTransferEncoding ( + String(CONSTASCII_STRINGPARAM("base64"))); + } + else if (eEncoding == INETMSG_ENCODING_QUOTED) + { + // Quoted-Printable. + pMsg->SetContentTransferEncoding ( + String(CONSTASCII_STRINGPARAM("quoted-printable"))); + } + else + { + // No need to specify default. + pMsg->SetContentTransferEncoding (String()); + } + + // Mark we're done. + eState = INETMSG_EOL_DONE; + } + + // Generate the message header. + int nRead = INetMessageIOStream::GetMsgLine (pData, nSize); + if (nRead <= 0) + { + // Reset state. + eState = INETMSG_EOL_BEGIN; + } + return nRead; + } + else + { + // Generate the message body. + if (pMsg->IsContainer()) + { + // Encapsulated message body. + while (eState == INETMSG_EOL_BEGIN) + { + if (pChildStrm == NULL) + { + INetMIMEMessage *pChild = pMsg->GetChild (nChildIndex); + if (pChild) + { + // Increment child index. + nChildIndex++; + + // Create child stream. + pChildStrm = new INetMIMEMessageStream; + pChildStrm->SetSourceMessage (pChild); + + if (pMsg->IsMultipart()) + { + // Insert multipart delimiter. + ByteString aDelim ("--"); + aDelim += pMsg->GetMultipartBoundary(); + aDelim += "\r\n"; + + rtl_copyMemory ( + pData, aDelim.GetBuffer(), aDelim.Len()); + return aDelim.Len(); + } + } + else + { + // No more parts. Mark we're done. + eState = INETMSG_EOL_DONE; + nChildIndex = 0; + + if (pMsg->IsMultipart()) + { + // Insert close delimiter. + ByteString aDelim ("--"); + aDelim += pMsg->GetMultipartBoundary(); + aDelim += "--\r\n"; + + rtl_copyMemory ( + pData, aDelim.GetBuffer(), aDelim.Len()); + return aDelim.Len(); + } + } + } + else + { + // Read current child stream. + int nRead = pChildStrm->Read (pData, nSize); + if (nRead > 0) + { + return nRead; + } + else + { + // Cleanup exhausted child stream. + delete pChildStrm; + pChildStrm = NULL; + } + } + } + return 0; + } + else + { + // Single part message body. + if (pMsg->GetDocumentLB() == NULL) + { + // Empty message body. + return 0; + } + else + { + // Check whether message body needs to be encoded. + if (eEncoding == INETMSG_ENCODING_7BIT) + { + // No Encoding. + return INetMessageIOStream::GetMsgLine (pData, nSize); + } + else + { + // Apply appropriate Encoding. + while (eState == INETMSG_EOL_BEGIN) + { + if (pEncodeStrm == NULL) + { + // Create encoder stream. + if (eEncoding == INETMSG_ENCODING_QUOTED) + { + // Quoted-Printable Encoding. + pEncodeStrm + = new INetMessageEncodeQPStream_Impl; + } + else + { + // Base64 Encoding. + pEncodeStrm + = new INetMessageEncode64Stream_Impl; + } + pEncodeStrm->SetSourceMessage (pMsg); + } + + // Read encoded message. + int nRead = pEncodeStrm->Read (pData, nSize); + if (nRead > 0) + { + return nRead; + } + else + { + // Cleanup exhausted encoder stream. + delete pEncodeStrm; + pEncodeStrm = NULL; + + // Mark we're done. + eState = INETMSG_EOL_DONE; + } + } + return 0; + } + } + } + } +} + +/* + * PutMsgLine. + * (Message Parser). + */ +int INetMIMEMessageStream::PutMsgLine (const sal_Char *pData, ULONG nSize) +{ + // Check for message container. + INetMIMEMessage *pMsg = GetTargetMessage(); + if (pMsg == NULL) return INETSTREAM_STATUS_ERROR; + + // Check for header or body. + if (!IsHeaderParsed()) + { + // Parse the message header. + int nRet = INetMessageIOStream::PutMsgLine (pData, nSize); + return nRet; + } + else + { + pMsg->SetHeaderParsed(); + // Parse the message body. + if (pMsg->IsContainer()) + { + + // Content-Transfer-Encoding MUST be "7bit" (RFC1521). + if (pMsg->IsMessage()) + { + if( !pChildStrm ) + { + // Encapsulated message. + pMsg->SetChildCount( pMsg->GetChildCount() + 1); + INetMIMEMessage* pNewMessage = new INetMIMEMessage; + pNewMessage->SetDocumentLB ( + new SvAsyncLockBytes(new SvCacheStream, FALSE)); + pMsg->AttachChild( *pNewMessage, TRUE ); + + // Encapsulated message body. Create message parser stream. + pChildStrm = new INetMIMEMessageStream; + pChildStrm->SetTargetMessage ( pNewMessage ); + + // Initialize control variables. + eState = INETMSG_EOL_BEGIN; + } + if ( nSize > 0) + { + // Bytes still in buffer. Put message down-stream. + int status = pChildStrm->Write( pData, nSize ); + if (status != INETSTREAM_STATUS_OK) + return status; + } + + return INetMessageIOStream::PutMsgLine (pData, nSize); + } + else + { + + // Multipart message body. Initialize multipart delimiters. + // Multipart message. + if (pMsg->GetMultipartBoundary().Len() == 0) + { + // Determine boundary. + ByteString aType ( + pMsg->GetContentType(), RTL_TEXTENCODING_ASCII_US); + ByteString aLowerType (aType); + aLowerType.ToLowerAscii(); + + USHORT nPos = aLowerType.Search ("boundary="); + ByteString aBoundary (aType.Copy (nPos + 9)); + + aBoundary.EraseLeadingAndTrailingChars (' '); + aBoundary.EraseLeadingAndTrailingChars ('"'); + + // Save boundary. + pMsg->SetMultipartBoundary (aBoundary); + } + + ByteString aPlainDelim (pMsg->GetMultipartBoundary()); + ByteString aDelim ("--"); + aDelim += aPlainDelim; + + ByteString aPlainClose (aPlainDelim); + aPlainClose += "--"; + + ByteString aClose (aDelim); + aClose += "--"; + + if (pMsgBuffer == NULL) pMsgBuffer = new SvMemoryStream; + pMsgBuffer->Write (pData, nSize); + ULONG nBufSize = pMsgBuffer->Tell(); + + const sal_Char* pChar; + const sal_Char* pOldPos; + for( pOldPos = pChar = (const sal_Char *) pMsgBuffer->GetData(); nBufSize--; + pChar++ ) + { + int status; + if( *pChar == '\r' || *pChar == '\n' ) + { + if( aDelim.CompareTo (pOldPos, aDelim.Len()) + != COMPARE_EQUAL && + aClose.CompareTo (pOldPos, aClose.Len()) + != COMPARE_EQUAL && + aPlainDelim.CompareTo (pOldPos, aPlainDelim.Len()) + != COMPARE_EQUAL && + aPlainClose.CompareTo(pOldPos, aPlainClose.Len()) + != COMPARE_EQUAL ) + { + if( nBufSize && + ( pChar[1] == '\r' || pChar[1] == '\n' ) ) + nBufSize--, pChar++; + if( pChildStrm ) + { + status = pChildStrm->Write( + pOldPos, pChar - pOldPos + 1 ); + if( status != INETSTREAM_STATUS_OK ) + return status; + } + else { + DBG_ERRORFILE( "Die Boundary nicht gefunden" ); + } + status = INetMessageIOStream::PutMsgLine( + pOldPos, pChar - pOldPos + 1 ); + if( status != INETSTREAM_STATUS_OK ) + return status; + pOldPos = pChar + 1; + } + else + { + if( nBufSize && + ( pChar[1] == '\r' || pChar[1] == '\n' ) ) + nBufSize--, pChar++; + pOldPos = pChar + 1; + DELETEZ( pChildStrm ); + + if (aClose.CompareTo (pOldPos, aClose.Len()) + != COMPARE_EQUAL && + aPlainClose.CompareTo (pOldPos, aClose.Len()) + != COMPARE_EQUAL ) + { + // Encapsulated message. + pMsg->SetChildCount(pMsg->GetChildCount() + 1); + INetMIMEMessage* pNewMessage = + new INetMIMEMessage; + pNewMessage->SetDocumentLB ( + new SvAsyncLockBytes ( + new SvCacheStream, FALSE)); + + pMsg->AttachChild( *pNewMessage, TRUE ); + + // Encapsulated message body. Create message parser stream. + pChildStrm = new INetMIMEMessageStream; + pChildStrm->SetTargetMessage ( pNewMessage ); + + // Initialize control variables. + } + eState = INETMSG_EOL_BEGIN; + status = INetMessageIOStream::PutMsgLine( + pOldPos, pChar - pOldPos + 1 ); + if( status != INETSTREAM_STATUS_OK ) + return status; + } + } + } + if( pOldPos < pChar ) + { + SvMemoryStream* pNewStream = new SvMemoryStream; + pNewStream->Write( pOldPos, pChar - pOldPos ); + SvMemoryStream* pTmp = pMsgBuffer; + pMsgBuffer = pNewStream; + delete pTmp; + } + else + { + pMsgBuffer->Seek( 0L ); + pMsgBuffer->SetStreamSize( 0 ); + } + return INETSTREAM_STATUS_OK; + } + } + else + { + /* + * Single part message. + * Remove any ContentTransferEncoding. + */ + if (pMsg->GetContentType().Len() == 0) + { + String aDefaultCT; + pMsg->GetDefaultContentType (aDefaultCT); + pMsg->SetContentType (aDefaultCT); + } + + if (eEncoding == INETMSG_ENCODING_BINARY) + { + String aEncoding (pMsg->GetContentTransferEncoding()); + if (aEncoding.CompareIgnoreCaseToAscii ( + "base64", 6) == COMPARE_EQUAL) + eEncoding = INETMSG_ENCODING_BASE64; + else if (aEncoding.CompareIgnoreCaseToAscii ( + "quoted-printable", 16) == COMPARE_EQUAL) + eEncoding = INETMSG_ENCODING_QUOTED; + else + eEncoding = INETMSG_ENCODING_7BIT; + } + + if (eEncoding == INETMSG_ENCODING_7BIT) + { + // No decoding necessary. + return INetMessageIOStream::PutMsgLine (pData, nSize); + } + else + { + if (pDecodeStrm == NULL) + { + if (eEncoding == INETMSG_ENCODING_QUOTED) + pDecodeStrm = new INetMessageDecodeQPStream_Impl; + else + pDecodeStrm = new INetMessageDecode64Stream_Impl; + + pDecodeStrm->SetTargetMessage (pMsg); + } + return pDecodeStrm->Write (pData, nSize); + } + } + } +} + + + diff --git a/tools/source/inet/makefile.mk b/tools/source/inet/makefile.mk new file mode 100644 index 000000000000..7760f1c21486 --- /dev/null +++ b/tools/source/inet/makefile.mk @@ -0,0 +1,49 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.6 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ = ..$/.. +PRJNAME = tools +TARGET = inet + +.INCLUDE: settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +SLOFILES=\ + $(SLO)$/inetmime.obj \ + $(SLO)$/inetmsg.obj \ + $(SLO)$/inetstrm.obj + +OBJFILES=\ + $(OBJ)$/inetmime.obj \ + $(OBJ)$/inetmsg.obj \ + $(OBJ)$/inetstrm.obj + +.INCLUDE: target.mk diff --git a/tools/source/makefile.mk b/tools/source/makefile.mk new file mode 100644 index 000000000000..9d67ca6621a7 --- /dev/null +++ b/tools/source/makefile.mk @@ -0,0 +1,62 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.5 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=.. + +TARGET=source + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Files -------------------------------------------------------- + +.IF "$(GUI)" == "UNX" +SUBDIRS+= solar +.ENDIF + +SUBDIRS+= \ + datetime \ + timestamp \ + debug \ + fsys \ + generic \ + intntl \ + memtools \ + misc \ + rc \ + ref \ + stream \ + zcodec + + +.INCLUDE : target.mk + diff --git a/tools/source/memtools/contnr.cxx b/tools/source/memtools/contnr.cxx new file mode 100644 index 000000000000..c1bbbdb94037 --- /dev/null +++ b/tools/source/memtools/contnr.cxx @@ -0,0 +1,1711 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: contnr.cxx,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#ifndef _LIMITS_H +#include <limits.h> +#endif + +#ifndef _STRING_H +#include <string.h> +#endif + +#ifndef _STDIO_H +#include <stdio.h> +#endif +#include <tools/solar.h> +#include <impcont.hxx> +#include <tools/contnr.hxx> +#include <tools/debug.hxx> + +// ----------------------------------------------------------------------- + +DBG_NAME( CBlock ) +DBG_NAME( Container ) + +/************************************************************************* +|* +|* DbgCheckCBlock() +|* +|* Beschreibung Pruefung eines CBlock fuer Debug-Utilities +|* Ersterstellung MI 30.01.92 +|* Letzte Aenderung TH 24.01.96 +|* +*************************************************************************/ + +#ifdef DBG_UTIL +const char* CBlock::DbgCheckCBlock( const void* pBlock ) +{ + CBlock* p = (CBlock*)pBlock; + + if ( p->nCount > p->nSize ) + return "nCount > nSize"; + + if ( p->nSize && !p->pNodes ) + return "nSize > 0 && pNodes == NULL"; + + return NULL; +} +#endif + +/************************************************************************* +|* +|* CBlock::CBlock() +|* +|* Beschreibung Construktor des Verwaltungsblocks +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +CBlock::CBlock( USHORT nInitSize, CBlock* _pPrev, CBlock* _pNext ) +{ + DBG_CTOR( CBlock, DbgCheckCBlock ); + + pPrev = _pPrev; + pNext = _pNext; + nSize = nInitSize; + nCount = 0; + + // Datenpuffer anlegen + pNodes = new PVOID[nSize]; +} + +/************************************************************************* +|* +|* CBlock::CBlock() +|* +|* Beschreibung Construktor des Verwaltungsblocks +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +CBlock::CBlock( USHORT _nSize, CBlock* _pPrev ) +{ + DBG_CTOR( CBlock, DbgCheckCBlock ); + DBG_ASSERT( _nSize, "CBlock::CBlock(): nSize == 0" ); + + pPrev = _pPrev; + pNext = NULL; + nSize = _nSize; + nCount = _nSize; + + // Datenpuffer anlegen und initialisieren + pNodes = new PVOID[nSize]; + memset( pNodes, 0, nSize*sizeof(PVOID) ); +} + +/************************************************************************* +|* +|* CBlock::CBlock() +|* +|* Beschreibung Copy-Construktor des Verwaltungsblocks +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +CBlock::CBlock( const CBlock& r, CBlock* _pPrev ) +{ + DBG_CTOR( CBlock, DbgCheckCBlock ); + DBG_CHKOBJ( &r, CBlock, DbgCheckCBlock ); + + pPrev = _pPrev; + pNext = NULL; + nSize = r.nSize; + nCount = r.nCount; + + // Datenpuffer anlegen und Daten kopieren + pNodes = new PVOID[nSize]; + memcpy( pNodes, r.pNodes, nCount*sizeof(PVOID) ); +} + +/************************************************************************* +|* +|* CBlock::~CBlock() +|* +|* Beschreibung Destruktor des Verwaltungsblocks +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +inline CBlock::~CBlock() +{ + DBG_DTOR( CBlock, DbgCheckCBlock ); + + // Daten loeschen + delete[] pNodes; +} + +/************************************************************************* +|* +|* CBlock::Insert() +|* +|* Beschreibung Fuegt einen Pointer ein +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void CBlock::Insert( void* p, USHORT nIndex, USHORT nReSize ) +{ + DBG_CHKTHIS( CBlock, DbgCheckCBlock ); + DBG_ASSERT( nIndex <= nCount, "CBlock::Insert(): Index > nCount" ); + + // Muss Block realokiert werden + if ( nCount == nSize ) + { + // Neue Daten anlegen + nSize = nSize + nReSize; // MSVC warns here if += is used + void** pNewNodes = new PVOID[nSize]; + + // Wird angehaengt + if ( nCount == nIndex ) + { + // Daten kopieren + memcpy( pNewNodes, pNodes, nCount*sizeof(PVOID) ); + } + else + { + // Daten kopieren + memcpy( pNewNodes, pNodes, nIndex*sizeof(PVOID) ); + memcpy( pNewNodes + nIndex + 1, + pNodes + nIndex, + (nCount-nIndex)*sizeof(PVOID) ); + } + + // Alte Daten loeschen und neue setzen + delete[] pNodes; + pNodes = pNewNodes; + } + else + { + if ( nIndex < nCount ) + { + memmove( pNodes + nIndex + 1, + pNodes + nIndex, + (nCount-nIndex)*sizeof(PVOID) ); + } + } + + // Neuen Pointer setzen und Elementgroesse erhoehen + pNodes[nIndex] = p; + nCount++; +} + +/************************************************************************* +|* +|* CBlock::Split() +|* +|* Beschreibung Fuegt einen Pointer ein und splittet den Block +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +CBlock* CBlock::Split( void* p, USHORT nIndex, USHORT nReSize ) +{ + DBG_CHKTHIS( CBlock, DbgCheckCBlock ); + + USHORT nNewSize; + USHORT nMiddle; + CBlock* pNewBlock; + + nMiddle = nCount/2; + + if ( ( nIndex == nCount ) || ( nIndex == 0 ) ) + nNewSize = nReSize; + else + { + // Der aktuelle Block wird in der Mitte geteilt + nNewSize = (nCount+1) / 2; + + if ( nNewSize < nReSize ) + nNewSize = nReSize; + else + { + // Neue Groesse muss ein vielfaches von Resize sein + if ( nNewSize % nReSize ) + nNewSize += nReSize - (nNewSize % nReSize); + else + nNewSize = nNewSize + nReSize; // MSVC warns here if += is used + } + } + + // Vor oder hinter dem aktuellem Block einfuegen? + if ( nIndex > nMiddle ) + { + // Neuen Split-Block anlegen und hinter dem aktuellem Block einfuegen + pNewBlock = new CBlock( nNewSize, this, pNext ); + + if ( pNext ) + pNext->pPrev = pNewBlock; + pNext = pNewBlock; + + if ( nIndex == nCount ) + { + // Neuen Pointer einfuegen + pNewBlock->pNodes[0] = p; + pNewBlock->nCount = 1; + } + else + { + nIndex = nIndex - nMiddle; // MSVC warns here if += is used + // Alles von Mitte bis Index kopieren + if ( nIndex ) + memcpy( pNewBlock->pNodes, pNodes+nMiddle, nIndex*sizeof(PVOID) ); + + // Neuen Pointer einfuegen + pNewBlock->pNodes[nIndex] = p; + + // Alles von Mitte bis Ende hinter Index kopieren + memcpy( pNewBlock->pNodes+nIndex+1, + pNodes+nMiddle+nIndex, + (nCount-nMiddle-nIndex) * sizeof(PVOID) ); + + pNewBlock->nCount = (nCount-nMiddle+1); + nCount = nMiddle; + + // Den aktuellen Datenbereich auch halbieren + if ( nSize != nNewSize ) + { + void** pNewNodes = new PVOID[nNewSize]; + memcpy( pNewNodes, pNodes, nCount*sizeof(PVOID) ); + delete[] pNodes; + pNodes = pNewNodes; + nSize = nNewSize; + } + } + } + else + { + // Neuen Split-Block anlegen und vor dem aktuellem Block einfuegen + pNewBlock = new CBlock( nNewSize, pPrev, this ); + + if ( pPrev ) + pPrev->pNext = pNewBlock; + pPrev = pNewBlock; + + if ( nIndex == 0 ) + { + // Neuen Pointer einfuegen + pNewBlock->pNodes[0] = p; + pNewBlock->nCount = 1; + } + else + { + // Alles von Anfang bis Index kopieren + memcpy( pNewBlock->pNodes, pNodes, nIndex*sizeof(PVOID) ); + + // Neuen Pointer einfuegen + pNewBlock->pNodes[nIndex] = p; + + // Alles von Index bis Mitte hinter Index kopieren + if ( nIndex != nMiddle ) + { + memcpy( pNewBlock->pNodes+nIndex+1, + pNodes+nIndex, + (nMiddle-nIndex) * sizeof(PVOID) ); + } + + pNewBlock->nCount = nMiddle+1; + nCount = nCount - nMiddle; // MSVC warns here if += is used + + // Die zweite Haelfte in einen neuen Block kopieren + if ( nSize != nNewSize ) + { + void** pNewNodes = new PVOID[nNewSize]; + memcpy( pNewNodes, pNodes+nMiddle, nCount*sizeof(PVOID) ); + delete[] pNodes; + pNodes = pNewNodes; + nSize = nNewSize; + } + else + memmove( pNodes, pNodes+nMiddle, nCount*sizeof(PVOID) ); + } + } + + // Neu angelegten Block zurueckgeben, da gegebenfalls die Blockpointer + // im Container angepast werden koennen + return pNewBlock; +} + +/************************************************************************* +|* +|* CBlock::Remove() +|* +|* Beschreibung Entfernt einen Pointer +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* CBlock::Remove( USHORT nIndex, USHORT nReSize ) +{ + DBG_CHKTHIS( CBlock, DbgCheckCBlock ); + + // Alten Pointer sichern + void* pOld = pNodes[nIndex]; + + // 1 Element weniger + nCount--; + + // Block verkleinern (wenn Reallokationsgroesse um 4 unterschritten wird) + if ( nCount == (nSize-nReSize-4) ) + { + // Neue Daten anlegen + nSize = nSize - nReSize; // MSVC warns here if += is used + void** pNewNodes = new PVOID[nSize]; + + // Wird letzter Eintrag geloescht + if ( nIndex == nCount ) + { + // Daten kopieren + memcpy( pNewNodes, pNodes, nCount*sizeof(PVOID) ); + } + else + { + // Daten kopieren + memcpy( pNewNodes, pNodes, nIndex*sizeof(PVOID) ); + memcpy( pNewNodes + nIndex, pNodes + nIndex+1, + (nCount-nIndex)*sizeof(PVOID) ); + } + + // Alte Daten loeschen und neue setzen + delete[] pNodes; + pNodes = pNewNodes; + } + else + { + // Wenn nicht das letzte Element, dann zusammenschieben + if ( nIndex < nCount ) + { + memmove( pNodes + nIndex, pNodes + nIndex + 1, + (nCount-nIndex)*sizeof(PVOID) ); + } + } + + // Alten Pointer zurueckgeben + return pOld; +} + +/************************************************************************* +|* +|* CBlock::Replace() +|* +|* Beschreibung Ersetzt einen Pointer +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +inline void* CBlock::Replace( void* p, USHORT nIndex ) +{ + DBG_CHKTHIS( CBlock, DbgCheckCBlock ); + + // Alten Pointer sichern, neuen setzen und alten zurueckgeben + void* pOld = pNodes[nIndex]; + pNodes[nIndex] = p; + return pOld; +} + +/************************************************************************* +|* +|* CBlock::GetObjectPtr() +|* +|* Beschreibung Gibt einen Pointer auf den Pointer aus dem Block +|* zurueck +|* Ersterstellung TH 26.01.93 +|* Letzte Aenderung TH 26.01.93 +|* +*************************************************************************/ + +inline void** CBlock::GetObjectPtr( USHORT nIndex ) +{ + DBG_CHKTHIS( CBlock, DbgCheckCBlock ); + + return &(pNodes[nIndex]); +} + +/************************************************************************* +|* +|* CBlock::SetSize() +|* +|* Beschreibung Aendert die Groesse des Blocks +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void CBlock::SetSize( USHORT nNewSize ) +{ + DBG_CHKTHIS( CBlock, DbgCheckCBlock ); + DBG_ASSERT( nNewSize, "CBlock::SetSize(): nNewSize == 0" ); + + // Unterscheidet sich die Groesse + if ( nNewSize != nCount ) + { + // Array erweitern + void** pNewNodes = new PVOID[nNewSize]; + + // Alte Tabelle in die Neue kopieren + if ( nNewSize < nCount ) + memcpy( pNewNodes, pNodes, nNewSize*sizeof(PVOID) ); + else + { + memcpy( pNewNodes, pNodes, nCount*sizeof(PVOID) ); + + // Array mit 0 initialisieren + memset( pNewNodes+nCount, 0, (nNewSize-nCount)*sizeof(PVOID) ); + } + + // Altes Array loeschen und neue Werte setzen + nSize = nNewSize; + nCount = nSize; + delete[] pNodes; + pNodes = pNewNodes; + } +} + +//------------------------------------------------------------------------ + +/************************************************************************* +|* +|* DbgCheckContainer() +|* +|* Beschreibung Pruefung eines Container fuer Debug-Utilities +|* Ersterstellung MI 30.01.92 +|* Letzte Aenderung TH 24.01.96 +|* +*************************************************************************/ + +#ifdef DBG_UTIL +const char* Container::DbgCheckContainer( const void* pCont ) +{ + Container* p = (Container*)pCont; + + if ( p->nCount && (!p->pFirstBlock || !p->pLastBlock || !p->pCurBlock) ) + return "nCount > 0 but no CBlocks"; + + return NULL; +} +#endif + +/************************************************************************* +|* +|* ImpCopyContainer() +|* +|* Beschreibung Kopiert alle Daten des Containers +|* Ersterstellung TH 24.01.96 +|* Letzte Aenderung TH 24.01.96 +|* +*************************************************************************/ + +void Container::ImpCopyContainer( const Container* pCont2 ) +{ + // Werte vom uebergebenen Container uebernehmen + nCount = pCont2->nCount; + nCurIndex = pCont2->nCurIndex; + nInitSize = pCont2->nInitSize; + nReSize = pCont2->nReSize; + nBlockSize = pCont2->nBlockSize; + + // Alle Bloecke kopieren + if ( pCont2->nCount ) + { + CBlock* pBlock1; + CBlock* pBlock2; + CBlock* pTempBlock; + + // Erstmal ersten Block kopieren + pBlock2 = pCont2->pFirstBlock; + pFirstBlock = new CBlock( *pBlock2, NULL ); + // Ist erster Block der Current-Block, dann Current-Block setzen + if ( pBlock2 == pCont2->pCurBlock ) + pCurBlock = pFirstBlock; + pBlock1 = pFirstBlock; + pBlock2 = pBlock2->GetNextBlock(); + while ( pBlock2 ) + { + // Neuen Block anlegen und aus der uebergebenen Liste kopieren + pTempBlock = new CBlock( *pBlock2, pBlock1 ); + pBlock1->SetNextBlock( pTempBlock ); + pBlock1 = pTempBlock; + + // Current-Block beruecksichtigen + if ( pBlock2 == pCont2->pCurBlock ) + pCurBlock = pBlock1; + + // Auf naechsten Block weitersetzen + pBlock2 = pBlock2->GetNextBlock(); + } + + // Letzten Block setzen + pLastBlock = pBlock1; + } + else + { + pFirstBlock = NULL; + pLastBlock = NULL; + pCurBlock = NULL; + } +} + +/************************************************************************* +|* +|* Container::Container() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +Container::Container( USHORT _nBlockSize, USHORT _nInitSize, USHORT _nReSize ) +{ + DBG_CTOR( Container, DbgCheckContainer ); + + // BlockSize muss mindestens 4 sein und kleiner als 64 KB + if ( _nBlockSize < 4 ) + nBlockSize = 4; + else + { + if ( _nBlockSize < CONTAINER_MAXBLOCKSIZE ) + nBlockSize = _nBlockSize; + else + nBlockSize = CONTAINER_MAXBLOCKSIZE; + } + + // ReSize muss mindestens 2 sein und kleiner als BlockSize + if ( _nReSize >= nBlockSize ) + nReSize = nBlockSize; + else + { + if ( _nReSize < 2 ) + nReSize = 2; + else + nReSize = _nReSize; + + // BlockSize muss ein vielfaches der Resizegroesse sein + if ( nBlockSize % nReSize ) + nBlockSize -= nReSize - (nBlockSize % nReSize); + } + + // InitSize muss groesser gleich ReSize sein und kleiner als BlockSize + if ( _nInitSize <= nReSize ) + nInitSize = nReSize; + else + { + if ( _nInitSize >= nBlockSize ) + nInitSize = nBlockSize; + else + { + nInitSize = _nInitSize; + + // InitSize muss ein vielfaches der Resizegroesse sein + if ( nInitSize % nReSize ) + nInitSize -= nReSize - (nInitSize % nReSize); + } + } + + // Werte initialisieren + pFirstBlock = NULL; + pLastBlock = NULL; + pCurBlock = NULL; + nCount = 0; + nCurIndex = 0; +} + +/************************************************************************* +|* +|* Container::Container() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +Container::Container( ULONG nSize ) +{ + DBG_CTOR( Container, DbgCheckContainer ); + + nCount = nSize; + nCurIndex = 0; + nBlockSize = CONTAINER_MAXBLOCKSIZE; + nInitSize = 1; + nReSize = 1; + + if ( !nSize ) + { + pFirstBlock = NULL; + pLastBlock = NULL; + pCurBlock = NULL; + } + else + { + // Muss mehr als ein Block angelegt werden + if ( nSize <= nBlockSize ) + { + pFirstBlock = new CBlock( (USHORT)nSize, NULL ); + pLastBlock = pFirstBlock; + } + else + { + CBlock* pBlock1; + CBlock* pBlock2; + + pFirstBlock = new CBlock( nBlockSize, NULL ); + pBlock1 = pFirstBlock; + nSize -= nBlockSize; + + // Solange die Blockgroesse ueberschritten wird, neue Bloecke anlegen + while ( nSize > nBlockSize ) + { + pBlock2 = new CBlock( nBlockSize, pBlock1 ); + pBlock1->SetNextBlock( pBlock2 ); + pBlock1 = pBlock2; + nSize -= nBlockSize; + } + + pLastBlock = new CBlock( (USHORT)nSize, pBlock1 ); + pBlock1->SetNextBlock( pLastBlock ); + } + + pCurBlock = pFirstBlock; + } +} + +/************************************************************************* +|* +|* Container::Container() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +Container::Container( const Container& r ) +{ + DBG_CTOR( Container, DbgCheckContainer ); + + // Daten kopieren + ImpCopyContainer( &r ); +} + +/************************************************************************* +|* +|* Container::~Container() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +Container::~Container() +{ + DBG_DTOR( Container, DbgCheckContainer ); + + // Alle Bloecke loeschen + CBlock* pBlock = pFirstBlock; + while ( pBlock ) + { + CBlock* pTemp = pBlock->GetNextBlock(); + delete pBlock; + pBlock = pTemp; + } +} + +/************************************************************************* +|* +|* Container::ImpInsert() +|* +|* Beschreibung Interne Methode zum Einfuegen eines Pointers +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung DV 01.07.97 +|* +*************************************************************************/ + +void Container::ImpInsert( void* p, CBlock* pBlock, USHORT nIndex ) +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + if ( !nCount ) + { + if ( !pBlock ) + { + pFirstBlock = new CBlock( nInitSize, NULL, NULL ); + pLastBlock = pFirstBlock; + pCurBlock = pFirstBlock; + } + pFirstBlock->Insert( p, nIndex, nReSize ); + } + else + { + // Ist im Block die maximale Blockgroesse erreicht, + // dann neuen Block anlegen + if ( pBlock->Count() == nBlockSize ) + { + // Block auftrennen + CBlock* pNewBlock = pBlock->Split( p, nIndex, nReSize ); + + // Wurde Block dahinter angehaegnt + if ( pBlock->pNext == pNewBlock ) + { + // Gegebenenfalls LastBlock anpassen + if ( pBlock == pLastBlock ) + pLastBlock = pNewBlock; + + // Current-Position nachfuehren + if ( pBlock == pCurBlock ) + { + if ( pBlock->nCount <= nCurIndex ) + { + if ( nIndex <= nCurIndex ) + nCurIndex++; + pCurBlock = pNewBlock; + nCurIndex = nCurIndex - pBlock->nCount; // MSVC warns here if += is used + } + } + } + else + { + // Gegebenenfalls FirstBlock anpassen + if ( pBlock == pFirstBlock ) + pFirstBlock = pNewBlock; + + // Current-Position nachfuehren + if ( pBlock == pCurBlock ) + { + if ( nIndex <= nCurIndex ) + nCurIndex++; + if ( pNewBlock->nCount <= nCurIndex ) + nCurIndex = nCurIndex - pNewBlock->nCount; // MSVC warns here if += is used + else + pCurBlock = pNewBlock; + } + } + } + else + { + // Sonst reicht normales einfuegen in den Block + pBlock->Insert( p, nIndex, nReSize ); + + // Current-Position nachfuehren + if ( (pBlock == pCurBlock) && (nIndex <= nCurIndex) ) + nCurIndex++; + } + } + + // Ein neues Item im Container + nCount++; +} + +/************************************************************************* +|* +|* Container::Insert() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void Container::Insert( void* p ) +{ + ImpInsert( p, pCurBlock, nCurIndex ); +} + +/************************************************************************* +|* +|* Container::Insert() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void Container::Insert( void* p, ULONG nIndex ) +{ + if ( nCount <= nIndex ) + { + if ( pLastBlock ) + ImpInsert( p, pLastBlock, pLastBlock->Count() ); + else + ImpInsert( p, NULL, 0 ); + } + else + { + // Block suchen + CBlock* pTemp = pFirstBlock; + while ( pTemp->Count() < nIndex ) + { + nIndex -= pTemp->Count(); + pTemp = pTemp->GetNextBlock(); + } + + ImpInsert( p, pTemp, (USHORT)nIndex ); + } +} + +/************************************************************************* +|* +|* Container::Insert() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void Container::Insert( void* pNew, void* pOld ) +{ + ULONG nIndex = GetPos( pOld ); + if ( nIndex != CONTAINER_ENTRY_NOTFOUND ) + Insert( pNew, nIndex ); +} + +/************************************************************************* +|* +|* Container::ImpRemove() +|* +|* Beschreibung Interne Methode zum Entfernen eines Pointers +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::ImpRemove( CBlock* pBlock, USHORT nIndex ) +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + void* pOld; + + // Ist Liste danach leer + if ( nCount == 1 ) + { + // Block und CurIndex zuruecksetzen + pOld = pBlock->GetObject( nIndex ); + pBlock->Reset(); + nCurIndex = 0; + } + else + { + // Ist Block nach Remove leer + if ( pBlock->Count() == 1 ) + { + // dann Block entfernen und Block-Pointer umsetzen + if ( pBlock->GetPrevBlock() ) + (pBlock->GetPrevBlock())->SetNextBlock( pBlock->GetNextBlock() ); + else + pFirstBlock = pBlock->GetNextBlock(); + + if ( pBlock->GetNextBlock() ) + (pBlock->GetNextBlock())->SetPrevBlock( pBlock->GetPrevBlock() ); + else + pLastBlock = pBlock->GetPrevBlock(); + + // Current-Position nachfuehren + if ( pBlock == pCurBlock ) + { + if ( pBlock->GetNextBlock() ) + { + pCurBlock = pBlock->GetNextBlock(); + nCurIndex = 0; + } + else + { + pCurBlock = pBlock->GetPrevBlock(); + nCurIndex = pCurBlock->Count()-1; + } + } + + pOld = pBlock->GetObject( nIndex ); + delete pBlock; + } + else + { + // Sonst Item aus dem Block entfernen + pOld = pBlock->Remove( nIndex, nReSize ); + + // Current-Position nachfuehren + if ( (pBlock == pCurBlock) && + ((nIndex < nCurIndex) || ((nCurIndex == pBlock->Count()) && nCurIndex)) ) + nCurIndex--; + } + } + + // Jetzt gibt es ein Item weniger + nCount--; + + // Und den Pointer zurueckgeben, der entfernt wurde + return pOld; +} + +/************************************************************************* +|* +|* Container::Remove() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::Remove() +{ + // Wenn kein Item vorhanden ist, NULL zurueckgeben + if ( !nCount ) + return NULL; + else + return ImpRemove( pCurBlock, nCurIndex ); +} + +/************************************************************************* +|* +|* Container::Remove() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::Remove( ULONG nIndex ) +{ + // Ist Index nicht innerhalb des Containers, dann NULL zurueckgeben + if ( nCount <= nIndex ) + return NULL; + else + { + // Block suchen + CBlock* pTemp = pFirstBlock; + while ( pTemp->Count() <= nIndex ) + { + nIndex -= pTemp->Count(); + pTemp = pTemp->GetNextBlock(); + } + + return ImpRemove( pTemp, (USHORT)nIndex ); + } +} + +/************************************************************************* +|* +|* Container::Replace() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::Replace( void* p ) +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + if ( !nCount ) + return NULL; + else + return pCurBlock->Replace( p, nCurIndex ); +} + +/************************************************************************* +|* +|* Container::Replace() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::Replace( void* p, ULONG nIndex ) +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // Ist Index nicht innerhalb des Containers, dann NULL zurueckgeben + if ( nCount <= nIndex ) + return NULL; + else + { + // Block suchen + CBlock* pTemp = pFirstBlock; + while ( pTemp->Count() <= nIndex ) + { + nIndex -= pTemp->Count(); + pTemp = pTemp->GetNextBlock(); + } + + return pTemp->Replace( p, (USHORT)nIndex ); + } +} + +/************************************************************************* +|* +|* Container::SetSize() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void Container::SetSize( ULONG nNewSize ) +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + if ( nNewSize ) + { + // Unterscheiden sich die Groessen + if ( nNewSize != nCount ) + { + CBlock* pTemp; + ULONG nTemp; + + // Wird verkleinert + if ( nNewSize < nCount ) + { + pTemp = pFirstBlock; + nTemp = 0; + while ( (nTemp+pTemp->Count()) < nNewSize ) + { + nTemp += pTemp->Count(); + pTemp = pTemp->GetNextBlock(); + } + + // Alle folgenden Bloecke loeschen + BOOL bLast = FALSE; + CBlock* pDelNext; + CBlock* pDelBlock = pTemp->GetNextBlock(); + while ( pDelBlock ) + { + // Muss CurrentBlock umgesetzt werden + if ( pDelBlock == pCurBlock ) + bLast = TRUE; + pDelNext = pDelBlock->GetNextBlock(); + delete pDelBlock; + pDelBlock = pDelNext; + } + + // Block in der Groesse anpassen, oder bei Groesse 0 loeschen + if ( nNewSize > nTemp ) + { + pLastBlock = pTemp; + pTemp->SetNextBlock( NULL ); + pTemp->SetSize( (USHORT)(nNewSize-nTemp) ); + } + else + { + pLastBlock = pTemp->GetPrevBlock(); + pLastBlock->SetNextBlock( NULL ); + delete pTemp; + } + + nCount = nNewSize; + if ( bLast ) + { + pCurBlock = pLastBlock; + nCurIndex = pCurBlock->Count()-1; + } + } + else + { + // Auf den letzen Puffer setzen + pTemp = pLastBlock; + nTemp = nNewSize - nCount; + + if ( !pTemp ) + { + // Muss mehr als ein Block angelegt werden + if ( nNewSize <= nBlockSize ) + { + pFirstBlock = new CBlock( (USHORT)nNewSize, NULL ); + pLastBlock = pFirstBlock; + } + else + { + CBlock* pBlock1; + CBlock* pBlock2; + + pFirstBlock = new CBlock( nBlockSize, NULL ); + pBlock1 = pFirstBlock; + nNewSize -= nBlockSize; + + // Solange die Blockgroesse ueberschritten wird, neue Bloecke anlegen + while ( nNewSize > nBlockSize ) + { + pBlock2 = new CBlock( nBlockSize, pBlock1 ); + pBlock1->SetNextBlock( pBlock2 ); + pBlock1 = pBlock2; + nNewSize -= nBlockSize; + } + + pLastBlock = new CBlock( (USHORT)nNewSize, pBlock1 ); + pBlock1->SetNextBlock( pLastBlock ); + } + + pCurBlock = pFirstBlock; + } + // Reicht es, den letzen Puffer in der Groesse anzupassen + else if ( (nTemp+pTemp->Count()) <= nBlockSize ) + pTemp->SetSize( (USHORT)(nTemp+pTemp->Count()) ); + else + { + // Puffer auf max. Blockgroesse setzen + nTemp -= nBlockSize - pTemp->GetSize(); + pTemp->SetSize( nBlockSize ); + + CBlock* pTemp2; + // Solange die Blockgroesse ueberschritten wird, + // neue Bloecke anlegen + while ( nTemp > nBlockSize ) + { + pTemp2 = new CBlock( nBlockSize, pTemp ); + pTemp->SetNextBlock( pTemp2 ); + pTemp = pTemp2; + nTemp -= nBlockSize; + } + + // Den letzten Block anlegen + if ( nTemp ) + { + pLastBlock = new CBlock( (USHORT)nTemp, pTemp ); + pTemp->SetNextBlock( pLastBlock ); + } + else + pLastBlock = pTemp; + } + + nCount = nNewSize; + } + } + } + else + Clear(); +} + +/************************************************************************* +|* +|* Container::Clear() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void Container::Clear() +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // Erst alle Bloecke loeschen + CBlock* pBlock = pFirstBlock; + while ( pBlock ) + { + CBlock* pTemp = pBlock->GetNextBlock(); + delete pBlock; + pBlock = pTemp; + } + + // Werte zuruecksetzen + pFirstBlock = NULL; + pLastBlock = NULL; + pCurBlock = NULL; + nCount = 0; + nCurIndex = 0; +} + +/************************************************************************* +|* +|* Container::GetCurObject() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::GetCurObject() const +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // NULL, wenn Container leer + if ( !nCount ) + return NULL; + else + return pCurBlock->GetObject( nCurIndex ); +} + +/************************************************************************* +|* +|* Container::GetCurPos() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +ULONG Container::GetCurPos() const +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // CONTAINER_ENTRY_NOTFOUND, wenn Container leer + if ( !nCount ) + return CONTAINER_ENTRY_NOTFOUND; + else + { + // Block suchen + CBlock* pTemp = pFirstBlock; + ULONG nTemp = 0; + while ( pTemp != pCurBlock ) + { + nTemp += pTemp->Count(); + pTemp = pTemp->GetNextBlock(); + } + + return nTemp+nCurIndex; + } +} + +/************************************************************************* +|* +|* Container::GetObjectPtr() +|* +|* Beschreibung Interne Methode fuer Referenz-Container +|* Ersterstellung TH 26.01.93 +|* Letzte Aenderung TH 26.01.93 +|* +*************************************************************************/ + +void** Container::GetObjectPtr( ULONG nIndex ) +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // Ist Index nicht innerhalb des Containers, dann NULL zurueckgeben + if ( nCount <= nIndex ) + return NULL; + else + { + // Block suchen + CBlock* pTemp = pFirstBlock; + while ( pTemp->Count() <= nIndex ) + { + nIndex -= pTemp->Count(); + pTemp = pTemp->GetNextBlock(); + } + + // Item innerhalb des gefundenen Blocks zurueckgeben + return pTemp->GetObjectPtr( (USHORT)nIndex ); + } +} + +/************************************************************************* +|* +|* Container::GetObject() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::GetObject( ULONG nIndex ) const +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // Ist Index nicht innerhalb des Containers, dann NULL zurueckgeben + if ( nCount <= nIndex ) + return NULL; + else + { + // Block suchen + CBlock* pTemp = pFirstBlock; + while ( pTemp->Count() <= nIndex ) + { + nIndex -= pTemp->Count(); + pTemp = pTemp->GetNextBlock(); + } + + // Item innerhalb des gefundenen Blocks zurueckgeben + return pTemp->GetObject( (USHORT)nIndex ); + } +} + +/************************************************************************* +|* +|* Container::GetPos() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +ULONG Container::GetPos( const void* p ) const +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + void** pNodes; + CBlock* pTemp; + ULONG nTemp; + USHORT nBlockCount; + USHORT i; + + // Block suchen + pTemp = pFirstBlock; + nTemp = 0; + while ( pTemp ) + { + pNodes = pTemp->GetNodes(); + i = 0; + nBlockCount = pTemp->Count(); + while ( i < nBlockCount ) + { + if ( p == *pNodes ) + return nTemp+i; + pNodes++; + i++; + } + nTemp += nBlockCount; + pTemp = pTemp->GetNextBlock(); + } + + return CONTAINER_ENTRY_NOTFOUND; +} + +/************************************************************************* +|* +|* Container::GetPos() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 14.09.94 +|* Letzte Aenderung TH 14.09.94 +|* +*************************************************************************/ + +ULONG Container::GetPos( const void* p, ULONG nStartIndex, + BOOL bForward ) const +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // Ist Index nicht innerhalb des Containers, dann NOTFOUND zurueckgeben + if ( nCount <= nStartIndex ) + return CONTAINER_ENTRY_NOTFOUND; + else + { + void** pNodes; + USHORT nBlockCount; + USHORT i; + + // Block suchen + CBlock* pTemp = pFirstBlock; + ULONG nTemp = 0; + while ( nTemp+pTemp->Count() <= nStartIndex ) + { + nTemp += pTemp->Count(); + pTemp = pTemp->GetNextBlock(); + } + + // Jetzt den Pointer suchen + if ( bForward ) + { + // Alle Bloecke durchrsuchen + i = (USHORT)(nStartIndex - nTemp); + pNodes = pTemp->GetObjectPtr( i ); + do + { + nBlockCount = pTemp->Count(); + while ( i < nBlockCount ) + { + if ( p == *pNodes ) + return nTemp+i; + pNodes++; + i++; + } + nTemp += nBlockCount; + pTemp = pTemp->GetNextBlock(); + if ( pTemp ) + { + i = 0; + pNodes = pTemp->GetNodes(); + } + else + break; + } + while ( TRUE ); + } + else + { + // Alle Bloecke durchrsuchen + i = (USHORT)(nStartIndex-nTemp)+1; + pNodes = pTemp->GetObjectPtr( i-1 ); + do + { + do + { + if ( p == *pNodes ) + return nTemp+i-1; + pNodes--; + i--; + } + while ( i ); + nTemp -= pTemp->Count(); + pTemp = pTemp->GetPrevBlock(); + if ( pTemp ) + { + i = pTemp->Count(); + // Leere Bloecke in der Kette darf es nicht geben. Nur + // wenn ein Block existiert, darf dieser leer sein + pNodes = pTemp->GetObjectPtr( i-1 ); + } + else + break; + } + while ( TRUE ); + } + } + + return CONTAINER_ENTRY_NOTFOUND; +} + +/************************************************************************* +|* +|* Container::Seek() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::Seek( ULONG nIndex ) +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // Ist der Container leer, dann NULL zurueckgeben + if ( nCount <= nIndex ) + return NULL; + else + { + // Block suchen + CBlock* pTemp = pFirstBlock; + while ( pTemp->Count() <= nIndex ) + { + nIndex -= pTemp->Count(); + pTemp = pTemp->GetNextBlock(); + } + + // Item innerhalb des gefundenen Blocks zurueckgeben + pCurBlock = pTemp; + nCurIndex = (USHORT)nIndex; + return pCurBlock->GetObject( nCurIndex ); + } +} + +/************************************************************************* +|* +|* Container::First() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::First() +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // Ist Container leer, dann NULL zurueckgeben + if ( !nCount ) + return NULL; + else + { + // Block und Index setzen und ersten Pointer zurueckgeben + pCurBlock = pFirstBlock; + nCurIndex = 0; + return pCurBlock->GetObject( nCurIndex ); + } +} + +/************************************************************************* +|* +|* Container::Last() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::Last() +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // Ist Container leer, dann NULL zurueckgeben + if ( !nCount ) + return NULL; + else + { + // Block und Index setzen und ersten Pointer zurueckgeben + pCurBlock = pLastBlock; + nCurIndex = pCurBlock->Count()-1; + return pCurBlock->GetObject( nCurIndex ); + } +} + +/************************************************************************* +|* +|* Container::Next() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::Next() +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // Ist Container leer, dann NULL zurueckgeben, ansonsten preufen ob + // naechste Position noch im aktuellen Block ist. Falls nicht, dann + // einen Block weiterschalten (geht ohne Gefahr, da leere Bloecke + // nicht vorkommen duerfen, es sein denn, es ist der einzige). + if ( !nCount ) + return NULL; + else if ( (nCurIndex+1) < pCurBlock->Count() ) + return pCurBlock->GetObject( ++nCurIndex ); + else if ( pCurBlock->GetNextBlock() ) + { + pCurBlock = pCurBlock->GetNextBlock(); + nCurIndex = 0; + return pCurBlock->GetObject( nCurIndex ); + } + else + return NULL; +} + +/************************************************************************* +|* +|* Container::Prev() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +void* Container::Prev() +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // Ist Container leer, dann NULL zurueckgeben, ansonsten preufen ob + // vorherige Position noch im aktuellen Block ist. Falls nicht, dann + // einen Block zurueckschalten (geht ohne Gefahr, da leere Bloecke + // nicht vorkommen duerfen, es sein denn, es ist der einzige). + if ( !nCount ) + return NULL; + else if ( nCurIndex ) + return pCurBlock->GetObject( --nCurIndex ); + else if ( pCurBlock->GetPrevBlock() ) + { + pCurBlock = pCurBlock->GetPrevBlock(); + nCurIndex = pCurBlock->Count() - 1; + return pCurBlock->GetObject( nCurIndex ); + } + else + return NULL; +} + +/************************************************************************* +|* +|* Container::operator =() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +Container& Container::operator =( const Container& r ) +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + // Erst alle Bloecke loeschen + CBlock* pBlock = pFirstBlock; + while ( pBlock ) + { + CBlock* pTemp = pBlock->GetNextBlock(); + delete pBlock; + pBlock = pTemp; + } + + // Daten kopieren + ImpCopyContainer( &r ); + return *this; +} + +/************************************************************************* +|* +|* Container::operator ==() +|* +|* Beschreibung CONTNR.SDW +|* Ersterstellung TH 17.09.91 +|* Letzte Aenderung TH 17.09.91 +|* +*************************************************************************/ + +BOOL Container::operator ==( const Container& r ) const +{ + DBG_CHKTHIS( Container, DbgCheckContainer ); + + if ( nCount != r.nCount ) + return FALSE; + + ULONG i = 0; + while ( i < nCount ) + { + if ( GetObject( i ) != r.GetObject( i ) ) + return FALSE; + i++; + } + + return TRUE; +} diff --git a/tools/source/memtools/makefile.mk b/tools/source/memtools/makefile.mk new file mode 100644 index 000000000000..037dadbf4a46 --- /dev/null +++ b/tools/source/memtools/makefile.mk @@ -0,0 +1,58 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.6 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=mtools + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES= $(SLO)$/contnr.obj \ + $(SLO)$/table.obj \ + $(SLO)$/unqidx.obj \ + $(SLO)$/mempool.obj \ + $(SLO)$/multisel.obj + +OBJFILES= $(OBJ)$/contnr.obj \ + $(OBJ)$/table.obj \ + $(OBJ)$/unqidx.obj \ + $(OBJ)$/mempool.obj \ + $(OBJ)$/multisel.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/tools/source/memtools/mempool.cxx b/tools/source/memtools/mempool.cxx new file mode 100644 index 000000000000..d2c37e82268d --- /dev/null +++ b/tools/source/memtools/mempool.cxx @@ -0,0 +1,86 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: mempool.cxx,v $ + * $Revision: 1.9 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <tools/mempool.hxx> +#include "rtl/alloc.h" + +#ifndef INCLUDED_STDIO_H +#include <stdio.h> +#endif + +/************************************************************************* +|* +|* FixedMemPool::FixedMemPool() +|* +*************************************************************************/ + +FixedMemPool::FixedMemPool ( + USHORT _nTypeSize, USHORT, USHORT) +{ + char name[RTL_CACHE_NAME_LENGTH + 1]; + snprintf (name, sizeof(name), "FixedMemPool_%d", (int)_nTypeSize); + m_pImpl = (FixedMemPool_Impl*)rtl_cache_create (name, _nTypeSize, 0, NULL, NULL, NULL, 0, NULL, 0); +} + +/************************************************************************* +|* +|* FixedMemPool::~FixedMemPool() +|* +*************************************************************************/ + +FixedMemPool::~FixedMemPool() +{ + rtl_cache_destroy ((rtl_cache_type*)(m_pImpl)); +} + +/************************************************************************* +|* +|* FixedMemPool::Alloc() +|* +*************************************************************************/ + +void* FixedMemPool::Alloc() +{ + return rtl_cache_alloc ((rtl_cache_type*)(m_pImpl)); +} + +/************************************************************************* +|* +|* FixedMemPool::Free() +|* +*************************************************************************/ + +void FixedMemPool::Free( void* pFree ) +{ + rtl_cache_free ((rtl_cache_type*)(m_pImpl), pFree); +} diff --git a/tools/source/memtools/multisel.cxx b/tools/source/memtools/multisel.cxx new file mode 100644 index 000000000000..6b32badc283e --- /dev/null +++ b/tools/source/memtools/multisel.cxx @@ -0,0 +1,867 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: multisel.cxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _SV_MULTISEL_CXX + +#ifdef MI_DEBUG +#define private public +#include <stdio.h> +#endif + +#include <tools/debug.hxx> +#include <tools/multisel.hxx> + +#ifdef MI_DEBUG +#define DBG(x) x +#else +#define DBG(x) +#endif + +//================================================================== + +#ifdef MI_DEBUG + +static void Print( const MultiSelection* pSel ) +{ + DbgOutf( "TotRange: %4ld-%4ld\n", + pSel->aTotRange.Min(), pSel->aTotRange.Max() ); + if ( pSel->bCurValid ) + { + DbgOutf( "CurSubSel: %4ld\n", pSel->nCurSubSel ); + DbgOutf( "CurIndex: %4ld\n", pSel->nCurIndex ); + } + DbgOutf( "SelCount: %4ld\n", pSel->nSelCount ); + DbgOutf( "SubCount: %4ld\n", pSel->aSels.Count() ); + for ( ULONG nPos = 0; nPos < pSel->aSels.Count(); ++nPos ) + { + DbgOutf( "SubSel #%2ld: %4ld-%4ld\n", nPos, + pSel->aSels.GetObject(nPos)->Min(), + pSel->aSels.GetObject(nPos)->Max() ); + } + DbgOutf( "\n" ); + fclose( pFile ); +} + +#endif + +// ----------------------------------------------------------------------- + +void MultiSelection::ImplClear() +{ + // no selected indexes + nSelCount = 0; + + Range* pRange = aSels.First(); + while ( pRange ) + { + delete pRange; + pRange = aSels.Next(); + } + aSels.Clear(); +} + +// ----------------------------------------------------------------------- + +ULONG MultiSelection::ImplFindSubSelection( long nIndex ) const +{ + // iterate through the sub selections + ULONG n = 0; + for ( ; + n < aSels.Count() && nIndex > aSels.GetObject(n)->Max(); + ++n ) {} /* empty loop */ + return n; +} + +// ----------------------------------------------------------------------- + +BOOL MultiSelection::ImplMergeSubSelections( ULONG nPos1, ULONG nPos2 ) +{ + // didn't a sub selection at nPos2 exist? + if ( nPos2 >= aSels.Count() ) + return FALSE; + + // did the sub selections touch each other? + if ( (aSels.GetObject(nPos1)->Max() + 1) == aSels.GetObject(nPos2)->Min() ) + { + // merge them + aSels.GetObject(nPos1)->Max() = aSels.GetObject(nPos2)->Max(); + delete aSels.Remove(nPos2); + return TRUE; + } + + return FALSE; +} + +// ----------------------------------------------------------------------- + +MultiSelection::MultiSelection(): + aTotRange( 0, -1 ), + nCurSubSel(0), + nSelCount(0), + bCurValid(FALSE), + bSelectNew(FALSE) +{ +} + +// ----------------------------------------------------------------------- + +MultiSelection::MultiSelection( const UniString& rString, sal_Unicode cRange, sal_Unicode cSep ): + aTotRange(0,RANGE_MAX), + nCurSubSel(0), + nSelCount(0), + bCurValid(FALSE), + bSelectNew(FALSE) +{ + // Dies ist nur ein Schnellschuss und sollte bald optimiert, + // an die verschiedenen Systeme (UNIX etc.) + // und die gewuenschte Eingabe-Syntax angepasst werden. + + UniString aStr( rString ); + sal_Unicode* pStr = aStr.GetBufferAccess(); + sal_Unicode* pOld = pStr; + BOOL bReady = FALSE; + BOOL bUntil = FALSE; + xub_StrLen nCut = 0; + + // Hier normieren wir den String, sodass nur Ziffern, + // Semikola als Trenn- und Minus als VonBis-Zeichen + // uebrigbleiben, z.B. "99-117;55;34;-17;37-43" + while ( *pOld ) + { + switch( *pOld ) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + DBG_ASSERT( *pOld != cRange, "digit for range char not allowed" ); + DBG_ASSERT( *pOld != cSep, "digit for separator not allowed" ); + if( bReady ) + { + *pStr++ = ';'; + nCut++; + bReady = FALSE; + } + *pStr++ = *pOld; + nCut++; + bUntil = FALSE; + break; + + case '-': + case ':': + case '/': + if ( *pOld != cSep ) + { + if ( !bUntil ) + { + *pStr++ = '-'; + nCut++; + bUntil = TRUE; + } + bReady = FALSE; + } + else + bReady = TRUE; + break; + + case ' ': + DBG_ASSERT( *pOld != cRange, "SPACE for range char not allowed" ); + DBG_ASSERT( *pOld != cSep, "SPACE for separator not allowed" ); + bReady = !bUntil; + break; + + default: + if ( *pOld == cRange ) + { + if ( !bUntil ) + { + *pStr++ = '-'; + nCut++; + bUntil = TRUE; + } + bReady = FALSE; + } + else + bReady = TRUE; + break; + } + + pOld++; + } + aStr.ReleaseBufferAccess( nCut ); + + // Jetzt wird der normierte String ausgewertet .. + UniString aNumStr; + Range aRg( 1, RANGE_MAX ); + const sal_Unicode* pCStr = aStr.GetBuffer(); + long nPage = 1; + long nNum = 1; + bUntil = FALSE; + while ( *pCStr ) + { + switch ( *pCStr ) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + aNumStr += *pCStr; + break; + case ';': + nNum = aNumStr.ToInt32(); + if ( bUntil ) + { + if ( !aNumStr.Len() ) + nNum = RANGE_MAX; + aRg.Min() = nPage; + aRg.Max() = nNum; + aRg.Justify(); + Select( aRg ); + } + else + Select( nNum ); + nPage = 0; + aNumStr.Erase(); + bUntil = FALSE; + break; + + case '-': + nPage = aNumStr.ToInt32(); + aNumStr.Erase(); + bUntil = TRUE; + break; + } + + pCStr++; + } + + nNum = aNumStr.ToInt32(); + if ( bUntil ) + { + if ( !aNumStr.Len() ) + nNum = RANGE_MAX; + aRg.Min() = nPage; + aRg.Max() = nNum; + aRg.Justify(); + Select( aRg ); + } + else + Select( nNum ); +} + +// ----------------------------------------------------------------------- + +MultiSelection::MultiSelection( const MultiSelection& rOrig ) : + aTotRange(rOrig.aTotRange), + nSelCount(rOrig.nSelCount), + bCurValid(rOrig.bCurValid), + bSelectNew(FALSE) +{ + if ( bCurValid ) + { + nCurSubSel = rOrig.nCurSubSel; + nCurIndex = rOrig.nCurIndex; + } + + // copy the sub selections + for ( ULONG n = 0; n < rOrig.aSels.Count(); ++n ) + aSels.Insert( new Range( *rOrig.aSels.GetObject(n) ), LIST_APPEND ); +} + +// ----------------------------------------------------------------------- + +MultiSelection::MultiSelection( const Range& rRange ): + aTotRange(rRange), + nCurSubSel(0), + nSelCount(0), + bCurValid(FALSE), + bSelectNew(FALSE) +{ +} + +// ----------------------------------------------------------------------- + +MultiSelection::~MultiSelection() +{ + Range* pRange = aSels.First(); + while ( pRange ) + { + delete pRange; + pRange = aSels.Next(); + } +} + +// ----------------------------------------------------------------------- + +MultiSelection& MultiSelection::operator= ( const MultiSelection& rOrig ) +{ + aTotRange = rOrig.aTotRange; + bCurValid = rOrig.bCurValid; + if ( bCurValid ) + { + nCurSubSel = rOrig.nCurSubSel; + nCurIndex = rOrig.nCurIndex; + } + + // clear the old and copy the sub selections + ImplClear(); + for ( ULONG n = 0; n < rOrig.aSels.Count(); ++n ) + aSels.Insert( new Range( *rOrig.aSels.GetObject(n) ), LIST_APPEND ); + nSelCount = rOrig.nSelCount; + + return *this; +} + +// ----------------------------------------------------------------------- + +BOOL MultiSelection::operator== ( MultiSelection& rWith ) +{ + if ( aTotRange != rWith.aTotRange || nSelCount != rWith.nSelCount || + aSels.Count() != rWith.aSels.Count() ) + return FALSE; + + // compare the sub seletions + for ( ULONG n = 0; n < aSels.Count(); ++n ) + if ( *aSels.GetObject(n) != *rWith.aSels.GetObject(n) ) + return FALSE; + return TRUE; +} + +// ----------------------------------------------------------------------- + +void MultiSelection::SelectAll( BOOL bSelect ) +{ + DBG(DbgOutf( "::SelectAll(%s)\n", bSelect ? "TRUE" : "FALSE" )); + + ImplClear(); + if ( bSelect ) + { + aSels.Insert( new Range(aTotRange), LIST_APPEND ); + nSelCount = aTotRange.Len(); + } + + DBG(Print( this )); +} + +// ----------------------------------------------------------------------- + +BOOL MultiSelection::Select( long nIndex, BOOL bSelect ) +{ + DBG_ASSERT( aTotRange.IsInside(nIndex), "selected index out of range" ); + + // out of range? + if ( !aTotRange.IsInside(nIndex) ) + return FALSE; + + // find the virtual target position + ULONG nSubSelPos = ImplFindSubSelection( nIndex ); + + if ( bSelect ) + { + // is it included in the found sub selection? + if ( nSubSelPos < aSels.Count() && + aSels.GetObject(nSubSelPos)->IsInside( nIndex ) ) + // already selected, nothing to do + return FALSE; + + // it will become selected + ++nSelCount; + + // is it at the end of the previous sub selection + if ( nSubSelPos > 0 && + aSels.GetObject(nSubSelPos-1)->Max() == (nIndex-1) ) + { + // expand the previous sub selection + aSels.GetObject(nSubSelPos-1)->Max() = nIndex; + + // try to merge the previous sub selection + ImplMergeSubSelections( nSubSelPos-1, nSubSelPos ); + } + // is is at the beginning of the found sub selection + else if ( nSubSelPos < aSels.Count() && + aSels.GetObject(nSubSelPos)->Min() == (nIndex+1) ) + // expand the found sub selection + aSels.GetObject(nSubSelPos)->Min() = nIndex; + else + { + // create a new sub selection + aSels.Insert( new Range( nIndex, nIndex ), nSubSelPos ); + if ( bCurValid && nCurSubSel >= nSubSelPos ) + ++nCurSubSel; + } + } + else + { + // is it excluded from the found sub selection? + if ( nSubSelPos >= aSels.Count() || + !aSels.GetObject(nSubSelPos)->IsInside( nIndex ) ) + { + // not selected, nothing to do + DBG(Print( this )); + return FALSE; + } + + // it will become deselected + --nSelCount; + + // is it the only index in the found sub selection? + if ( aSels.GetObject(nSubSelPos)->Len() == 1 ) + { + // remove the complete sub selection + delete aSels.Remove( nSubSelPos ); + DBG(Print( this )); + return TRUE; + } + + // is it at the beginning of the found sub selection? + if ( aSels.GetObject(nSubSelPos)->Min() == nIndex ) + ++aSels.GetObject(nSubSelPos)->Min(); + // is it at the end of the found sub selection? + else if ( aSels.GetObject(nSubSelPos)->Max() == nIndex ) + --aSels.GetObject(nSubSelPos)->Max(); + // it is in the middle of the found sub selection? + else + { + // split the sub selection + aSels.Insert( + new Range( aSels.GetObject(nSubSelPos)->Min(), nIndex-1 ), + nSubSelPos ); + aSels.GetObject(nSubSelPos+1)->Min() = nIndex + 1; + } + } + + DBG(Print( this )); + + return TRUE; +} + +// ----------------------------------------------------------------------- + +void MultiSelection::Select( const Range& rIndexRange, BOOL bSelect ) +{ + Range* pRange; + long nOld; + + ULONG nTmpMin = rIndexRange.Min(); + ULONG nTmpMax = rIndexRange.Max(); + ULONG nCurMin = FirstSelected(); + ULONG nCurMax = LastSelected(); + DBG_ASSERT(aTotRange.IsInside(nTmpMax), "selected index out of range" ); + DBG_ASSERT(aTotRange.IsInside(nTmpMin), "selected index out of range" ); + + // gesamte Selektion ersetzen ? + if( nTmpMin <= nCurMin && nTmpMax >= nCurMax ) + { + ImplClear(); + if ( bSelect ) + { + aSels.Insert( new Range(rIndexRange), LIST_APPEND ); + nSelCount = rIndexRange.Len(); + } + return; + } + // links erweitern ? + if( nTmpMax < nCurMin ) + { + if( bSelect ) + { + // ersten Range erweitern ? + if( nCurMin > (nTmpMax+1) ) + { + pRange = new Range( rIndexRange ); + aSels.Insert( pRange, (ULONG)0 ); + nSelCount += pRange->Len(); + } + else + { + pRange = aSels.First(); + nOld = pRange->Min(); + pRange->Min() = (long)nTmpMin; + nSelCount += ( nOld - nTmpMin ); + } + bCurValid = FALSE; + } + return; + } + // rechts erweitern ? + else if( nTmpMin > nCurMax ) + { + if( bSelect ) + { + // letzten Range erweitern ? + if( nTmpMin > (nCurMax+1) ) + { + pRange = new Range( rIndexRange ); + aSels.Insert( pRange, LIST_APPEND ); + nSelCount += pRange->Len(); + } + else + { + pRange = aSels.Last(); + nOld = pRange->Max(); + pRange->Max() = (long)nTmpMax; + nSelCount += ( nTmpMax - nOld ); + } + bCurValid = FALSE; + } + return; + } + + //HACK(Hier muss noch optimiert werden) + while( nTmpMin <= nTmpMax ) + { + Select( nTmpMin, bSelect ); + nTmpMin++; + } +} + +// ----------------------------------------------------------------------- + +BOOL MultiSelection::IsSelected( long nIndex ) const +{ + // find the virtual target position + ULONG nSubSelPos = ImplFindSubSelection( nIndex ); + + return nSubSelPos < aSels.Count() && + aSels.GetObject(nSubSelPos)->IsInside(nIndex); +} + +// ----------------------------------------------------------------------- + +void MultiSelection::Insert( long nIndex, long nCount ) +{ + DBG(DbgOutf( "::Insert(%ld, %ld)\n", nIndex, nCount )); + + // find the virtual target position + ULONG nSubSelPos = ImplFindSubSelection( nIndex ); + + // did we need to shift the sub selections? + if ( nSubSelPos < aSels.Count() ) + { + // did we insert an unselected into an existing sub selection? + if ( !bSelectNew && aSels.GetObject(nSubSelPos)->Min() != nIndex && + aSels.GetObject(nSubSelPos)->IsInside(nIndex) ) + { + // split the sub selection + aSels.Insert( + new Range( aSels.GetObject(nSubSelPos)->Min(), nIndex-1 ), + nSubSelPos ); + ++nSubSelPos; + aSels.GetObject(nSubSelPos)->Min() = nIndex; + } + + // did we append an selected to an existing sub selection? + else if ( bSelectNew && nSubSelPos > 0 && + aSels.GetObject(nSubSelPos)->Max() == nIndex-1 ) + // expand the previous sub selection + aSels.GetObject(nSubSelPos-1)->Max() += nCount; + + // did we insert an selected into an existing sub selection? + else if ( bSelectNew && aSels.GetObject(nSubSelPos)->Min() == nIndex ) + { + // expand the sub selection + aSels.GetObject(nSubSelPos)->Max() += nCount; + ++nSubSelPos; + } + + // shift the sub selections behind the inserting position + for ( ULONG nPos = nSubSelPos; nPos < aSels.Count(); ++nPos ) + { + aSels.GetObject(nPos)->Min() += nCount; + aSels.GetObject(nPos)->Max() += nCount; + } + } + + bCurValid = FALSE; + aTotRange.Max() += nCount; + if ( bSelectNew ) + nSelCount += nCount; + + DBG(Print( this )); +} + +// ----------------------------------------------------------------------- + +void MultiSelection::Remove( long nIndex ) +{ + DBG(DbgOutf( "::Remove(%ld)\n", nIndex )); + + // find the virtual target position + ULONG nSubSelPos = ImplFindSubSelection( nIndex ); + + // did we remove from an existing sub selection? + if ( nSubSelPos < aSels.Count() && + aSels.GetObject(nSubSelPos)->IsInside(nIndex) ) + { + // does this sub selection only contain the index to be deleted + if ( aSels.GetObject(nSubSelPos)->Len() == 1 ) + // completely remove the sub selection + aSels.Remove(nSubSelPos); + else + // shorten this sub selection + --( aSels.GetObject(nSubSelPos++)->Max() ); + + // adjust the selected counter + --nSelCount; + } + + // shift the sub selections behind the removed index + for ( ULONG nPos = nSubSelPos; nPos < aSels.Count(); ++nPos ) + { + --( aSels.GetObject(nPos)->Min() ); + --( aSels.GetObject(nPos)->Max() ); + } + + bCurValid = FALSE; + aTotRange.Max() -= 1; + + DBG(Print( this )); +} + +// ----------------------------------------------------------------------- + +void MultiSelection::Append( long nCount ) +{ + long nPrevLast = aTotRange.Max(); + aTotRange.Max() += nCount; + if ( bSelectNew ) + { + nSelCount += nCount; + aSels.Insert( new Range( nPrevLast+1, nPrevLast + nCount ), + LIST_APPEND ); + if ( aSels.Count() > 1 ) + ImplMergeSubSelections( aSels.Count() - 2, aSels.Count() ); + } +} + +// ----------------------------------------------------------------------- + +long MultiSelection::ImplFwdUnselected() +{ + if ( !bCurValid ) + return SFX_ENDOFSELECTION; + + if ( ( nCurSubSel < aSels.Count() ) && + ( aSels.GetObject(nCurSubSel)->Min() <= nCurIndex ) ) + nCurIndex = aSels.GetObject(nCurSubSel++)->Max() + 1; + + if ( nCurIndex <= aTotRange.Max() ) + return nCurIndex; + else + return SFX_ENDOFSELECTION; +} + +// ----------------------------------------------------------------------- + +long MultiSelection::ImplBwdUnselected() +{ + if ( !bCurValid ) + return SFX_ENDOFSELECTION; + + if ( aSels.GetObject(nCurSubSel)->Max() < nCurIndex ) + return nCurIndex; + + nCurIndex = aSels.GetObject(nCurSubSel--)->Min() - 1; + if ( nCurIndex >= 0 ) + return nCurIndex; + else + return SFX_ENDOFSELECTION; +} + +// ----------------------------------------------------------------------- + +long MultiSelection::FirstSelected( BOOL bInverse ) +{ + bInverseCur = bInverse; + nCurSubSel = 0; + + if ( bInverseCur ) + { + bCurValid = nSelCount < ULONG(aTotRange.Len()); + if ( bCurValid ) + { + nCurIndex = 0; + return ImplFwdUnselected(); + } + } + else + { + bCurValid = aSels.Count() > 0; + if ( bCurValid ) + return nCurIndex = aSels.GetObject(0)->Min(); + } + + return SFX_ENDOFSELECTION; +} + +// ----------------------------------------------------------------------- + +long MultiSelection::LastSelected() +{ + nCurSubSel = aSels.Count() - 1; + bCurValid = aSels.Count() > 0; + + if ( bCurValid ) + return nCurIndex = aSels.GetObject(nCurSubSel)->Max(); + + return SFX_ENDOFSELECTION; +} + +// ----------------------------------------------------------------------- + +long MultiSelection::NextSelected() +{ + if ( !bCurValid ) + return SFX_ENDOFSELECTION; + + if ( bInverseCur ) + { + ++nCurIndex; + return ImplFwdUnselected(); + } + else + { + // is the next index in the current sub selection too? + if ( nCurIndex < aSels.GetObject(nCurSubSel)->Max() ) + return ++nCurIndex; + + // are there further sub selections? + if ( ++nCurSubSel < aSels.Count() ) + return nCurIndex = aSels.GetObject(nCurSubSel)->Min(); + + // we are at the end! + return SFX_ENDOFSELECTION; + } +} + +// ----------------------------------------------------------------------- + +long MultiSelection::PrevSelected() +{ + if ( !bCurValid ) + return SFX_ENDOFSELECTION; + + if ( bInverseCur ) + { + --nCurIndex; + return ImplBwdUnselected(); + } + else + { + // is the previous index in the current sub selection too? + if ( nCurIndex > aSels.GetObject(nCurSubSel)->Min() ) + return --nCurIndex; + + // are there previous sub selections? + if ( nCurSubSel > 0 ) + { + --nCurSubSel; + return nCurIndex = aSels.GetObject(nCurSubSel)->Max(); + } + + // we are at the beginning! + return SFX_ENDOFSELECTION; + } +} + +// ----------------------------------------------------------------------- + +void MultiSelection::SetTotalRange( const Range& rTotRange ) +{ + aTotRange = rTotRange; + + // die untere Bereichsgrenze anpassen + Range* pRange = aSels.GetObject( 0 ); + while( pRange ) + { + if( pRange->Max() < aTotRange.Min() ) + { + delete pRange; + aSels.Remove( (ULONG)0 ); + } + else if( pRange->Min() < aTotRange.Min() ) + { + pRange->Min() = aTotRange.Min(); + break; + } + else + break; + + pRange = aSels.GetObject( 0 ); + } + + // die obere Bereichsgrenze anpassen + ULONG nCount = aSels.Count(); + while( nCount ) + { + pRange = aSels.GetObject( nCount - 1 ); + if( pRange->Min() > aTotRange.Max() ) + { + delete pRange; + aSels.Remove( (ULONG)(nCount - 1) ); + } + else if( pRange->Max() > aTotRange.Max() ) + { + pRange->Max() = aTotRange.Max(); + break; + } + else + break; + + nCount = aSels.Count(); + } + + // Selection-Count neu berechnen + nSelCount = 0; + pRange = aSels.First(); + while( pRange ) + { + nSelCount += pRange->Len(); + pRange = aSels.Next(); + } + + bCurValid = FALSE; + nCurIndex = 0; +} diff --git a/tools/source/memtools/table.cxx b/tools/source/memtools/table.cxx new file mode 100644 index 000000000000..75aafddb6c58 --- /dev/null +++ b/tools/source/memtools/table.cxx @@ -0,0 +1,416 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: table.cxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _TOOLS_TABLE_CXX + +// ----------------------------------------------------------------------- +#include <tools/debug.hxx> +#include <impcont.hxx> +#include <tools/table.hxx> + +// ======================================================================= + +ULONG Table::ImplGetIndex( ULONG nKey, ULONG* pIndex ) const +{ + // Abpruefen, ob der erste Key groesser als der Vergleichskey ist + if ( !nCount || (nKey < (ULONG)Container::ImpGetObject(0)) ) + return TABLE_ENTRY_NOTFOUND; + + ULONG nLow; + ULONG nHigh; + ULONG nMid; + ULONG nCompareKey; + void** pNodes = Container::ImpGetOnlyNodes(); + + // Binaeres Suchen + nLow = 0; + nHigh = nCount-1; + if ( pNodes ) + { + do + { + nMid = (nLow + nHigh) / 2; + nCompareKey = (ULONG)pNodes[nMid*2]; + if ( nKey < nCompareKey ) + nHigh = nMid-1; + else + { + if ( nKey > nCompareKey ) + nLow = nMid + 1; + else + return nMid*2; + } + } + while ( nLow <= nHigh ); + } + else + { + do + { + nMid = (nLow + nHigh) / 2; + nCompareKey = (ULONG)Container::ImpGetObject( nMid*2 ); + if ( nKey < nCompareKey ) + nHigh = nMid-1; + else + { + if ( nKey > nCompareKey ) + nLow = nMid + 1; + else + return nMid*2; + } + } + while ( nLow <= nHigh ); + } + + if ( pIndex ) + { + if ( nKey > nCompareKey ) + *pIndex = (nMid+1)*2; + else + *pIndex = nMid*2; + } + + return TABLE_ENTRY_NOTFOUND; +} + +// ======================================================================= + +Table::Table( USHORT _nInitSize, USHORT _nReSize ) : + Container( CONTAINER_MAXBLOCKSIZE, _nInitSize*2, _nReSize*2 ) +{ + DBG_ASSERT( _nInitSize <= 32767, "Table::Table(): InitSize > 32767" ); + DBG_ASSERT( _nReSize <= 32767, "Table::Table(): ReSize > 32767" ); + nCount = 0; +} + +// ----------------------------------------------------------------------- + +BOOL Table::Insert( ULONG nKey, void* p ) +{ + // Tabellenelement einsortieren + ULONG i; + if ( nCount ) + { + if ( nCount <= 24 ) + { + USHORT n = 0; + USHORT nTempCount = (USHORT)nCount * 2; + //<!--Modified by PengYunQuan for resolving a NULL pointer access + + if( void** pNodes = Container::ImpGetOnlyNodes() ) + { + ULONG nCompareKey = (ULONG)(*pNodes); + while ( nKey > nCompareKey ) + { + n += 2; + pNodes += 2; + if ( n < nTempCount ) + nCompareKey = (ULONG)(*pNodes); + else + { + nCompareKey = 0; + break; + } + } + + // Testen, ob sich der Key schon in der Tabelle befindet + if ( nKey == nCompareKey ) + return FALSE; + + i = n; + } + else + { + i = 0; + if ( ImplGetIndex( nKey, &i ) != TABLE_ENTRY_NOTFOUND ) + return FALSE; + } + //-->Modified by PengYunQuan for resolving a NULL pointer access + } + else + { + i = 0; + if ( ImplGetIndex( nKey, &i ) != TABLE_ENTRY_NOTFOUND ) + return FALSE; + } + } + else + i = 0; + + // Eintrag einfuegen (Key vor Pointer) + Container::Insert( (void*)nKey, i ); + Container::Insert( p, i+1 ); + + // Ein neuer Eintrag + nCount++; + + return TRUE; +} + +// ----------------------------------------------------------------------- + +void* Table::Remove( ULONG nKey ) +{ + // Index besorgen + ULONG nIndex = ImplGetIndex( nKey ); + + // Testen, ob sich der Key in der Tabelle befindet + if ( nIndex == TABLE_ENTRY_NOTFOUND ) + return NULL; + + // Itemanzahl erniedrigen + nCount--; + + // Key entfernen + Container::Remove( nIndex ); + + // Pointer entfernen und zurueckgeben + return Container::Remove( nIndex ); +} + +// ----------------------------------------------------------------------- + +void* Table::Replace( ULONG nKey, void* p ) +{ + // Index abfragen + ULONG nIndex = ImplGetIndex( nKey ); + + // Existiert kein Eintrag mit dem Schluessel + if ( nIndex == TABLE_ENTRY_NOTFOUND ) + return NULL; + else + return Container::Replace( p, nIndex+1 ); +} + +// ----------------------------------------------------------------------- + +void* Table::Get( ULONG nKey ) const +{ + // Index besorgen + ULONG nIndex = ImplGetIndex( nKey ); + + // Testen, ob sich der Key in der Tabelle befindet + if ( nIndex == TABLE_ENTRY_NOTFOUND ) + return NULL; + else + return Container::ImpGetObject( nIndex+1 ); +} + +// ----------------------------------------------------------------------- + +void* Table::GetCurObject() const +{ + return Container::ImpGetObject( Container::GetCurPos()+1 ); +} + +// ----------------------------------------------------------------------- + +ULONG Table::GetKey( const void* p ) const +{ + ULONG nIndex = 0; + + // Solange noch Eintraege Vorhanden sind + while ( nIndex < nCount ) + { + // Stimmt der Pointer ueberein, wird der Key zurueckgegeben + if ( p == Container::ImpGetObject( (nIndex*2)+1 ) ) + return (ULONG)Container::ImpGetObject( nIndex*2 ); + + nIndex++; + } + + return TABLE_ENTRY_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +BOOL Table::IsKeyValid( ULONG nKey ) const +{ + return (ImplGetIndex( nKey ) != TABLE_ENTRY_NOTFOUND) ? TRUE : FALSE; +} + +// ----------------------------------------------------------------------- + +ULONG Table::GetUniqueKey( ULONG nStartKey ) const +{ + DBG_ASSERT( (nStartKey > 1) && (nStartKey < 0xFFFFFFFF), + "Table::GetUniqueKey() - nStartKey == 0 or nStartKey >= 0xFFFFFFFF" ); + + if ( !nCount ) + return nStartKey; + + ULONG nLastKey = (ULONG)Container::GetObject( (nCount*2)-2 ); + if ( nLastKey < nStartKey ) + return nStartKey; + else + { + if ( nLastKey < 0xFFFFFFFE ) + return nLastKey+1; + else + { + ULONG nPos; + ULONG nTempPos = ImplGetIndex( nStartKey, &nPos ); + if ( nTempPos != TABLE_ENTRY_NOTFOUND ) + nPos = nTempPos; + nLastKey = (ULONG)Container::GetObject( nPos ); + if ( nStartKey < nLastKey ) + return nStartKey; + while ( nLastKey < 0xFFFFFFFE ) + { + nPos += 2; + nLastKey++; + if ( nLastKey != (ULONG)Container::GetObject( nPos ) ) + return nLastKey; + } + } + } + + return 0; +} + +// ----------------------------------------------------------------------- + +ULONG Table::SearchKey( ULONG nKey, ULONG* pPos ) const +{ + *pPos = 0; + ULONG nPos = ImplGetIndex( nKey, pPos ); + if ( nPos != TABLE_ENTRY_NOTFOUND ) + { + nPos /= 2; + *pPos = nPos; + } + else + *pPos /= 2; + return nPos; +} + +// ----------------------------------------------------------------------- + +void* Table::Seek( ULONG nKey ) +{ + // Testen, ob ein Eintrag vorhanden ist + if ( nCount ) + { + ULONG nIndex = ImplGetIndex( nKey ); + + // Ist Key nicht enthalten + if ( nIndex == TABLE_ENTRY_NOTFOUND ) + return NULL; + else + { + // Index setzen + Container::Seek( nIndex ); + + // Pointer zurueckgeben + return Container::ImpGetObject( Container::GetCurPos() + 1 ); + } + } + else + return NULL; +} + +// ----------------------------------------------------------------------- + +void* Table::Seek( void* p ) +{ + ULONG nKey = GetKey( p ); + + // Ist Key vorhanden, dann als aktuellen Eintrag setzen + if ( nKey != TABLE_ENTRY_NOTFOUND ) + return Seek( nKey ); + else + return NULL; +} + +// ----------------------------------------------------------------------- + +void* Table::First() +{ + // Testen, ob ein Eintrag vorhanden ist + if ( nCount ) + { + // Auf ersten Eintag setzen + Container::First(); + + // Pointer zurueckgeben + return Container::ImpGetObject( 1 ); + } + else + return NULL; +} + +// ----------------------------------------------------------------------- + +void* Table::Last() +{ + // Testen, ob ein Eintrag vorhanden ist + if ( nCount ) + { + // Last auf letzten Eintrag setzen + void* p = Container::Last(); + Container::Prev(); + + // Pointer zurueckgeben + return p; + } + else + return NULL; +} + +// ----------------------------------------------------------------------- + +void* Table::Next() +{ + // Ueber den Pointer weiterschalten + Container::Next(); + + // Nachsten Eintag + Container::Next(); + + // Pointer vom naechsten Key zurueckgeben + return Container::ImpGetObject( Container::GetCurPos() + 1 ); +} + +// ----------------------------------------------------------------------- + +void* Table::Prev() +{ + // Ueber den Pointer weiterschalten + void* p = Container::Prev(); + + // Nachsten Eintag + Container::Prev(); + + // Pointer vom vorherigen Key zurueckgeben + return p; +} diff --git a/tools/source/memtools/unqidx.cxx b/tools/source/memtools/unqidx.cxx new file mode 100644 index 000000000000..4f49e29b66ee --- /dev/null +++ b/tools/source/memtools/unqidx.cxx @@ -0,0 +1,604 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: unqidx.cxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" +#include <impcont.hxx> +#include <tools/unqidx.hxx> +#include <tools/unqid.hxx> + +/************************************************************************* +|* +|* UniqueIndex::UniqueIndex() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +UniqueIndex::UniqueIndex( ULONG _nStartIndex, + ULONG _nInitSize, ULONG _nReSize ) : + Container( _nInitSize ) +{ + nReSize = _nReSize; + nStartIndex = _nStartIndex; + nUniqIndex = 0; + nCount = 0; +} + +/************************************************************************* +|* +|* UniqueIndex::UniqueIndex() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +UniqueIndex::UniqueIndex( const UniqueIndex& rIdx ) : + Container( rIdx ) +{ + nReSize = rIdx.nReSize; + nStartIndex = rIdx.nStartIndex; + nUniqIndex = rIdx.nUniqIndex; + nCount = rIdx.nCount; +} + +/************************************************************************* +|* +|* UniqueIndex::Insert() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +ULONG UniqueIndex::Insert( void* p ) +{ + // NULL-Pointer ist nicht erlaubt + if ( !p ) + return UNIQUEINDEX_ENTRY_NOTFOUND; + + // Ist Array voll, dann expandieren + if ( nCount == Container::GetSize() ) + SetSize( nCount + nReSize ); + + // Damit UniqIndex nicht ueberlaeuft, wenn Items geloescht wurden + nUniqIndex = nUniqIndex % Container::GetSize(); + + // Leeren Eintrag suchen + while ( Container::ImpGetObject( nUniqIndex ) != NULL ) + nUniqIndex = (nUniqIndex+1) % Container::GetSize(); + + // Object im Array speichern + Container::Replace( p, nUniqIndex ); + + // Anzahl der Eintraege erhoehen und Index zurueckgeben + nCount++; + nUniqIndex++; + return ( nUniqIndex + nStartIndex - 1 ); +} + +/************************************************************************* +|* +|* UniqueIndex::Insert() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung MM 21.04.96 +|* Letzte Aenderung MM 21.04.96 +|* +*************************************************************************/ + +ULONG UniqueIndex::Insert( ULONG nIndex, void* p ) +{ + // NULL-Pointer ist nicht erlaubt + if ( !p ) + return UNIQUEINDEX_ENTRY_NOTFOUND; + + ULONG nContIndex = nIndex - nStartIndex; + // Ist Array voll, dann expandieren + if ( nContIndex >= Container::GetSize() ) + SetSize( nContIndex + nReSize ); + + // Object im Array speichern + Container::Replace( p, nContIndex ); + + // Anzahl der Eintraege erhoehen und Index zurueckgeben + nCount++; + return nIndex; +} + +/************************************************************************* +|* +|* UniqueIndex::Remove() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +void* UniqueIndex::Remove( ULONG nIndex ) +{ + // Ist Index zulaessig + if ( (nIndex >= nStartIndex) && + (nIndex < (Container::GetSize()+nStartIndex)) ) + { + // Index-Eintrag als leeren Eintrag setzen und Anzahl der + // gespeicherten Indexe erniedriegen, wenn Eintrag belegt war + void* p = Container::Replace( NULL, nIndex-nStartIndex ); + if ( p ) + nCount--; + return p; + } + else + return NULL; +} + +/************************************************************************* +|* +|* UniqueIndex::Replace() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +void* UniqueIndex::Replace( ULONG nIndex, void* p ) +{ + // NULL-Pointer ist nicht erlaubt + if ( !p ) + return NULL; + + // Ist Index zulaessig + if ( IsIndexValid( nIndex ) ) + { + // Index-Eintrag ersetzen und alten zurueckgeben + return Container::Replace( p, nIndex-nStartIndex ); + } + else + return NULL; +} + +/************************************************************************* +|* +|* UniqueIndex::Get() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +void* UniqueIndex::Get( ULONG nIndex ) const +{ + // Ist Index zulaessig + if ( (nIndex >= nStartIndex) && + (nIndex < (Container::GetSize()+nStartIndex)) ) + return Container::ImpGetObject( nIndex-nStartIndex ); + else + return NULL; +} + +/************************************************************************* +|* +|* UniqueIndex::GetCurIndex() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +ULONG UniqueIndex::GetCurIndex() const +{ + ULONG nPos = Container::GetCurPos(); + + // Ist der Current-Index nicht belegt, dann gibt es keinen Current-Index + if ( !Container::ImpGetObject( nPos ) ) + return UNIQUEINDEX_ENTRY_NOTFOUND; + else + return nPos+nStartIndex; +} + +/************************************************************************* +|* +|* UniqueIndex::GetIndex() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +ULONG UniqueIndex::GetIndex( const void* p ) const +{ + // Wird ein NULL-Pointer uebergeben, dann wurde Pointer nicht gefunden + if ( !p ) + return UNIQUEINDEX_ENTRY_NOTFOUND; + + ULONG nIndex = Container::GetPos( p ); + + if ( nIndex != CONTAINER_ENTRY_NOTFOUND ) + return nIndex+nStartIndex; + else + return UNIQUEINDEX_ENTRY_NOTFOUND; +} + +/************************************************************************* +|* +|* UniqueIndex::IsIndexValid() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +BOOL UniqueIndex::IsIndexValid( ULONG nIndex ) const +{ + // Ist Index zulaessig + if ( (nIndex >= nStartIndex) && + (nIndex < (Container::GetSize()+nStartIndex)) ) + { + // Index ist nur zulaessig, wenn Eintrag auch belegt ist + if ( Container::ImpGetObject( nIndex-nStartIndex ) ) + return TRUE; + else + return FALSE; + } + else + return FALSE; +} + +/************************************************************************* +|* +|* UniqueIndex::Seek() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +void* UniqueIndex::Seek( ULONG nIndex ) +{ + // Index-Eintrag als aktuellen setzten, wenn er gueltig ist + if ( IsIndexValid( nIndex ) ) + return Container::Seek( nIndex-nStartIndex ); + else + return NULL; +} + +/************************************************************************* +|* +|* UniqueIndex::Seek() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +void* UniqueIndex::Seek( void* p ) +{ + // Wird ein NULL-Pointer uebergeben, dann wurde Pointer nicht gefunden + if ( !p ) + return NULL; + + ULONG nIndex = GetIndex( p ); + + // Ist Index vorhanden, dann als aktuellen Eintrag setzen + if ( nIndex != UNIQUEINDEX_ENTRY_NOTFOUND ) + return Container::Seek( nIndex-nStartIndex ); + else + return NULL; +} + +/************************************************************************* +|* +|* UniqueIndex::First() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +void* UniqueIndex::First() +{ + void* p = Container::First(); + + while ( !p && (Container::GetCurPos() < (Container::GetSize()-1)) ) + p = Container::Next(); + + return p; +} + +/************************************************************************* +|* +|* UniqueIndex::Last() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +void* UniqueIndex::Last() +{ + void* p = Container::Last(); + + while ( !p && Container::GetCurPos() ) + p = Container::Prev(); + + return p; +} + +/************************************************************************* +|* +|* UniqueIndex::Next() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +void* UniqueIndex::Next() +{ + void* p = NULL; + + while ( !p && (Container::GetCurPos() < (Container::GetSize()-1)) ) + p = Container::Next(); + + return p; +} + +/************************************************************************* +|* +|* UniqueIndex::Prev() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +void* UniqueIndex::Prev() +{ + void* p = NULL; + + while ( !p && Container::GetCurPos() ) + p = Container::Prev(); + + return p; +} + +/************************************************************************* +|* +|* UniqueIndex::operator =() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +UniqueIndex& UniqueIndex::operator =( const UniqueIndex& rIdx ) +{ + // Neue Werte zuweisen + Container::operator =( rIdx ); + nReSize = rIdx.nReSize; + nStartIndex = rIdx.nStartIndex; + nUniqIndex = rIdx.nUniqIndex; + nCount = rIdx.nCount; + return *this; +} + +/************************************************************************* +|* +|* UniqueIndex::operator ==() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung TH 24.09.91 +|* Letzte Aenderung TH 24.09.91 +|* +*************************************************************************/ + +BOOL UniqueIndex::operator ==( const UniqueIndex& rIdx ) const +{ + // Neue Werte zuweisen + if ( (nStartIndex == rIdx.nStartIndex) && + (nCount == rIdx.nCount) && + (Container::operator ==( rIdx )) ) + return TRUE; + else + return FALSE; +} +/************************************************************************* +|* +|* UniqueIdContainer::UniqueIdContainer () +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung MM 29.04.96 +|* Letzte Aenderung MM 29.04.96 +|* +*************************************************************************/ + +UniqueIdContainer::UniqueIdContainer( const UniqueIdContainer& rObj ) + : UniqueIndex( rObj ) + , nCollectCount( rObj.nCollectCount ) +{ + ULONG nCur = GetCurIndex(); + + ImpUniqueId * pEle = (ImpUniqueId *)First(); + while( pEle ) + { + pEle->nRefCount++; + pEle = (ImpUniqueId *)Next(); + } + Seek( nCur ); +} + +/************************************************************************* +|* +|* UniqueIdContainer::operator = () +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung MM 01.08.94 +|* Letzte Aenderung MM 01.08.94 +|* +*************************************************************************/ + +UniqueIdContainer& UniqueIdContainer::operator = ( const UniqueIdContainer & rObj ) +{ + UniqueIndex::operator = ( rObj ); + nCollectCount = rObj.nCollectCount; + + ULONG nCur = GetCurIndex(); + + ImpUniqueId * pEle = (ImpUniqueId *)First(); + while( pEle ) + { + pEle->nRefCount++; + pEle = (ImpUniqueId *)Next(); + } + Seek( nCur ); + return *this; +} + +/************************************************************************* +|* +|* UniqueIdContainer::Clear() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung MM 01.08.94 +|* Letzte Aenderung MM 01.08.94 +|* +*************************************************************************/ + +void UniqueIdContainer::Clear( BOOL bAll ) +{ + USHORT nFree = bAll ? 0xFFFF : 1; + + ImpUniqueId* pId = (ImpUniqueId*)Last(); + BOOL bLast = TRUE; + while ( pId ) + { + if ( pId->nRefCount <= nFree ) + { + ((ImpUniqueId *)Remove( pId->nId ))->Release(); + if( bLast ) + pId = (ImpUniqueId *)Last(); + else + pId = (ImpUniqueId *)Prev(); + } + else + { + pId = (ImpUniqueId *)Prev(); + bLast = FALSE; + } + } +} + +/************************************************************************* +|* +|* UniqueIdContainer::CreateId() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung MM 01.08.94 +|* Letzte Aenderung MM 01.08.94 +|* +*************************************************************************/ + +UniqueItemId UniqueIdContainer::CreateId() +{ + if( nCollectCount > 50 ) + { // aufraeumen + Clear( FALSE ); + nCollectCount = 0; + } + nCollectCount++; + + ImpUniqueId * pId = new ImpUniqueId; + pId->nRefCount = 1; + pId->nId = Insert( pId ); + return UniqueItemId( pId ); +} + +/************************************************************************* +|* +|* UniqueIdContainer::CreateIdProt() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung MM 01.08.94 +|* Letzte Aenderung MM 01.08.94 +|* +*************************************************************************/ + +UniqueItemId UniqueIdContainer::CreateFreeId( ULONG nId ) +{ + // Einfach erzeugen, fuer abgeleitete Klasse + ImpUniqueId * pId = new ImpUniqueId; + pId->nRefCount = 0; + pId->nId = nId; + return UniqueItemId( pId ); +} + +/************************************************************************* +|* +|* UniqueIdContainer::CreateIdProt() +|* +|* Beschreibung UNQIDX.SDW +|* Ersterstellung MM 01.08.94 +|* Letzte Aenderung MM 01.08.94 +|* +*************************************************************************/ + +UniqueItemId UniqueIdContainer::CreateIdProt( ULONG nId ) +{ + if ( IsIndexValid( nId ) ) + return UniqueItemId( (ImpUniqueId *)Get( nId ) ); + + ImpUniqueId * pId; + do + { + pId = new ImpUniqueId; + pId->nRefCount = 1; + pId->nId = Insert( pId ); + } + while( pId->nId != nId ); + return UniqueItemId( pId ); +} diff --git a/tools/source/misc/appendunixshellword.cxx b/tools/source/misc/appendunixshellword.cxx new file mode 100644 index 000000000000..eee34d3f16ad --- /dev/null +++ b/tools/source/misc/appendunixshellword.cxx @@ -0,0 +1,79 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: appendunixshellword.cxx,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_tools.hxx" +#include "sal/config.h" + +#if defined UNX + +#include <cstddef> + +#include "osl/diagnose.h" +#include "rtl/strbuf.hxx" +#include "rtl/string.h" +#include "rtl/string.hxx" +#include "sal/types.h" +#include "tools/appendunixshellword.hxx" + +namespace tools { + +void appendUnixShellWord( + rtl::OStringBuffer * accumulator, rtl::OString const & text) +{ + OSL_ASSERT(accumulator != NULL); + if (text.getLength() == 0) { + accumulator->append(RTL_CONSTASCII_STRINGPARAM("''")); + } else { + bool quoted = false; + for (sal_Int32 i = 0; i < text.getLength(); ++i) { + char c = text[i]; + if (c == '\'') { + if (quoted) { + accumulator->append('\''); + quoted = false; + } + accumulator->append(RTL_CONSTASCII_STRINGPARAM("\\'")); + } else { + if (!quoted) { + accumulator->append('\''); + quoted = true; + } + accumulator->append(c); + } + } + if (quoted) { + accumulator->append('\''); + } + } +} + +} + +#endif diff --git a/tools/source/misc/extendapplicationenvironment.cxx b/tools/source/misc/extendapplicationenvironment.cxx new file mode 100644 index 000000000000..c01d7bbd31e5 --- /dev/null +++ b/tools/source/misc/extendapplicationenvironment.cxx @@ -0,0 +1,106 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: extendapplicationenvironment.cxx,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_tools.hxx" +#include "sal/config.h" + +#include <stdlib.h> + // not <cstdlib> as putenv is POSIX-only; setenv instead of putenv would be + // better but is not supported by Solaris 9 and earlier + +#if defined UNX +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/types.h> +#endif + +#include "osl/process.h" +#include "osl/thread.h" +#include "rtl/bootstrap.hxx" +#include "rtl/string.hxx" +#include "rtl/textcvt.h" +#include "rtl/ustrbuf.hxx" +#include "rtl/ustring.h" +#include "rtl/ustring.hxx" +#include "sal/types.h" +#include "tools/extendapplicationenvironment.hxx" + +namespace tools { + +void extendApplicationEnvironment() { +#if defined UNX + // Try to set RLIMIT_NOFILE as large as possible (failure is harmless): + rlimit l; + if (getrlimit(RLIMIT_NOFILE, &l) == 0) { + l.rlim_cur = l.rlim_max; + setrlimit(RLIMIT_NOFILE, &l); + } +#endif + + // Make sure URE_BOOTSTRAP environment variable is set (failure is fatal): + rtl::OUStringBuffer env; + env.appendAscii(RTL_CONSTASCII_STRINGPARAM("URE_BOOTSTRAP=")); + rtl::OUString uri; + if (rtl::Bootstrap::get( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("URE_BOOTSTRAP")), uri)) + { + if (!uri.matchIgnoreAsciiCaseAsciiL( + RTL_CONSTASCII_STRINGPARAM("vnd.sun.star.pathname:"))) + { + uri = rtl::Bootstrap::encode(uri); + } + env.append(uri); + } else { + if (osl_getExecutableFile(&uri.pData) != osl_Process_E_None) { + abort(); + } + sal_Int32 i = uri.lastIndexOf('/'); + if (i >= 0) { + uri = uri.copy(0, i + 1); + } + env.append(rtl::Bootstrap::encode(uri)); + env.appendAscii( + RTL_CONSTASCII_STRINGPARAM(SAL_CONFIGFILE("fundamental"))); + } + rtl::OString s; + if (!env.makeStringAndClear().convertToString( + &s, osl_getThreadTextEncoding(), + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)) + { + abort(); + } + rtl_string_acquire(s.pData); // argument to putenv must leak + if (putenv(const_cast< char * >(s.getStr())) != 0) { + abort(); + } +} + +} diff --git a/tools/source/misc/getprocessworkingdir.cxx b/tools/source/misc/getprocessworkingdir.cxx new file mode 100644 index 000000000000..c86f3d0375ca --- /dev/null +++ b/tools/source/misc/getprocessworkingdir.cxx @@ -0,0 +1,67 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: getprocessworkingdir.cxx,v $ + * $Revision: 1.2 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_tools.hxx" +#include "sal/config.h" + +#include <cstddef> + +#include "osl/diagnose.h" +#include "osl/file.hxx" +#include "osl/process.h" +#include "rtl/bootstrap.hxx" +#include "rtl/ustring.h" +#include "rtl/ustring.hxx" +#include "tools/getprocessworkingdir.hxx" + +namespace tools { + +bool getProcessWorkingDir(rtl::OUString * url) { + OSL_ASSERT(url != NULL); + rtl::OUString s(RTL_CONSTASCII_USTRINGPARAM("$OOO_CWD")); + rtl::Bootstrap::expandMacros(s); + if (s.getLength() == 0) { + if (osl_getProcessWorkingDir(&url->pData) == osl_Process_E_None) { + return true; + } + } else if (s[0] == '1') { + *url = s.copy(1); + return true; + } else if (s[0] == '2' && + (osl::FileBase::getFileURLFromSystemPath(s.copy(1), *url) == + osl::FileBase::E_None)) + { + return true; + } + *url = rtl::OUString(); + return false; +} + +} diff --git a/tools/source/misc/makefile.mk b/tools/source/misc/makefile.mk new file mode 100644 index 000000000000..7742c471445f --- /dev/null +++ b/tools/source/misc/makefile.mk @@ -0,0 +1,50 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.5 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ = ..$/.. +PRJNAME = tools +TARGET = misc +LIBTARGET = NO +ENABLE_EXCEPTIONS = TRUE + +.INCLUDE: settings.mk +.INCLUDE: $(PRJ)$/util$/makefile.pmk + +LIB1TARGET = $(SLB)$/$(TARGET).lib +LIB1OBJFILES = \ + $(SLO)$/appendunixshellword.obj \ + $(SLO)$/extendapplicationenvironment.obj \ + $(SLO)$/getprocessworkingdir.obj + +OBJFILES = $(OBJ)$/pathutils.obj +SLOFILES = $(SLO)$/pathutils.obj $(LIB1OBJFILES) + +.INCLUDE: target.mk diff --git a/tools/source/misc/pathutils.cxx b/tools/source/misc/pathutils.cxx new file mode 100644 index 000000000000..cb014dbb537a --- /dev/null +++ b/tools/source/misc/pathutils.cxx @@ -0,0 +1,222 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pathutils.cxx,v $ + * $Revision: 1.2 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_tools.hxx" +#include "sal/config.h" + +#if defined WNT + +#include <cstddef> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include "sal/types.h" +#include "tools/pathutils.hxx" + +namespace tools { + +WCHAR * filename(WCHAR * path) { + WCHAR * f = path; + for (WCHAR * p = path;;) { + switch (*p++) { + case L'\0': + return f; + case L'\\': + f = p; + break; + } + } +} + +WCHAR * buildPath( + WCHAR * path, WCHAR const * frontBegin, WCHAR const * frontEnd, + WCHAR const * backBegin, std::size_t backLength) +{ + // Remove leading ".." segments in the second path together with matching + // segments in the first path that are neither empty nor "." nor ".." nor + // end in ":" (which is not foolprove, as it can erroneously erase the start + // of a UNC path, but only if the input is bad data): + while (backLength >= 2 && backBegin[0] == L'.' && backBegin[1] == L'.' && + (backLength == 2 || backBegin[2] == L'\\')) + { + if (frontEnd - frontBegin < 2 || frontEnd[-1] != L'\\' || + frontEnd[-2] == L'\\' || frontEnd[-2] == L':' || + (frontEnd[-2] == L'.' && + (frontEnd - frontBegin < 3 || frontEnd[-3] == L'\\' || + (frontEnd[-3] == L'.' && + (frontEnd - frontBegin < 4 || frontEnd[-4] == L'\\'))))) + { + break; + } + WCHAR const * p = frontEnd - 1; + while (p != frontBegin && p[-1] != L'\\') { + --p; + } + if (p == frontBegin) { + break; + } + frontEnd = p; + if (backLength == 2) { + backBegin += 2; + backLength -= 2; + } else { + backBegin += 3; + backLength -= 3; + } + } + if (backLength < + static_cast< std::size_t >(MAX_PATH - (frontEnd - frontBegin))) + // hopefully std::size_t is large enough + { + WCHAR * p; + if (frontBegin == path) { + p = const_cast< WCHAR * >(frontEnd); + } else { + p = path; + while (frontBegin != frontEnd) { + *p++ = *frontBegin++; + } + } + for (; backLength > 0; --backLength) { + *p++ = *backBegin++; + } + *p = L'\0'; + return p; + } else { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + return NULL; + } +} + +WCHAR * resolveLink(WCHAR * path) { + HANDLE h = CreateFileW( + path, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (h == INVALID_HANDLE_VALUE) { + return NULL; + } + char p1[MAX_PATH]; + DWORD n; + BOOL ok = ReadFile(h, p1, MAX_PATH, &n, NULL); + CloseHandle(h); + if (!ok) { + return NULL; + } + WCHAR p2[MAX_PATH]; + std::size_t n2 = 0; + bool colon = false; + for (DWORD i = 0; i < n;) { + unsigned char c = static_cast< unsigned char >(p1[i++]); + switch (c) { + case '\0': + SetLastError(ERROR_BAD_PATHNAME); + return NULL; + case '\x0A': + case '\x0D': + if (n2 == MAX_PATH) { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + return NULL; + } + p2[n2] = L'\0'; + break; + case ':': + colon = true; + // fall through + default: + // Convert from UTF-8 to UTF-16: + if (c <= 0x7F) { + p2[n2++] = c; + } else if (c >= 0xC2 && c <= 0xDF && i < n && + static_cast< unsigned char >(p1[i]) >= 0x80 && + static_cast< unsigned char >(p1[i]) <= 0xBF) + { + p2[n2++] = ((c & 0x1F) << 6) | + (static_cast< unsigned char >(p1[i++]) & 0x3F); + } else if (n - i > 1 && + ((c == 0xE0 && + static_cast< unsigned char >(p1[i]) >= 0xA0 && + static_cast< unsigned char >(p1[i]) <= 0xBF) || + ((c >= 0xE1 && c <= 0xEC || c >= 0xEE && c <= 0xEF) && + static_cast< unsigned char >(p1[i]) >= 0x80 && + static_cast< unsigned char >(p1[i]) <= 0xBF) || + (c == 0xED && + static_cast< unsigned char >(p1[i]) >= 0x80 && + static_cast< unsigned char >(p1[i]) <= 0x9F)) && + static_cast< unsigned char >(p1[i + 1]) >= 0x80 && + static_cast< unsigned char >(p1[i + 1]) <= 0xBF) + { + p2[n2++] = ((c & 0x0F) << 12) | + ((static_cast< unsigned char >(p1[i]) & 0x3F) << 6) | + (static_cast< unsigned char >(p1[i + 1]) & 0x3F); + i += 2; + } else if (n - 2 > 1 && + ((c == 0xF0 && + static_cast< unsigned char >(p1[i]) >= 0x90 && + static_cast< unsigned char >(p1[i]) <= 0xBF) || + (c >= 0xF1 && c <= 0xF3 && + static_cast< unsigned char >(p1[i]) >= 0x80 && + static_cast< unsigned char >(p1[i]) <= 0xBF) || + (c == 0xF4 && + static_cast< unsigned char >(p1[i]) >= 0x80 && + static_cast< unsigned char >(p1[i]) <= 0x8F)) && + static_cast< unsigned char >(p1[i + 1]) >= 0x80 && + static_cast< unsigned char >(p1[i + 1]) <= 0xBF && + static_cast< unsigned char >(p1[i + 2]) >= 0x80 && + static_cast< unsigned char >(p1[i + 2]) <= 0xBF) + { + sal_Int32 u = ((c & 0x07) << 18) | + ((static_cast< unsigned char >(p1[i]) & 0x3F) << 12) | + ((static_cast< unsigned char >(p1[i + 1]) & 0x3F) << 6) | + (static_cast< unsigned char >(p1[i + 2]) & 0x3F); + i += 3; + p2[n2++] = static_cast< WCHAR >(((u - 0x10000) >> 10) | 0xD800); + p2[n2++] = static_cast< WCHAR >( + ((u - 0x10000) & 0x3FF) | 0xDC00); + } else { + SetLastError(ERROR_BAD_PATHNAME); + return NULL; + } + break; + } + } + WCHAR * end; + if (colon || p2[0] == L'\\') { + // Interpret p2 as an absolute path: + end = path; + } else { + // Interpret p2 as a relative path: + end = filename(path); + } + return buildPath(path, path, end, p2, n2); +} + +} + +#endif diff --git a/tools/source/rc/isofallback.cxx b/tools/source/rc/isofallback.cxx new file mode 100644 index 000000000000..e030d1f8d312 --- /dev/null +++ b/tools/source/rc/isofallback.cxx @@ -0,0 +1,70 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: isofallback.cxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <tools/isofallback.hxx> + +// ----------------------------------------------------------------------- + +// Return true if valid fallback found +sal_Bool GetIsoFallback( ByteString& rLanguage ) +{ + rLanguage.EraseLeadingAndTrailingChars(); + if( rLanguage.Len() ){ + xub_StrLen nSepPos = rLanguage.Search( '-' ); + if ( nSepPos == STRING_NOTFOUND ){ + if ( rLanguage.Equals("en")) + { + // en -> "" + rLanguage.Erase(); + return false; + } + else + { + // de -> en-US ; + rLanguage = ByteString("en-US"); + return true; + } + } + else if( !( nSepPos == 1 && ( rLanguage.GetChar(0) == 'x' || rLanguage.GetChar(0) == 'X' ) ) ) + { + // de-CH -> de ; + // try erase from - + rLanguage = rLanguage.GetToken( 0, '-'); + return true; + } + } + // "" -> ""; x-no-translate -> "" + rLanguage.Erase(); + return false; +} + diff --git a/tools/source/rc/makefile.mk b/tools/source/rc/makefile.mk new file mode 100644 index 000000000000..234cc344b65d --- /dev/null +++ b/tools/source/rc/makefile.mk @@ -0,0 +1,57 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.11 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=rc +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES= $(SLO)$/rc.obj \ + $(SLO)$/isofallback.obj \ + $(SLO)$/resmgr.obj \ + $(SLO)$/resary.obj + +OBJFILES= $(OBJ)$/rc.obj \ + $(OBJ)$/isofallback.obj \ + $(OBJ)$/resmgr.obj \ + $(OBJ)$/resary.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/tools/source/rc/rc.cxx b/tools/source/rc/rc.cxx new file mode 100644 index 000000000000..23bd82d58afb --- /dev/null +++ b/tools/source/rc/rc.cxx @@ -0,0 +1,100 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: rc.cxx,v $ + * $Revision: 1.12 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _TOOLS_RC_CXX + +#include <string.h> +#include <tools/date.hxx> +#include <tools/time.hxx> +#include <tools/rc.hxx> +#include <tools/rcid.h> + +// ======================================================================= + +Resource::Resource( const ResId& rResId ) +{ + GetRes( rResId.SetRT( RSC_RESOURCE ) ); +} + +// ----------------------------------------------------------------------- + +void Resource::GetRes( const ResId& rResId ) +{ + if( rResId.GetResMgr() ) + m_pResMgr = rResId.GetResMgr(); + m_pResMgr->GetResource( rResId, this ); + IncrementRes( sizeof( RSHEADER_TYPE ) ); +} + +// ----------------------------------------------------------------------- + +// ======================================================================= + +Time::Time( const ResId& rResId ) +{ + nTime = 0; + rResId.SetRT( RSC_TIME ); + ResMgr* pResMgr = NULL; + + ResMgr::GetResourceSkipHeader( rResId, &pResMgr ); + + ULONG nObjMask = (USHORT)pResMgr->ReadLong(); + + if ( 0x01 & nObjMask ) + SetHour( (USHORT)pResMgr->ReadShort() ); + if ( 0x02 & nObjMask ) + SetMin( (USHORT)pResMgr->ReadShort() ); + if ( 0x04 & nObjMask ) + SetSec( (USHORT)pResMgr->ReadShort() ); + if ( 0x08 & nObjMask ) + Set100Sec( (USHORT)pResMgr->ReadShort() ); +} + +// ======================================================================= + +Date::Date( const ResId& rResId ) : nDate(0) +{ + rResId.SetRT( RSC_DATE ); + ResMgr* pResMgr = NULL; + + ResMgr::GetResourceSkipHeader( rResId, &pResMgr ); + + ULONG nObjMask = (USHORT)pResMgr->ReadLong(); + + if ( 0x01 & nObjMask ) + SetYear( (USHORT)pResMgr->ReadShort() ); + if ( 0x02 & nObjMask ) + SetMonth( (USHORT)pResMgr->ReadShort() ); + if ( 0x04 & nObjMask ) + SetDay( (USHORT)pResMgr->ReadShort() ); +} diff --git a/tools/source/rc/resary.cxx b/tools/source/rc/resary.cxx new file mode 100644 index 000000000000..ac5b5e11db63 --- /dev/null +++ b/tools/source/rc/resary.cxx @@ -0,0 +1,81 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: resary.cxx,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _TOOLS_RESARY_CXX +#include <tools/resary.hxx> +#include <tools/rcid.h> + +// ======================================================================= + +ResStringArray::ResStringArray( const ResId& rResId ) +{ + rResId.SetRT( RSC_STRINGARRAY ); + ResMgr* pMgr = rResId.GetResMgr(); + if( pMgr && pMgr->GetResource( rResId ) ) + { + pMgr->GetClass(); + pMgr->Increment( sizeof( RSHEADER_TYPE ) ); + const sal_uInt32 nItems = pMgr->ReadLong(); + if ( nItems ) + { + m_aStrings.reserve( nItems ); + for ( sal_uInt32 i = 0; i < nItems; i++ ) + { + // load string + m_aStrings.push_back( ImplResStringItem( pMgr->ReadString() ) ); + + // load value + m_aStrings[i].m_nValue = pMgr->ReadLong(); + } + } + } +} + +// ----------------------------------------------------------------------- + +ResStringArray::~ResStringArray() +{ +} + +// ----------------------------------------------------------------------- + +sal_uInt32 ResStringArray::FindIndex( long nValue ) const +{ + const sal_uInt32 nItems = m_aStrings.size(); + for ( sal_uInt32 i = 0; i < nItems; i++ ) + { + if ( m_aStrings[i].m_nValue == nValue ) + return i; + } + return RESARRAY_INDEX_NOTFOUND; +} diff --git a/tools/source/rc/resmgr.cxx b/tools/source/rc/resmgr.cxx new file mode 100644 index 000000000000..8c9293e334c1 --- /dev/null +++ b/tools/source/rc/resmgr.cxx @@ -0,0 +1,2077 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: resmgr.cxx,v $ + * $Revision: 1.52.30.2 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <vos/signal.hxx> +#include <tools/debug.hxx> +#ifndef _TABLE_HXX +#include <tools/table.hxx> +#endif +#include <tools/stream.hxx> +#include <tools/resmgr.hxx> +#include <tools/rc.hxx> +#include <tools/rcid.h> +#include <osl/endian.h> +#include <osl/process.h> +#include <osl/thread.h> +#include <osl/file.hxx> +#include <osl/mutex.hxx> +#include <rtl/ustrbuf.hxx> +#include <tools/urlobj.hxx> +#include <rtl/instance.hxx> +#include <rtl/bootstrap.hxx> +#include <i18npool/mslangid.hxx> +#include <tools/simplerm.hxx> + +#include <tools/isofallback.hxx> + +#include <functional> +#include <algorithm> +#include <hash_map> +#include <list> +#include <set> + +#ifdef UNX +#define SEARCH_PATH_DELIMITER_CHAR_STRING ":" +#define SEARCH_PATH_DELIMITER ':' +#else +#define SEARCH_PATH_DELIMITER_CHAR_STRING ";" +#define SEARCH_PATH_DELIMITER ';' +#endif + +#define SEARCH_PATH_DELIMITER_STRING ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SEARCH_PATH_DELIMITER_CHAR_STRING ) ) + +using namespace rtl; +using namespace osl; + +// for thread safety +static osl::Mutex* pResMgrMutex = NULL; +static osl::Mutex& getResMgrMutex() +{ + if( !pResMgrMutex ) + { + osl::Guard<osl::Mutex> aGuard( *osl::Mutex::getGlobalMutex() ); + if( ! pResMgrMutex ) + pResMgrMutex = new osl::Mutex(); + } + return *pResMgrMutex; +} + +struct ImpContent; +class InternalResMgr +{ + friend class ResMgr; + friend class SimpleResMgr; + friend class ResMgrContainer; + + ImpContent * pContent; + UINT32 nOffCorrection; + BYTE * pStringBlock; + SvStream * pStm; + BOOL bEqual2Content; + UINT32 nEntries; + OUString aFileName; + OUString aPrefix; + OUString aResName; + bool bSingular; + com::sun::star::lang::Locale aLocale; + std::hash_map<sal_uInt64, int>* pResUseDump; + + InternalResMgr( const OUString& rFileURL, + const OUString& aPrefix, + const OUString& aResName, + const com::sun::star::lang::Locale& rLocale ); + ~InternalResMgr(); + BOOL Create(); + + BOOL IsGlobalAvailable( RESOURCE_TYPE nRT, sal_uInt32 nId ) const; + void * LoadGlobalRes( RESOURCE_TYPE nRT, sal_uInt32 nId, + void **pResHandle ); +public: + void FreeGlobalRes( void *, void * ); + + SvStream * GetBitmapStream( sal_uInt32 nResId ); +}; + +// ======================================================================= + +class ResMgrContainer +{ + static ResMgrContainer* pOneInstance; + + struct ContainerElement + { + InternalResMgr* pResMgr; + OUString aFileURL; + int nRefCount; + int nLoadCount; + + ContainerElement() : + pResMgr( NULL ), + nRefCount( 0 ), + nLoadCount( 0 ) + {} + }; + + std::hash_map< OUString, ContainerElement, OUStringHash> m_aResFiles; + com::sun::star::lang::Locale m_aDefLocale; + + ResMgrContainer() { init(); } + ~ResMgrContainer(); + + void init(); + public: + + static ResMgrContainer& get(); + static void release(); + + InternalResMgr* getResMgr( const OUString& rPrefix, + com::sun::star::lang::Locale& rLocale, + bool bForceNewInstance = false + ); + InternalResMgr* getNextFallback( InternalResMgr* pResMgr ); + + void freeResMgr( InternalResMgr* pResMgr ); + + void setDefLocale( const com::sun::star::lang::Locale& rLocale ) + { m_aDefLocale = rLocale; } + const com::sun::star::lang::Locale& getDefLocale() const + { return m_aDefLocale; } +}; + +ResMgrContainer* ResMgrContainer::pOneInstance = NULL; + +ResMgrContainer& ResMgrContainer::get() +{ + if( ! pOneInstance ) + pOneInstance = new ResMgrContainer(); + return *pOneInstance; +} + +ResMgrContainer::~ResMgrContainer() +{ + for( std::hash_map< OUString, ContainerElement, OUStringHash >::iterator it = + m_aResFiles.begin(); it != m_aResFiles.end(); ++it ) + { + OSL_TRACE( "Resource file %s loaded %d times\n", + OUStringToOString( it->second.aFileURL, osl_getThreadTextEncoding() ).getStr(), + it->second.nLoadCount ); + delete it->second.pResMgr; + } +} + +void ResMgrContainer::release() +{ + delete pOneInstance; + pOneInstance = NULL; +} + +void ResMgrContainer::init() +{ + // get resource path + std::list< OUString > aDirs; + sal_Int32 nIndex = 0; + + // 1. fixed locations + rtl::OUString uri( + RTL_CONSTASCII_USTRINGPARAM("$BRAND_BASE_DIR/program/resource")); + rtl::Bootstrap::expandMacros(uri); + aDirs.push_back(uri); + uri = rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM("$OOO_BASE_DIR/program/resource")); + rtl::Bootstrap::expandMacros(uri); + aDirs.push_back(uri); + + // 2. in STAR_RESOURCEPATH + const sal_Char* pEnv = getenv( "STAR_RESOURCEPATH" ); + if( pEnv ) + { + OUString aEnvPath( OStringToOUString( OString( pEnv ), osl_getThreadTextEncoding() ) ); + nIndex = 0; + while( nIndex >= 0 ) + { + OUString aPathElement( aEnvPath.getToken( 0, SEARCH_PATH_DELIMITER, nIndex ) ); + if( aPathElement.getLength() ) + { + OUString aFileURL; + File::getFileURLFromSystemPath( aPathElement, aFileURL ); + aDirs.push_back( aFileURL); + } + } + } + + // collect all possible resource files + for( std::list< OUString >::const_iterator dir_it = aDirs.begin(); dir_it != aDirs.end(); ++dir_it ) + { + Directory aDir( *dir_it ); + if( aDir.open() == FileBase::E_None ) + { + DirectoryItem aItem; + while( aDir.getNextItem( aItem ) == FileBase::E_None ) + { + FileStatus aStatus(FileStatusMask_FileName); + if( aItem.getFileStatus( aStatus ) == FileBase::E_None ) + { + OUString aFileName = aStatus.getFileName(); + if( aFileName.getLength() < 5 ) + continue; + if( ! aFileName.endsWithIgnoreAsciiCaseAsciiL( ".res", 4 ) ) + continue; + OUString aResName = aFileName.copy( 0, aFileName.getLength()-4 ); + if( m_aResFiles.find( aResName ) != m_aResFiles.end() ) + continue; + OUStringBuffer aURL( dir_it->getLength() + aFileName.getLength() + 1 ); + aURL.append( *dir_it ); + if( !dir_it->endsWithIgnoreAsciiCaseAsciiL( "/", 1 ) ) + aURL.append( sal_Unicode('/') ); + aURL.append( aFileName ); + m_aResFiles[ aResName ].aFileURL = aURL.makeStringAndClear(); + } + } + } + #if OSL_DEBUG_LEVEL > 1 + else + OSL_TRACE( "opening dir %s failed\n", OUStringToOString( *dir_it, osl_getThreadTextEncoding() ).getStr() ); + #endif + } + #if OSL_DEBUG_LEVEL > 1 + for( std::hash_map< OUString, ContainerElement, OUStringHash >::const_iterator it = + m_aResFiles.begin(); it != m_aResFiles.end(); ++it ) + { + OSL_TRACE( "ResMgrContainer: %s -> %s\n", + OUStringToOString( it->first, osl_getThreadTextEncoding() ).getStr(), + OUStringToOString( it->second.aFileURL, osl_getThreadTextEncoding() ).getStr() ); + } + #endif + + // set default language + LanguageType nLang = MsLangId::getSystemUILanguage(); + MsLangId::convertLanguageToLocale(nLang, m_aDefLocale); +} + +InternalResMgr* ResMgrContainer::getResMgr( const OUString& rPrefix, + com::sun::star::lang::Locale& rLocale, + bool bForceNewInstance + ) +{ + com::sun::star::lang::Locale aLocale( rLocale ); + OUStringBuffer aSearch( rPrefix.getLength() + 16 ); + std::hash_map< OUString, ContainerElement, OUStringHash >::iterator it = m_aResFiles.end(); + + int nTries = 0; + if( aLocale.Language.getLength() > 0 ) + nTries = 1; + if( aLocale.Country.getLength() > 0 ) + nTries = 2; + if( aLocale.Variant.getLength() > 0 ) + nTries = 3; + while( nTries-- ) + { + aSearch.append( rPrefix ); + if( nTries > -1 ) + { + aSearch.append( aLocale.Language ); + } + if( nTries > 0 ) + { + aSearch.append( sal_Unicode('-') ); + aSearch.append( aLocale.Country ); + } + if( nTries > 1 ) + { + aSearch.append( sal_Unicode('-') ); + aSearch.append( aLocale.Variant ); + } + it = m_aResFiles.find( aSearch.makeStringAndClear() ); + if( it != m_aResFiles.end() ) + { + // ensure InternalResMgr existance + if( ! it->second.pResMgr ) + { + InternalResMgr* pImp = + new InternalResMgr( it->second.aFileURL, rPrefix, it->first, aLocale ); + if( ! pImp->Create() ) + { + delete pImp; + continue; + } + it->second.pResMgr = pImp; + } + break; + } + if( nTries == 0 && !aLocale.Language.equalsIgnoreAsciiCaseAscii( "en" ) ) + { + // locale fallback failed + // fallback to en-US locale + nTries = 2; + aLocale.Language = OUString( RTL_CONSTASCII_USTRINGPARAM( "en" ) ); + aLocale.Country = OUString( RTL_CONSTASCII_USTRINGPARAM( "US" ) ); + aLocale.Variant = OUString(); + } + } + // try if there is anything with this prefix at all + if( it == m_aResFiles.end() ) + { + aLocale = com::sun::star::lang::Locale(); + it = m_aResFiles.find( rPrefix ); + if( it == m_aResFiles.end() ) + { + for( it = m_aResFiles.begin(); it != m_aResFiles.end(); ++it ) + { + if( it->first.matchIgnoreAsciiCase( rPrefix ) ) + { + // ensure InternalResMgr existance + if( ! it->second.pResMgr ) + { + InternalResMgr* pImp = + new InternalResMgr( it->second.aFileURL, + rPrefix, + it->first, + aLocale ); + if( ! pImp->Create() ) + { + delete pImp; + continue; + } + it->second.pResMgr = pImp; + } + // try to guess locale + sal_Int32 nIndex = rPrefix.getLength(); + aLocale.Language = it->first.getToken( 0, '-', nIndex ); + if( nIndex > 0 ) + aLocale.Country = it->first.getToken( 0, '-', nIndex ); + if( nIndex > 0 ) + aLocale.Variant = it->first.getToken( 0, '-', nIndex ); + break; + } + } + } + } + // give up + if( it == m_aResFiles.end() ) + { + OUStringBuffer sKey = rPrefix; + sKey.append( rLocale.Language ); + if( rLocale.Country.getLength() ) + { + sKey.append( sal_Unicode('-') ); + sKey.append( rLocale.Country ); + } + if( rLocale.Variant.getLength() ) + { + sKey.append( sal_Unicode('-') ); + sKey.append( rLocale.Variant ); + } // if( aLocale.Variant.getLength() ) + ::rtl::OUString sURL = sKey.makeStringAndClear(); + sURL += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".res")); + if ( m_aResFiles.find(sURL) == m_aResFiles.end() ) + { + m_aResFiles[ sURL ].aFileURL = sURL; + return getResMgr(rPrefix,rLocale,bForceNewInstance); + } // if ( m_aResFiles.find(sURL) == m_aResFiles.end() ) + return NULL; + } + + rLocale = aLocale; + // at this point it->second.pResMgr must be filled either by creating a new one + // (then the refcount is still 0) or because we already had one + InternalResMgr* pImp = it->second.pResMgr; + + if( it->second.nRefCount == 0 ) + it->second.nLoadCount++; + + // for SimpleResMgr + if( bForceNewInstance ) + { + if( it->second.nRefCount == 0 ) + { + // shortcut: the match algorithm already created the InternalResMgr + // take it instead of creating yet another one + it->second.pResMgr = NULL; + pImp->bSingular = true; + } + else + { + pImp = new InternalResMgr( it->second.aFileURL, rPrefix, it->first, aLocale ); + pImp->bSingular = true; + if( !pImp->Create() ) + { + delete pImp; + pImp = NULL; + } + else + it->second.nLoadCount++; + } + } + else + it->second.nRefCount++; + + return pImp; +} + +InternalResMgr* ResMgrContainer::getNextFallback( InternalResMgr* pMgr ) +{ + com::sun::star::lang::Locale aLocale = pMgr->aLocale; + if( aLocale.Variant.getLength() ) + aLocale.Variant = OUString(); + else if( aLocale.Country.getLength() ) + aLocale.Country = OUString(); + else if( ! aLocale.Language.equalsIgnoreAsciiCaseAscii( "en" ) ) + { + aLocale.Language = OUString( RTL_CONSTASCII_USTRINGPARAM( "en" ) ); + aLocale.Country = OUString( RTL_CONSTASCII_USTRINGPARAM( "US" ) ); + } + InternalResMgr* pNext = getResMgr( pMgr->aPrefix, aLocale, pMgr->bSingular ); + // prevent recursion + if( pNext == pMgr || pNext->aResName.equals( pMgr->aResName ) ) + { + if( pNext->bSingular ) + delete pNext; + pNext = NULL; + } + return pNext; +} + +void ResMgrContainer::freeResMgr( InternalResMgr* pResMgr ) +{ + if( pResMgr->bSingular ) + delete pResMgr; + else + { + std::hash_map< OUString, ContainerElement, OUStringHash >::iterator it = + m_aResFiles.find( pResMgr->aResName ); + if( it != m_aResFiles.end() ) + { + DBG_ASSERT( it->second.nRefCount > 0, "InternalResMgr freed too often" ); + if( it->second.nRefCount > 0 ) + it->second.nRefCount--; + if( it->second.nRefCount == 0 ) + { + delete it->second.pResMgr; + it->second.pResMgr = NULL; + } + } + } +} + +// ======================================================================= + +void Resource::TestRes() +{ + if( m_pResMgr ) + m_pResMgr->TestStack( this ); +} + +struct ImpContent +{ + sal_uInt64 nTypeAndId; + sal_uInt32 nOffset; +}; + +struct ImpContentLessCompare : public ::std::binary_function< ImpContent, ImpContent, bool> +{ + inline bool operator() (const ImpContent& lhs, const ImpContent& rhs) const + { + return lhs.nTypeAndId < rhs.nTypeAndId; + } +}; + +struct ImpContentMixLessCompare : public ::std::binary_function< ImpContent, sal_uInt64, bool> +{ + inline bool operator() (const ImpContent& lhs, const sal_uInt64& rhs) const + { + return lhs.nTypeAndId < rhs; + } + inline bool operator() (const sal_uInt64& lhs, const ImpContent& rhs) const + { + return lhs < rhs.nTypeAndId; + } +}; + + +// ======================================================================= + +static ResHookProc pImplResHookProc = 0; + +// ======================================================================= + +SvStream * InternalResMgr::GetBitmapStream( sal_uInt32 nId ) +{ + // Anfang der Strings suchen + ImpContent * pFind = ::std::lower_bound(pContent, + pContent + nEntries, + ((sal_uInt64(RT_SYS_BITMAP) << 32) | nId), + ImpContentMixLessCompare()); + if ( (pFind != (pContent + nEntries)) && (pFind->nTypeAndId == ((sal_uInt64(RT_SYS_BITMAP) << 32) | nId)) ) + { + pStm->Seek( pFind->nOffset ); + return pStm; + } + return NULL; +} + +// ----------------------------------------------------------------------- + +InternalResMgr::InternalResMgr( const OUString& rFileURL, + const OUString& rPrefix, + const OUString& rResName, + const com::sun::star::lang::Locale& rLocale ) + : pContent( NULL ) + , pStringBlock( NULL ) + , pStm( NULL ) + , bEqual2Content( TRUE ) + , nEntries( 0 ) + , aFileName( rFileURL ) + , aPrefix( rPrefix ) + , aResName( rResName ) + , bSingular( false ) + , aLocale( rLocale ) + , pResUseDump( 0 ) +{ +} + +// ----------------------------------------------------------------------- + +InternalResMgr::~InternalResMgr() +{ + rtl_freeMemory(pContent); + rtl_freeMemory(pStringBlock); + delete pStm; + +#ifdef DBG_UTIL + if( pResUseDump ) + { + const sal_Char* pLogFile = getenv( "STAR_RESOURCE_LOGGING" ); + if ( pLogFile ) + { + SvFileStream aStm( UniString( pLogFile, RTL_TEXTENCODING_ASCII_US ), STREAM_WRITE ); + aStm.Seek( STREAM_SEEK_TO_END ); + ByteString aLine( "FileName: " ); + aLine.Append( ByteString( OUStringToOString( aFileName, RTL_TEXTENCODING_UTF8 ) ) ); + aStm.WriteLine( aLine ); + + for( std::hash_map<sal_uInt64, int>::const_iterator it = pResUseDump->begin(); + it != pResUseDump->end(); ++it ) + { + sal_uInt64 nKeyId = it->first; + aLine.Assign( "Type/Id: " ); + aLine.Append( ByteString::CreateFromInt32( sal::static_int_cast< sal_Int32 >((nKeyId >> 32) & 0xFFFFFFFF) ) ); + aLine.Append( '/' ); + aLine.Append( ByteString::CreateFromInt32( sal::static_int_cast< sal_Int32 >(nKeyId & 0xFFFFFFFF) ) ); + aStm.WriteLine( aLine ); + } + } + } +#endif + + delete pResUseDump; +} + +// ----------------------------------------------------------------------- + + +BOOL InternalResMgr::Create() +{ + ResMgrContainer::get(); + BOOL bDone = FALSE; + + pStm = new SvFileStream( aFileName, (STREAM_READ | STREAM_SHARE_DENYWRITE | STREAM_NOCREATE) ); + if( pStm->GetError() == 0 ) + { + INT32 lContLen = 0; + + pStm->Seek( STREAM_SEEK_TO_END ); + /* + if( ( pInternalResMgr->pHead = (RSHEADER_TYPE *)mmap( 0, nResourceFileSize, + PROT_READ, MAP_PRIVATE, + fRes, 0 ) ) != (RSHEADER_TYPE *)-1) + */ + pStm->SeekRel( - (int)sizeof( lContLen ) ); + pStm->Read( &lContLen, sizeof( lContLen ) ); + // is bigendian, swab to the right endian + lContLen = ResMgr::GetLong( &lContLen ); + pStm->SeekRel( -lContLen ); + // allocate stored ImpContent data (12 bytes per unit) + BYTE* pContentBuf = (BYTE*)rtl_allocateMemory( lContLen ); + pStm->Read( pContentBuf, lContLen ); + // allocate ImpContent space (sizeof(ImpContent) per unit, not necessarily 12) + pContent = (ImpContent *)rtl_allocateMemory( sizeof(ImpContent)*lContLen/12 ); + // Auf die Anzahl der ImpContent k�rzen + nEntries = (UINT32)lContLen / 12; + bEqual2Content = TRUE; // Die Daten der Resourcen liegen + // genauso wie das Inhaltsverzeichnis + BOOL bSorted = TRUE; + if( nEntries ) + { +#ifdef DBG_UTIL + const sal_Char* pLogFile = getenv( "STAR_RESOURCE_LOGGING" ); + if ( pLogFile ) + { + pResUseDump = new std::hash_map<sal_uInt64, int>; + for( sal_uInt32 i = 0; i < nEntries; ++i ) + (*pResUseDump)[pContent[i].nTypeAndId] = 1; + } +#endif + // swap the content to the right endian + pContent[0].nTypeAndId = ResMgr::GetUInt64( pContentBuf ); + pContent[0].nOffset = ResMgr::GetLong( pContentBuf+8 ); + sal_uInt32 nCount = nEntries - 1; + for( sal_uInt32 i = 0,j=1; i < nCount; ++i,++j ) + { + // swap the content to the right endian + pContent[j].nTypeAndId = ResMgr::GetUInt64( pContentBuf + (12*j) ); + pContent[j].nOffset = ResMgr::GetLong( pContentBuf + (12*j+8) ); + if( pContent[i].nTypeAndId >= pContent[j].nTypeAndId ) + bSorted = FALSE; + if( (pContent[i].nTypeAndId & 0xFFFFFFFF00000000LL) == (pContent[j].nTypeAndId & 0xFFFFFFFF00000000LL) + && pContent[i].nOffset >= pContent[j].nOffset ) + bEqual2Content = FALSE; + } + } + rtl_freeMemory( pContentBuf ); +#ifndef OS2 + OSL_ENSURE( bSorted, "content not sorted" ); +#endif + OSL_ENSURE( bEqual2Content, "resource structure wrong" ); + if( !bSorted ) + ::std::sort(pContent,pContent+nEntries,ImpContentLessCompare()); + // qsort( pContent, nEntries, sizeof( ImpContent ), Compare ); + + bDone = TRUE; + } + + return bDone; +} + +// ----------------------------------------------------------------------- + +BOOL InternalResMgr::IsGlobalAvailable( RESOURCE_TYPE nRT, sal_uInt32 nId ) const +{ + // Anfang der Strings suchen + sal_uInt64 nValue = ((sal_uInt64(nRT) << 32) | nId); + ImpContent * pFind = ::std::lower_bound(pContent, + pContent + nEntries, + nValue, + ImpContentMixLessCompare()); + return (pFind != (pContent + nEntries)) && (pFind->nTypeAndId == nValue); +} + +// ----------------------------------------------------------------------- + +void* InternalResMgr::LoadGlobalRes( RESOURCE_TYPE nRT, sal_uInt32 nId, + void **pResHandle ) +{ +#ifdef DBG_UTIL + if( pResUseDump ) + pResUseDump->erase( (sal_uInt64(nRT) << 32) | nId ); +#endif + // Anfang der Strings suchen + sal_uInt64 nValue = ((sal_uInt64(nRT) << 32) | nId); + ImpContent* pEnd = (pContent + nEntries); + ImpContent* pFind = ::std::lower_bound( pContent, + pEnd, + nValue, + ImpContentMixLessCompare()); + if( pFind && (pFind != pEnd) && (pFind->nTypeAndId == nValue) ) + { + if( nRT == RSC_STRING && bEqual2Content ) + { + // String Optimierung + if( !pStringBlock ) + { + // Anfang der Strings suchen + ImpContent * pFirst = pFind; + ImpContent * pLast = pFirst; + while( pFirst > pContent && ((pFirst -1)->nTypeAndId >> 32) == RSC_STRING ) + --pFirst; + while( pLast < pEnd && (pLast->nTypeAndId >> 32) == RSC_STRING ) + ++pLast; + nOffCorrection = pFirst->nOffset; + UINT32 nSize; + --pLast; + pStm->Seek( pLast->nOffset ); + RSHEADER_TYPE aHdr; + pStm->Read( &aHdr, sizeof( aHdr ) ); + nSize = pLast->nOffset + aHdr.GetGlobOff() - nOffCorrection; + pStringBlock = (BYTE*)rtl_allocateMemory( nSize ); + pStm->Seek( pFirst->nOffset ); + pStm->Read( pStringBlock, nSize ); + } + *pResHandle = pStringBlock; + return (BYTE*)pStringBlock + pFind->nOffset - nOffCorrection; + } // if( nRT == RSC_STRING && bEqual2Content ) + else + { + *pResHandle = 0; + RSHEADER_TYPE aHeader; + pStm->Seek( pFind->nOffset ); + pStm->Read( &aHeader, sizeof( RSHEADER_TYPE ) ); + void * pRes = rtl_allocateMemory( aHeader.GetGlobOff() ); + memcpy( pRes, &aHeader, sizeof( RSHEADER_TYPE ) ); + pStm->Read( (BYTE*)pRes + sizeof( RSHEADER_TYPE ), + aHeader.GetGlobOff() - sizeof( RSHEADER_TYPE ) ); + return pRes; + } + } // if( pFind && (pFind != pEnd) && (pFind->nTypeAndId == nValue) ) + *pResHandle = 0; + //Resource holen + return NULL; +} + +// ----------------------------------------------------------------------- + +void InternalResMgr::FreeGlobalRes( void * pResHandle, void * pResource ) +{ + if ( !pResHandle ) + // REsource wurde extra allokiert + rtl_freeMemory(pResource); +} + +// ======================================================================= + +#ifdef DBG_UTIL + +UniString GetTypeRes_Impl( const ResId& rTypeId ) +{ + // Funktion verlassen, falls Resourcefehler in dieser Funktion + static int bInUse = FALSE; + UniString aTypStr( UniString::CreateFromInt32( rTypeId.GetId() ) ); + + if ( !bInUse ) + { + bInUse = TRUE; + + ResId aResId( sal_uInt32(RSCVERSION_ID), *rTypeId.GetResMgr() ); + aResId.SetRT( RSC_VERSIONCONTROL ); + + if ( rTypeId.GetResMgr()->GetResource( aResId ) ) + { + rTypeId.SetRT( RSC_STRING ); + if ( rTypeId.GetResMgr()->IsAvailable( rTypeId ) ) + { + aTypStr = UniString( rTypeId ); + // Versions Resource Klassenzeiger ans Ende setzen + rTypeId.GetResMgr()->Increment( sizeof( RSHEADER_TYPE ) ); + } + } + bInUse = FALSE; + } + + return aTypStr; +} + +// ----------------------------------------------------------------------- + +void ResMgr::RscError_Impl( const sal_Char* pMessage, ResMgr* pResMgr, + RESOURCE_TYPE nRT, sal_uInt32 nId, + std::vector< ImpRCStack >& rResStack, int nDepth ) +{ + // create a separate ResMgr with its own stack + // first get a second reference of the InternalResMgr + InternalResMgr* pImp = + ResMgrContainer::get().getResMgr( pResMgr->pImpRes->aPrefix, + pResMgr->pImpRes->aLocale, + true ); + + ResMgr* pNewResMgr = new ResMgr( pImp ); + + ByteString aStr = OUStringToOString( pResMgr->GetFileName(), RTL_TEXTENCODING_UTF8 ); + if ( aStr.Len() ) + aStr += '\n'; + + aStr.Append( "Class: " ); + aStr.Append( ByteString( GetTypeRes_Impl( ResId( nRT, *pNewResMgr ) ), RTL_TEXTENCODING_UTF8 ) ); + aStr.Append( ", Id: " ); + aStr.Append( ByteString::CreateFromInt32( (long)nId ) ); + aStr.Append( ". " ); + aStr.Append( pMessage ); + + aStr.Append( "\nResource Stack\n" ); + while( nDepth > 0 ) + { + aStr.Append( "Class: " ); + aStr.Append( ByteString( GetTypeRes_Impl( ResId( rResStack[nDepth].pResource->GetRT(), *pNewResMgr ) ), RTL_TEXTENCODING_UTF8 ) ); + aStr.Append( ", Id: " ); + aStr.Append( ByteString::CreateFromInt32( (long)rResStack[nDepth].pResource->GetId() ) ); + nDepth--; + } + + // clean up + delete pNewResMgr; + + DBG_ERROR( aStr.GetBuffer() ); +} + +#endif + +// ======================================================================= + +static void RscException_Impl() +{ + switch ( NAMESPACE_VOS(OSignalHandler)::raise( OSL_SIGNAL_USER_RESOURCEFAILURE, (void*)"" ) ) + { + case NAMESPACE_VOS(OSignalHandler)::TAction_CallNextHandler: + abort(); + + case NAMESPACE_VOS(OSignalHandler)::TAction_Ignore: + return; + + case NAMESPACE_VOS(OSignalHandler)::TAction_AbortApplication: + abort(); + + case NAMESPACE_VOS(OSignalHandler)::TAction_KillApplication: + exit(-1); + } +} + +// ======================================================================= + +void ImpRCStack::Init( ResMgr* pMgr, const Resource* pObj, sal_uInt32 Id ) +{ + pResource = NULL; + pClassRes = NULL; + Flags = RC_NOTYPE; + aResHandle = NULL; + pResObj = pObj; + nId = Id & ~RSC_DONTRELEASE; //TLX: Besser Init aendern + pResMgr = pMgr; + if ( !(Id & RSC_DONTRELEASE) ) + Flags |= RC_AUTORELEASE; +} + +// ----------------------------------------------------------------------- + +void ImpRCStack::Clear() +{ + pResource = NULL; + pClassRes = NULL; + Flags = RC_NOTYPE; + aResHandle = NULL; + pResObj = NULL; + nId = 0; + pResMgr = NULL; +} + +// ----------------------------------------------------------------------- + +static RSHEADER_TYPE* LocalResource( const ImpRCStack* pStack, + RESOURCE_TYPE nRTType, + sal_uInt32 nId ) +{ + // Gibt die Position der Resource zurueck, wenn sie gefunden wurde. + // Ansonsten gibt die Funktion Null zurueck. + RSHEADER_TYPE* pTmp; // Zeiger auf Kind-Resourceobjekte + RSHEADER_TYPE* pEnd; // Zeiger auf das Ende der Resource + + if ( pStack->pResource && pStack->pClassRes ) + { + pTmp = (RSHEADER_TYPE*) + ((BYTE*)pStack->pResource + pStack->pResource->GetLocalOff()); + pEnd = (RSHEADER_TYPE*) + ((BYTE*)pStack->pResource + pStack->pResource->GetGlobOff()); + while ( pTmp != pEnd ) + { + if ( pTmp->GetRT() == nRTType && pTmp->GetId() == nId ) + return pTmp; + pTmp = (RSHEADER_TYPE*)((BYTE*)pTmp + pTmp->GetGlobOff()); + } + } + + return NULL; +} + +// ======================================================================= + +void* ResMgr::pEmptyBuffer = NULL; + +void* ResMgr::getEmptyBuffer() +{ + if( ! pEmptyBuffer ) + pEmptyBuffer = rtl_allocateZeroMemory( 1024 ); + return pEmptyBuffer; +} + +void ResMgr::DestroyAllResMgr() +{ + { + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + if( pEmptyBuffer ) + { + rtl_freeMemory( pEmptyBuffer ); + pEmptyBuffer = NULL; + } + ResMgrContainer::release(); + } + delete pResMgrMutex; + pResMgrMutex = NULL; +} + +// ----------------------------------------------------------------------- + +void ResMgr::Init( const OUString& rFileName ) +{ + (void) rFileName; // avoid warning about unused parameter + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if ( !pImpRes ) + { +#ifdef DBG_UTIL + ByteString aStr( "Resourcefile not found:\n" ); + aStr += ByteString( OUStringToOString( rFileName, RTL_TEXTENCODING_UTF8 ) ); + DBG_ERROR( aStr.GetBuffer() ); +#endif + RscException_Impl(); + } +#ifdef DBG_UTIL + else + { + void* aResHandle = 0; // Hilfvariable fuer Resource + void* pVoid; // Zeiger auf die Resource + + pVoid = pImpRes->LoadGlobalRes( RSC_VERSIONCONTROL, RSCVERSION_ID, + &aResHandle ); + if ( pVoid ) + pImpRes->FreeGlobalRes( aResHandle, pVoid ); + else + { + ByteString aStr( "Wrong version:\n" ); + aStr += ByteString( OUStringToOString( pImpRes->aFileName, RTL_TEXTENCODING_UTF8 ) ); + DbgError( aStr.GetBuffer() ); + } + } +#endif + nCurStack = -1; + aStack.clear(); + pFallbackResMgr = pOriginalResMgr = NULL; + incStack(); +} + +// ----------------------------------------------------------------------- + +ResMgr::ResMgr( InternalResMgr * pImpMgr ) +{ + pImpRes = pImpMgr; + Init( pImpMgr->aFileName ); +} + +// ----------------------------------------------------------------------- + +ResMgr::~ResMgr() +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + ResMgrContainer::get().freeResMgr( pImpRes ); + + // clean up possible left rc stack frames + while( nCurStack > 0 ) + { + if( ( aStack[nCurStack].Flags & (RC_GLOBAL | RC_NOTFOUND) ) == RC_GLOBAL ) + pImpRes->FreeGlobalRes( aStack[nCurStack].aResHandle, + aStack[nCurStack].pResource ); + nCurStack--; + } +} + + +void ResMgr::incStack() +{ + nCurStack++; + if( nCurStack >= int(aStack.size()) ) + aStack.push_back( ImpRCStack() ); + aStack[nCurStack].Clear(); + + DBG_ASSERT( nCurStack < 32, "Resource stack unreasonably large" ); +} + +void ResMgr::decStack() +{ + DBG_ASSERT( nCurStack > 0, "resource stack underrun !" ); + if( (aStack[nCurStack].Flags & RC_FALLBACK_UP) ) + { + nCurStack--; + // warning: this will delete *this, see below + pOriginalResMgr->decStack(); + } + else + { + ImpRCStack& rTop = aStack[nCurStack]; + if( (rTop.Flags & RC_FALLBACK_DOWN) ) + { + #if OSL_DEBUG_LEVEL > 1 + OSL_TRACE( "returning from fallback %s\n", + OUStringToOString(pFallbackResMgr->GetFileName(), osl_getThreadTextEncoding() ).getStr() ); + #endif + delete pFallbackResMgr; + pFallbackResMgr = NULL; + } + nCurStack--; + } +} + +#ifdef DBG_UTIL + +void ResMgr::TestStack( const Resource* pResObj ) +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if ( DbgIsResource() ) + { + for( int i = 1; i <= nCurStack; ++i ) + { + if ( aStack[i].pResObj == pResObj ) + { +#ifdef DBG_UTIL + RscError_Impl( "Resource not freed! ", this, + aStack[i].pResource->GetRT(), + aStack[i].pResource->GetId(), + aStack, i-1 ); +#endif + } + } + } +} + +#else + +void ResMgr::TestStack( const Resource* ) +{ +} + +#endif + +// ----------------------------------------------------------------------- +BOOL ResMgr::IsAvailable( const ResId& rId, const Resource* pResObj ) const +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + BOOL bAvailable = FALSE; + RSHEADER_TYPE* pClassRes = rId.GetpResource(); + RESOURCE_TYPE nRT = rId.GetRT2(); + sal_uInt32 nId = rId.GetId(); + const ResMgr* pMgr = rId.GetResMgr(); + + if ( !pMgr ) + pMgr = this; + + if( pMgr->pFallbackResMgr ) + { + ResId aId( rId ); + aId.SetResMgr( NULL ); + return pMgr->pFallbackResMgr->IsAvailable( aId, pResObj ); + } + + if ( !pResObj || pResObj == pMgr->aStack[pMgr->nCurStack].pResObj ) + { + if ( !pClassRes ) + pClassRes = LocalResource( &pMgr->aStack[pMgr->nCurStack], nRT, nId ); + if ( pClassRes ) + { + if ( pClassRes->GetRT() == nRT ) + bAvailable = TRUE; + } + } + + // vieleicht globale Resource + if ( !pClassRes ) + bAvailable = pMgr->pImpRes->IsGlobalAvailable( nRT, nId ); + + return bAvailable; +} + +// ----------------------------------------------------------------------- + +void* ResMgr::GetClass() +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if( pFallbackResMgr ) + return pFallbackResMgr->GetClass(); + + return aStack[nCurStack].pClassRes; +} + +// ----------------------------------------------------------------------- + +BOOL ResMgr::GetResource( const ResId& rId, const Resource* pResObj ) +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if( pFallbackResMgr ) + { + ResId aId( rId ); + aId.SetResMgr( NULL ); + return pFallbackResMgr->GetResource( aId, pResObj ); + } + + ResMgr* pMgr = rId.GetResMgr(); + if ( pMgr && (this != pMgr) ) + return pMgr->GetResource( rId, pResObj ); + + // normally Increment will pop the context; this is + // not possible in RC_NOTFOUND case, so pop a frame here + ImpRCStack* pTop = &aStack[nCurStack]; + if( (pTop->Flags & RC_NOTFOUND) ) + { + decStack(); + } + + RSHEADER_TYPE* pClassRes = rId.GetpResource(); + RESOURCE_TYPE nRT = rId.GetRT2(); + sal_uInt32 nId = rId.GetId(); + + incStack(); + pTop = &aStack[nCurStack]; + pTop->Init( pMgr, pResObj, nId | + (rId.IsAutoRelease() ? 0 : RSC_DONTRELEASE) ); + + if ( pClassRes ) + { + if ( pClassRes->GetRT() == nRT ) + pTop->pClassRes = pClassRes; + else + { +#ifdef DBG_UTIL + RscError_Impl( "Different class and resource type!", + this, nRT, nId, aStack, nCurStack-1 ); +#endif + pTop->Flags |= RC_NOTFOUND; + pTop->pClassRes = getEmptyBuffer(); + pTop->pResource = (RSHEADER_TYPE*)pTop->pClassRes; + return FALSE; + } + } + else + { + OSL_ENSURE( nCurStack > 0, "stack of 1 to shallow" ); + pTop->pClassRes = LocalResource( &aStack[nCurStack-1], nRT, nId ); + } + + if ( pTop->pClassRes ) + // lokale Resource, nicht system Resource + pTop->pResource = (RSHEADER_TYPE *)pTop->pClassRes; + else + { + pTop->pClassRes = pImpRes->LoadGlobalRes( nRT, nId, &pTop->aResHandle ); + if ( pTop->pClassRes ) + { + pTop->Flags |= RC_GLOBAL; + pTop->pResource = (RSHEADER_TYPE *)pTop->pClassRes; + } + else + { + // try to get a fallback resource + pFallbackResMgr = CreateFallbackResMgr( rId, pResObj ); + if( pFallbackResMgr ) + { + pTop->Flags |= RC_FALLBACK_DOWN; + #ifdef DBG_UTIL + ByteString aMess( "found resource " ); + aMess.Append( ByteString::CreateFromInt32( nId ) ); + aMess.Append( " in fallback " ); + aMess.Append( ByteString( OUStringToOString( pFallbackResMgr->GetFileName(), osl_getThreadTextEncoding() ) ) ); + aMess.Append( "\n" ); + RscError_Impl( aMess.GetBuffer(), + this, nRT, nId, aStack, nCurStack-1 ); + #endif + } + else + { + #ifdef DBG_UTIL + RscError_Impl( "Cannot load resource! ", + this, nRT, nId, aStack, nCurStack-1 ); + #endif + pTop->Flags |= RC_NOTFOUND; + pTop->pClassRes = getEmptyBuffer(); + pTop->pResource = (RSHEADER_TYPE*)pTop->pClassRes; + return FALSE; + } + } + } + + return TRUE; +} + +// ----------------------------------------------------------------------- + +void * ResMgr::GetResourceSkipHeader( const ResId& rResId, ResMgr ** ppResMgr ) +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + DBG_ASSERT( rResId.GetResMgr(), "illegal ResId without ResMgr" ); + *ppResMgr = rResId.GetResMgr(); + if( *ppResMgr ) + { + (*ppResMgr)->GetResource( rResId ); + (*ppResMgr)->Increment( sizeof( RSHEADER_TYPE ) ); + return (*ppResMgr)->GetClass(); + } + return getEmptyBuffer(); +} + +// ----------------------------------------------------------------------- + +void ResMgr::PopContext( const Resource* pResObj ) +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if( pFallbackResMgr ) + { + pFallbackResMgr->PopContext( pResObj ); + return; + } + +#ifdef DBG_UTIL + if ( DbgIsResource() ) + { + if ( (aStack[nCurStack].pResObj != pResObj) || nCurStack == 0 ) + { + RscError_Impl( "Cannot free resource! ", this, + RSC_NOTYPE, 0, aStack, nCurStack ); + } + } +#endif + + if ( nCurStack > 0 ) + { + ImpRCStack* pTop = &aStack[nCurStack]; +#ifdef DBG_UTIL + if ( DbgIsResource() && !(pTop->Flags & RC_NOTFOUND) ) + { + void* pRes = (BYTE*)pTop->pResource + + pTop->pResource->GetLocalOff(); + + if ( pTop->pClassRes != pRes ) + { + RscError_Impl( "Classpointer not at the end!", + this, pTop->pResource->GetRT(), + pTop->pResource->GetId(), + aStack, nCurStack-1 ); + } + } +#endif + + // Resource freigeben + if( (pTop->Flags & (RC_GLOBAL | RC_NOTFOUND)) == RC_GLOBAL ) + // kann auch Fremd-Ressource sein + pImpRes->FreeGlobalRes( pTop->aResHandle, pTop->pResource ); + decStack(); + } +} + +// ----------------------------------------------------------------------- + +RSHEADER_TYPE* ResMgr::CreateBlock( const ResId& rId ) +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if( pFallbackResMgr ) + { + ResId aId( rId ); + aId.SetResMgr( NULL ); + return pFallbackResMgr->CreateBlock( aId ); + } + + RSHEADER_TYPE* pHeader = NULL; + if ( GetResource( rId ) ) + { + // Der Zeiger steht am Anfang, deswegen zeigt der Klassen-Pointer + // auf den Header und die restliche Groesse ist die Gesammte. + pHeader = (RSHEADER_TYPE*)rtl_allocateMemory( GetRemainSize() ); + memcpy( pHeader, GetClass(), GetRemainSize() ); + Increment( pHeader->GetLocalOff() ); //ans Ende setzen + if ( pHeader->GetLocalOff() != pHeader->GetGlobOff() ) + // Hat Sub-Ressourcen, deshalb extra freigeben + PopContext(); + } + + return pHeader; +} + +// ------------------------------------------------------------------ + +INT16 ResMgr::GetShort( void * pShort ) +{ + return ((*((sal_uInt8*)pShort + 0) << 8) | + (*((sal_uInt8*)pShort + 1) << 0) ); +} + +// ------------------------------------------------------------------ + +INT32 ResMgr::GetLong( void * pLong ) +{ + return ((*((sal_uInt8*)pLong + 0) << 24) | + (*((sal_uInt8*)pLong + 1) << 16) | + (*((sal_uInt8*)pLong + 2) << 8) | + (*((sal_uInt8*)pLong + 3) << 0) ); +} + +// ------------------------------------------------------------------ + +sal_uInt64 ResMgr::GetUInt64( void* pDatum ) +{ + return ((sal_uInt64(*((sal_uInt8*)pDatum + 0)) << 56) | + (sal_uInt64(*((sal_uInt8*)pDatum + 1)) << 48) | + (sal_uInt64(*((sal_uInt8*)pDatum + 2)) << 40) | + (sal_uInt64(*((sal_uInt8*)pDatum + 3)) << 32) | + (sal_uInt64(*((sal_uInt8*)pDatum + 4)) << 24) | + (sal_uInt64(*((sal_uInt8*)pDatum + 5)) << 16) | + (sal_uInt64(*((sal_uInt8*)pDatum + 6)) << 8) | + (sal_uInt64(*((sal_uInt8*)pDatum + 7)) << 0) ); +} + +// ----------------------------------------------------------------------- +sal_uInt32 ResMgr::GetStringWithoutHook( UniString& rStr, const BYTE* pStr ) +{ + sal_uInt32 nRet = GetStringSize( pStr ); + UniString aString( (sal_Char*)pStr, RTL_TEXTENCODING_UTF8, + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_MAPTOPRIVATE | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT ); + rStr = aString; + return nRet; +} + +sal_uInt32 ResMgr::GetString( UniString& rStr, const BYTE* pStr ) +{ + UniString aString; + sal_uInt32 nRet = GetStringWithoutHook( aString, pStr ); + if ( pImplResHookProc ) + pImplResHookProc( aString ); + rStr = aString; + return nRet; +} + +// ------------------------------------------------------------------ + +sal_uInt32 ResMgr::GetStringSize( const BYTE* pStr ) +{ + return GetStringSize( strlen( (const char*)pStr ) ); +} + +// ----------------------------------------------------------------------- + +sal_uInt32 ResMgr::GetRemainSize() +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if( pFallbackResMgr ) + return pFallbackResMgr->GetRemainSize(); + + const ImpRCStack& rTop = aStack[nCurStack]; + return (sal_uInt32)((long)(BYTE *)rTop.pResource + + rTop.pResource->GetLocalOff() - + (long)(BYTE *)rTop.pClassRes); +} + +// ----------------------------------------------------------------------- + +void* ResMgr::Increment( sal_uInt32 nSize ) +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if( pFallbackResMgr ) + return pFallbackResMgr->Increment( nSize ); + + ImpRCStack& rStack = aStack[nCurStack]; + if( (rStack.Flags & RC_NOTFOUND) ) + return rStack.pClassRes; + + BYTE* pClassRes = (BYTE*)rStack.pClassRes + nSize; + + rStack.pClassRes = pClassRes; + + RSHEADER_TYPE* pRes = rStack.pResource; + + sal_uInt32 nLocalOff = pRes->GetLocalOff(); + if ( (pRes->GetGlobOff() == nLocalOff) && + (((char*)pRes + nLocalOff) == rStack.pClassRes) && + (rStack.Flags & RC_AUTORELEASE)) + { + PopContext( rStack.pResObj ); + } + + return pClassRes; +} + +ResMgr* ResMgr::CreateFallbackResMgr( const ResId& rId, const Resource* pResource ) +{ + ResMgr *pFallback = NULL; + if( nCurStack > 0 ) + { + // get the next fallback level in resource file scope + InternalResMgr* pRes = ResMgrContainer::get().getNextFallback( pImpRes ); + if( pRes ) + { + // check that the fallback locale is not already in the chain of + // fallbacks - prevent fallback loops + ResMgr* pResMgr = this; + while( pResMgr && + ( pResMgr->pImpRes->aLocale.Language != pRes->aLocale.Language || + pResMgr->pImpRes->aLocale.Country != pRes->aLocale.Country || + pResMgr->pImpRes->aLocale.Variant != pRes->aLocale.Variant ) + ) + { + pResMgr = pResMgr->pOriginalResMgr; + } + if( pResMgr ) + { + // found a recursion, no fallback possible + ResMgrContainer::get().freeResMgr( pRes ); + return NULL; + } + OSL_TRACE( "trying fallback: %s\n", OUStringToOString( pRes->aFileName, osl_getThreadTextEncoding() ).getStr() ); + pFallback = new ResMgr( pRes ); + pFallback->pOriginalResMgr = this; + // try to recreate the resource stack + bool bHaveStack = true; + for( int i = 1; i < nCurStack; i++ ) + { + if( !aStack[i].pResource ) + { + bHaveStack = false; + break; + } + ResId aId( aStack[i].pResource->GetId(), *pFallbackResMgr ); + aId.SetRT( aStack[i].pResource->GetRT() ); + if( !pFallback->GetResource( aId ) ) + { + bHaveStack = false; + break; + } + } + if( bHaveStack ) + { + ResId aId( rId.GetId(), *pFallback ); + aId.SetRT( rId.GetRT() ); + if( !pFallback->GetResource( aId, pResource ) ) + bHaveStack = false; + else + pFallback->aStack[pFallback->nCurStack].Flags |= RC_FALLBACK_UP; + } + if( !bHaveStack ) + { + delete pFallback; + pFallback = NULL; + } + } + } + return pFallback; +} + +//--------------------------------------------------------------------------- +// +// method left here for SDK compatibility, +// used in "framework/source/services/substitutepathvars.cxx" +// +// phone numbers no longer in use for resource files +// +//--------------------------------------------------------------------------- + +const char* ResMgr::GetLang( LanguageType& nType, USHORT nPrio ) +{ + if ( nType == LANGUAGE_SYSTEM || nType == LANGUAGE_DONTKNOW ) + nType = MsLangId::getSystemUILanguage(); + + if ( nPrio == 0 ) + { + switch ( nType ) + { + case LANGUAGE_DANISH: + return "45"; + + case LANGUAGE_DUTCH: + case LANGUAGE_DUTCH_BELGIAN: + return "31"; + + case LANGUAGE_ENGLISH: + case LANGUAGE_ENGLISH_UK: + case LANGUAGE_ENGLISH_EIRE: + case LANGUAGE_ENGLISH_SAFRICA: + case LANGUAGE_ENGLISH_JAMAICA: + case LANGUAGE_ENGLISH_BELIZE: + case LANGUAGE_ENGLISH_TRINIDAD: + case LANGUAGE_ENGLISH_ZIMBABWE: + case LANGUAGE_ENGLISH_PHILIPPINES: + return "44"; + + case LANGUAGE_ENGLISH_US: + case LANGUAGE_ENGLISH_CAN: + return "01"; + + case LANGUAGE_ENGLISH_AUS: + case LANGUAGE_ENGLISH_NZ: + return "61"; + case LANGUAGE_ESTONIAN: + return "77"; + + + case LANGUAGE_FINNISH: + return "35"; + + case LANGUAGE_FRENCH_CANADIAN: + return "02"; + + case LANGUAGE_FRENCH: + case LANGUAGE_FRENCH_BELGIAN: + case LANGUAGE_FRENCH_SWISS: + case LANGUAGE_FRENCH_LUXEMBOURG: + case LANGUAGE_FRENCH_MONACO: + return "33"; + + case LANGUAGE_GERMAN: + case LANGUAGE_GERMAN_SWISS: + case LANGUAGE_GERMAN_AUSTRIAN: + case LANGUAGE_GERMAN_LUXEMBOURG: + case LANGUAGE_GERMAN_LIECHTENSTEIN: + return "49"; + + case LANGUAGE_ITALIAN: + case LANGUAGE_ITALIAN_SWISS: + return "39"; + + case LANGUAGE_NORWEGIAN: + case LANGUAGE_NORWEGIAN_BOKMAL: + return "47"; + + case LANGUAGE_PORTUGUESE: + return "03"; + + case LANGUAGE_PORTUGUESE_BRAZILIAN: + return "55"; + + case LANGUAGE_SPANISH_DATED: + case LANGUAGE_SPANISH_MEXICAN: + case LANGUAGE_SPANISH_MODERN: + case LANGUAGE_SPANISH_GUATEMALA: + case LANGUAGE_SPANISH_COSTARICA: + case LANGUAGE_SPANISH_PANAMA: + case LANGUAGE_SPANISH_DOMINICAN_REPUBLIC: + case LANGUAGE_SPANISH_VENEZUELA: + case LANGUAGE_SPANISH_COLOMBIA: + case LANGUAGE_SPANISH_PERU: + case LANGUAGE_SPANISH_ARGENTINA: + case LANGUAGE_SPANISH_ECUADOR: + case LANGUAGE_SPANISH_CHILE: + case LANGUAGE_SPANISH_URUGUAY: + case LANGUAGE_SPANISH_PARAGUAY: + case LANGUAGE_SPANISH_BOLIVIA: + return "34"; + + case LANGUAGE_SWEDISH: + return "46"; + + case LANGUAGE_POLISH: + return "48"; + case LANGUAGE_CZECH: + return "42"; + case LANGUAGE_SLOVENIAN: + return "50"; + case LANGUAGE_HUNGARIAN: + return "36"; + case LANGUAGE_RUSSIAN: + return "07"; + case LANGUAGE_SLOVAK: + return "43"; + case LANGUAGE_GREEK: + return "30"; + case LANGUAGE_TURKISH: + return "90"; + + case LANGUAGE_CHINESE_SIMPLIFIED: + return "86"; + case LANGUAGE_CHINESE_TRADITIONAL: + return "88"; + case LANGUAGE_JAPANESE: + return "81"; + case LANGUAGE_KOREAN: + case LANGUAGE_KOREAN_JOHAB: + return "82"; + case LANGUAGE_THAI: + return "66"; + case LANGUAGE_HINDI: + return "91"; + + case LANGUAGE_ARABIC_PRIMARY_ONLY: + case LANGUAGE_ARABIC_IRAQ: + case LANGUAGE_ARABIC_EGYPT: + case LANGUAGE_ARABIC_LIBYA: + case LANGUAGE_ARABIC_ALGERIA: + case LANGUAGE_ARABIC_MOROCCO: + case LANGUAGE_ARABIC_TUNISIA: + case LANGUAGE_ARABIC_OMAN: + case LANGUAGE_ARABIC_YEMEN: + case LANGUAGE_ARABIC_SYRIA: + case LANGUAGE_ARABIC_JORDAN: + case LANGUAGE_ARABIC_LEBANON: + case LANGUAGE_ARABIC_KUWAIT: + case LANGUAGE_ARABIC_UAE: + case LANGUAGE_ARABIC_BAHRAIN: + case LANGUAGE_ARABIC_QATAR: + return "96"; + + case LANGUAGE_HEBREW: + return "97"; + + case LANGUAGE_CATALAN: + return "37"; + + default: + return "99"; + } + } + else if ( nPrio == 1 ) + { + switch ( nType ) + { + case LANGUAGE_FRENCH_CANADIAN: + return "33"; + + case LANGUAGE_PORTUGUESE_BRAZILIAN: + return "03"; + + default: + return NULL; + } + } + else if ( nPrio == 2 ) + return "01"; + else if ( nPrio == 3 ) + return "44"; + else if ( nPrio == 4 ) + return "49"; + else + return "99"; +} + +// ----------------------------------------------------------------------- + +ResMgr* ResMgr::CreateResMgr( const sal_Char* pPrefixName, + com::sun::star::lang::Locale aLocale ) +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + OUString aPrefix( pPrefixName, strlen( pPrefixName ), osl_getThreadTextEncoding() ); + + if( ! aLocale.Language.getLength() ) + aLocale = ResMgrContainer::get().getDefLocale(); + + InternalResMgr* pImp = ResMgrContainer::get().getResMgr( aPrefix, aLocale ); + return pImp ? new ResMgr( pImp ) : NULL; +} + +// ----------------------------------------------------------------------- + +ResMgr* ResMgr::SearchCreateResMgr( + const sal_Char* pPrefixName, + com::sun::star::lang::Locale& rLocale ) +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + OUString aPrefix( pPrefixName, strlen( pPrefixName ), osl_getThreadTextEncoding() ); + + if( ! rLocale.Language.getLength() ) + rLocale = ResMgrContainer::get().getDefLocale(); + + InternalResMgr* pImp = ResMgrContainer::get().getResMgr( aPrefix, rLocale ); + return pImp ? new ResMgr( pImp ) : NULL; +} + +// ----------------------------------------------------------------------- + +INT16 ResMgr::ReadShort() +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if( pFallbackResMgr ) + return pFallbackResMgr->ReadShort(); + + INT16 n = GetShort( GetClass() ); + Increment( sizeof( INT16 ) ); + return n; +} + +// ----------------------------------------------------------------------- + +INT32 ResMgr::ReadLong() +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if( pFallbackResMgr ) + return pFallbackResMgr->ReadLong(); + + INT32 n = GetLong( GetClass() ); + Increment( sizeof( INT32 ) ); + return n; +} + +// ----------------------------------------------------------------------- + +UniString ResMgr::ReadStringWithoutHook() +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if( pFallbackResMgr ) + return pFallbackResMgr->ReadStringWithoutHook(); + + UniString aRet; + + const ImpRCStack& rTop = aStack[nCurStack]; + if( (rTop.Flags & RC_NOTFOUND) ) + { + #if OSL_DEBUG_LEVEL > 0 + aRet = OUString( RTL_CONSTASCII_USTRINGPARAM( "<resource not found>" ) ); + #endif + } + else + Increment( GetStringWithoutHook( aRet, (const BYTE*)GetClass() ) ); + + return aRet; +} + +UniString ResMgr::ReadString() +{ + UniString aRet = ReadStringWithoutHook(); + if ( pImplResHookProc ) + pImplResHookProc( aRet ); + return aRet; +} + +// ----------------------------------------------------------------------- + +ULONG ResMgr::GetAutoHelpId() +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + + if( pFallbackResMgr ) + return pFallbackResMgr->GetAutoHelpId(); + + DBG_ASSERT( nCurStack, "resource stack empty in Auto help id generation" ); + if( nCurStack < 1 || nCurStack > 2 ) + return 0; + + const ImpRCStack *pRC = StackTop( nCurStack==1 ? 0 : 1 ); + + DBG_ASSERT( pRC->pResource, "MM hat gesagt, dass der immer einen hat" ); + ULONG nGID = pRC->pResource->GetId(); + + if( !nGID || nGID > 32767 ) + return 0; + + ULONG nHID = 0; + + // GGGg gggg::gggg gggg::ggLL LLLl::llll llll + switch( pRC->pResource->GetRT() ) { // maximal 7 + case RSC_DOCKINGWINDOW: + nHID += 0x20000000L; + case RSC_WORKWIN: + nHID += 0x20000000L; + case RSC_MODELESSDIALOG: + nHID += 0x20000000L; + case RSC_FLOATINGWINDOW: + nHID += 0x20000000L; + case RSC_MODALDIALOG: + nHID += 0x20000000L; + case RSC_TABPAGE: + nHID += 0x20000000L; + + if( nCurStack == 2 ) { + pRC = StackTop(); + ULONG nLID = pRC->pResource->GetId(); + + if( !nLID || nLID > 511 ) + return 0; + + switch( pRC->pResource->GetRT() ) { // maximal 32 + case RSC_TABCONTROL: nHID |= 0x0000; break; + case RSC_RADIOBUTTON: nHID |= 0x0200; break; + case RSC_CHECKBOX: nHID |= 0x0400; break; + case RSC_TRISTATEBOX: nHID |= 0x0600; break; + case RSC_EDIT: nHID |= 0x0800; break; + case RSC_MULTILINEEDIT: nHID |= 0x0A00; break; + case RSC_MULTILISTBOX: nHID |= 0x0C00; break; + case RSC_LISTBOX: nHID |= 0x0E00; break; + case RSC_COMBOBOX: nHID |= 0x1000; break; + case RSC_PUSHBUTTON: nHID |= 0x1200; break; + case RSC_SPINFIELD: nHID |= 0x1400; break; + case RSC_PATTERNFIELD: nHID |= 0x1600; break; + case RSC_NUMERICFIELD: nHID |= 0x1800; break; + case RSC_METRICFIELD: nHID |= 0x1A00; break; + case RSC_CURRENCYFIELD: nHID |= 0x1C00; break; + case RSC_DATEFIELD: nHID |= 0x1E00; break; + case RSC_TIMEFIELD: nHID |= 0x2000; break; + case RSC_IMAGERADIOBUTTON: nHID |= 0x2200; break; + case RSC_NUMERICBOX: nHID |= 0x2400; break; + case RSC_METRICBOX: nHID |= 0x2600; break; + case RSC_CURRENCYBOX: nHID |= 0x2800; break; + case RSC_DATEBOX: nHID |= 0x2A00; break; + case RSC_TIMEBOX: nHID |= 0x2C00; break; + case RSC_IMAGEBUTTON: nHID |= 0x2E00; break; + case RSC_MENUBUTTON: nHID |= 0x3000; break; + case RSC_MOREBUTTON: nHID |= 0x3200; break; + default: + return 0; + } // of switch + nHID |= nLID; + } // of if + break; + default: + return 0; + } // of switch + nHID |= nGID << 14; + + return nHID; +} + +// ----------------------------------------------------------------------- + +void ResMgr::SetReadStringHook( ResHookProc pProc ) +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + pImplResHookProc = pProc; +} + +// ----------------------------------------------------------------------- + +ResHookProc ResMgr::GetReadStringHook() +{ + return pImplResHookProc; +} + +// ----------------------------------------------------------------------- + +void ResMgr::SetDefaultLocale( const com::sun::star::lang::Locale& rLocale ) +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + ResMgrContainer::get().setDefLocale( rLocale ); +} + +// ----------------------------------------------------------------------- + +const OUString& ResMgr::GetFileName() const +{ + return pImpRes->aFileName; +} + +// ======================================================================= + +SimpleResMgr::SimpleResMgr( const sal_Char* pPrefixName, + const ::com::sun::star::lang::Locale& rLocale ) +{ + OUString aPrefix( pPrefixName, strlen( pPrefixName ), osl_getThreadTextEncoding() ); + com::sun::star::lang::Locale aLocale( rLocale ); + + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + if( ! aLocale.Language.getLength() ) + aLocale = ResMgrContainer::get().getDefLocale(); + + m_pResImpl = ResMgrContainer::get().getResMgr( aPrefix, aLocale, true ); + DBG_ASSERT( m_pResImpl, "SimpleResMgr::SimpleResMgr : have no impl class !" ); +} + +// ----------------------------------------------------------------------- +SimpleResMgr::SimpleResMgr( const ::rtl::OUString& _rPrefixName, ::com::sun::star::lang::Locale& _inout_Locale ) +{ + osl::Guard<osl::Mutex> aGuard( getResMgrMutex() ); + m_pResImpl = ResMgrContainer::get().getResMgr( _rPrefixName, _inout_Locale, true ); +} + +// ----------------------------------------------------------------------- +SimpleResMgr::~SimpleResMgr() +{ + delete m_pResImpl; +} + +// ----------------------------------------------------------------------- +SimpleResMgr* SimpleResMgr::Create( const sal_Char* pPrefixName, com::sun::star::lang::Locale aLocale ) +{ + return new SimpleResMgr( pPrefixName, aLocale ); +} + +// ----------------------------------------------------------------------- +bool SimpleResMgr::IsAvailable( RESOURCE_TYPE _resourceType, sal_uInt32 _resourceId ) +{ + NAMESPACE_VOS(OGuard) aGuard(m_aAccessSafety); + + if ( ( RSC_STRING != _resourceType ) && ( RSC_RESOURCE != _resourceType ) ) + return false; + + DBG_ASSERT( m_pResImpl, "SimpleResMgr::IsAvailable: have no impl class !" ); + return m_pResImpl->IsGlobalAvailable( _resourceType, _resourceId ); +} + +// ----------------------------------------------------------------------- +UniString SimpleResMgr::ReadString( sal_uInt32 nId ) +{ + NAMESPACE_VOS(OGuard) aGuard(m_aAccessSafety); + + DBG_ASSERT( m_pResImpl, "SimpleResMgr::ReadString : have no impl class !" ); + // perhaps constructed with an invalid filename ? + + UniString sReturn; + if ( !m_pResImpl ) + return sReturn; + + void* pResHandle = NULL; + InternalResMgr* pFallback = m_pResImpl; + RSHEADER_TYPE* pResHeader = (RSHEADER_TYPE*)m_pResImpl->LoadGlobalRes( RSC_STRING, nId, &pResHandle ); + if ( !pResHeader ) + { + osl::Guard<osl::Mutex> aGuard2( getResMgrMutex() ); + + // try fallback + while( ! pResHandle && pFallback ) + { + InternalResMgr* pOldFallback = pFallback; + pFallback = ResMgrContainer::get().getNextFallback( pFallback ); + if( pOldFallback != m_pResImpl ) + ResMgrContainer::get().freeResMgr( pOldFallback ); + if( pFallback ) + { + // handle possible recursion + if( pFallback->aLocale.Language != m_pResImpl->aLocale.Language || + pFallback->aLocale.Country != m_pResImpl->aLocale.Country || + pFallback->aLocale.Variant != m_pResImpl->aLocale.Variant ) + { + pResHeader = (RSHEADER_TYPE*)pFallback->LoadGlobalRes( RSC_STRING, nId, &pResHandle ); + } + else + { + ResMgrContainer::get().freeResMgr( pFallback ); + pFallback = NULL; + } + } + } + if( ! pResHandle ) + // no such resource + return sReturn; + } + + // ULONG nLen = pResHeader->GetLocalOff() - sizeof(RSHEADER_TYPE); + ResMgr::GetString( sReturn, (const BYTE*)(pResHeader+1) ); + + // not neccessary with te current implementation which holds the string table permanently, but to be sure .... + // note: pFallback cannot be NULL here and is either the fallback or m_pResImpl + pFallback->FreeGlobalRes( pResHeader, pResHandle ); + if( m_pResImpl != pFallback ) + { + osl::Guard<osl::Mutex> aGuard2( getResMgrMutex() ); + + ResMgrContainer::get().freeResMgr( pFallback ); + } + return sReturn; +} + +// ----------------------------------------------------------------------- + +const ::com::sun::star::lang::Locale& SimpleResMgr::GetLocale() const +{ + DBG_ASSERT( IsValid(), "SimpleResMgr::ReadBlob: invalid, this will crash!" ); + return m_pResImpl->aLocale; +} + +// ----------------------------------------------------------------------- + +sal_uInt32 SimpleResMgr::ReadBlob( sal_uInt32 nId, void** pBuffer ) +{ + NAMESPACE_VOS(OGuard) aGuard(m_aAccessSafety); + + DBG_ASSERT( m_pResImpl, "SimpleResMgr::ReadBlob : have no impl class !" ); + + // perhaps constructed with an invalid filename ? + DBG_ASSERT( pBuffer, "SimpleResMgr::ReadBlob : invalid argument !" ); + *pBuffer = NULL; + + void* pResHandle = NULL; + InternalResMgr* pFallback = m_pResImpl; + RSHEADER_TYPE* pResHeader = (RSHEADER_TYPE*)m_pResImpl->LoadGlobalRes( RSC_RESOURCE, nId, &pResHandle ); + DBG_ASSERT( pResHeader, "SimpleResMgr::ReadBlob : couldn't find the resource with the given id !" ); + + if ( !pResHeader ) + { + osl::Guard<osl::Mutex> aGuard2( getResMgrMutex() ); + + // try fallback + while( ! pResHandle && pFallback ) + { + InternalResMgr* pOldFallback = pFallback; + pFallback = ResMgrContainer::get().getNextFallback( pFallback ); + if( pOldFallback != m_pResImpl ) + ResMgrContainer::get().freeResMgr( pOldFallback ); + if( pFallback ) + { + // handle possible recursion + if( pFallback->aLocale.Language != m_pResImpl->aLocale.Language || + pFallback->aLocale.Country != m_pResImpl->aLocale.Country || + pFallback->aLocale.Variant != m_pResImpl->aLocale.Variant ) + { + pResHeader = (RSHEADER_TYPE*)pFallback->LoadGlobalRes( RSC_RESOURCE, nId, &pResHandle ); + } + else + { + ResMgrContainer::get().freeResMgr( pFallback ); + pFallback = NULL; + } + } + } + if( ! pResHandle ) + // no exception handling, this would require the locking of the solar mutex which isn't allowed within this class + return 0; + } + + DBG_ASSERT( pResHandle == NULL, "SimpleResMgr::ReadBlob : behaviour of LoadGlobalRes changed !" ); + // if pResHandle is not NULL the FreeBlob wouldn't have to delete the pointer given as pBuffer, but + // FreeBlob doesn't know that so it would probably crash .... + + sal_uInt32 nRemaining = pResHeader->GetLocalOff() - sizeof(RSHEADER_TYPE); + *pBuffer = (void*)(((BYTE*)pResHeader) + sizeof(RSHEADER_TYPE)); + + // free an eventual fallback InternalResMgr + if( m_pResImpl != pFallback ) + { + osl::Guard<osl::Mutex> aGuard2( getResMgrMutex() ); + + ResMgrContainer::get().freeResMgr( pFallback ); + } + + return nRemaining; +} + +// ----------------------------------------------------------------------- + +void SimpleResMgr::FreeBlob( void* pBuffer ) +{ + void* pCompleteBuffer = (void*)(((BYTE*)pBuffer) - sizeof(RSHEADER_TYPE)); + rtl_freeMemory(pCompleteBuffer); +} diff --git a/tools/source/ref/errinf.cxx b/tools/source/ref/errinf.cxx new file mode 100644 index 000000000000..25d894f4b925 --- /dev/null +++ b/tools/source/ref/errinf.cxx @@ -0,0 +1,465 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: errinf.cxx,v $ + * $Revision: 1.9 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <limits.h> +#include <tools/shl.hxx> +#include <tools/debug.hxx> +#include <tools/errinf.hxx> +#include <tools/string.hxx> + +class ErrorHandler; + +namespace { + +typedef void (* DisplayFnPtr)(); + +} + +struct EDcrData +{ + public: + + ErrorHandler *pFirstHdl; + ErrorContext *pFirstCtx; + DisplayFnPtr pDsp; + BOOL bIsWindowDsp; + + + DynamicErrorInfo *ppDcr[ERRCODE_DYNAMIC_COUNT]; + USHORT nNextDcr; + EDcrData(); + +static EDcrData *GetData(); + +}; + +class EDcr_Impl +{ + ULONG lErrId; + USHORT nMask; + + void RegisterEDcr(DynamicErrorInfo *); + void UnRegisterEDcr(DynamicErrorInfo *); + static ErrorInfo *GetDynamicErrorInfo(ULONG lId); + +friend class DynamicErrorInfo; +friend class ErrorInfo; +}; + + +EDcrData::EDcrData() +{ + for(USHORT n=0;n<ERRCODE_DYNAMIC_COUNT;n++) + ppDcr[n]=0; + nNextDcr=0; + pFirstHdl=0; + pDsp=0; + pFirstCtx=0; +} + + +EDcrData *EDcrData::GetData() +{ +#ifdef BOOTSTRAP + return 0x0; +#else + EDcrData **ppDat=(EDcrData **)GetAppData(SHL_ERR); + if(!*ppDat) + { + return (*ppDat=new EDcrData); + } + else + return *ppDat; +#endif +} + +void EDcr_Impl::RegisterEDcr(DynamicErrorInfo *pDcr) +{ + //Vergibt eine dynamische Id + + EDcrData* pData=EDcrData::GetData(); + lErrId= (((ULONG)pData->nNextDcr + 1) << ERRCODE_DYNAMIC_SHIFT) + + pDcr->GetErrorCode(); + DynamicErrorInfo **ppDcr=pData->ppDcr; + USHORT nNext=pData->nNextDcr; + + // bei einem Ringbuffer koennen wir uns das ASSERT wohl sparen! + // DBG_ASSERT(ppDcr[nNext]==0,"ErrHdl: Alle Errors belegt"); + if(ppDcr[nNext]) + { + delete ppDcr[nNext]; + } + ppDcr[nNext]=pDcr; + if(++pData->nNextDcr>=ERRCODE_DYNAMIC_COUNT) + pData->nNextDcr=0; +} + + +void EDcr_Impl::UnRegisterEDcr(DynamicErrorInfo *pDcr) +{ + + EDcrData* pData=EDcrData::GetData(); + DynamicErrorInfo **ppDcr=pData->ppDcr; + ULONG lIdx=( + ((ULONG)(*pDcr) & ERRCODE_DYNAMIC_MASK)>>ERRCODE_DYNAMIC_SHIFT)-1; + DBG_ASSERT(ppDcr[lIdx]==pDcr,"ErrHdl: Error nicht gefunden"); + if(ppDcr[lIdx]==pDcr) + ppDcr[lIdx]=0; +} + +TYPEINIT0(ErrorInfo); +TYPEINIT1(DynamicErrorInfo, ErrorInfo); +TYPEINIT1(StandardErrorInfo, DynamicErrorInfo); +TYPEINIT1(StringErrorInfo, DynamicErrorInfo); +TYPEINIT1(TwoStringErrorInfo, DynamicErrorInfo); +TYPEINIT1(MessageInfo, DynamicErrorInfo); + + +ErrorInfo *ErrorInfo::GetErrorInfo(ULONG lId) +{ + if(lId & ERRCODE_DYNAMIC_MASK) + return EDcr_Impl::GetDynamicErrorInfo(lId); + else + return new ErrorInfo(lId); +} + +DynamicErrorInfo::operator ULONG() const +{ + return pImpl->lErrId; +} + +DynamicErrorInfo::DynamicErrorInfo(ULONG lArgUserId, USHORT nMask) +: ErrorInfo(lArgUserId) +{ + pImpl=new EDcr_Impl; + pImpl->RegisterEDcr(this); + pImpl->nMask=nMask; +} + +DynamicErrorInfo::~DynamicErrorInfo() +{ + pImpl->UnRegisterEDcr(this); + delete pImpl; +} + +ErrorInfo* EDcr_Impl::GetDynamicErrorInfo(ULONG lId) +{ + ULONG lIdx=((lId & ERRCODE_DYNAMIC_MASK)>>ERRCODE_DYNAMIC_SHIFT)-1; + DynamicErrorInfo* pDcr=EDcrData::GetData()->ppDcr[lIdx]; + if(pDcr && (ULONG)(*pDcr)==lId) + return pDcr; + else + return new ErrorInfo(lId & ~ERRCODE_DYNAMIC_MASK); +} + + +USHORT DynamicErrorInfo::GetDialogMask() const +{ + return pImpl->nMask; +} + + +StandardErrorInfo::StandardErrorInfo( + ULONG UserId, ULONG lArgExtId, USHORT nFlags) +: DynamicErrorInfo(UserId, nFlags), lExtId(lArgExtId) +{ +} + + +StringErrorInfo::StringErrorInfo( + ULONG UserId, const String& aStringP, USHORT nFlags) +: DynamicErrorInfo(UserId, nFlags), aString(aStringP) +{ +} + + +class ErrHdl_Impl +{ + public: + + ErrorHandler *pNext; + static BOOL CreateString(const ErrorHandler *pStart, + const ErrorInfo*, String&, USHORT&); +}; + + +static void aDspFunc(const String &rErr, const String &rAction) +{ + ByteString aErr("Aktion: "); + aErr+= ByteString( rAction, RTL_TEXTENCODING_ASCII_US ); + aErr+=" Fehler: "; + aErr+= ByteString( rErr, RTL_TEXTENCODING_ASCII_US ); + DBG_ERROR(aErr.GetBuffer()); +} + + +ErrorContext::ErrorContext(Window *pWinP) +{ + EDcrData *pData=EDcrData::GetData(); + ErrorContext *&pHdl=pData->pFirstCtx; + pWin=pWinP; + pNext=pHdl; + pHdl=this; +} + +ErrorContext::~ErrorContext() +{ + ErrorContext **ppCtx=&(EDcrData::GetData()->pFirstCtx); + while(*ppCtx && *ppCtx!=this) + ppCtx=&((*ppCtx)->pNext); + if(*ppCtx) + *ppCtx=(*ppCtx)->pNext; +} + +ErrorContext *ErrorContext::GetContext() +{ + return EDcrData::GetData()->pFirstCtx; +} + +ErrorHandler::ErrorHandler() +{ + pImpl=new ErrHdl_Impl; + EDcrData *pData=EDcrData::GetData(); + ErrorHandler *&pHdl=pData->pFirstHdl; + pImpl->pNext=pHdl; + pHdl=this; + if(!pData->pDsp) + RegisterDisplay(&aDspFunc); +} + +ErrorHandler::~ErrorHandler() +{ + ErrorHandler **ppHdl=&(EDcrData::GetData()->pFirstHdl); + while(*ppHdl && *ppHdl!=this) + ppHdl=&((*ppHdl)->pImpl->pNext); + if(*ppHdl) + *ppHdl=(*ppHdl)->pImpl->pNext; + delete pImpl; +} + +void ErrorHandler::RegisterDisplay(WindowDisplayErrorFunc *aDsp) +{ + EDcrData *pData=EDcrData::GetData(); + pData->bIsWindowDsp=TRUE; + pData->pDsp = reinterpret_cast< DisplayFnPtr >(aDsp); +} + +void ErrorHandler::RegisterDisplay(BasicDisplayErrorFunc *aDsp) +{ + EDcrData *pData=EDcrData::GetData(); + pData->bIsWindowDsp=FALSE; + pData->pDsp = reinterpret_cast< DisplayFnPtr >(aDsp); +} + +USHORT ErrorHandler::HandleError_Impl( + ULONG lId, USHORT nFlags, BOOL bJustCreateString, String & rError) +{ + +/* [Beschreibung] + Handelt einen Fehler ab. lId ist die FehlerId, nFlags sind die + ErrorFlags. Werden nFlags nicht abgegeben, so werden die in + der DynamicErrorInfo angegebenen Flags bzw. die aus der Resource + verwendet. + + Also: + + 1. nFlags, + 2. Resource Flags + 3. Dynamic Flags + 4. Default ERRCODE_BUTTON_OK, ERRCODE_MSG_ERROR + + + */ + + String aErr; + String aAction; + if(!lId || lId == ERRCODE_ABORT) + return 0; + EDcrData *pData=EDcrData::GetData(); + ErrorInfo *pInfo=ErrorInfo::GetErrorInfo(lId); + ErrorContext *pCtx=ErrorContext::GetContext(); + if(pCtx) + pCtx->GetString(pInfo->GetErrorCode(), aAction); + Window *pParent=0; + //Nimm den Parent aus dem Konext + for(;pCtx;pCtx=pCtx->pNext) + if(pCtx->GetParent()) + { + pParent=pCtx->GetParent(); + break; + } + + BOOL bWarning = ((lId & ERRCODE_WARNING_MASK) == ERRCODE_WARNING_MASK); + USHORT nErrFlags = ERRCODE_BUTTON_DEF_OK | ERRCODE_BUTTON_OK; + if (bWarning) + nErrFlags |= ERRCODE_MSG_WARNING; + else + nErrFlags |= ERRCODE_MSG_ERROR; + + DynamicErrorInfo* pDynPtr=PTR_CAST(DynamicErrorInfo,pInfo); + if(pDynPtr) + { + USHORT nDynFlags = pDynPtr->GetDialogMask(); + if( nDynFlags ) + nErrFlags = nDynFlags; + } + + if(ErrHdl_Impl::CreateString(pData->pFirstHdl,pInfo,aErr,nErrFlags)) + { + if (bJustCreateString) + { + rError = aErr; + return 1; + } + else + { + if(!pData->pDsp) + { + ByteString aStr("Action: "); + aStr += ByteString( aAction, RTL_TEXTENCODING_ASCII_US ); + aStr += ByteString("\nFehler: "); + aStr += ByteString( aErr, RTL_TEXTENCODING_ASCII_US ); + DBG_ERROR( aStr.GetBuffer() ); + } + else + { + delete pInfo; + if(!pData->bIsWindowDsp) + { + (*(BasicDisplayErrorFunc*)pData->pDsp)(aErr,aAction); + return 0; + } + else + { + if( nFlags != USHRT_MAX ) + nErrFlags = nFlags; + return (*(WindowDisplayErrorFunc*)pData->pDsp)( + pParent, nErrFlags, aErr, aAction); + } + } + } + } + DBG_ERROR("Error nicht behandelt"); + // Error 1 ist General Error im Sfx + if(pInfo->GetErrorCode()!=1) { + HandleError_Impl(1, USHRT_MAX, bJustCreateString, rError); + } + else { + DBG_ERROR("Error 1 nicht gehandeled"); + } + delete pInfo; + return 0; +} + +// static +BOOL ErrorHandler::GetErrorString(ULONG lId, String& rStr) +{ + return (BOOL)HandleError_Impl( lId, USHRT_MAX, TRUE, rStr ); +} + +USHORT ErrorHandler::HandleError(ULONG lId, USHORT nFlags) +{ + +/* [Beschreibung] + Handelt einen Fehler ab. lId ist die FehlerId, nFlags sind die + ErrorFlags. Werden nFlags nicht abgegeben, so werden die in + der DynamicErrorInfo angegebenen Flags bzw. die aus der Resource + verwendet. + + Also: + + 1. nFlags, + 2. Resource Flags + 3. Dynamic Flags + 4. Default ERRCODE_BUTTON_OK, ERRCODE_MSG_ERROR + + + */ + + String aDummy; + return HandleError_Impl( lId, nFlags, FALSE, aDummy ); +} + +BOOL ErrorHandler::ForwCreateString(const ErrorInfo* pInfo, String& rStr, USHORT &rFlags) const +{ + return ErrHdl_Impl::CreateString(this->pImpl->pNext, pInfo, rStr, rFlags); +} + +BOOL ErrHdl_Impl::CreateString( const ErrorHandler *pStart, + const ErrorInfo* pInfo, String& pStr, + USHORT &rFlags) +{ + for(const ErrorHandler *pHdl=pStart;pHdl;pHdl=pHdl->pImpl->pNext) + { + if(pHdl->CreateString( pInfo, pStr, rFlags)) + return TRUE; + } + return FALSE; +} + +BOOL SimpleErrorHandler::CreateString( + const ErrorInfo *pInfo, String &rStr, USHORT &) const +{ + ULONG nId = pInfo->GetErrorCode(); + ByteString aStr; + aStr="Id "; + aStr+=ByteString::CreateFromInt32(nId); + aStr+=" only handled by SimpleErrorHandler"; + aStr+="\nErrorCode: "; + aStr+=ByteString::CreateFromInt32(nId & ((1L << ERRCODE_CLASS_SHIFT) - 1 )); + aStr+="\nErrorClass: "; + aStr+=ByteString::CreateFromInt32((nId & ERRCODE_CLASS_MASK) >> ERRCODE_CLASS_SHIFT); + aStr+="\nErrorArea: "; + aStr+=ByteString::CreateFromInt32((nId & ERRCODE_ERROR_MASK & + ~((1 << ERRCODE_AREA_SHIFT ) -1 ) ) >> ERRCODE_AREA_SHIFT); + DynamicErrorInfo *pDyn=PTR_CAST(DynamicErrorInfo,pInfo); + if(pDyn) + { + aStr+="\nDId "; + aStr+=ByteString::CreateFromInt32((ULONG)*pDyn); + } + StandardErrorInfo *pStd=PTR_CAST(StandardErrorInfo,pInfo); + if(pStd) + { + aStr+="\nXId "; + aStr+=ByteString::CreateFromInt32(pStd->GetExtendedErrorCode()); + } + rStr = String( aStr, RTL_TEXTENCODING_ASCII_US ); + return TRUE; +} + +SimpleErrorHandler::SimpleErrorHandler() + : ErrorHandler() +{ +} + diff --git a/tools/source/ref/globname.cxx b/tools/source/ref/globname.cxx new file mode 100644 index 000000000000..50d028acacc4 --- /dev/null +++ b/tools/source/ref/globname.cxx @@ -0,0 +1,456 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: globname.cxx,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include <tools/stream.hxx> +#include <tools/globname.hxx> + +/************** class ImpSvGlobalName ************************************/ +ImpSvGlobalName::ImpSvGlobalName( const ImpSvGlobalName & rObj ) +{ + nRefCount = 0; + memcpy( szData, rObj.szData, sizeof( szData ) ); +} + +/************** class ImpSvGlobalName ************************************/ +ImpSvGlobalName::ImpSvGlobalName( int ) +{ + nRefCount = 1; + memset( szData, 0, sizeof( szData ) ); +} + +/************************************************************************* +|* ImpSvGlobalName::operator ==() +*************************************************************************/ +BOOL ImpSvGlobalName::operator == ( const ImpSvGlobalName & rObj ) const +{ + return !memcmp( szData, rObj.szData, sizeof( szData ) ); +} + +/************************************************************************* +|* SvGlobalName::SvGlobalName() +*************************************************************************/ +SvGlobalName::SvGlobalName() +{ + static ImpSvGlobalName aNoName( 0 ); + + pImp = &aNoName; + pImp->nRefCount++; +} + +// locker die Struktur von Windows kopiert +#ifdef WNT +struct _GUID +#else +struct GUID +#endif +{ + UINT32 Data1; + UINT16 Data2; + UINT16 Data3; + BYTE Data4[8]; +}; +SvGlobalName::SvGlobalName( const CLSID & rId ) +{ + pImp = new ImpSvGlobalName(); + pImp->nRefCount++; + memcpy( pImp->szData, &rId, sizeof( pImp->szData ) ); +} + +SvGlobalName::SvGlobalName( UINT32 n1, USHORT n2, USHORT n3, + BYTE b8, BYTE b9, BYTE b10, BYTE b11, + BYTE b12, BYTE b13, BYTE b14, BYTE b15 ) +{ + pImp = new ImpSvGlobalName(); + pImp->nRefCount++; + + *(UINT32 *)pImp->szData = n1; + *(USHORT *)&pImp->szData[ 4 ] = n2; + *(USHORT *)&pImp->szData[ 6 ] = n3; + pImp->szData[ 8 ] = b8; + pImp->szData[ 9 ] = b9; + pImp->szData[ 10 ] = b10; + pImp->szData[ 11 ] = b11; + pImp->szData[ 12 ] = b12; + pImp->szData[ 13 ] = b13; + pImp->szData[ 14 ] = b14; + pImp->szData[ 15 ] = b15; +} + +/************************************************************************* +|* SvGlobalName::~SvGlobalName() +*************************************************************************/ +SvGlobalName::~SvGlobalName() +{ + pImp->nRefCount--; + if( !pImp->nRefCount ) + delete pImp; +} + +/************************************************************************* +|* SvGlobalName::operator = () +*************************************************************************/ +SvGlobalName & SvGlobalName::operator = ( const SvGlobalName & rObj ) +{ + rObj.pImp->nRefCount++; + pImp->nRefCount--; + if( !pImp->nRefCount ) + delete pImp; + pImp = rObj.pImp; + return *this; +} + +/************************************************************************* +|* SvGlobalName::NewImp() +*************************************************************************/ +void SvGlobalName::NewImp() +{ + if( pImp->nRefCount > 1 ) + { + pImp->nRefCount--; + pImp = new ImpSvGlobalName( *pImp ); + pImp->nRefCount++; + } +} + +/************************************************************************* +|* SvGlobalName::operator << () +|* SvGlobalName::operator >> () +*************************************************************************/ +SvStream& operator << ( SvStream& rOStr, const SvGlobalName & rObj ) +{ + rOStr << *(UINT32 *)rObj.pImp->szData; + rOStr << *(USHORT *)&rObj.pImp->szData[ 4 ]; + rOStr << *(USHORT *)&rObj.pImp->szData[ 6 ]; + rOStr.Write( (sal_Char *)&rObj.pImp->szData[ 8 ], 8 ); + return rOStr; +} + +SvStream& operator >> ( SvStream& rStr, SvGlobalName & rObj ) +{ + rObj.NewImp(); // kopieren, falls noetig + rStr >> *(UINT32 *)rObj.pImp->szData; + rStr >> *(USHORT *)&rObj.pImp->szData[ 4 ]; + rStr >> *(USHORT *)&rObj.pImp->szData[ 6 ]; + rStr.Read( (sal_Char *)&rObj.pImp->szData[ 8 ], 8 ); + return rStr; +} + + +/************************************************************************* +|* SvGlobalName::operator < () +*************************************************************************/ +BOOL SvGlobalName::operator < ( const SvGlobalName & rObj ) const +{ + int n = memcmp( pImp->szData +6, rObj.pImp->szData +6, + sizeof( pImp->szData ) -6); + if( n < 0 ) + return TRUE; + else if( n > 0 ) + return FALSE; + else if( *(USHORT *)&pImp->szData[ 4 ] < *(USHORT *)&rObj.pImp->szData[ 4 ] ) + return TRUE; + else if( *(USHORT *)&pImp->szData[ 4 ] == *(USHORT *)&rObj.pImp->szData[ 4 ] ) + return *(UINT32 *)pImp->szData < *(UINT32 *)rObj.pImp->szData; + else + return FALSE; + +} + +/************************************************************************* +|* SvGlobalName::operator +=() +*************************************************************************/ +SvGlobalName & SvGlobalName::operator += ( UINT32 n ) +{ + NewImp(); + UINT32 nOld = (*(UINT32 *)pImp->szData); + (*(UINT32 *)pImp->szData) += n; + if( nOld > *(UINT32 *)pImp->szData ) + // ueberlauf + (*(USHORT *)&pImp->szData[ 4 ])++; + return *this; +} + +/************************************************************************* +|* SvGlobalName::operator ==() +*************************************************************************/ +BOOL SvGlobalName::operator == ( const SvGlobalName & rObj ) const +{ + return *pImp == *rObj.pImp; +} + +void SvGlobalName::MakeFromMemory( void * pData ) +{ + NewImp(); + memcpy( pImp->szData, pData, sizeof( pImp->szData ) ); +} + +/************************************************************************* +|* SvGlobalName::MakeId() +*************************************************************************/ +BOOL SvGlobalName::MakeId( const String & rIdStr ) +{ + ByteString aStr( rIdStr, RTL_TEXTENCODING_ASCII_US ); + sal_Char * pStr = (sal_Char *)aStr.GetBuffer(); + if( rIdStr.Len() == 36 + && '-' == pStr[ 8 ] && '-' == pStr[ 13 ] + && '-' == pStr[ 18 ] && '-' == pStr[ 23 ] ) + { + UINT32 nFirst = 0; + int i = 0; + for( i = 0; i < 8; i++ ) + { + if( isxdigit( *pStr ) ) + if( isdigit( *pStr ) ) + nFirst = nFirst * 16 + (*pStr - '0'); + else + nFirst = nFirst * 16 + (toupper( *pStr ) - 'A' + 10 ); + else + return FALSE; + pStr++; + } + + UINT16 nSec = 0; + pStr++; + for( i = 0; i < 4; i++ ) + { + if( isxdigit( *pStr ) ) + if( isdigit( *pStr ) ) + nSec = nSec * 16 + (*pStr - '0'); + else + nSec = nSec * 16 + (UINT16)(toupper( *pStr ) - 'A' + 10 ); + else + return FALSE; + pStr++; + } + + UINT16 nThird = 0; + pStr++; + for( i = 0; i < 4; i++ ) + { + if( isxdigit( *pStr ) ) + if( isdigit( *pStr ) ) + nThird = nThird * 16 + (*pStr - '0'); + else + nThird = nThird * 16 + (UINT16)(toupper( *pStr ) - 'A' + 10 ); + else + return FALSE; + pStr++; + } + + BYTE szRemain[ 8 ]; + memset( szRemain, 0, sizeof( szRemain ) ); + pStr++; + for( i = 0; i < 16; i++ ) + { + if( isxdigit( *pStr ) ) + if( isdigit( *pStr ) ) + szRemain[i/2] = szRemain[i/2] * 16 + (*pStr - '0'); + else + szRemain[i/2] = szRemain[i/2] * 16 + (BYTE)(toupper( *pStr ) - 'A' + 10 ); + else + return FALSE; + pStr++; + if( i == 3 ) + pStr++; + } + + NewImp(); + *(UINT32 *)pImp->szData = nFirst; + *(USHORT *)&pImp->szData[ 4 ] = nSec; + *(USHORT *)&pImp->szData[ 6 ] = nThird; + memcpy( &pImp->szData[ 8 ], szRemain, 8 ); + return TRUE; + } + return FALSE; +} + +/************************************************************************* +|* SvGlobalName::GetctorName() +*************************************************************************/ +String SvGlobalName::GetctorName() const +{ + ByteString aRet; + + sal_Char buf[ 20 ]; + sprintf( buf, "0x%8.8lX", (ULONG)*(UINT32 *)pImp->szData ); + aRet += buf; + USHORT i; + for( i = 4; i < 8; i += 2 ) + { + aRet += ','; + sprintf( buf, "0x%4.4X", *(USHORT *)&pImp->szData[ i ] ); + aRet += buf; + } + for( i = 8; i < 16; i++ ) + { + aRet += ','; + sprintf( buf, "0x%2.2x", pImp->szData[ i ] ); + aRet += buf; + } + return String( aRet, RTL_TEXTENCODING_ASCII_US ); +} + +/************************************************************************* +|* SvGlobalName::GetHexName() +*************************************************************************/ +String SvGlobalName::GetHexName() const +{ + ByteString aRet; + + sal_Char buf[ 10 ]; + sprintf( buf, "%8.8lX", (ULONG)*(UINT32 *)pImp->szData ); + aRet += buf; + aRet += '-'; + USHORT i ; + for( i = 4; i < 8; i += 2 ) + { + sprintf( buf, "%4.4X", *(USHORT *)&pImp->szData[ i ] ); + aRet += buf; + aRet += '-'; + } + for( i = 8; i < 10; i++ ) + { + sprintf( buf, "%2.2x", pImp->szData[ i ] ); + aRet += buf; + } + aRet += '-'; + for( i = 10; i < 16; i++ ) + { + sprintf( buf, "%2.2x", pImp->szData[ i ] ); + aRet += buf; + } + return String( aRet, RTL_TEXTENCODING_ASCII_US ); +} + +/************** SvGlobalNameList ****************************************/ +/************************************************************************/ +/************************************************************************* +|* SvGlobalNameList::SvGlobalNameList() +*************************************************************************/ +SvGlobalNameList::SvGlobalNameList() + : aList( 1, 1 ) +{ +} + +/************************************************************************* +|* SvGlobalNameList::~SvGlobalNameList() +*************************************************************************/ +SvGlobalNameList::~SvGlobalNameList() +{ + for( ULONG i = Count(); i > 0; i-- ) + { + ImpSvGlobalName * pImp = (ImpSvGlobalName *)aList.GetObject( i -1 ); + pImp->nRefCount--; + if( !pImp->nRefCount ) + delete pImp; + } +} + +/************************************************************************* +|* SvGlobalNameList::Append() +*************************************************************************/ +void SvGlobalNameList::Append( const SvGlobalName & rName ) +{ + rName.pImp->nRefCount++; + aList.Insert( rName.pImp, LIST_APPEND ); +} + +/************************************************************************* +|* SvGlobalNameList::GetObject() +*************************************************************************/ +SvGlobalName SvGlobalNameList::GetObject( ULONG nPos ) +{ + return SvGlobalName( (ImpSvGlobalName *)aList.GetObject( nPos ) ); +} + +/************************************************************************* +|* SvGlobalNameList::IsEntry() +*************************************************************************/ +BOOL SvGlobalNameList::IsEntry( const SvGlobalName & rName ) +{ + for( ULONG i = Count(); i > 0; i-- ) + { + if( *rName.pImp == *(ImpSvGlobalName *)aList.GetObject( i -1 ) ) + return TRUE; + } + return FALSE; +} + +com::sun::star::uno::Sequence < sal_Int8 > SvGlobalName::GetByteSequence() const +{ + // platform independent representation of a "GlobalName" + // maybe transported remotely + com::sun::star::uno::Sequence< sal_Int8 > aResult( 16 ); + + aResult[0] = (sal_Int8) (*(UINT32 *)pImp->szData >> 24); + aResult[1] = (sal_Int8) ((*(UINT32 *)pImp->szData << 8 ) >> 24); + aResult[2] = (sal_Int8) ((*(UINT32 *)pImp->szData << 16 ) >> 24); + aResult[3] = (sal_Int8) ((*(UINT32 *)pImp->szData << 24 ) >> 24); + aResult[4] = (sal_Int8) (*(USHORT *)&pImp->szData[ 4 ] >> 8); + aResult[5] = (sal_Int8) ((*(USHORT *)&pImp->szData[ 4 ] << 8 ) >> 8); + aResult[6] = (sal_Int8) (*(USHORT *)&pImp->szData[ 6 ] >> 8); + aResult[7] = (sal_Int8) ((*(USHORT *)&pImp->szData[ 6 ] << 8 ) >> 8); + aResult[8] = pImp->szData[ 8 ]; + aResult[9] = pImp->szData[ 9 ]; + aResult[10] = pImp->szData[ 10 ]; + aResult[11] = pImp->szData[ 11 ]; + aResult[12] = pImp->szData[ 12 ]; + aResult[13] = pImp->szData[ 13 ]; + aResult[14] = pImp->szData[ 14 ]; + aResult[15] = pImp->szData[ 15 ]; + + return aResult; +} + +SvGlobalName::SvGlobalName( const com::sun::star::uno::Sequence < sal_Int8 >& aSeq ) +{ + // create SvGlobalName from a platform independent representation + GUID aResult; + memset( &aResult, 0, sizeof( aResult ) ); + if ( aSeq.getLength() == 16 ) + { + aResult.Data1 = ( ( ( ( ( ( sal_uInt8 )aSeq[0] << 8 ) + ( sal_uInt8 )aSeq[1] ) << 8 ) + ( sal_uInt8 )aSeq[2] ) << 8 ) + ( sal_uInt8 )aSeq[3]; + aResult.Data2 = ( ( sal_uInt8 )aSeq[4] << 8 ) + ( sal_uInt8 )aSeq[5]; + aResult.Data3 = ( ( sal_uInt8 )aSeq[6] << 8 ) + ( sal_uInt8 )aSeq[7]; + for( int nInd = 0; nInd < 8; nInd++ ) + aResult.Data4[nInd] = ( sal_uInt8 )aSeq[nInd+8]; + } + + pImp = new ImpSvGlobalName(); + pImp->nRefCount++; + memcpy( pImp->szData, &aResult, sizeof( pImp->szData ) ); +} diff --git a/tools/source/ref/makefile.mk b/tools/source/ref/makefile.mk new file mode 100644 index 000000000000..2c22027532b0 --- /dev/null +++ b/tools/source/ref/makefile.mk @@ -0,0 +1,57 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.7 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=ref +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES= $(SLO)$/ref.obj \ + $(SLO)$/pstm.obj \ + $(SLO)$/globname.obj \ + $(SLO)$/errinf.obj + +OBJFILES= $(OBJ)$/ref.obj \ + $(OBJ)$/pstm.obj \ + $(OBJ)$/globname.obj \ + $(OBJ)$/errinf.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/tools/source/ref/pstm.cxx b/tools/source/ref/pstm.cxx new file mode 100644 index 000000000000..a336bbaab360 --- /dev/null +++ b/tools/source/ref/pstm.cxx @@ -0,0 +1,918 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pstm.cxx,v $ + * $Revision: 1.9 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <tools/debug.hxx> +#include <tools/pstm.hxx> + +#define STOR_NO_OPTIMIZE + +/***********************************************************************/ +/************************************************************************ +|* SvClassManager::Register() +*************************************************************************/ +void SvClassManager::Register( USHORT nClassId, SvCreateInstancePersist pFunc ) +{ +#ifdef DBG_UTIL + SvCreateInstancePersist p; + p = Get( nClassId ); + DBG_ASSERT( !p || p == pFunc, "register class with same id" ); +#endif + aAssocTable.insert(Map::value_type(nClassId, pFunc)); +} + +/************************************************************************ +|* SvClassManager::Get() +*************************************************************************/ +SvCreateInstancePersist SvClassManager::Get( USHORT nClassId ) +{ + Map::const_iterator i(aAssocTable.find(nClassId)); + return i == aAssocTable.end() ? 0 : i->second; +} + +/****************** SvRttiBase *******************************************/ +TYPEINIT0( SvRttiBase ); + +/****************** SvPersistBaseMemberList ******************************/ + +SvPersistBaseMemberList::SvPersistBaseMemberList(){} +SvPersistBaseMemberList::SvPersistBaseMemberList( + USHORT nInitSz, USHORT nResize ) + : SuperSvPersistBaseMemberList( nInitSz, nResize ){} + +#define PERSIST_LIST_VER (BYTE)0 +#define PERSIST_LIST_DBGUTIL (BYTE)0x80 + +/************************************************************************ +|* SvPersistBaseMemberList::WriteOnlyStreamedObjects() +*************************************************************************/ +void SvPersistBaseMemberList::WriteObjects( SvPersistStream & rStm, + BOOL bOnlyStreamed ) const +{ +#ifdef STOR_NO_OPTIMIZE + rStm << (BYTE)(PERSIST_LIST_VER | PERSIST_LIST_DBGUTIL); + UINT32 nObjPos = rStm.WriteDummyLen(); +#else + BYTE bTmp = PERSIST_LIST_VER; + rStm << bTmp; +#endif + UINT32 nCountMember = Count(); + ULONG nCountPos = rStm.Tell(); + UINT32 nWriteCount = 0; + rStm << nCountMember; + //bloss die Liste nicht veraendern, + //wegen Seiteneffekten beim Save + for( ULONG n = 0; n < nCountMember; n++ ) + { + SvPersistBase * pObj = GetObject( n ); + if( !bOnlyStreamed || rStm.IsStreamed( pObj ) ) + { // Objekt soll geschrieben werden + rStm << GetObject( n ); + nWriteCount++; + } + } + if( nWriteCount != nCountMember ) + { + // nicht alle Objekte geschrieben, Count anpassen + ULONG nPos = rStm.Tell(); + rStm.Seek( nCountPos ); + rStm << nWriteCount; + rStm.Seek( nPos ); + } +#ifdef STOR_NO_OPTIMIZE + rStm.WriteLen( nObjPos ); +#endif +} + +/************************************************************************ +|* operator << () +*************************************************************************/ +SvPersistStream& operator << ( SvPersistStream & rStm, + const SvPersistBaseMemberList & rLst ) +{ + rLst.WriteObjects( rStm ); + return rStm; +} + +/************************************************************************ +|* operator >> () +*************************************************************************/ +SvPersistStream& operator >> ( SvPersistStream & rStm, + SvPersistBaseMemberList & rLst ) +{ + BYTE nVer; + rStm >> nVer; + + if( (nVer & ~PERSIST_LIST_DBGUTIL) != PERSIST_LIST_VER ) + { + rStm.SetError( SVSTREAM_GENERALERROR ); + DBG_ERROR( "persist list, false version" ); + } + + UINT32 nObjLen(0), nObjPos(0); + if( nVer & PERSIST_LIST_DBGUTIL ) + nObjLen = rStm.ReadLen( &nObjPos ); + + sal_uInt32 nCount; + rStm >> nCount; + for( ULONG n = 0; n < nCount && rStm.GetError() == SVSTREAM_OK; n++ ) + { + SvPersistBase * pObj; + rStm >> pObj; + if( pObj ) + rLst.Append( pObj ); + } +#ifdef DBG_UTIL + if( nObjLen + nObjPos != rStm.Tell() ) + { + ByteString aStr( "false list len: read = " ); + aStr += ByteString::CreateFromInt32( (long)(rStm.Tell() - nObjPos) ); + aStr += ", should = "; + aStr += ByteString::CreateFromInt64(nObjLen); + DBG_ERROR( aStr.GetBuffer() ); + } +#endif + return rStm; +} + +//========================================================================= +SvPersistStream::SvPersistStream +( + SvClassManager & rMgr, /* Alle Factorys, deren Objekt geladen und + gespeichert werdn k"onnen */ + SvStream * pStream, /* Dieser Stream wird als Medium genommen, auf + dem der PersistStream arbeitet */ + UINT32 nStartIdxP /* Ab diesem Index werden die Id's f"ur + die Objekte vergeben, er muss gr"osser + als Null sein. */ +) + : rClassMgr( rMgr ) + , pStm( pStream ) + , aPUIdx( nStartIdxP ) + , nStartIdx( nStartIdxP ) + , pRefStm( NULL ) + , nFlags( 0 ) +/* [Beschreibung] + + Der Konstruktor der Klasse SvPersistStream. Die Objekte rMgr und + pStream d"urfen nicht ver"andert werden, solange sie in einem + SvPersistStream eingesetzt sind. Eine Aussnahme gibt es f"ur + pStream (siehe <SvPersistStream::SetStream>). +*/ +{ + DBG_ASSERT( nStartIdx != 0, "zero index not allowed" ); + bIsWritable = TRUE; + if( pStm ) + { + SetVersion( pStm->GetVersion() ); + SetError( pStm->GetError() ); + SyncSvStream( pStm->Tell() ); + } +} + +//========================================================================= +SvPersistStream::SvPersistStream +( + SvClassManager & rMgr, /* Alle Factorys, deren Objekt geladen und + gespeichert werdn k"onnen */ + SvStream * pStream, /* Dieser Stream wird als Medium genommen, auf + dem der PersistStream arbeitet */ + const SvPersistStream & rPersStm + /* Wenn PersistStream's verschachtelt werden, + dann ist dies der Parent-Stream. */ +) + : rClassMgr( rMgr ) + , pStm( pStream ) + // Bereiche nicht ueberschneiden, deshalb nur groessere Indexe + , aPUIdx( rPersStm.GetCurMaxIndex() +1 ) + , nStartIdx( rPersStm.GetCurMaxIndex() +1 ) + , pRefStm( &rPersStm ) + , nFlags( 0 ) +/* [Beschreibung] + + Der Konstruktor der Klasse SvPersistStream. Die Objekte rMgr und + pStream d"urfen nicht ver"andert werden, solange sie in einem + SvPersistStream eingesetzt sind. Eine Aussnahme gibt es f"ur + pStream (siehe <SvPersistStream::SetStream>). + Durch diesen Konstruktor wird eine Hierarchiebildung unterst"utzt. + Alle Objekte aus einer Hierarchie m"ussen erst geladen werden, + wenn das erste aus dieser Hierarchie benutzt werden soll. +*/ +{ + bIsWritable = TRUE; + if( pStm ) + { + SetVersion( pStm->GetVersion() ); + SetError( pStm->GetError() ); + SyncSvStream( pStm->Tell() ); + } +} + +//========================================================================= +SvPersistStream::~SvPersistStream() +/* [Beschreibung] + + Der Detruktor ruft die Methode <SvPersistStream::SetStream> + mit NULL. +*/ +{ + SetStream( NULL ); +} + +//========================================================================= +void SvPersistStream::SetStream +( + SvStream * pStream /* auf diesem Stream arbeitet der PersistStream */ + +) +/* [Beschreibung] + + Es wird ein Medium (pStream) eingesetzt, auf dem PersistStream arbeitet. + Dieses darf nicht von aussen modifiziert werden, solange es + eingesetzt ist. Es sei denn, w"ahrend auf dem Medium gearbeitet + wird, wird keine Methode von SvPersistStream gerufen, bevor + nicht <SvPersistStream::SetStream> mit demselben Medium gerufen + wurde. +*/ +{ + if( pStm != pStream ) + { + if( pStm ) + { + SyncSysStream(); + pStm->SetError( GetError() ); + } + pStm = pStream; + } + if( pStm ) + { + SetVersion( pStm->GetVersion() ); + SetError( pStm->GetError() ); + SyncSvStream( pStm->Tell() ); + } +} + +//========================================================================= +USHORT SvPersistStream::IsA() const +/* [Beschreibung] + + Gibt den Identifier dieses Streamklasse zur"uck. + + [R"uckgabewert] + + USHORT ID_PERSISTSTREAM wird zur"uckgegeben. + + + [Querverweise] + + <SvStream::IsA> +*/ +{ + return ID_PERSISTSTREAM; +} + + +/************************************************************************* +|* SvPersistStream::ResetError() +*************************************************************************/ +void SvPersistStream::ResetError() +{ + SvStream::ResetError(); + DBG_ASSERT( pStm, "stream not set" ); + pStm->ResetError(); +} + +/************************************************************************* +|* SvPersistStream::GetData() +*************************************************************************/ +ULONG SvPersistStream::GetData( void* pData, ULONG nSize ) +{ + DBG_ASSERT( pStm, "stream not set" ); + ULONG nRet = pStm->Read( pData, nSize ); + SetError( pStm->GetError() ); + return nRet; +} + +/************************************************************************* +|* SvPersistStream::PutData() +*************************************************************************/ +ULONG SvPersistStream::PutData( const void* pData, ULONG nSize ) +{ + DBG_ASSERT( pStm, "stream not set" ); + ULONG nRet = pStm->Write( pData, nSize ); + SetError( pStm->GetError() ); + return nRet; +} + +/************************************************************************* +|* SvPersistStream::Seek() +*************************************************************************/ +ULONG SvPersistStream::SeekPos( ULONG nPos ) +{ + DBG_ASSERT( pStm, "stream not set" ); + ULONG nRet = pStm->Seek( nPos ); + SetError( pStm->GetError() ); + return nRet; +} + +/************************************************************************* +|* SvPersistStream::FlushData() +*************************************************************************/ +void SvPersistStream::FlushData() +{ +} + +/************************************************************************* +|* SvPersistStream::GetCurMaxIndex() +*************************************************************************/ +ULONG SvPersistStream::GetCurMaxIndex( const SvPersistUIdx & rIdx ) const +{ + // const bekomme ich nicht den hoechsten Index + SvPersistUIdx * p = (SvPersistUIdx *)&rIdx; + // alten merken + ULONG nCurIdx = p->GetCurIndex(); + p->Last(); + // Bereiche nicht ueberschneiden, deshalb nur groessere Indexe + ULONG nMaxIdx = p->GetCurIndex(); + // wieder herstellen + p->Seek( nCurIdx ); + return nMaxIdx; +} + +/************************************************************************* +|* SvPersistStream::GetIndex() +*************************************************************************/ +ULONG SvPersistStream::GetIndex( SvPersistBase * pObj ) const +{ + ULONG nId = (ULONG)aPTable.Get( (ULONG)pObj ); + if( !nId && pRefStm ) + return pRefStm->GetIndex( pObj ); + return nId; +} + +/************************************************************************* +|* SvPersistStream::GetObject) +*************************************************************************/ +SvPersistBase * SvPersistStream::GetObject( ULONG nIdx ) const +{ + if( nIdx >= nStartIdx ) + return aPUIdx.Get( nIdx ); + else if( pRefStm ) + return pRefStm->GetObject( nIdx ); + return NULL; +} + +//========================================================================= +#define LEN_1 0x80 +#define LEN_2 0x40 +#define LEN_4 0x20 +#define LEN_5 0x10 +UINT32 SvPersistStream::ReadCompressed +( + SvStream & rStm /* Aus diesem Stream werden die komprimierten Daten + gelesen */ +) +/* [Beschreibung] + + Ein im Stream komprimiert abgelegtes Wort wird gelesen. In welchem + Format komprimiert wird, siehe <SvPersistStream::WriteCompressed>. + + [R"uckgabewert] + + UINT32 Das nicht komprimierte Wort wird zur"uckgegeben. + + [Querverweise] + +*/ +{ + UINT32 nRet(0); + BYTE nMask; + rStm >> nMask; + if( nMask & LEN_1 ) + nRet = ~LEN_1 & nMask; + else if( nMask & LEN_2 ) + { + nRet = ~LEN_2 & nMask; + nRet <<= 8; + rStm >> nMask; + nRet |= nMask; + } + else if( nMask & LEN_4 ) + { + nRet = ~LEN_4 & nMask; + nRet <<= 8; + rStm >> nMask; + nRet |= nMask; + nRet <<= 16; + USHORT n; + rStm >> n; + nRet |= n; + } + else if( nMask & LEN_5 ) + { + if( nMask & 0x0F ) + { + rStm.SetError( SVSTREAM_FILEFORMAT_ERROR ); + DBG_ERROR( "format error" ); + } + rStm >> nRet; + } + else + { + rStm.SetError( SVSTREAM_FILEFORMAT_ERROR ); + DBG_ERROR( "format error" ); + } + return nRet; +} + +//========================================================================= +void SvPersistStream::WriteCompressed +( + SvStream & rStm,/* Aus diesem Stream werden die komprimierten Daten + gelesen */ + UINT32 nVal /* Dieser Wert wird komprimiert geschrieben */ +) +/* [Beschreibung] + + Das "ubergebene Wort wird komprimiert und in den Stream + geschrieben. Folgendermassen wir komprimiert. + nVal < 0x80 => 0x80 + nVal ist 1 Byte gross. + nVal < 0x4000 => 0x4000 + nVal ist 2 Byte gross. + nVal < 0x20000000 => 0x20000000 + nVal ist 4 Byte gross. + nVal > 0x1FFFFFFF => 0x1000000000+ nVal ist 5 Byte gross. + + [Querverweise] + + <SvPersistStream::ReadCompressed> +*/ +{ +#ifdef STOR_NO_OPTIMIZE + if( nVal < 0x80 ) + rStm << (BYTE)(LEN_1 | nVal); + else if( nVal < 0x4000 ) + { + rStm << (BYTE)(LEN_2 | (nVal >> 8)); + rStm << (BYTE)nVal; + } + else if( nVal < 0x20000000 ) + { + // hoechstes BYTE + rStm << (BYTE)(LEN_4 | (nVal >> 24)); + // 2. hoechstes BYTE + rStm << (BYTE)(nVal >> 16); + rStm << (USHORT)(nVal); + } + else +#endif + { + rStm << (BYTE)LEN_5; + rStm << nVal; + } +} + +//========================================================================= +UINT32 SvPersistStream::WriteDummyLen() +/* [Beschreibung] + + Die Methode schreibt 4 Byte in den Stream und gibt die Streamposition + zur"uck. + + [R"uckgabewert] + + UINT32 Die Position hinter der L"angenangabe wird zur"uckgegeben. + + [Beispiel] + + UINT32 nObjPos = rStm.WriteDummyLen(); + ... + // Daten schreiben + ... + rStm.WriteLen( nObjPos ); + + [Querverweise] + + <SvPersistStream::ReadLen>, <SvPersistStream::WriteLen> + +*/ +{ +#ifdef DBG_UTIL + UINT32 nPos = Tell(); +#endif + UINT32 n0 = 0; + *this << n0; // wegen Sun sp + // keine Assertion bei Streamfehler + DBG_ASSERT( GetError() != SVSTREAM_OK + || (sizeof( UINT32 ) == Tell() -nPos), + "keine 4-Byte fuer Langenangabe" ); + return Tell(); +} + +//========================================================================= +void SvPersistStream::WriteLen +( + UINT32 nObjPos /* die Position + 4, an der die L"ange geschrieben + wird. */ +) +/* [Beschreibung] + + Die Methode schreibt die Differenz zwischen der aktuellen und + nObjPos als UINT32 an die Position nObjPos -4 im Stream. Danach + wird der Stream wieder auf die alte Position gestellt. + + [Beispiel] + + Die Differenz enth"alt nicht die L"angenangabe. + + UINT32 nObjPos = rStm.WriteDummyLen(); + ... + // Daten schreiben + ... + rStm.WriteLen( nObjPos ); + // weitere Daten schreiben + + [Querverweise] + + <SvPersistStream::ReadLen>, <SvPersistStream::WriteDummyLen> +*/ +{ + UINT32 nPos = Tell(); + UINT32 nLen = nPos - nObjPos; + // die Laenge mu� im stream 4-Byte betragen + Seek( nObjPos - sizeof( UINT32 ) ); + // Laenge schreiben + *this << nLen; + Seek( nPos ); +} + +//========================================================================= +UINT32 SvPersistStream::ReadLen +( + UINT32 * pTestPos /* Die Position des Streams, nach dem Lesen der + L"ange, wird zur"uckgegeben. Es darf auch NULL + "ubergeben werden. */ +) +/* [Beschreibung] + + Liest die L"ange die vorher mit <SvPersistStream::WriteDummyLen> + und <SvPersistStream::WriteLen> geschrieben wurde. +*/ +{ + UINT32 nLen; + *this >> nLen; + if( pTestPos ) + *pTestPos = Tell(); + return nLen; +} + +//========================================================================= +// Dateirormat abw"arts kompatibel +#ifdef STOR_NO_OPTIMIZE +#define P_VER (BYTE)0x00 +#else +#define P_VER (BYTE)0x01 +#endif +#define P_VER_MASK (BYTE)0x0F +#define P_ID_0 (BYTE)0x80 +#define P_OBJ (BYTE)0x40 +#define P_DBGUTIL (BYTE)0x20 +#define P_ID (BYTE)0x10 +#ifdef STOR_NO_OPTIMIZE +#define P_STD P_DBGUTIL +#else +#define P_STD 0 +#endif + +static void WriteId +( + SvStream & rStm, + BYTE nHdr, + UINT32 nId, + USHORT nClassId +) +{ +#ifdef STOR_NO_OPTIMIZE + nHdr |= P_ID; +#endif + nHdr |= P_VER; + if( nHdr & P_ID ) + { + if( (nHdr & P_OBJ) || nId != 0 ) + { // Id nur bei Zeiger, oder DBGUTIL + rStm << (BYTE)(nHdr); + SvPersistStream::WriteCompressed( rStm, nId ); + } + else + { // NULL Pointer + rStm << (BYTE)(nHdr | P_ID_0); + return; + } + } + else + rStm << nHdr; + + if( (nHdr & P_DBGUTIL) || (nHdr & P_OBJ) ) + // Objekte haben immer eine Klasse, + // Pointer nur bei DBG_UTIL und != NULL + SvPersistStream::WriteCompressed( rStm, nClassId ); +} + +//========================================================================= +static void ReadId +( + SvStream & rStm, + BYTE & nHdr, + UINT32 & nId, + USHORT & nClassId +) +{ + nClassId = 0; + rStm >> nHdr; + if( nHdr & P_ID_0 ) + nId = 0; + else + { + if( (nHdr & P_VER_MASK) == 0 ) + { + if( (nHdr & P_DBGUTIL) || !(nHdr & P_OBJ) ) + nId = SvPersistStream::ReadCompressed( rStm ); + else + nId = 0; + } + else if( nHdr & P_ID ) + nId = SvPersistStream::ReadCompressed( rStm ); + + if( (nHdr & P_DBGUTIL) || (nHdr & P_OBJ) ) + nClassId = (USHORT)SvPersistStream::ReadCompressed( rStm ); + } +} + +//========================================================================= +void SvPersistStream::WriteObj +( + BYTE nHdr, + SvPersistBase * pObj +) +{ +#ifdef STOR_NO_OPTIMIZE + UINT32 nObjPos = 0; + if( nHdr & P_DBGUTIL ) + // Position fuer Laenge merken + nObjPos = WriteDummyLen(); +#endif + pObj->Save( *this ); +#ifdef STOR_NO_OPTIMIZE + if( nHdr & P_DBGUTIL ) + WriteLen( nObjPos ); +#endif +} + +//========================================================================= +SvPersistStream& SvPersistStream::WritePointer +( + SvPersistBase * pObj +) +{ + BYTE nP = P_STD; + + if( pObj ) + { + ULONG nId = GetIndex( pObj ); + if( nId ) + nP |= P_ID; + else + { + nId = aPUIdx.Insert( pObj ); + aPTable.Insert( (ULONG)pObj, (void *)nId ); + nP |= P_OBJ; + } + WriteId( *this, nP, nId, pObj->GetClassId() ); + if( nP & P_OBJ ) + WriteObj( nP, pObj ); + } + else + { // NULL Pointer + WriteId( *this, nP | P_ID, 0, 0 ); + } + return *this; +} + +//========================================================================= +UINT32 SvPersistStream::ReadObj +( + SvPersistBase * & rpObj, + BOOL bRegister +) +{ + BYTE nHdr; + UINT32 nId = 0; + USHORT nClassId; + + rpObj = NULL; // Spezifikation: Im Fehlerfall 0. + ReadId( *this, nHdr, nId, nClassId ); + + // reine Versionsnummer durch maskieren + if( P_VER < (nHdr & P_VER_MASK) ) + { + SetError( SVSTREAM_FILEFORMAT_ERROR ); + DBG_ERROR( "false version" ); + } + + if( !(nHdr & P_ID_0) && GetError() == SVSTREAM_OK ) + { + if( P_OBJ & nHdr ) + { // read object, nId nur bei P_DBGUTIL gesetzt + DBG_ASSERT( !(nHdr & P_DBGUTIL) || NULL == aPUIdx.Get( nId ), + "object already exist" ); + SvCreateInstancePersist pFunc = rClassMgr.Get( nClassId ); + + UINT32 nObjLen(0), nObjPos(0); + if( nHdr & P_DBGUTIL ) + nObjLen = ReadLen( &nObjPos ); + if( !pFunc ) + { +#ifdef DBG_UTIL + ByteString aStr( "no class with id: " ); + aStr += ByteString::CreateFromInt32( nClassId ); + aStr += " registered"; + DBG_WARNING( aStr.GetBuffer() ); +#endif + SetError( ERRCODE_IO_NOFACTORY ); + return 0; + } + pFunc( &rpObj ); + // Sichern + rpObj->AddRef(); + + if( bRegister ) + { + // unbedingt erst in Tabelle eintragen + ULONG nNewId = aPUIdx.Insert( rpObj ); + // um den gleichen Zustand, wie nach dem Speichern herzustellen + aPTable.Insert( (ULONG)rpObj, (void *)nNewId ); + DBG_ASSERT( !(nHdr & P_DBGUTIL) || nId == nNewId, + "read write id conflict: not the same" ); + } + // und dann Laden + rpObj->Load( *this ); +#ifdef DBG_UTIL + if( nObjLen + nObjPos != Tell() ) + { + ByteString aStr( "false object len: read = " ); + aStr += ByteString::CreateFromInt32( (long)(Tell() - nObjPos) ); + aStr += ", should = "; + aStr += ByteString::CreateFromInt32( nObjLen ); + DBG_ERROR( aStr.GetBuffer() ); + } +#endif + rpObj->RestoreNoDelete(); + rpObj->ReleaseRef(); + } + else + { + rpObj = GetObject( nId ); + DBG_ASSERT( rpObj != NULL, "object does not exist" ); + DBG_ASSERT( rpObj->GetClassId() == nClassId, "class mismatch" ); + } + } + return nId; +} + +//========================================================================= +SvPersistStream& SvPersistStream::ReadPointer +( + SvPersistBase * & rpObj +) +{ + ReadObj( rpObj, TRUE ); + return *this; +} + +//========================================================================= +SvPersistStream& operator << +( + SvPersistStream & rStm, + SvPersistBase * pObj +) +{ + return rStm.WritePointer( pObj ); +} + +//========================================================================= +SvPersistStream& operator >> +( + SvPersistStream & rStm, + SvPersistBase * & rpObj +) +{ + return rStm.ReadPointer( rpObj ); +} + +//========================================================================= +SvStream& operator << +( + SvStream & rStm, + SvPersistStream & rThis +) +{ + SvStream * pOldStm = rThis.GetStream(); + rThis.SetStream( &rStm ); + + BYTE bTmp = 0; + rThis << bTmp; // Version + UINT32 nCount = (UINT32)rThis.aPUIdx.Count(); + rThis << nCount; + SvPersistBase * pEle = rThis.aPUIdx.First(); + for( UINT32 i = 0; i < nCount; i++ ) + { + BYTE nP = P_OBJ | P_ID | P_STD; + WriteId( rThis, nP, rThis.aPUIdx.GetCurIndex(), + pEle->GetClassId() ); + rThis.WriteObj( nP, pEle ); + pEle = rThis.aPUIdx.Next(); + } + rThis.SetStream( pOldStm ); + return rStm; +} + +//========================================================================= +SvStream& operator >> +( + SvStream & rStm, + SvPersistStream & rThis +) +{ + SvStream * pOldStm = rThis.GetStream(); + rThis.SetStream( &rStm ); + + BYTE nVers; + rThis >> nVers; // Version + if( 0 == nVers ) + { + UINT32 nCount = 0; + rThis >> nCount; + for( UINT32 i = 0; i < nCount; i++ ) + { + SvPersistBase * pEle; + // Lesen, ohne in die Tabellen einzutragen + UINT32 nId = rThis.ReadObj( pEle, FALSE ); + if( rThis.GetError() ) + break; + + // Die Id eines Objektes wird nie modifiziert + rThis.aPUIdx.Insert( nId, pEle ); + rThis.aPTable.Insert( (ULONG)pEle, (void *)nId ); + } + } + else + rThis.SetError( SVSTREAM_FILEFORMAT_ERROR ); + + rThis.SetStream( pOldStm ); + return rStm; +} + +//========================================================================= +ULONG SvPersistStream::InsertObj( SvPersistBase * pObj ) +{ + ULONG nId = aPUIdx.Insert( pObj ); + aPTable.Insert( (ULONG)pObj, (void *)nId ); + return nId; +} + +//========================================================================= +ULONG SvPersistStream::RemoveObj( SvPersistBase * pObj ) +{ + ULONG nIdx = GetIndex( pObj ); + aPUIdx.Remove( nIdx ); + aPTable.Remove( (ULONG)pObj ); + return nIdx; +} + diff --git a/tools/source/ref/ref.cxx b/tools/source/ref/ref.cxx new file mode 100644 index 000000000000..c99a043fe92d --- /dev/null +++ b/tools/source/ref/ref.cxx @@ -0,0 +1,54 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ref.cxx,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <tools/ref.hxx> + +/****************** SvRefBaseMemberList **********************************/ +SV_IMPL_REF_LIST( SvRefBase,SvRefBase* ) + +/************************************************************************** +#* SvRefBase::~SvRefBase() +**************************************************************************/ +SvRefBase::~SvRefBase() +{ +} + +/************************************************************************** +#* SvRefBase::QueryDelete() +**************************************************************************/ +void SvRefBase::QueryDelete() +{ + nRefCount = SV_NO_DELETE_REFCOUNT / 2; + delete this; +} + diff --git a/tools/source/solar/makefile.mk b/tools/source/solar/makefile.mk new file mode 100644 index 000000000000..fa7a015879d6 --- /dev/null +++ b/tools/source/solar/makefile.mk @@ -0,0 +1,67 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.8 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=mksvconf +TARGETTYPE=CUI + +LIBSALCPPRT=$(0) + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +CFILES= solar.c + +OBJFILES= $(OBJ)$/solar.obj + +APP1TARGET= $(TARGET) +APP1OBJS= $(OBJFILES) +APP1STDLIBS= +APP1DEPN= +APP1DEF= + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +.IF "$(L10N-framework)"=="" +ALLTAR : $(INCCOM)$/svconf.h +.ENDIF # "$(L10N-framework)"=="" + +$(INCCOM)$/svconf.h : $(BIN)$/$(TARGET) + $(BIN)$/$(TARGET) $@ + diff --git a/tools/source/solar/solar.c b/tools/source/solar/solar.c new file mode 100644 index 000000000000..3ca82270712a --- /dev/null +++ b/tools/source/solar/solar.c @@ -0,0 +1,565 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: solar.c,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +/* POSIX defines that a program is undefined after a SIG_SEGV. The + * code stopped working on Linux Kernel 2.6 so I have moved this back to + * use FORK. + * If at a later time the signals work correctly with the Linux Kernel 2.6 + * then this change may be reverted although not strictly posix safe. */ +#define USE_FORK_TO_CHECK 1 + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +#include <unistd.h> +#include <sys/types.h> + +#define I_STDARG +#ifdef I_STDARG +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#define NO_USE_FORK_TO_CHECK +#ifdef USE_FORK_TO_CHECK +#include <sys/wait.h> +#else +#include <signal.h> +#include <setjmp.h> +#endif + +#define printTypeSize(Type,Name) printf( "sizeof(%s)\t= %d\n", Name, sizeof (Type) ) + +#define isSignedType(Type) (((Type)-1) < 0) +#define printTypeSign(Type,Name) printf( "%s\t= %s %s\n", Name, ( isSignedType(Type) ? "signed" : "unsigned" ), Name ) + + +/************************************************************************* +|* +|* IsBigEndian() +|* +|* Beschreibung True, wenn CPU BigEndian ist +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +int IsBigEndian() +{ + long l = 1; + return ! *(char*)&l; +} + +/************************************************************************* +|* +|* IsStackGrowingDown() +|* +|* Beschreibung True, wenn der Stack nach unten waechst +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +int IsStackGrowingDown_2( int * pI ) +{ + int i = 1; + return ((unsigned long)&i) < (unsigned long)pI; +} + +int IsStackGrowingDown() +{ + int i = 1; + return IsStackGrowingDown_2(&i); +} + +/************************************************************************* +|* +|* GetStackAlignment() +|* +|* Beschreibung Alignment von char Parametern, die (hoffentlich) +|* ueber den Stack uebergeben werden +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +int GetStackAlignment_3( char*p, long l, int i, short s, char b, char c, ... ) +{ + if ( IsStackGrowingDown() ) + return &c - &b; + else + return &b - &c; +} + +int GetStackAlignment_2( char*p, long l, int i, short s, char b, char c ) +{ + if ( IsStackGrowingDown() ) + return &c - &b; + else + return &b - &c; +} + +int GetStackAlignment() +{ + int nStackAlignment = GetStackAlignment_3(0,1,2,3,4,5); + if ( nStackAlignment != GetStackAlignment_2(0,1,2,3,4,5) ) + printf( "Pascal calling convention\n" ); + return nStackAlignment; +} + + +/************************************************************************* +|* +|* Typdeclarations for memory access test functions +|* +*************************************************************************/ +typedef enum { t_char, t_short, t_int, t_long, t_double } Type; +typedef int (*TestFunc)( Type, void* ); + + +/************************************************************************* +|* +|* PrintArgs() +|* +|* Beschreibung Testfunktion fuer variable Parameter +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +#ifdef I_STDARG +void PrintArgs( int p, ... ) +#else +void PrintArgs( p, va_alist ) +int p; +va_dcl +#endif +{ + int value; + va_list ap; + +#ifdef I_STDARG + va_start( ap, p ); +#else + va_start( ap ); +#endif + + printf( "value = %d", p ); + + while ( ( value = va_arg(ap, int) ) != 0 ) + printf( " %d", value ); + + printf( "\n" ); + va_end(ap); +} + +#ifndef USE_FORK_TO_CHECK +/************************************************************************* +|* +|* SignalHdl() +|* +|* Beschreibung faengt SIGBUS und SIGSEGV in check() ab +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +static jmp_buf check_env; +static int bSignal; +void SignalHdl( int sig ) +{ + bSignal = 1; + + fprintf( stderr, "Signal %d caught\n", sig ); + signal( SIGSEGV, SIG_DFL ); + signal( SIGBUS, SIG_DFL ); + siglongjmp( check_env, sig ); +} +#endif + +/************************************************************************* +|* +|* check() +|* +|* Beschreibung Testet MemoryZugriff (read/write) +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +int check( TestFunc func, Type eT, void* p ) +{ +#ifdef USE_FORK_TO_CHECK + pid_t nChild = fork(); + if ( nChild ) + { + int exitVal; + wait( &exitVal ); + if ( exitVal & 0xff ) + return -1; + else + return exitVal >> 8; + } + else + { + exit( func( eT, p ) ); + } +#else + int result; + + bSignal = 0; + + if ( !sigsetjmp( check_env, 1 ) ) + { + signal( SIGSEGV, SignalHdl ); + signal( SIGBUS, SignalHdl ); + result = func( eT, p ); + signal( SIGSEGV, SIG_DFL ); + signal( SIGBUS, SIG_DFL ); + } + + if ( bSignal ) + return -1; + else + return 0; +#endif +} + +/************************************************************************* +|* +|* GetAtAddress() +|* +|* Beschreibung memory read access +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +int GetAtAddress( Type eT, void* p ) +{ + switch ( eT ) + { + case t_char: return *((char*)p); + case t_short: return *((short*)p); + case t_int: return *((int*)p); + case t_long: return *((long*)p); + case t_double: return *((double*)p); + } + abort(); +} + +/************************************************************************* +|* +|* SetAtAddress() +|* +|* Beschreibung memory write access +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +int SetAtAddress( Type eT, void* p ) +{ + switch ( eT ) + { + case t_char: return *((char*)p) = 0; + case t_short: return *((short*)p) = 0; + case t_int: return *((int*)p) = 0; + case t_long: return *((long*)p) = 0; + case t_double: return *((double*)p)= 0; + } + abort(); +} + +char* TypeName( Type eT ) +{ + switch ( eT ) + { + case t_char: return "char"; + case t_short: return "short"; + case t_int: return "int"; + case t_long: return "long"; + case t_double: return "double"; + } + abort(); +} + +/************************************************************************* +|* +|* Check(Get|Set)Access() +|* +|* Beschreibung Testet MemoryZugriff (read/write) +|* Zugriffsverletzungen werden abgefangen +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +int CheckGetAccess( Type eT, void* p ) +{ + int b; + b = -1 != check( (TestFunc)GetAtAddress, eT, p ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, + "%s read %s at %p\n", + (b? "can" : "can not" ), TypeName(eT), p ); +#endif + return b; +} +int CheckSetAccess( Type eT, void* p ) +{ + int b; + + b = -1 != check( (TestFunc)SetAtAddress, eT, p ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, + "%s write %s at %p\n", + (b? "can" : "can not" ), TypeName(eT), p ); +#endif + return b; +} + +/************************************************************************* +|* +|* GetAlignment() +|* +|* Beschreibung Bestimmt das Alignment verschiedener Typen +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +int GetAlignment( Type eT ) +{ + char a[ 16*8 ]; + long p = (long)(void*)a; + int i; + + /* clear a[...] to set legal value for double access */ + for ( i = 0; i < 16*8; i++ ) + a[i] = 0; + + p = ( p + 0xF ) & ~0xF; + for ( i = 1; i < 16; i++ ) + if ( CheckGetAccess( eT, (void*)(p+i) ) ) + return i; + return 0; +} + +/************************************************************************* +|* +|* struct Description +|* +|* Beschreibung Beschreibt die Parameter der Architektur +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +struct Description +{ + int bBigEndian; + int bStackGrowsDown; + int nStackAlignment; + int nAlignment[3]; /* 2,4,8 */ +}; + +/************************************************************************* +|* +|* Description_Ctor() +|* +|* Beschreibung Bestimmt die Parameter der Architektur +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +void Description_Ctor( struct Description* pThis ) +{ + pThis->bBigEndian = IsBigEndian(); + pThis->bStackGrowsDown = IsStackGrowingDown(); + pThis->nStackAlignment = GetStackAlignment(); + + if ( sizeof(short) != 2 ) + abort(); + pThis->nAlignment[0] = GetAlignment( t_short ); + if ( sizeof(int) != 4 ) + abort(); + pThis->nAlignment[1] = GetAlignment( t_int ); + + if ( sizeof(long) == 8 ) + pThis->nAlignment[2] = GetAlignment( t_long ); + else if ( sizeof(double) == 8 ) + pThis->nAlignment[2] = GetAlignment( t_double ); + else + abort(); +} + +/************************************************************************* +|* +|* Description_Print() +|* +|* Beschreibung Schreibt die Parameter der Architektur als Header +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +void Description_Print( struct Description* pThis, char* name ) +{ + int i; + FILE* f = fopen( name, "w" ); + if( ! f ) { + fprintf( stderr, "Unable to open file %s: %s\n", name, strerror( errno ) ); + exit( 99 ); + } + fprintf( f, "#define __%s\n", + pThis->bBigEndian ? "BIGENDIAN" : "LITTLEENDIAN" ); + for ( i = 0; i < 3; i++ ) + fprintf( f, "#define __ALIGNMENT%d\t%d\n", + 1 << (i+1), pThis->nAlignment[i] ); + fprintf( f, "/* Stack alignment is not used... */\n" ); + fprintf( f, "#define __STACKALIGNMENT\t%d\n", pThis->nStackAlignment ); + fprintf( f, "#define __STACKDIRECTION\t%d\n", + pThis->bStackGrowsDown ? -1 : 1 ); + fprintf( f, "#define __SIZEOFCHAR\t%d\n", sizeof( char ) ); + fprintf( f, "#define __SIZEOFSHORT\t%d\n", sizeof( short ) ); + fprintf( f, "#define __SIZEOFINT\t%d\n", sizeof( int ) ); + fprintf( f, "#define __SIZEOFLONG\t%d\n", sizeof( long ) ); + fprintf( f, "#define __SIZEOFPOINTER\t%d\n", sizeof( void* ) ); + fprintf( f, "#define __SIZEOFDOUBLE\t%d\n", sizeof( double ) ); + fprintf( f, "#define __IEEEDOUBLE\n" ); + fclose( f ); +} + +/************************************************************************* +|* +|* InfoMemoryAccess() +|* +|* Beschreibung Informeller Bytezugriffstest +|* +|* Ersterstellung EG 26.06.96 +|* Letzte Aenderung +|* +*************************************************************************/ +void InfoMemoryAccess( char* p ) +{ + if ( CheckGetAccess( t_char, p ) ) + printf( "can read address %p\n", p ); + else + printf( "can not read address %p\n", p ); + + if ( CheckSetAccess( t_char, p ) ) + printf( "can write address %p\n", p ); + else + printf( "can not write address %p\n", p ); +} + +/************************************************************************* +|* +|* InfoMemoryTypeAccess() +|* +|* Beschreibung Informeller Zugriffstest verschiedener Typen +|* +|* Ersterstellung EG 15.08.96 +|* Letzte Aenderung +|* +*************************************************************************/ +void InfoMemoryTypeAccess( Type eT ) +{ + char a[64]; + int i; + + /* clear a[...] to set legal value for double access */ + for ( i = 0; i < 64; i++ ) + a[i] = 0; + + for ( i = 56; i >= 7; i >>= 1 ) + { + printf( "Zugriff %s auf %i-Aligned Adresse : ", TypeName( eT ), i / 7 ); + printf( ( CheckGetAccess( eT, (long*)&a[i] ) ? "OK\n" : "ERROR\n" ) ); + } +} +/************************************************************************ + * + * Use C code to determine the characteristics of the building platform. + * + ************************************************************************/ +int main( int argc, char* argv[] ) +{ + printTypeSign( char, "char" ); + printTypeSign( short, "short" ); + printTypeSign( int, "int" ); + printTypeSign( long, "long" ); + + printTypeSize( char, "char" ); + printTypeSize( short, "short" ); + printTypeSize( int, "int" ); + printTypeSize( long, "long" ); + printTypeSize( float, "float" ); + printTypeSize( double, "double" ); + printTypeSize( void *, "void *" ); + + if ( IsBigEndian() ) + printf( "BIGENDIAN (Sparc, MC680x0, RS6000, IP22, IP32, g3)\n" ); + else + printf( "LITTLEENDIAN (Intel, VAX, PowerPC)\n" ); + + if( IsStackGrowingDown() ) + printf( "Stack waechst nach unten\n" ); + else + printf( "Stack waechst nach oben\n" ); + + printf( "STACKALIGNMENT : %d\n", GetStackAlignment() ); + + /* PrintArgs( 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ); */ + + if ( argc > 1 ) + { + struct Description description; + Description_Ctor( &description ); + Description_Print( &description, argv[1] ); + } + { + char* p = NULL; + InfoMemoryAccess( p ); + p = (char*)&p; + InfoMemoryAccess( p ); + InfoMemoryTypeAccess( t_short ); + InfoMemoryTypeAccess( t_int ); + InfoMemoryTypeAccess( t_long ); + InfoMemoryTypeAccess( t_double ); + } + + exit( 0 ); +} diff --git a/tools/source/stream/cachestr.cxx b/tools/source/stream/cachestr.cxx new file mode 100644 index 000000000000..fae1e823b1aa --- /dev/null +++ b/tools/source/stream/cachestr.cxx @@ -0,0 +1,293 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: cachestr.cxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <tools/debug.hxx> +#include <tools/stream.hxx> +#include <tools/cachestr.hxx> +#include <tools/tempfile.hxx> + +/************************************************************************* +|* +|* SvCacheStream::SvCacheStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 27.09.94 +|* Letzte Aenderung OV 27.09.94 +|* +*************************************************************************/ + +SvCacheStream::SvCacheStream( ULONG nMaxMemSize ) +{ + if( !nMaxMemSize ) + nMaxMemSize = 20480; + SvStream::bIsWritable = TRUE; + nMaxSize = nMaxMemSize; + bPersistent = FALSE; + pSwapStream = 0; + pCurrentStream = new SvMemoryStream( nMaxMemSize ); + pTempFile = 0; +} + +/************************************************************************* +|* +|* SvCacheStream::SvCacheStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 27.09.94 +|* Letzte Aenderung OV 27.09.94 +|* +*************************************************************************/ + +SvCacheStream::SvCacheStream( const String &rFileName, + ULONG nExpectedSize, + ULONG nMaxMemSize ) +{ + if( !nMaxMemSize ) + nMaxMemSize = 20480; + + if( nExpectedSize > nMaxMemSize ) + nExpectedSize = nMaxMemSize; // oder gleich in File schreiben + else if( !nExpectedSize ) + nExpectedSize = 4096; + + SvStream::bIsWritable = TRUE; + nMaxSize = nMaxMemSize; + bPersistent = TRUE; + aFileName = rFileName; + pSwapStream = 0; + pCurrentStream = new SvMemoryStream( nExpectedSize ); + pTempFile = 0; +} + +/************************************************************************* +|* +|* SvCacheStream::~SvCacheStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 27.09.94 +|* Letzte Aenderung OV 27.09.94 +|* +*************************************************************************/ + +SvCacheStream::~SvCacheStream() +{ + if( pCurrentStream != pSwapStream ) + delete pSwapStream; + delete pCurrentStream; + + if( pSwapStream && !bPersistent && pTempFile ) + { + // temporaeres File loeschen + pTempFile->EnableKillingFile( TRUE ); + } + + delete pTempFile; +} + +/************************************************************************* +|* +|* SvCacheStream::SwapOut() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 27.09.94 +|* Letzte Aenderung OV 27.09.94 +|* +*************************************************************************/ + +void SvCacheStream::SwapOut() +{ + if( pCurrentStream != pSwapStream ) + { + if( !pSwapStream && !aFileName.Len() ) + { + if (aFilenameLinkHdl.IsSet()) + { + // pSwapStream wird zum Schutz gegen Reentranz genutzt + pSwapStream = pCurrentStream; + Link aLink( aFilenameLinkHdl ); + aFilenameLinkHdl = Link(); + aLink.Call(this); + // pSwapStream nur zuruecksetzen, wenn nicht ueber + // SetSwapStream geaendert + if( pSwapStream == pCurrentStream ) pSwapStream = 0; + } + else + { + pTempFile = new TempFile; + aFileName = pTempFile->GetName(); + } + } + + ULONG nPos = pCurrentStream->Tell(); + pCurrentStream->Seek( 0 ); + if( !pSwapStream ) + pSwapStream = new SvFileStream( aFileName, STREAM_READWRITE | STREAM_TRUNC ); + *pSwapStream << *pCurrentStream; + pSwapStream->Flush(); + delete pCurrentStream; + pCurrentStream = pSwapStream; + pCurrentStream->Seek( nPos ); + } +} + +/************************************************************************* +|* +|* SvCacheStream::GetData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 27.09.94 +|* Letzte Aenderung OV 27.09.94 +|* +*************************************************************************/ + +ULONG SvCacheStream::GetData( void* pData, ULONG nSize ) +{ + return pCurrentStream->Read( pData, nSize ); +} + +/************************************************************************* +|* +|* SvCacheStream::PutData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 27.09.94 +|* Letzte Aenderung OV 27.09.94 +|* +*************************************************************************/ + +ULONG SvCacheStream::PutData( const void* pData, ULONG nSize ) +{ + // lieber unnoetig auslagern als unnoetig umkopieren + if( pCurrentStream != pSwapStream + && pCurrentStream->Tell() + nSize > nMaxSize ) + SwapOut(); + return pCurrentStream->Write( pData, nSize ); +} + +/************************************************************************* +|* +|* SvCacheStream::SeekPos() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 27.09.94 +|* Letzte Aenderung OV 27.09.94 +|* +*************************************************************************/ + +ULONG SvCacheStream::SeekPos( ULONG nPos ) +{ + return pCurrentStream->Seek( nPos ); +} + +/************************************************************************* +|* +|* SvCacheStream::FlushData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 27.09.94 +|* Letzte Aenderung OV 27.09.94 +|* +*************************************************************************/ + +void SvCacheStream::FlushData() +{ + pCurrentStream->Flush(); + if( pCurrentStream != pSwapStream + && ((SvMemoryStream*)pCurrentStream)->GetSize() > nMaxSize ) + SwapOut(); +} + +/************************************************************************* +|* +|* SvCacheStream::GetStr() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 27.09.94 +|* Letzte Aenderung OV 27.09.94 +|* +*************************************************************************/ + +const void* SvCacheStream::GetBuffer() +{ + Flush(); + if( pCurrentStream != pSwapStream ) + return ((SvMemoryStream*)pCurrentStream)->GetData(); + else + return 0; +} + +/************************************************************************* +|* +|* SvCacheStream::SetSize() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 27.09.94 +|* Letzte Aenderung OV 27.09.94 +|* +*************************************************************************/ + +void SvCacheStream::SetSize( ULONG nSize ) +{ + pCurrentStream->SetStreamSize( nSize ); +} + +/************************************************************************* +|* +|* SvCacheStream::GetSize() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 27.09.94 +|* Letzte Aenderung OV 27.09.94 +|* +*************************************************************************/ + +ULONG SvCacheStream::GetSize() +{ + // ACHTUNG: SvMemoryStream::GetSize() gibt Groesse + // des allozierten Buffers zurueck + Flush(); + ULONG nTemp = Tell(); + ULONG nLength = Seek( STREAM_SEEK_TO_END ); + Seek( nTemp ); + return nLength; +} + +void SvCacheStream::SetFilenameHdl( const Link& rLink) +{ + aFilenameLinkHdl = rLink; +} + +const Link& SvCacheStream::GetFilenameHdl() const +{ + return aFilenameLinkHdl; +} diff --git a/tools/source/stream/makefile.mk b/tools/source/stream/makefile.mk new file mode 100644 index 000000000000..c8374f5e7e25 --- /dev/null +++ b/tools/source/stream/makefile.mk @@ -0,0 +1,62 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.10 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=stream + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES= $(SLO)$/stream.obj \ + $(SLO)$/strmsys.obj \ + $(SLO)$/cachestr.obj \ + $(SLO)$/vcompat.obj + +OBJFILES+= $(OBJ)$/stream.obj \ + $(OBJ)$/strmsys.obj \ + $(OBJ)$/cachestr.obj \ + $(OBJ)$/vcompat.obj + +# --- Targets ------------------------------------------------------- + +.INCLUDE : target.mk + +$(SLO)$/strmsys.obj : \ + strmwnt.cxx \ + strmos2.cxx \ + strmunx.cxx + diff --git a/tools/source/stream/stream.cxx b/tools/source/stream/stream.cxx new file mode 100644 index 000000000000..de802ece9f05 --- /dev/null +++ b/tools/source/stream/stream.cxx @@ -0,0 +1,2844 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: stream.cxx,v $ + * $Revision: 1.27 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +// ToDo: +// - Read->RefreshBuffer->Auf Aenderungen von nBufActualLen reagieren + +#include <cstddef> + +#include <string.h> +#include <stdio.h> +#include <ctype.h> // isspace +#include <stdlib.h> // strtol, _crotl + +#include "boost/static_assert.hpp" + +/* +#if defined( DBG_UTIL ) && (OSL_DEBUG_LEVEL > 1) +// prueft Synchronisation des Buffers nach allen Read, Write, Seek +#define OV_DEBUG +#endif +*/ + +#include <tools/solar.h> + +#if defined(BLC) +#define SWAPNIBBLES(c) c=_crotl(c,4); +#else +#define SWAPNIBBLES(c) \ +unsigned char nSwapTmp=c; \ +nSwapTmp <<= 4; \ +c >>= 4; \ +c |= nSwapTmp; +#endif + +#include <tools/debug.hxx> +#define ENABLE_BYTESTRING_STREAM_OPERATORS +#include <tools/stream.hxx> +#include <osl/thread.h> +#include <algorithm> + +// ----------------------------------------------------------------------- + +DBG_NAME( Stream ) + +// ----------------------------------------------------------------------- + +// sprintf Param-Mode +#define SPECIAL_PARAM_NONE 0 // Format-Str, Number +#define SPECIAL_PARAM_WIDTH 1 // Format-Str, Width, Number +#define SPECIAL_PARAM_PRECISION 2 // Format-Str, Precision, Number +#define SPECIAL_PARAM_BOTH 3 // Format-Str, Width, Precision, Number + +// ----------------------------------------------------------------------- + +// !!! Nicht inline, wenn Operatoren <<,>> inline sind +inline static void SwapUShort( sal_uInt16& r ) + { r = SWAPSHORT(r); } +inline static void SwapShort( short& r ) + { r = SWAPSHORT(r); } +inline static void SwapLong( long& r ) + { r = SWAPLONG(r); } +inline static void SwapULong( sal_uInt32& r ) + { r = SWAPLONG(r); } +inline static void SwapLongInt( int& r ) + { r = SWAPLONG(r); } +inline static void SwapLongUInt( unsigned int& r ) + { r = SWAPLONG(r); } +#ifdef UNX +inline static void SwapFloat( float& r ) + { + *((sal_uInt32*)(void*)&r) = SWAPLONG( *((sal_uInt32*)(void*)&r) ); + } +inline static void SwapDouble( double& r ) + { + if( sizeof(double) != 8 ) + { + DBG_ASSERT( FALSE, "Can only swap 8-Byte-doubles\n" ); + } + else + { + sal_uInt32* c = (sal_uInt32*)(void*)&r; + c[0] ^= c[1]; // zwei 32-Bit-Werte in situ vertauschen + c[1] ^= c[0]; + c[0] ^= c[1]; + c[0] = SWAPLONG(c[0]); // und die beiden 32-Bit-Werte selbst in situ drehen + c[1] = SWAPLONG(c[1]); + } + } +#endif + +//SDO + +#define READNUMBER_WITHOUT_SWAP(datatype,value) \ +{\ +int tmp = eIOMode; \ +if( (tmp == STREAM_IO_READ) && sizeof(datatype)<=nBufFree) \ +{\ + for (std::size_t i = 0; i < sizeof(datatype); i++)\ + ((char *)&value)[i] = pBufPos[i];\ + nBufActualPos += sizeof(datatype);\ + pBufPos += sizeof(datatype);\ + nBufFree -= sizeof(datatype);\ +}\ +else\ + Read( (char*)&value, sizeof(datatype) );\ +} + +#define WRITENUMBER_WITHOUT_SWAP(datatype,value) \ +{\ +int tmp = eIOMode; \ +if( (tmp==STREAM_IO_WRITE) && sizeof(datatype) <= nBufFree)\ +{\ + for (std::size_t i = 0; i < sizeof(datatype); i++)\ + pBufPos[i] = ((char *)&value)[i];\ + nBufFree -= sizeof(datatype);\ + nBufActualPos += sizeof(datatype);\ + if( nBufActualPos > nBufActualLen )\ + nBufActualLen = nBufActualPos;\ + pBufPos += sizeof(datatype);\ + bIsDirty = TRUE;\ +}\ +else\ + Write( (char*)&value, sizeof(datatype) );\ +} + +//============================================================================ +// +// class SvLockBytes +// +//============================================================================ + +void SvLockBytes::close() +{ + if (m_bOwner) + delete m_pStream; + m_pStream = 0; +} + +//============================================================================ +TYPEINIT0(SvLockBytes); + +//============================================================================ +// virtual +ErrCode SvLockBytes::ReadAt(sal_Size nPos, void * pBuffer, sal_Size nCount, + sal_Size * pRead) const +{ + if (!m_pStream) + { + DBG_ERROR("SvLockBytes::ReadAt(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->Seek(nPos); + sal_Size nTheRead = m_pStream->Read(pBuffer, nCount); + if (pRead) + *pRead = nTheRead; + return m_pStream->GetErrorCode(); +} + +//============================================================================ +// virtual +ErrCode SvLockBytes::WriteAt(sal_Size nPos, const void * pBuffer, sal_Size nCount, + sal_Size * pWritten) +{ + if (!m_pStream) + { + DBG_ERROR("SvLockBytes::WriteAt(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->Seek(nPos); + sal_Size nTheWritten = m_pStream->Write(pBuffer, nCount); + if (pWritten) + *pWritten = nTheWritten; + return m_pStream->GetErrorCode(); +} + +//============================================================================ +// virtual +ErrCode SvLockBytes::Flush() const +{ + if (!m_pStream) + { + DBG_ERROR("SvLockBytes::Flush(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->Flush(); + return m_pStream->GetErrorCode(); +} + +//============================================================================ +// virtual +ErrCode SvLockBytes::SetSize(sal_Size nSize) +{ + if (!m_pStream) + { + DBG_ERROR("SvLockBytes::SetSize(): Bad stream"); + return ERRCODE_NONE; + } + + m_pStream->SetStreamSize(nSize); + return m_pStream->GetErrorCode(); +} + +//============================================================================ +ErrCode SvLockBytes::LockRegion(sal_Size, sal_Size, LockType) +{ + DBG_ERROR("SvLockBytes::LockRegion(): Not implemented"); + return ERRCODE_NONE; +} + +//============================================================================ + +ErrCode SvLockBytes::UnlockRegion(sal_Size, sal_Size, LockType) +{ + DBG_ERROR("SvLockBytes::UnlockRegion(): Not implemented"); + return ERRCODE_NONE; +} + +//============================================================================ +ErrCode SvLockBytes::Stat(SvLockBytesStat * pStat, SvLockBytesStatFlag) const +{ + if (!m_pStream) + { + DBG_ERROR("SvLockBytes::Stat(): Bad stream"); + return ERRCODE_NONE; + } + + if (pStat) + { + sal_Size nPos = m_pStream->Tell(); + pStat->nSize = m_pStream->Seek(STREAM_SEEK_TO_END); + m_pStream->Seek(nPos); + } + return ERRCODE_NONE; +} + +//============================================================================ +// +// class SvOpenLockBytes +// +//============================================================================ + +TYPEINIT1(SvOpenLockBytes, SvLockBytes); + +//============================================================================ +// +// class SvAsyncLockBytes +// +//============================================================================ + +TYPEINIT1(SvAsyncLockBytes, SvOpenLockBytes); + +//============================================================================ +// virtual +ErrCode SvAsyncLockBytes::ReadAt(sal_Size nPos, void * pBuffer, sal_Size nCount, + sal_Size * pRead) const +{ + if (m_bTerminated) + return SvOpenLockBytes::ReadAt(nPos, pBuffer, nCount, pRead); + else + { + sal_Size nTheCount = std::min(nPos < m_nSize ? m_nSize - nPos : 0, nCount); + ErrCode nError = SvOpenLockBytes::ReadAt(nPos, pBuffer, nTheCount, + pRead); + return !nCount || nTheCount == nCount || nError ? nError : + ERRCODE_IO_PENDING; + } +} + +//============================================================================ +// virtual +ErrCode SvAsyncLockBytes::WriteAt(sal_Size nPos, const void * pBuffer, + sal_Size nCount, sal_Size * pWritten) +{ + if (m_bTerminated) + return SvOpenLockBytes::WriteAt(nPos, pBuffer, nCount, pWritten); + else + { + sal_Size nTheCount = std::min(nPos < m_nSize ? m_nSize - nPos : 0, nCount); + ErrCode nError = SvOpenLockBytes::WriteAt(nPos, pBuffer, nTheCount, + pWritten); + return !nCount || nTheCount == nCount || nError ? nError : + ERRCODE_IO_PENDING; + } +} + +//============================================================================ +// virtual +ErrCode SvAsyncLockBytes::FillAppend(const void * pBuffer, sal_Size nCount, + sal_Size * pWritten) +{ + sal_Size nTheWritten; + ErrCode nError = SvOpenLockBytes::WriteAt(m_nSize, pBuffer, nCount, + &nTheWritten); + if (!nError) + m_nSize += nTheWritten; + if (pWritten) + *pWritten = nTheWritten; + return nError; +} + +//============================================================================ +// virtual +sal_Size SvAsyncLockBytes::Seek(sal_Size nPos) +{ + if (nPos != STREAM_SEEK_TO_END) + m_nSize = nPos; + return m_nSize; +} + +//============================================================================ +// +// class SvStream +// +//============================================================================ + +sal_Size SvStream::GetData( void* pData, sal_Size nSize ) +{ + if( !GetError() ) + { + DBG_ASSERT( xLockBytes.Is(), "pure virtual function" ); + sal_Size nRet; + nError = xLockBytes->ReadAt( nActPos, pData, nSize, &nRet ); + nActPos += nRet; + return nRet; + } + else return 0; +} + +ErrCode SvStream::SetLockBytes( SvLockBytesRef& rLB ) +{ + xLockBytes = rLB; + RefreshBuffer(); + return ERRCODE_NONE; +} + +//======================================================================== + +sal_Size SvStream::PutData( const void* pData, sal_Size nSize ) +{ + if( !GetError() ) + { + DBG_ASSERT( xLockBytes.Is(), "pure virtual function" ); + sal_Size nRet; + nError = xLockBytes->WriteAt( nActPos, pData, nSize, &nRet ); + nActPos += nRet; + return nRet; + } + else return 0; +} + +//======================================================================== + +sal_Size SvStream::SeekPos( sal_Size nPos ) +{ + if( !GetError() && nPos == STREAM_SEEK_TO_END ) + { + DBG_ASSERT( xLockBytes.Is(), "pure virtual function" ); + SvLockBytesStat aStat; + xLockBytes->Stat( &aStat, SVSTATFLAG_DEFAULT ); + nActPos = aStat.nSize; + } + else + nActPos = nPos; + return nActPos; +} + +//======================================================================== + +void SvStream::FlushData() +{ + if( !GetError() ) + { + DBG_ASSERT( xLockBytes.Is(), "pure virtual function" ); + nError = xLockBytes->Flush(); + } +} + +//======================================================================== + +void SvStream::SetSize( sal_Size nSize ) +{ + DBG_ASSERT( xLockBytes.Is(), "pure virtual function" ); + nError = xLockBytes->SetSize( nSize ); +} + +void SvStream::ImpInit() +{ + nActPos = 0; + nCompressMode = COMPRESSMODE_NONE; + eStreamCharSet = osl_getThreadTextEncoding(); +// eTargetCharSet = osl_getThreadTextEncoding(); + nCryptMask = 0; + bIsEof = FALSE; +#if defined UNX + eLineDelimiter = LINEEND_LF; // UNIX-Format +#else + eLineDelimiter = LINEEND_CRLF; // DOS-Format +#endif + + SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + nBufFilePos = 0; + nBufActualPos = 0; + bIsDirty = FALSE; + bIsConsistent = TRUE; + bIsWritable = TRUE; + + pRWBuf = 0; + pBufPos = 0; + nBufSize = 0; + nBufActualLen = 0; + eIOMode = STREAM_IO_DONTKNOW; + nBufFree = 0; + + nRadix = 10; + nPrecision = 0; // all significant digits + nWidth = 0; // default width + cFiller = ' '; + nJustification = JUSTIFY_RIGHT; + eStreamMode = 0; + CreateFormatString(); + + nVersion = 0; + + ClearError(); +} + +/************************************************************************* +|* +|* Stream::Stream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +SvStream::SvStream( SvLockBytes* pLockBytesP ) +{ + DBG_CTOR( Stream, NULL ); + + ImpInit(); + xLockBytes = pLockBytesP; + const SvStream* pStrm; + if( pLockBytesP ) { + pStrm = pLockBytesP->GetStream(); + if( pStrm ) { + SetError( pStrm->GetErrorCode() ); + } + } + SetBufferSize( 256 ); +} + +SvStream::SvStream() +{ + DBG_CTOR( Stream, NULL ); + + ImpInit(); +} + +/************************************************************************* +|* +|* Stream::~Stream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +SvStream::~SvStream() +{ + DBG_DTOR( Stream, NULL ); + + if ( xLockBytes.Is() ) + Flush(); + + if( pRWBuf ) + delete[] pRWBuf; +} + +/************************************************************************* +|* +|* Stream::IsA() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +sal_uInt16 SvStream::IsA() const +{ + return (sal_uInt16)ID_STREAM; +} + +/************************************************************************* +|* +|* Stream::ClearError() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +void SvStream::ClearError() +{ + bIsEof = FALSE; + nError = SVSTREAM_OK; +} + +/************************************************************************* +|* +|* Stream::SetError() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +void SvStream::SetError( sal_uInt32 nErrorCode ) +{ + if ( nError == SVSTREAM_OK ) + nError = nErrorCode; +} + + +/************************************************************************* +|* +|* Stream::SetNumberFormatInt() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +void SvStream::SetNumberFormatInt( sal_uInt16 nNewFormat ) +{ + nNumberFormatInt = nNewFormat; + bSwap = FALSE; +#ifdef OSL_BIGENDIAN + if( nNumberFormatInt == NUMBERFORMAT_INT_LITTLEENDIAN ) + bSwap = TRUE; +#else + if( nNumberFormatInt == NUMBERFORMAT_INT_BIGENDIAN ) + bSwap = TRUE; +#endif +} + +/************************************************************************* +|* +|* Stream::SetBufferSize() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +void SvStream::SetBufferSize( sal_uInt16 nBufferSize ) +{ + sal_Size nActualFilePos = Tell(); + sal_Bool bDontSeek = (sal_Bool)(pRWBuf == 0); + + if( bIsDirty && bIsConsistent && bIsWritable ) // wg. Windows NT: Access denied + Flush(); + + if( nBufSize ) + { + delete[] pRWBuf; + nBufFilePos += nBufActualPos; + } + + pRWBuf = 0; + nBufActualLen = 0; + nBufActualPos = 0; + nBufSize = nBufferSize; + if( nBufSize ) + pRWBuf = new BYTE[ nBufSize ]; + bIsConsistent = TRUE; + pBufPos = pRWBuf; + eIOMode = STREAM_IO_DONTKNOW; + if( !bDontSeek ) + SeekPos( nActualFilePos ); +} + +/************************************************************************* +|* +|* Stream::ClearBuffer() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +void SvStream::ClearBuffer() +{ + nBufActualLen = 0; + nBufActualPos = 0; + nBufFilePos = 0; + pBufPos = pRWBuf; + bIsDirty = FALSE; + bIsConsistent = TRUE; + eIOMode = STREAM_IO_DONTKNOW; + + bIsEof = FALSE; +} + +/************************************************************************* +|* +|* Stream::ResetError() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +void SvStream::ResetError() +{ + ClearError(); +} + +/************************************************************************* +|* +|* Stream::ReadLine() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +sal_Bool SvStream::ReadByteStringLine( String& rStr, rtl_TextEncoding eSrcCharSet ) +{ + sal_Bool bRet; + ByteString aStr; + + bRet = ReadLine(aStr); + rStr = UniString( aStr, eSrcCharSet ); + return bRet; +} + +sal_Bool SvStream::ReadLine( ByteString& rStr ) +{ + sal_Char buf[256+1]; + sal_Bool bEnd = FALSE; + sal_Size nOldFilePos = Tell(); + sal_Char c = 0; + sal_Size nTotalLen = 0; + + rStr.Erase(); + while( !bEnd && !GetError() ) // !!! nicht auf EOF testen, + // !!! weil wir blockweise + // !!! lesen + { + sal_uInt16 nLen = (sal_uInt16)Read( buf, sizeof(buf)-1 ); + if ( !nLen ) + { + if ( rStr.Len() == 0 ) + { + // der allererste Blockread hat fehlgeschlagen -> Abflug + bIsEof = TRUE; + 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; + } + // 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; + } + } + if ( n ) + rStr.Append( buf, n ); + nTotalLen += j; + } + + if ( !bEnd && !GetError() && rStr.Len() ) + bEnd = TRUE; + + nOldFilePos += nTotalLen; + if( Tell() > nOldFilePos ) + nOldFilePos++; + Seek( nOldFilePos ); // seeken wg. obigem BlockRead! + + if ( bEnd && (c=='\r' || c=='\n') ) // Sonderbehandlung DOS-Dateien + { + char cTemp; + sal_Size nLen = Read((char*)&cTemp , sizeof(cTemp) ); + if ( nLen ) { + if( cTemp == c || (cTemp != '\n' && cTemp != '\r') ) + Seek( nOldFilePos ); + } + } + + if ( bEnd ) + bIsEof = FALSE; + return bEnd; +} + +sal_Bool SvStream::ReadUniStringLine( String& rStr ) +{ + sal_Unicode buf[256+1]; + sal_Bool bEnd = FALSE; + sal_Size nOldFilePos = Tell(); + sal_Unicode c = 0; + sal_Size nTotalLen = 0; + + DBG_ASSERT( sizeof(sal_Unicode) == sizeof(sal_uInt16), "ReadUniStringLine: swapping sizeof(sal_Unicode) not implemented" ); + + rStr.Erase(); + while( !bEnd && !GetError() ) // !!! nicht auf EOF testen, + // !!! weil wir blockweise + // !!! lesen + { + sal_uInt16 nLen = (sal_uInt16)Read( (char*)buf, sizeof(buf)-sizeof(sal_Unicode) ); + nLen /= sizeof(sal_Unicode); + if ( !nLen ) + { + if ( rStr.Len() == 0 ) + { + // der allererste Blockread hat fehlgeschlagen -> Abflug + bIsEof = TRUE; + return FALSE; + } + else + break; + } + + sal_uInt16 j, n; + for( j = n = 0; j < nLen ; ++j ) + { + if ( bSwap ) + SwapUShort( 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; + } + } + if ( n ) + rStr.Append( buf, n ); + nTotalLen += j; + } + + if ( !bEnd && !GetError() && rStr.Len() ) + bEnd = TRUE; + + nOldFilePos += nTotalLen * sizeof(sal_Unicode); + if( Tell() > nOldFilePos ) + nOldFilePos += sizeof(sal_Unicode); + Seek( nOldFilePos ); // seeken wg. obigem BlockRead! + + if ( bEnd && (c=='\r' || c=='\n') ) // Sonderbehandlung DOS-Dateien + { + sal_Unicode cTemp; + Read( (char*)&cTemp, sizeof(cTemp) ); + if ( bSwap ) + SwapUShort( cTemp ); + if( cTemp == c || (cTemp != '\n' && cTemp != '\r') ) + Seek( nOldFilePos ); + } + + if ( bEnd ) + bIsEof = FALSE; + return bEnd; +} + +sal_Bool SvStream::ReadUniOrByteStringLine( String& rStr, rtl_TextEncoding eSrcCharSet ) +{ + if ( eSrcCharSet == RTL_TEXTENCODING_UNICODE ) + return ReadUniStringLine( rStr ); + else + return ReadByteStringLine( rStr, eSrcCharSet ); +} + +/************************************************************************* +|* +|* Stream::ReadCString +|* +*************************************************************************/ + +sal_Bool SvStream::ReadCString( ByteString& rStr ) +{ + if( rStr.Len() ) + rStr.Erase(); + + sal_Char buf[ 256 + 1 ]; + sal_Bool bEnd = FALSE; + sal_Size nFilePos = Tell(); + + while( !bEnd && !GetError() ) + { + sal_uInt16 nLen = (sal_uInt16)Read( buf, sizeof(buf)-1 ); + sal_uInt16 nReallyRead = nLen; + if( !nLen ) + break; + + const sal_Char* pPtr = buf; + while( *pPtr && nLen ) + ++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 + ); + + rStr.Append( buf, ::sal::static_int_cast< xub_StrLen >( pPtr - buf ) ); + } + + nFilePos += rStr.Len(); + if( Tell() > nFilePos ) + nFilePos++; + Seek( nFilePos ); // seeken wg. obigem BlockRead! + return bEnd; +} + +sal_Bool SvStream::ReadCString( String& rStr, rtl_TextEncoding eToEncode ) +{ + ByteString sStr; + sal_Bool bRet = ReadCString( sStr ); + rStr = String( sStr, eToEncode ); + return bRet; +} + + +/************************************************************************* +|* +|* Stream::WriteUnicodeText() +|* +*************************************************************************/ + +sal_Bool SvStream::WriteUnicodeText( const String& rStr ) +{ + DBG_ASSERT( sizeof(sal_Unicode) == sizeof(sal_uInt16), "WriteUnicodeText: swapping sizeof(sal_Unicode) not implemented" ); + if ( bSwap ) + { + xub_StrLen nLen = rStr.Len(); + sal_Unicode aBuf[384]; + sal_Unicode* const pTmp = ( nLen > 384 ? new sal_Unicode[nLen] : aBuf); + memcpy( pTmp, rStr.GetBuffer(), nLen * sizeof(sal_Unicode) ); + sal_Unicode* p = pTmp; + const sal_Unicode* const pStop = pTmp + nLen; + while ( p < pStop ) + { + SwapUShort( *p ); + p++; + } + Write( (char*)pTmp, nLen * sizeof(sal_Unicode) ); + if ( pTmp != aBuf ) + delete [] pTmp; + } + else + Write( (char*)rStr.GetBuffer(), rStr.Len() * sizeof(sal_Unicode) ); + return nError == SVSTREAM_OK; +} + +sal_Bool SvStream::WriteUnicodeOrByteText( const String& rStr, rtl_TextEncoding eDestCharSet ) +{ + if ( eDestCharSet == RTL_TEXTENCODING_UNICODE ) + return WriteUnicodeText( rStr ); + else + { + ByteString aStr( rStr, eDestCharSet ); + Write( aStr.GetBuffer(), aStr.Len() ); + return nError == SVSTREAM_OK; + } +} + +/************************************************************************* +|* +|* Stream::WriteLine() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +sal_Bool SvStream::WriteByteStringLine( const String& rStr, rtl_TextEncoding eDestCharSet ) +{ + return WriteLine( ByteString( rStr, eDestCharSet ) ); +} + +sal_Bool SvStream::WriteLine( const ByteString& rStr ) +{ + Write( rStr.GetBuffer(), rStr.Len() ); + endl(*this); + return nError == SVSTREAM_OK; +} + +sal_Bool SvStream::WriteUniStringLine( const String& rStr ) +{ + WriteUnicodeText( rStr ); + endlu(*this); + return nError == SVSTREAM_OK; +} + +sal_Bool SvStream::WriteUniOrByteStringLine( const String& rStr, rtl_TextEncoding eDestCharSet ) +{ + if ( eDestCharSet == RTL_TEXTENCODING_UNICODE ) + return WriteUniStringLine( rStr ); + else + return WriteByteStringLine( rStr, eDestCharSet ); +} + +/************************************************************************* +|* +|* Stream::WriteLines() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 17.07.95 +|* Letzte Aenderung OV 17.07.95 +|* +*************************************************************************/ + +sal_Bool SvStream::WriteByteStringLines( const String& rStr, rtl_TextEncoding eDestCharSet ) +{ + return WriteLines( ByteString( rStr, eDestCharSet ) ); +} + +sal_Bool SvStream::WriteLines( const ByteString& rStr ) +{ + ByteString aStr( rStr ); + aStr.ConvertLineEnd( eLineDelimiter ); + Write( aStr.GetBuffer(), aStr.Len() ); + endl( *this ); + return (sal_Bool)(nError == SVSTREAM_OK); +} + +sal_Bool SvStream::WriteUniStringLines( const String& rStr ) +{ + String aStr( rStr ); + aStr.ConvertLineEnd( eLineDelimiter ); + WriteUniStringLine( aStr ); + return nError == SVSTREAM_OK; +} + +sal_Bool SvStream::WriteUniOrByteStringLines( const String& rStr, rtl_TextEncoding eDestCharSet ) +{ + if ( eDestCharSet == RTL_TEXTENCODING_UNICODE ) + return WriteUniStringLines( rStr ); + else + return WriteByteStringLines( rStr, eDestCharSet ); +} + +/************************************************************************* +|* +|* Stream::WriteUniOrByteChar() +|* +*************************************************************************/ + +sal_Bool SvStream::WriteUniOrByteChar( sal_Unicode ch, rtl_TextEncoding eDestCharSet ) +{ + if ( eDestCharSet == RTL_TEXTENCODING_UNICODE ) + *this << ch; + else + { + ByteString aStr( ch, eDestCharSet ); + Write( aStr.GetBuffer(), aStr.Len() ); + } + return nError == SVSTREAM_OK; +} + +/************************************************************************* +|* +|* Stream::StartWritingUnicodeText() +|* +*************************************************************************/ + +sal_Bool SvStream::StartWritingUnicodeText() +{ + SetEndianSwap( FALSE ); // write native format + // 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 + *this << sal_uInt16( 0xfeff ); + return nError == SVSTREAM_OK; +} + +/************************************************************************* +|* +|* Stream::StartReadingUnicodeText() +|* +*************************************************************************/ + +sal_Bool SvStream::StartReadingUnicodeText() +{ + sal_uInt16 nFlag; + *this >> nFlag; + switch ( nFlag ) + { + case 0xfeff : + // native + break; + case 0xfffe : + SetEndianSwap( !bSwap ); + break; + default: + SeekRel( -((sal_sSize)sizeof(nFlag)) ); // no BOM, pure data + } + return nError == SVSTREAM_OK; +} + +/************************************************************************* +|* +|* Stream::ReadCsvLine() +|* +*************************************************************************/ + +// Precondition: pStr is guaranteed to be non-NULL and points to a 0-terminated +// array. +inline const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr, + sal_Unicode c ) +{ + while (*pStr) + { + if (*pStr == c) + return pStr; + ++pStr; + } + return 0; +} + +sal_Bool SvStream::ReadCsvLine( String& rStr, sal_Bool bEmbeddedLineBreak, + const String& rFieldSeparators, sal_Unicode cFieldQuote, + sal_Bool bAllowBackslashEscape) +{ + ReadUniOrByteStringLine( rStr); + + if (bEmbeddedLineBreak) + { + const sal_Unicode* pSeps = rFieldSeparators.GetBuffer(); + xub_StrLen nLastOffset = 0; + xub_StrLen nQuotes = 0; + while (!IsEof() && rStr.Len() < STRING_MAXLEN) + { + bool bBackslashEscaped = false; + const sal_Unicode *p, *pStart; + p = pStart = rStr.GetBuffer(); + p += nLastOffset; + while (*p) + { + if (nQuotes) + { + if (*p == cFieldQuote && !bBackslashEscaped) + ++nQuotes; + else if (bAllowBackslashEscape) + { + if (*p == '\\') + bBackslashEscaped = !bBackslashEscaped; + else + bBackslashEscaped = false; + } + } + else if (*p == cFieldQuote && (p == pStart || + lcl_UnicodeStrChr( pSeps, p[-1]))) + nQuotes = 1; + // A quote character inside a field content does not start + // a quote. + ++p; + } + + if (nQuotes % 2 == 0) + break; + else + { + nLastOffset = rStr.Len(); + String aNext; + ReadUniOrByteStringLine( aNext); + rStr += sal_Unicode(_LF); + rStr += aNext; + } + } + } + return nError == SVSTREAM_OK; +} + +/************************************************************************* +|* +|* Stream::SeekRel() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +sal_Size SvStream::SeekRel( sal_sSize nPos ) +{ + sal_Size nActualPos = Tell(); + + if ( nPos >= 0 ) + { + if ( SAL_MAX_SIZE - nActualPos > (sal_Size)nPos ) + nActualPos += nPos; + } + else + { + sal_Size nAbsPos = (sal_Size)-nPos; + if ( nActualPos >= nAbsPos ) + nActualPos -= nAbsPos; + } + + pBufPos = pRWBuf + nActualPos; + return Seek( nActualPos ); +} + +/************************************************************************* +|* +|* Stream::operator>>() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +SvStream& SvStream::operator >> ( sal_uInt16& r ) +{ + READNUMBER_WITHOUT_SWAP(sal_uInt16,r) + if( bSwap ) + SwapUShort(r); + return *this; +} + +SvStream& SvStream::operator>> ( sal_uInt32& r ) +{ + READNUMBER_WITHOUT_SWAP(sal_uInt32,r) + if( bSwap ) + SwapULong(r); + return *this; +} + +SvStream& SvStream::operator >> ( long& r ) +{ +#if(SAL_TYPES_SIZEOFLONG != 4) + int tmp = r; + *this >> tmp; + r = tmp; +#else + READNUMBER_WITHOUT_SWAP(long,r) + if( bSwap ) + SwapLong(r); +#endif + return *this; +} + +SvStream& SvStream::operator >> ( short& r ) +{ + READNUMBER_WITHOUT_SWAP(short,r) + if( bSwap ) + SwapShort(r); + return *this; +} + +SvStream& SvStream::operator >> ( int& r ) +{ + READNUMBER_WITHOUT_SWAP(int,r) + if( bSwap ) + SwapLongInt(r); + return *this; +} + +SvStream& SvStream::operator>>( signed char& r ) +{ + if( (eIOMode == STREAM_IO_READ || !bIsConsistent) && + sizeof(signed char) <= nBufFree ) + { + r = *pBufPos; + nBufActualPos += sizeof(signed char); + pBufPos += sizeof(signed char); + nBufFree -= sizeof(signed char); + } + else + Read( (char*)&r, sizeof(signed char) ); + return *this; +} + +// Sonderbehandlung fuer Chars wegen PutBack + +SvStream& SvStream::operator>>( char& r ) +{ + if( (eIOMode == STREAM_IO_READ || !bIsConsistent) && + sizeof(char) <= nBufFree ) + { + r = *pBufPos; + nBufActualPos += sizeof(char); + pBufPos += sizeof(char); + nBufFree -= sizeof(char); + } + else + Read( (char*)&r, sizeof(char) ); + return *this; +} + +SvStream& SvStream::operator>>( unsigned char& r ) +{ + if( (eIOMode == STREAM_IO_READ || !bIsConsistent) && + sizeof(char) <= nBufFree ) + { + r = *pBufPos; + nBufActualPos += sizeof(char); + pBufPos += sizeof(char); + nBufFree -= sizeof(char); + } + else + Read( (char*)&r, sizeof(char) ); + return *this; +} + +SvStream& SvStream::operator>>( float& r ) +{ + // Read( (char*)&r, sizeof(float) ); + READNUMBER_WITHOUT_SWAP(float,r) +#if defined UNX + if( bSwap ) + SwapFloat(r); +#endif + return *this; +} + +SvStream& SvStream::operator>>( double& r ) +{ + // Read( (char*)&r, sizeof(double) ); + READNUMBER_WITHOUT_SWAP(double,r) +#if defined UNX + if( bSwap ) + SwapDouble(r); +#endif + return *this; +} + +SvStream& SvStream::operator>> ( SvStream& rStream ) +{ + const sal_uInt32 cBufLen = 0x8000; + char* pBuf = new char[ cBufLen ]; + + sal_uInt32 nCount; + do { + nCount = Read( pBuf, cBufLen ); + rStream.Write( pBuf, nCount ); + } while( nCount == cBufLen ); + + delete[] pBuf; + return *this; +} + +/************************************************************************* +|* +|* Stream::operator<<() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +SvStream& SvStream::operator<< ( sal_uInt16 v ) +{ + if( bSwap ) + SwapUShort(v); + WRITENUMBER_WITHOUT_SWAP(sal_uInt16,v) + return *this; +} + +SvStream& SvStream::operator<< ( sal_uInt32 v ) +{ + if( bSwap ) + SwapULong(v); + WRITENUMBER_WITHOUT_SWAP(sal_uInt32,v) + return *this; +} + +SvStream& SvStream::operator<< ( long v ) +{ +#if(SAL_TYPES_SIZEOFLONG != 4) + int tmp = v; + *this << tmp; +#else + if( bSwap ) + SwapLong(v); + WRITENUMBER_WITHOUT_SWAP(long,v) +#endif + return *this; +} + +SvStream& SvStream::operator<< ( short v ) +{ + if( bSwap ) + SwapShort(v); + WRITENUMBER_WITHOUT_SWAP(short,v) + return *this; +} + +SvStream& SvStream::operator<<( int v ) +{ + if( bSwap ) + SwapLongInt( v ); + WRITENUMBER_WITHOUT_SWAP(int,v) + return *this; +} + +SvStream& SvStream::operator<< ( signed char v ) +{ + //SDO + int tmp = eIOMode; + if(tmp == STREAM_IO_WRITE && sizeof(signed char) <= nBufFree ) + { + *pBufPos = v; + pBufPos++; // sizeof(char); + nBufActualPos++; + if( nBufActualPos > nBufActualLen ) // Append ? + nBufActualLen = nBufActualPos; + nBufFree--; // = sizeof(char); + bIsDirty = TRUE; + } + else + Write( (char*)&v, sizeof(signed char) ); + return *this; +} + +// Sonderbehandlung fuer chars wegen PutBack + +SvStream& SvStream::operator<< ( char v ) +{ + //SDO + int tmp = eIOMode; + if(tmp == STREAM_IO_WRITE && sizeof(char) <= nBufFree ) + { + *pBufPos = v; + pBufPos++; // sizeof(char); + nBufActualPos++; + if( nBufActualPos > nBufActualLen ) // Append ? + nBufActualLen = nBufActualPos; + nBufFree--; // = sizeof(char); + bIsDirty = TRUE; + } + else + Write( (char*)&v, sizeof(char) ); + return *this; +} + +SvStream& SvStream::operator<< ( unsigned char v ) +{ +//SDO + int tmp = eIOMode; + if(tmp == STREAM_IO_WRITE && sizeof(char) <= nBufFree ) + { + *(unsigned char*)pBufPos = v; + pBufPos++; // = sizeof(char); + nBufActualPos++; // = sizeof(char); + if( nBufActualPos > nBufActualLen ) // Append ? + nBufActualLen = nBufActualPos; + nBufFree--; + bIsDirty = TRUE; + } + else + Write( (char*)&v, sizeof(char) ); + return *this; +} + +SvStream& SvStream::operator<< ( float v ) +{ +#ifdef UNX + if( bSwap ) + SwapFloat(v); +#endif + WRITENUMBER_WITHOUT_SWAP(float,v) + return *this; +} + +SvStream& SvStream::operator<< ( const double& r ) +{ +// Write( (char*)&r, sizeof( double ) ); +#if defined UNX + if( bSwap ) + { + double nHelp = r; + SwapDouble(nHelp); + WRITENUMBER_WITHOUT_SWAP(double,nHelp) + return *this; + } + else +#endif + WRITENUMBER_WITHOUT_SWAP(double,r) + + return *this; +} + +SvStream& SvStream::operator<< ( const char* pBuf ) +{ + Write( pBuf, strlen( pBuf ) ); + return *this; +} + +SvStream& SvStream::operator<< ( const unsigned char* pBuf ) +{ + Write( (char*)pBuf, strlen( (char*)pBuf ) ); + return *this; +} + +SvStream& SvStream::operator<< ( SvStream& rStream ) +{ + const sal_uInt32 cBufLen = 0x8000; + char* pBuf = new char[ cBufLen ]; + sal_uInt32 nCount; + do { + nCount = rStream.Read( pBuf, cBufLen ); + Write( pBuf, nCount ); + } while( nCount == cBufLen ); + + delete[] pBuf; + return *this; +} + +// ----------------------------------------------------------------------- + +SvStream& SvStream::ReadByteString( UniString& rStr, rtl_TextEncoding eSrcCharSet ) +{ + // read UTF-16 string directly from stream ? + if (eSrcCharSet == RTL_TEXTENCODING_UNICODE) + { + sal_uInt32 nLen; + operator>> (nLen); + if (nLen) + { + if (nLen > STRING_MAXLEN) { + SetError(SVSTREAM_GENERALERROR); + return *this; + } + sal_Unicode *pStr = rStr.AllocBuffer( + static_cast< xub_StrLen >(nLen)); + BOOST_STATIC_ASSERT(STRING_MAXLEN <= SAL_MAX_SIZE / 2); + Read( pStr, nLen << 1 ); + + if (bSwap) + for (sal_Unicode *pEnd = pStr + nLen; pStr < pEnd; pStr++) + SwapUShort(*pStr); + } + else + rStr.Erase(); + + return *this; + } + + ByteString aStr; + ReadByteString( aStr ); + rStr = UniString( aStr, eSrcCharSet ); + return *this; +} + +// ----------------------------------------------------------------------- + +SvStream& SvStream::ReadByteString( ByteString& rStr ) +{ + sal_uInt16 nLen = 0; + operator>>( nLen ); + if( nLen ) + { + char* pTmp = rStr.AllocBuffer( nLen ); + nLen = (sal_uInt16)Read( pTmp, nLen ); + } + else + rStr.Erase(); + return *this; +} + +// ----------------------------------------------------------------------- + +SvStream& SvStream::WriteByteString( const UniString& rStr, rtl_TextEncoding eDestCharSet ) +{ + // write UTF-16 string directly into stream ? + if (eDestCharSet == RTL_TEXTENCODING_UNICODE) + { + sal_uInt32 nLen = rStr.Len(); + operator<< (nLen); + if (nLen) + { + if (bSwap) + { + const sal_Unicode *pStr = rStr.GetBuffer(); + const sal_Unicode *pEnd = pStr + nLen; + + for (; pStr < pEnd; pStr++) + { + sal_Unicode c = *pStr; + SwapUShort(c); + WRITENUMBER_WITHOUT_SWAP(sal_uInt16,c) + } + } + else + Write( rStr.GetBuffer(), nLen << 1 ); + } + + return *this; + } + + return WriteByteString(ByteString( rStr, eDestCharSet )); +} + +// ----------------------------------------------------------------------- + +SvStream& SvStream::WriteByteString( const ByteString& rStr) +{ + sal_uInt16 nLen = rStr.Len(); + operator<< ( nLen ); + if( nLen != 0 ) + Write( rStr.GetBuffer(), nLen ); + return *this; +} + +/************************************************************************* +|* +|* Stream::Read() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +sal_Size SvStream::Read( void* pData, sal_Size nCount ) +{ + sal_Size nSaveCount = nCount; + if( !bIsConsistent ) + RefreshBuffer(); + + if( !pRWBuf ) + { + nCount = GetData( (char*)pData,nCount); + if( nCryptMask ) + EncryptBuffer(pData, nCount); + nBufFilePos += nCount; + } + else + { + // ist Block komplett im Puffer + eIOMode = STREAM_IO_READ; + if( nCount <= (sal_Size)(nBufActualLen - nBufActualPos ) ) + { + // Ja! + memcpy(pData, pBufPos, (size_t) nCount); + nBufActualPos = nBufActualPos + (sal_uInt16)nCount; + pBufPos += nCount; + nBufFree = nBufFree - (sal_uInt16)nCount; + } + else + { + if( bIsDirty ) // Flushen ? + { + SeekPos( nBufFilePos ); + if( nCryptMask ) + CryptAndWriteBuffer(pRWBuf, nBufActualLen); + else + PutData( pRWBuf, nBufActualLen ); + bIsDirty = FALSE; + } + + // passt der Datenblock in den Puffer ? + if( nCount > nBufSize ) + { + // Nein! Deshalb ohne Umweg ueber den Puffer direkt + // in den Zielbereich einlesen + + eIOMode = STREAM_IO_DONTKNOW; + + SeekPos( nBufFilePos + nBufActualPos ); + nBufActualLen = 0; + pBufPos = pRWBuf; + nCount = GetData( (char*)pData, nCount ); + if( nCryptMask ) + EncryptBuffer(pData, nCount); + nBufFilePos += nCount; + nBufFilePos += nBufActualPos; + nBufActualPos = 0; + } + else + { + // Der Datenblock passt komplett in den Puffer. Deshalb + // Puffer fuellen und dann die angeforderten Daten in den + // Zielbereich kopieren. + + nBufFilePos += nBufActualPos; + SeekPos( nBufFilePos ); + + // TODO: Typecast vor GetData, sal_uInt16 nCountTmp + sal_Size nCountTmp = GetData( pRWBuf, nBufSize ); + if( nCryptMask ) + EncryptBuffer(pRWBuf, nCountTmp); + nBufActualLen = (sal_uInt16)nCountTmp; + if( nCount > nCountTmp ) + { + nCount = nCountTmp; // zurueckstutzen, Eof siehe unten + } + memcpy( pData, pRWBuf, (size_t)nCount ); + nBufActualPos = (sal_uInt16)nCount; + pBufPos = pRWBuf + nCount; + } + } + } + bIsEof = FALSE; + nBufFree = nBufActualLen - nBufActualPos; + if( nCount != nSaveCount && nError != ERRCODE_IO_PENDING ) + bIsEof = TRUE; + if( nCount == nSaveCount && nError == ERRCODE_IO_PENDING ) + nError = ERRCODE_NONE; + return nCount; +} + +/************************************************************************* +|* +|* Stream::Write() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +sal_Size SvStream::Write( const void* pData, sal_Size nCount ) +{ + if( !nCount ) + return 0; + if( !bIsWritable ) + { + SetError( ERRCODE_IO_CANTWRITE ); + return 0; + } + if( !bIsConsistent ) + RefreshBuffer(); // Aenderungen des Puffers durch PutBack loeschen + + if( !pRWBuf ) + { + if( nCryptMask ) + nCount = CryptAndWriteBuffer( pData, nCount ); + else + nCount = PutData( (char*)pData, nCount ); + nBufFilePos += nCount; + return nCount; + } + + eIOMode = STREAM_IO_WRITE; + if( nCount <= (sal_Size)(nBufSize - nBufActualPos) ) + { + memcpy( pBufPos, pData, (size_t)nCount ); + nBufActualPos = nBufActualPos + (sal_uInt16)nCount; + // wurde der Puffer erweitert ? + if( nBufActualPos > nBufActualLen ) + nBufActualLen = nBufActualPos; + + pBufPos += nCount; + bIsDirty = TRUE; + } + else + { + // Flushen ? + if( bIsDirty ) + { + SeekPos( nBufFilePos ); + if( nCryptMask ) + CryptAndWriteBuffer( pRWBuf, (sal_Size)nBufActualLen ); + else + PutData( pRWBuf, nBufActualLen ); + bIsDirty = FALSE; + } + + // passt der Block in den Puffer ? + if( nCount > nBufSize ) + { + eIOMode = STREAM_IO_DONTKNOW; + nBufFilePos += nBufActualPos; + nBufActualLen = 0; + nBufActualPos = 0; + pBufPos = pRWBuf; + SeekPos( nBufFilePos ); + if( nCryptMask ) + nCount = CryptAndWriteBuffer( pData, nCount ); + else + nCount = PutData( (char*)pData, nCount ); + nBufFilePos += nCount; + } + else + { + // Block in Puffer stellen + memcpy( pRWBuf, pData, (size_t)nCount ); + + // Reihenfolge! + nBufFilePos += nBufActualPos; + nBufActualPos = (sal_uInt16)nCount; + pBufPos = pRWBuf + nCount; + nBufActualLen = (sal_uInt16)nCount; + bIsDirty = TRUE; + } + } + nBufFree = nBufSize - nBufActualPos; + return nCount; +} + + +/************************************************************************* +|* +|* Stream::Seek() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +sal_Size SvStream::Seek( sal_Size nFilePos ) +{ + eIOMode = STREAM_IO_DONTKNOW; + + bIsEof = FALSE; + if( !pRWBuf ) + { + nBufFilePos = SeekPos( nFilePos ); + DBG_ASSERT(Tell()==nBufFilePos,"Out Of Sync!"); + return nBufFilePos; + } + + // Ist Position im Puffer ? + if( nFilePos >= nBufFilePos && nFilePos <= (nBufFilePos + nBufActualLen)) + { + nBufActualPos = (sal_uInt16)(nFilePos - nBufFilePos); + pBufPos = pRWBuf + nBufActualPos; + // nBufFree korrigieren, damit wir nicht von einem + // PutBack (ignoriert den StreamMode) getoetet werden + nBufFree = nBufActualLen - nBufActualPos; + } + else + { + if( bIsDirty && bIsConsistent) + { + SeekPos( nBufFilePos ); + if( nCryptMask ) + CryptAndWriteBuffer( pRWBuf, nBufActualLen ); + else + PutData( pRWBuf, nBufActualLen ); + bIsDirty = FALSE; + } + nBufActualLen = 0; + nBufActualPos = 0; + pBufPos = pRWBuf; + nBufFilePos = SeekPos( nFilePos ); + } +#ifdef OV_DEBUG + { + sal_Size nDebugTemp = nBufFilePos + nBufActualPos; + DBG_ASSERT(Tell()==nDebugTemp,"Sync?"); + } +#endif + return nBufFilePos + nBufActualPos; +} + +/************************************************************************* +|* +|* Stream::Flush() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +void SvStream::Flush() +{ + if( bIsDirty && bIsConsistent ) + { + SeekPos( nBufFilePos ); + if( nCryptMask ) + CryptAndWriteBuffer( pRWBuf, (sal_Size)nBufActualLen ); + else + if( PutData( pRWBuf, nBufActualLen ) != nBufActualLen ) + SetError( SVSTREAM_WRITE_ERROR ); + bIsDirty = FALSE; + } + if( bIsWritable ) + FlushData(); +} + + +/************************************************************************* +|* +|* Stream::PutBack() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 01.08.94 +|* Letzte Aenderung OV 01.08.94 +|* +*************************************************************************/ + +/* + 4 Faelle : + + 1. Datenzeiger steht mitten im Puffer (nBufActualPos >= 1) + 2. Datenzeiger auf Position 0, Puffer ist voll + 3. Datenzeiger auf Position 0, Puffer ist teilweise gefuellt + 4. Datenzeiger auf Position 0, Puffer ist leer -> Fehler! +*/ + +SvStream& SvStream::PutBack( char aCh ) +{ + // wenn kein Buffer oder Zurueckscrollen nicht moeglich -> Fehler + if( !pRWBuf || !nBufActualLen || ( !nBufActualPos && !nBufFilePos ) ) + { + // 4. Fall + SetError( SVSTREAM_GENERALERROR ); + return *this; + } + + // Flush() (Phys. Flushen aber nicht notwendig, deshalb selbst schreiben) + if( bIsConsistent && bIsDirty ) + { + SeekPos( nBufFilePos ); + if( nCryptMask ) + CryptAndWriteBuffer( pRWBuf, nBufActualLen ); + else + PutData( pRWBuf, nBufActualLen ); + bIsDirty = FALSE; + } + bIsConsistent = FALSE; // Puffer enthaelt jetzt TRASH + if( nBufActualPos ) + { + // 1. Fall + nBufActualPos--; + pBufPos--; + *pBufPos = aCh; + nBufFree++; + } + else // Puffer muss verschoben werden + { + // Ist Puffer am Anschlag ? + if( nBufSize == nBufActualLen ) + { + // 2. Fall + memmove( pRWBuf+1, pRWBuf, nBufSize-1 ); + // nBufFree behaelt den Wert! + } + else + { + // 3. Fall -> Puffer vergroessern + memmove( pRWBuf+1, pRWBuf, (sal_uInt16)nBufActualLen ); + nBufActualLen++; + nBufFree++; + } + nBufFilePos--; + *pRWBuf = aCh; + } + eIOMode = STREAM_IO_DONTKNOW; + bIsEof = FALSE; + return *this; +} + +/************************************************************************* +|* +|* Stream::EatWhite() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 01.08.94 +|* Letzte Aenderung OV 01.08.94 +|* +*************************************************************************/ + +void SvStream::EatWhite() +{ + char aCh; + Read(&aCh, sizeof(char) ); + while( !bIsEof && isspace((int)aCh) ) //( aCh == ' ' || aCh == '\t' ) ) + Read(&aCh, sizeof(char) ); + if( !bIsEof ) // konnte das letzte Char gelesen werden ? + SeekRel( -1L ); +} + +/************************************************************************* +|* +|* Stream::RefreshBuffer() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 01.08.94 +|* Letzte Aenderung OV 01.08.94 +|* +*************************************************************************/ + +void SvStream::RefreshBuffer() +{ + if( bIsDirty && bIsConsistent ) + { + SeekPos( nBufFilePos ); + if( nCryptMask ) + CryptAndWriteBuffer( pRWBuf, (sal_Size)nBufActualLen ); + else + PutData( pRWBuf, nBufActualLen ); + bIsDirty = FALSE; + } + SeekPos( nBufFilePos ); + nBufActualLen = (sal_uInt16)GetData( pRWBuf, nBufSize ); + if( nBufActualLen && nError == ERRCODE_IO_PENDING ) + nError = ERRCODE_NONE; + if( nCryptMask ) + EncryptBuffer(pRWBuf, (sal_Size)nBufActualLen); + bIsConsistent = TRUE; + eIOMode = STREAM_IO_DONTKNOW; +} + + +/************************************************************************* +|* +|* Stream::CreateFormatString() +|* +|* Beschreibung Baut Formatstring zusammen +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +void SvStream::CreateFormatString() +{ + aFormatString = '%'; + nPrintfParams = SPECIAL_PARAM_NONE; + + if( nJustification ) + { + aFormatString += '-'; + } + + if( nWidth ) + { + if( cFiller != ' ' ) + aFormatString += '0'; + aFormatString += '*'; + nPrintfParams = SPECIAL_PARAM_WIDTH; + } + + if( nPrecision ) + { + aFormatString += ".*"; + if( nWidth ) + nPrintfParams = SPECIAL_PARAM_BOTH; + else + nPrintfParams = SPECIAL_PARAM_PRECISION; + } +} + +/************************************************************************* +|* +|* Stream::ReadNumber() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +#define BUFSIZE_LONG 21 // log( 2 hoch 64 ) + 1 + +SvStream& SvStream::ReadNumber( long& rLong ) +{ + EatWhite(); + if( bIsEof || nError ) + { + SetError( SVSTREAM_GENERALERROR ); + return *this; + } + sal_Size nFPtr = Tell(); + char buf[ BUFSIZE_LONG ]; + memset( buf, 0, BUFSIZE_LONG ); + sal_Size nTemp = Read( buf, BUFSIZE_LONG-1 ); + if( !nTemp || nError ) + { + SetError( SVSTREAM_GENERALERROR ); + return *this; + } + char *pEndPtr; + rLong = strtol( buf, &pEndPtr, (int)nRadix ); + nFPtr += ( (sal_Size)pEndPtr - (sal_Size)(&(buf[0])) ); + Seek( nFPtr ); + bIsEof = FALSE; + return *this; +} + +SvStream& SvStream::ReadNumber( sal_uInt32& rUInt32 ) +{ + EatWhite(); + if( bIsEof || nError ) + { + SetError( SVSTREAM_GENERALERROR ); + return *this; + } + sal_Size nFPtr = Tell(); + char buf[ BUFSIZE_LONG ]; + memset( buf, 0, BUFSIZE_LONG ); + sal_Size nTemp = Read( buf, BUFSIZE_LONG-1 ); + if( !nTemp || nError ) + { + SetError( SVSTREAM_GENERALERROR ); + return *this; + } + char *pEndPtr; + rUInt32 = strtoul( buf, &pEndPtr, (int)nRadix ); + nFPtr += ( (sal_uIntPtr)pEndPtr - (sal_uIntPtr)buf ); + Seek( nFPtr ); + bIsEof = FALSE; + return *this; +} + +SvStream& SvStream::ReadNumber( double& rDouble ) +{ + EatWhite(); + if( bIsEof || nError ) + { + SetError( SVSTREAM_GENERALERROR ); + return *this; + } + sal_Size nFPtr = Tell(); + char buf[ BUFSIZE_LONG ]; + memset( buf, 0, BUFSIZE_LONG ); + sal_Size nTemp = Read( buf, BUFSIZE_LONG-1 ); + if( !nTemp || nError ) + { + SetError( SVSTREAM_GENERALERROR ); + return *this; + } + char *pEndPtr; + rDouble = strtod( buf, &pEndPtr ); + nFPtr += ( (sal_Size)pEndPtr - (sal_Size)buf ); + Seek( nFPtr ); + bIsEof = FALSE; + return *this; +} + + +/************************************************************************* +|* +|* Stream::WriteNumber() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +SvStream& SvStream::WriteNumber( long nLong ) +{ + char buffer[256+12]; + char pType[] = "ld"; // Nicht static! + if( nRadix == 16 ) + pType[1] = 'x'; + else if( nRadix == 8 ) + pType[1] = 'o'; + ByteString aFStr( aFormatString); + aFStr += pType; + int nLen; + switch ( nPrintfParams ) + { + case SPECIAL_PARAM_NONE : + nLen = sprintf( buffer, aFStr.GetBuffer(), nLong ); + break; + case SPECIAL_PARAM_WIDTH : + nLen = sprintf( buffer, aFStr.GetBuffer(), nWidth, nLong ); + break; + case SPECIAL_PARAM_PRECISION : + nLen = sprintf( buffer, aFStr.GetBuffer(), nPrecision,nLong); + break; + default: + nLen=sprintf(buffer, aFStr.GetBuffer(),nWidth,nPrecision,nLong); + } + Write( buffer, (long)nLen ); + return *this; +} + +SvStream& SvStream::WriteNumber( sal_uInt32 nUInt32 ) +{ + char buffer[256+12]; + char pType[] = "lu"; // Nicht static! + if( nRadix == 16 ) + pType[1] = 'x'; + else if( nRadix == 8 ) + pType[1] = 'o'; + ByteString aFStr( aFormatString); + aFStr += pType; + int nLen; + switch ( nPrintfParams ) + { + case SPECIAL_PARAM_NONE : + nLen = sprintf( buffer, aFStr.GetBuffer(), nUInt32 ); + break; + case SPECIAL_PARAM_WIDTH : + nLen = sprintf( buffer, aFStr.GetBuffer(), nWidth, nUInt32 ); + break; + case SPECIAL_PARAM_PRECISION : + nLen = sprintf( buffer, aFStr.GetBuffer(), nPrecision, nUInt32 ); + break; + default: + nLen=sprintf(buffer,aFStr.GetBuffer(),nWidth,nPrecision,nUInt32 ); + } + Write( buffer, (long)nLen ); + return *this; +} + + +SvStream& SvStream::WriteNumber( const double& rDouble ) +{ + char buffer[256+24]; + ByteString aFStr( aFormatString); + aFStr += "lf"; + int nLen; + switch ( nPrintfParams ) + { + case SPECIAL_PARAM_NONE : + nLen = sprintf( buffer, aFStr.GetBuffer(), rDouble ); + break; + case SPECIAL_PARAM_WIDTH : + nLen = sprintf( buffer, aFStr.GetBuffer(), nWidth, rDouble ); + break; + case SPECIAL_PARAM_PRECISION : + nLen = sprintf( buffer, aFStr.GetBuffer(), nPrecision, rDouble); + break; + default: + nLen=sprintf(buffer, aFStr.GetBuffer(),nWidth,nPrecision,rDouble); + } + Write( buffer, (long)nLen ); + return *this; +} + +/************************************************************************* +|* +|* Stream::CryptAndWriteBuffer() +|* +|* Beschreibung Verschluesseln und Schreiben +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +#define CRYPT_BUFSIZE 1024 + +sal_Size SvStream::CryptAndWriteBuffer( const void* pStart, sal_Size nLen) +{ + unsigned char pTemp[CRYPT_BUFSIZE]; + unsigned char* pDataPtr = (unsigned char*)pStart; + sal_Size nCount = 0; + sal_Size nBufCount; + unsigned char nMask = nCryptMask; + do + { + if( nLen >= CRYPT_BUFSIZE ) + nBufCount = CRYPT_BUFSIZE; + else + nBufCount = nLen; + nLen -= nBufCount; + memcpy( pTemp, pDataPtr, (sal_uInt16)nBufCount ); + // **** Verschluesseln ***** + for ( sal_uInt16 n=0; n < CRYPT_BUFSIZE; n++ ) + { + unsigned char aCh = pTemp[n]; + aCh ^= nMask; + SWAPNIBBLES(aCh) + pTemp[n] = aCh; + } + // ************************* + nCount += PutData( (char*)pTemp, nBufCount ); + pDataPtr += nBufCount; + } + while ( nLen ); + return nCount; +} + +/************************************************************************* +|* +|* Stream::EncryptBuffer() +|* +|* Beschreibung Buffer entschluesseln +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +sal_Bool SvStream::EncryptBuffer(void* pStart, sal_Size nLen) +{ + unsigned char* pTemp = (unsigned char*)pStart; + unsigned char nMask = nCryptMask; + + for ( sal_Size n=0; n < nLen; n++, pTemp++ ) + { + unsigned char aCh = *pTemp; + SWAPNIBBLES(aCh) + aCh ^= nMask; + *pTemp = aCh; + } + return TRUE; +} + +/************************************************************************* +|* +|* Stream::SetKey() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +unsigned char implGetCryptMask(const sal_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_uInt16 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::SetKey( const ByteString& rKey ) +{ + aKey = rKey; + nCryptMask = implGetCryptMask( aKey.GetBuffer(), aKey.Len(), GetVersion() ); +} + +/************************************************************************* +|* +|* Stream::SyncSvStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +void SvStream::SyncSvStream( sal_Size nNewStreamPos ) +{ + ClearBuffer(); + SvStream::nBufFilePos = nNewStreamPos; +} + +/************************************************************************* +|* +|* Stream::SyncSysStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +void SvStream::SyncSysStream() +{ + Flush(); + SeekPos( Tell() ); +} + +/************************************************************************* +|* +|* Stream::SetStreamSize() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +sal_Bool SvStream::SetStreamSize( sal_Size nSize ) +{ +#ifdef DBG_UTIL + sal_Size nFPos = Tell(); +#endif + sal_uInt16 nBuf = nBufSize; + SetBufferSize( 0 ); + SetSize( nSize ); + SetBufferSize( nBuf ); + DBG_ASSERT(Tell()==nFPos,"SetStreamSize failed"); + return (sal_Bool)(nError == 0); +} + +//============================================================================ + +void SvStream::AddMark( sal_Size ) +{ +} + +//============================================================================ + +void SvStream::RemoveMark( sal_Size ) +{ +} + +/************************************************************************* +|* +|* endl() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung TH 13.11.96 +|* +*************************************************************************/ + +SvStream& endl( SvStream& rStr ) +{ + LineEnd eDelim = rStr.GetLineDelimiter(); + if ( eDelim == LINEEND_CR ) + rStr << _CR; + else if( eDelim == LINEEND_LF ) + rStr << _LF; + else + rStr << _CR << _LF; + return rStr; +} + +SvStream& endlu( SvStream& rStrm ) +{ + switch ( rStrm.GetLineDelimiter() ) + { + case LINEEND_CR : + rStrm << sal_Unicode(_CR); + break; + case LINEEND_LF : + rStrm << sal_Unicode(_LF); + break; + default: + rStrm << sal_Unicode(_CR) << sal_Unicode(_LF); + } + return rStrm; +} + +SvStream& endlub( SvStream& rStrm ) +{ + if ( rStrm.GetStreamCharSet() == RTL_TEXTENCODING_UNICODE ) + return endlu( rStrm ); + else + return endl( rStrm ); +} + +/************************************************************************* +|* +|* SvMemoryStream::SvMemoryStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +SvMemoryStream::SvMemoryStream( void* pBuffer, sal_Size bufSize, + StreamMode eMode ) +{ + if( eMode & STREAM_WRITE ) + bIsWritable = TRUE; + else + bIsWritable = FALSE; + nEndOfData = bufSize; + bOwnsData = FALSE; + pBuf = (BYTE *) pBuffer; + nResize = 0L; + nSize = bufSize; + nPos = 0L; + SetBufferSize( 0 ); +} + +/************************************************************************* +|* +|* SvMemoryStream::SvMemoryStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +SvMemoryStream::SvMemoryStream( sal_Size nInitSize, sal_Size nResizeOffset ) +{ + bIsWritable = TRUE; + bOwnsData = TRUE; + nEndOfData = 0L; + nResize = nResizeOffset; + nPos = 0; + pBuf = 0; + if( nResize != 0 && nResize < 16 ) + nResize = 16; + if( nInitSize && !AllocateMemory( nInitSize ) ) + { + SetError( SVSTREAM_OUTOFMEMORY ); + nSize = 0; + } + else + nSize = nInitSize; + SetBufferSize( 64 ); +} + +/************************************************************************* +|* +|* SvMemoryStream::~SvMemoryStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +SvMemoryStream::~SvMemoryStream() +{ + if( pBuf ) + { + if( bOwnsData ) + FreeMemory(); + else + Flush(); + } +} + +/************************************************************************* +|* +|* SvMemoryStream::IsA() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +sal_uInt16 SvMemoryStream::IsA() const +{ + return (sal_uInt16)ID_MEMORYSTREAM; +} + +/************************************************************************* +|* +|* SvMemoryStream::SetBuffer() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +void* SvMemoryStream::SetBuffer( void* pNewBuf, sal_Size nCount, + sal_Bool bOwnsDat, sal_Size nEOF ) +{ + void* pResult; + SetBufferSize( 0 ); // Buffering in der Basisklasse initialisieren + Seek( 0 ); + if( bOwnsData ) + { + pResult = 0; + if( pNewBuf != pBuf ) + FreeMemory(); + } + else + pResult = pBuf; + + pBuf = (BYTE *) pNewBuf; + nPos = 0; + nSize = nCount; + nResize = 0; + bOwnsData = bOwnsDat; + + if( nEOF > nCount ) + nEOF = nCount; + nEndOfData = nEOF; + + ResetError(); + + DBG_ASSERT( nEndOfData<STREAM_SEEK_TO_END,"Invalid EOF"); + return pResult; +} + +/************************************************************************* +|* +|* SvMemoryStream::GetData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +sal_Size SvMemoryStream::GetData( void* pData, sal_Size nCount ) +{ + sal_Size nMaxCount = nEndOfData-nPos; + if( nCount > nMaxCount ) + nCount = nMaxCount; + memcpy( pData, pBuf+nPos, (size_t)nCount ); + nPos += nCount; + return nCount; +} + +/************************************************************************* +|* +|* SvMemoryStream::PutData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +sal_Size SvMemoryStream::PutData( const void* pData, sal_Size nCount ) +{ + if( GetError() ) + return 0L; + + sal_Size nMaxCount = nSize-nPos; + + // auf Ueberlauf testen + if( nCount > nMaxCount ) + { + if( nResize == 0 ) + { + // soviel wie moeglich rueberschaufeln + nCount = nMaxCount; + SetError( SVSTREAM_OUTOFMEMORY ); + } + else + { + long nNewResize; + if( nSize && nSize > nResize ) + nNewResize = nSize; + else + nNewResize = nResize; + + if( (nCount-nMaxCount) < nResize ) + { + // fehlender Speicher ist kleiner als Resize-Offset, + // deshalb um Resize-Offset vergroessern + if( !ReAllocateMemory( nNewResize) ) + { + nCount = 0; + SetError( SVSTREAM_WRITE_ERROR ); + } + } + else + { + // fehlender Speicher ist groesser als Resize-Offset + // deshalb um Differenz+ResizeOffset vergroessern + if( !ReAllocateMemory( nCount-nMaxCount+nNewResize ) ) + { + nCount = 0; + SetError( SVSTREAM_WRITE_ERROR ); + } + } + } + } + DBG_ASSERT(pBuf,"Possibly Reallocate failed"); + memcpy( pBuf+nPos, pData, (size_t)nCount); + + nPos += nCount; + if( nPos > nEndOfData ) + nEndOfData = nPos; + return nCount; +} + +/************************************************************************* +|* +|* SvMemoryStream::SeekPos() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +// nEndOfData: Erste Position im Stream, die nicht gelesen werden darf +// nSize: Groesse des allozierten Speichers + +sal_Size SvMemoryStream::SeekPos( sal_Size nNewPos ) +{ + if( nNewPos < nEndOfData ) + nPos = nNewPos; + else if( nNewPos == STREAM_SEEK_TO_END ) + nPos = nEndOfData; + else + { + if( nNewPos >= nSize ) // muss Buffer vergroessert werden ? + { + if( nResize ) // ist vergroeseern erlaubt ? + { + long nDiff = (long)(nNewPos - nSize + 1); + nDiff += (long)nResize; + ReAllocateMemory( nDiff ); + nPos = nNewPos; + nEndOfData = nNewPos; + } + else // vergroessern ist nicht erlaubt -> ans Ende setzen + { + // SetError( SVSTREAM_OUTOFMEMORY ); + nPos = nEndOfData; + } + } + else // gueltigen Bereich innerhalb des Buffers vergroessern + { + nPos = nNewPos; + nEndOfData = nNewPos; + } + } + return nPos; +} + +/************************************************************************* +|* +|* SvMemoryStream::FlushData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +void SvMemoryStream::FlushData() +{ +} + +/************************************************************************* +|* +|* SvMemoryStream::ResetError() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +void SvMemoryStream::ResetError() +{ + SvStream::ClearError(); +} + +/************************************************************************* +|* +|* SvMemoryStream::AllocateMemory() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +sal_Bool SvMemoryStream::AllocateMemory( sal_Size nNewSize ) +{ + pBuf = new BYTE[nNewSize]; + return( pBuf != 0 ); +} + +/************************************************************************* +|* +|* SvMemoryStream::ReAllocateMemory() (Bozo-Algorithmus) +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 20.06.94 +|* Letzte Aenderung OV 20.06.94 +|* +*************************************************************************/ + +sal_Bool SvMemoryStream::ReAllocateMemory( long nDiff ) +{ + sal_Bool bRetVal = FALSE; + long nTemp = (long)nSize; + nTemp += nDiff; + sal_Size nNewSize = (sal_Size)nTemp; + + if( nNewSize ) + { + BYTE* pNewBuf = new BYTE[nNewSize]; + + if( pNewBuf ) + { + bRetVal = TRUE; // Success! + if( nNewSize < nSize ) // Verkleinern ? + { + memcpy( pNewBuf, pBuf, (size_t)nNewSize ); + if( nPos > nNewSize ) + nPos = 0L; + if( nEndOfData >= nNewSize ) + nEndOfData = nNewSize-1L; + } + else + { + memcpy( pNewBuf, pBuf, (size_t)nSize ); + } + + FreeMemory(); + + pBuf = pNewBuf; + nSize = nNewSize; + } + } + else + { + bRetVal = TRUE; + FreeMemory(); + pBuf = 0; + nSize = 0; + nEndOfData = 0; + nPos = 0; + } + + return bRetVal; +} + +void SvMemoryStream::FreeMemory() +{ + delete[] pBuf; +} + +/************************************************************************* +|* +|* SvMemoryStream::SwitchBuffer() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 26.07.94 +|* Letzte Aenderung OV 26.07.94 +|* +*************************************************************************/ + +void* SvMemoryStream::SwitchBuffer( sal_Size nInitSize, sal_Size nResizeOffset) +{ + Flush(); + if( !bOwnsData ) + return 0; + Seek( STREAM_SEEK_TO_BEGIN ); + + void* pRetVal = pBuf; + pBuf = 0; + nEndOfData = 0L; + nResize = nResizeOffset; + nPos = 0; + + if( nResize != 0 && nResize < 16 ) + nResize = 16; + + ResetError(); + + if( nInitSize && !AllocateMemory(nInitSize) ) + { + SetError( SVSTREAM_OUTOFMEMORY ); + nSize = 0; + } + else + nSize = nInitSize; + + SetBufferSize( 64 ); + return pRetVal; +} + +void SvMemoryStream::SetSize( sal_Size nNewSize ) +{ + long nDiff = (long)nNewSize - (long)nSize; + ReAllocateMemory( nDiff ); +} + +TYPEINIT0 ( SvDataCopyStream ) + +void SvDataCopyStream::Assign( const SvDataCopyStream& ) +{ +} diff --git a/tools/source/stream/strmos2.cxx b/tools/source/stream/strmos2.cxx new file mode 100644 index 000000000000..2b3b33f60224 --- /dev/null +++ b/tools/source/stream/strmos2.cxx @@ -0,0 +1,867 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: strmos2.cxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <string.h> +#include <limits.h> + +#define INCL_PM +#define INCL_DOS +#define INCL_DOSERRORS +#include <svpm.h> + +#include <tools/debug.hxx> +#include <tools/fsys.hxx> +#include <tools/stream.hxx> + +// class FileBase +#include <osl/file.hxx> + +using namespace osl; + +// class FileBase +#ifndef _OSL_FILE_HXX_ +#include <osl/file.hxx> +#endif + +using namespace osl; + +// ----------------------------------------------------------------------- + +// -------------- +// - StreamData - +// -------------- + +class StreamData +{ +public: + HFILE hFile; + BOOL bIsEof; + + StreamData() + { + hFile = 0; + bIsEof = TRUE; + } +}; + +// ----------------------------------------------------------------------- + +ULONG GetSvError( APIRET nPMError ) +{ + static struct { APIRET pm; ULONG sv; } errArr[] = + { + { ERROR_FILE_NOT_FOUND, SVSTREAM_FILE_NOT_FOUND }, + { ERROR_PATH_NOT_FOUND, SVSTREAM_PATH_NOT_FOUND }, + { ERROR_TOO_MANY_OPEN_FILES, SVSTREAM_TOO_MANY_OPEN_FILES }, + { ERROR_ACCESS_DENIED, SVSTREAM_ACCESS_DENIED }, + { ERROR_INVALID_ACCESS, SVSTREAM_INVALID_ACCESS }, + { ERROR_SHARING_VIOLATION, SVSTREAM_SHARING_VIOLATION }, + { ERROR_SHARING_BUFFER_EXCEEDED,SVSTREAM_SHARE_BUFF_EXCEEDED }, + { ERROR_CANNOT_MAKE, SVSTREAM_CANNOT_MAKE }, + { ERROR_INVALID_PARAMETER, SVSTREAM_INVALID_PARAMETER }, + { ERROR_DRIVE_LOCKED, SVSTREAM_LOCKING_VIOLATION }, + { ERROR_LOCK_VIOLATION, SVSTREAM_LOCKING_VIOLATION }, + { ERROR_FILENAME_EXCED_RANGE, SVSTREAM_INVALID_PARAMETER }, + { ERROR_ATOMIC_LOCK_NOT_SUPPORTED, SVSTREAM_INVALID_PARAMETER }, + { ERROR_READ_LOCKS_NOT_SUPPORTED, SVSTREAM_INVALID_PARAMETER }, + + + { 0xFFFF, SVSTREAM_GENERALERROR } + }; + + ULONG nRetVal = SVSTREAM_GENERALERROR; // Standardfehler + int i=0; + do + { + if( errArr[i].pm == nPMError ) + { + nRetVal = errArr[i].sv; + break; + } + i++; + } + while( errArr[i].pm != 0xFFFF ); + return nRetVal; +} + +/************************************************************************* +|* +|* SvFileStream::SvFileStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +SvFileStream::SvFileStream( const String& rFileName, StreamMode nOpenMode ) +{ + bIsOpen = FALSE; + nLockCounter = 0; + bIsWritable = FALSE; + pInstanceData = new StreamData; + + SetBufferSize( 8192 ); + // convert URL to SystemPath, if necessary + ::rtl::OUString aFileName, aNormPath; + + if ( FileBase::getSystemPathFromFileURL( rFileName, aFileName ) != FileBase::E_None ) + aFileName = rFileName; + Open( aFileName, nOpenMode ); +} + +/************************************************************************* +|* +|* SvFileStream::SvFileStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 22.11.94 +|* Letzte Aenderung OV 22.11.94 +|* +*************************************************************************/ + +SvFileStream::SvFileStream() +{ + bIsOpen = FALSE; + nLockCounter = 0; + bIsWritable = FALSE; + pInstanceData = new StreamData; + SetBufferSize( 8192 ); +} + +/************************************************************************* +|* +|* SvFileStream::~SvFileStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 14.06.94 +|* Letzte Aenderung OV 14.06.94 +|* +*************************************************************************/ + +SvFileStream::~SvFileStream() +{ + Close(); + if( pInstanceData ) + delete pInstanceData; +} + +/************************************************************************* +|* +|* SvFileStream::GetFileHandle() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 14.06.94 +|* Letzte Aenderung OV 14.06.94 +|* +*************************************************************************/ + +ULONG SvFileStream::GetFileHandle() const +{ + return (ULONG)pInstanceData->hFile; +} + +/************************************************************************* +|* +|* SvFileStream::IsA() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 14.06.94 +|* Letzte Aenderung OV 14.06.94 +|* +*************************************************************************/ + +USHORT SvFileStream::IsA() const +{ + return ID_FILESTREAM; +} + +/************************************************************************* +|* +|* SvFileStream::GetData() +|* +|* Beschreibung STREAM.SDW, Prueft nicht Eof; IsEof danach rufbar +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +ULONG SvFileStream::GetData( void* pData, ULONG nSize ) +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "SvFileStream::GetData(): " ); + aTraceStr += ByteString::CreateFromInt64(nSize); + aTraceStr += " Bytes from "; + aTraceStr += ByteString(aFilename, osl_getThreadTextEncoding()); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + ULONG nCount = 0L; + if( IsOpen() ) + { + APIRET nResult; + nResult = DosRead( pInstanceData->hFile,(PVOID)pData,nSize,&nCount ); + if( nResult ) + SetError(::GetSvError(nResult) ); + } + return nCount; +} + +/************************************************************************* +|* +|* SvFileStream::PutData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +ULONG SvFileStream::PutData( const void* pData, ULONG nSize ) +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "SvFileStrean::PutData: " ); + aTraceStr += ByteString::CreateFromInt64(nSize); + aTraceStr += " Bytes to "; + aTraceStr += ByteString(aFilename, osl_getThreadTextEncoding()); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + ULONG nCount = 0L; + if( IsOpen() ) + { + APIRET nResult; + nResult = DosWrite( pInstanceData->hFile,(PVOID)pData,nSize,&nCount ); + if( nResult ) + SetError(::GetSvError(nResult) ); + else if( !nCount ) + SetError( SVSTREAM_DISK_FULL ); + } + return nCount; +} + +/************************************************************************* +|* +|* SvFileStream::SeekPos() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +ULONG SvFileStream::SeekPos( ULONG nPos ) +{ + ULONG nNewPos = 0L; + if( IsOpen() ) + { + APIRET nResult; + + if( nPos != STREAM_SEEK_TO_END ) + nResult = DosSetFilePtr( pInstanceData->hFile,(long)nPos, + FILE_BEGIN, &nNewPos ); + else + nResult = DosSetFilePtr( pInstanceData->hFile,0L, + FILE_END, &nNewPos ); + + if( nResult ) + SetError(::GetSvError(nResult) ); + } + else + SetError( SVSTREAM_GENERALERROR ); + return nNewPos; +} + +/************************************************************************* +|* +|* SvFileStream::Tell() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ +/* +ULONG SvFileStream::Tell() +{ + ULONG nPos = 0L; + + if( IsOpen() ) + { + APIRET nResult; + nResult = DosSetFilePtr(pInstanceData->hFile,0L,FILE_CURRENT,&nPos); + if( nResult ) + SetError(::GetSvError(nResult) ); + } + return nPos; +} +*/ + +/************************************************************************* +|* +|* SvFileStream::FlushData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::FlushData() +{ + if( IsOpen() ) + { + APIRET nResult; + nResult = DosResetBuffer(pInstanceData->hFile ); + if( nResult ) + SetError(::GetSvError(nResult) ); + } +} + +/************************************************************************* +|* +|* SvFileStream::LockRange() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +sal_Bool SvFileStream::LockRange( ULONG nByteOffset, ULONG nBytes ) +{ + sal_Bool bRetVal = FALSE; + if( IsOpen() ) + { + APIRET nResult; + FILELOCK aLockArea, aUnlockArea; + aUnlockArea.lOffset = 0L; + aUnlockArea.lRange = 0L; + aLockArea.lOffset = (long)nByteOffset; + aLockArea.lRange = (long)nBytes; + + nResult = DosSetFileLocks(pInstanceData->hFile, + &aUnlockArea, &aLockArea, + 1000UL, // Zeit in ms bis Abbruch + 0L // kein Atomic-Lock + ); + + if( nResult ) + SetError(::GetSvError(nResult) ); + else + bRetVal = TRUE; + } + return bRetVal; +} + +/************************************************************************* +|* +|* SvFileStream::UnlockRange() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +sal_Bool SvFileStream::UnlockRange( ULONG nByteOffset, ULONG nBytes ) +{ + sal_Bool bRetVal = FALSE; + if( IsOpen() ) + { + APIRET nResult; + FILELOCK aLockArea, aUnlockArea; + aLockArea.lOffset = 0L; + aLockArea.lRange = 0L; + aUnlockArea.lOffset = (long)nByteOffset; + aUnlockArea.lRange = (long)nBytes; + + nResult = DosSetFileLocks(pInstanceData->hFile, + &aUnlockArea, &aLockArea, + 1000UL, // Zeit in ms bis Abbruch + 0L // kein Atomic-Lock + ); + + if( nResult ) + SetError(::GetSvError(nResult) ); + else + bRetVal = TRUE; + } + return bRetVal; +} + +/************************************************************************* +|* +|* SvFileStream::LockFile() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +sal_Bool SvFileStream::LockFile() +{ + sal_Bool bRetVal = FALSE; + if( !nLockCounter ) + { + if( LockRange( 0L, LONG_MAX ) ) + { + nLockCounter = 1; + bRetVal = TRUE; + } + } + else + { + nLockCounter++; + bRetVal = TRUE; + } + return bRetVal; +} + +/************************************************************************* +|* +|* SvFileStream::UnlockFile() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +sal_Bool SvFileStream::UnlockFile() +{ + sal_Bool bRetVal = FALSE; + if( nLockCounter > 0) + { + if( nLockCounter == 1) + { + if( UnlockRange( 0L, LONG_MAX ) ) + { + nLockCounter = 0; + bRetVal = TRUE; + } + } + else + { + nLockCounter--; + bRetVal = TRUE; + } + } + return bRetVal; +} + +/************************************************************************* +|* +|* SvFileStream::Open() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +#if 0 +BOOL createLongNameEA ( const PCSZ pszPath, ULONG ulAttributes, const String& aLongName ); +#endif + +void SvFileStream::Open( const String& rFilename, StreamMode nOpenMode ) +{ + String aParsedFilename; + +#if 0 + if ( Folder::IsAvailable() && (rFilename.Search('{') < 9) ) + { + String aVirtualPart; + String aRealPart; + String aVirtualPath; + ItemIDPath aVirtualURL; + ULONG nDivider = 0; + + String aVirtualString(rFilename); + + for (int x=aVirtualString.Len(); x>0; x--) + { + if (aVirtualString.Copy(x,1).Compare("}")==COMPARE_EQUAL) + { + nDivider = x; + break; + } + } + + aVirtualPart = aVirtualString.Copy(0,nDivider+1); + aRealPart = aVirtualString.Copy(nDivider+2); + + aVirtualURL = aVirtualPart; + aVirtualPath = aVirtualURL.GetHostNotationPath(); + + DirEntry aTempDirEntry(aVirtualPath); + + aTempDirEntry += aRealPart; + + aParsedFilename = aTempDirEntry.GetFull(); + } + else +#endif // 0 + { + aParsedFilename = rFilename; + } + + Close(); + SvStream::ClearBuffer(); + + ULONG nActionTaken; + ULONG nOpenAction = 0L; + ULONG nShareBits = 0L; + ULONG nReadWriteBits = 0L; + + eStreamMode = nOpenMode; + eStreamMode &= ~STREAM_TRUNC; // beim ReOpen nicht cutten + + nOpenMode |= STREAM_SHARE_DENYNONE; // definierten Zustand garantieren + + // ********* Zugriffsflags *********** + if( nOpenMode & STREAM_SHARE_DENYNONE) + nShareBits = OPEN_SHARE_DENYNONE; + + if( nOpenMode & STREAM_SHARE_DENYREAD) + nShareBits = OPEN_SHARE_DENYREAD; + + if( nOpenMode & STREAM_SHARE_DENYWRITE) + nShareBits = OPEN_SHARE_DENYWRITE; + + if( nOpenMode & STREAM_SHARE_DENYALL) + nShareBits = OPEN_SHARE_DENYREADWRITE; + + if( (nOpenMode & STREAM_READ) ) + { + if( nOpenMode & STREAM_WRITE ) + nReadWriteBits |= OPEN_ACCESS_READWRITE; + else + { + nReadWriteBits |= OPEN_ACCESS_READONLY; + nOpenMode |= STREAM_NOCREATE; + } + } + else + nReadWriteBits |= OPEN_ACCESS_WRITEONLY; + + + if( nOpenMode & STREAM_NOCREATE ) + { + // Datei nicht erzeugen + nOpenAction = OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; + } + else + { + // Datei erzeugen, wenn nicht vorhanden + nOpenAction = OPEN_ACTION_CREATE_IF_NEW; + if( nOpenMode & STREAM_TRUNC ) + // Auf Nullaenge kuerzen, wenn existiert + nOpenAction |= OPEN_ACTION_REPLACE_IF_EXISTS; + else + // Inhalt der Datei nicht wegwerfen + nOpenAction |= OPEN_ACTION_OPEN_IF_EXISTS; + } + +#if 0 // YD + // + // resolves long FAT names used by OS2 + // + BOOL bIsLongOS2=FALSE; + if (Folder::IsAvailable()) + { + DirEntry aDirEntry(rFilename); + if (aDirEntry.IsLongNameOnFAT()) + { + // in kurzen Pfad wandeln + ItemIDPath aItemIDPath(rFilename); + aParsedFilename = aItemIDPath.GetHostNotationPath(); + bIsLongOS2 = TRUE; + } + } +#endif + + aFilename = aParsedFilename; + ByteString aFileNameA( aFilename, gsl_getSystemTextEncoding()); + FSysRedirector::DoRedirect( aFilename ); + +#ifdef DBG_UTIL + ByteString aTraceStr( "SvFileStream::Open(): " ); + aTraceStr += aFileNameA; + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + APIRET nRet = DosOpen( aFileNameA.GetBuffer(), &pInstanceData->hFile, + &nActionTaken, 0L, FILE_NORMAL, nOpenAction, + nReadWriteBits | nShareBits | OPEN_FLAGS_NOINHERIT, 0L); + + if( nRet == ERROR_TOO_MANY_OPEN_FILES ) + { + long nToAdd = 10; + ULONG nCurMaxFH; + nRet = DosSetRelMaxFH( &nToAdd, &nCurMaxFH ); + nRet = DosOpen( aFileNameA.GetBuffer(), &pInstanceData->hFile, + &nActionTaken, 0L, FILE_NORMAL, nOpenAction, + nReadWriteBits | nShareBits | OPEN_FLAGS_NOINHERIT, 0L); + } + + // Bei Fehler pruefen, ob wir lesen duerfen + if( nRet==ERROR_ACCESS_DENIED || nRet==ERROR_SHARING_VIOLATION ) + { + nReadWriteBits = OPEN_ACCESS_READONLY; + nRet = DosOpen( aFileNameA.GetBuffer(), &pInstanceData->hFile, + &nActionTaken, 0L, FILE_NORMAL, nOpenAction, + nReadWriteBits | nShareBits | OPEN_FLAGS_NOINHERIT, 0L); + } + + if( nRet ) + { + bIsOpen = FALSE; + SetError(::GetSvError(nRet) ); + } + else + { + bIsOpen = TRUE; + pInstanceData->bIsEof = FALSE; + if( nReadWriteBits != OPEN_ACCESS_READONLY ) + bIsWritable = TRUE; + } + +#if 0 + if (bIsOpen && bIsLongOS2) + { + //file schließen, da sonst createLongName u.U. nicht möglich + Close(); + + // erzeugtem File langen Namen geben + DirEntry aDirEntry(rFilename); + createLongNameEA(aFileNameA.GetBuffer(), FILE_NORMAL, aDirEntry.GetName()); + + // und wieder oeffnen + ReOpen(); + } +#endif + +} + +/************************************************************************* +|* +|* SvFileStream::ReOpen() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::ReOpen() +{ + if( !bIsOpen && aFilename.Len() ) + Open( aFilename, eStreamMode ); +} + +/************************************************************************* +|* +|* SvFileStream::Close() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::Close() +{ + if( IsOpen() ) + { +#ifdef DBG_UTIL + ByteString aTraceStr( "SvFileStream::Close(): " ); + aTraceStr += ByteString(aFilename, osl_getThreadTextEncoding()); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + if( nLockCounter ) + { + nLockCounter = 1; + UnlockFile(); + } + Flush(); + DosClose( pInstanceData->hFile ); + } + + bIsOpen = FALSE; + nLockCounter= 0; + bIsWritable = FALSE; + pInstanceData->bIsEof = TRUE; + SvStream::ClearBuffer(); + SvStream::ClearError(); +} + +/************************************************************************* +|* +|* SvFileStream::ResetError() +|* +|* Beschreibung STREAM.SDW; Setzt Filepointer auf Dateianfang +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::ResetError() +{ + SvStream::ClearError(); +} + +/************************************************************************* +|* +|* SvFileStream::SetSize() +|* +|* Beschreibung +|* Ersterstellung OV 19.10.95 +|* Letzte Aenderung OV 19.10.95 +|* +*************************************************************************/ + +void SvFileStream::SetSize( ULONG nSize ) +{ + if( IsOpen() ) + { + APIRET nRet = DosSetFileSize( pInstanceData->hFile, nSize ); + if( nRet ) + SetError( ::GetSvError( nRet ) ); + } +} + +#if 0 +/************************************************************************* +|* +|* SvSharedMemoryStream::AllocateMemory() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung CL 05.05.95 +|* Letzte Aenderung CL 05.05.95 +|* +*************************************************************************/ + +sal_Bool SvSharedMemoryStream::AllocateMemory( ULONG nNewSize ) +{ + DBG_ASSERT(aHandle==0,"Keine Handles unter OS/2"); + DBG_ASSERT(nNewSize,"Cannot allocate zero Bytes"); + APIRET nRet = DosAllocSharedMem( (void**)&pBuf, (PSZ)NULL, nNewSize, + PAG_READ | PAG_WRITE | PAG_COMMIT | + OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_ANY); + return( nRet == 0 ); +} + +/************************************************************************* +|* +|* SvSharedMemoryStream::ReAllocateMemory() (Bozo-Algorithmus) +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung CL 05.05.95 +|* Letzte Aenderung CL 05.05.95 +|* +*************************************************************************/ + +sal_Bool SvSharedMemoryStream::ReAllocateMemory( long nDiff ) +{ + DBG_ASSERT(aHandle==0,"Keine Handles unter OS/2"); + sal_Bool bRetVal = FALSE; + ULONG nNewSize = nSize + nDiff; + if( nNewSize ) + { + // neuen Speicher nicht ueber AllocateMemory holen, da wir den + // alten Speicher behalten wollen, falls nicht genuegend Platz + // fuer den neuen Block da ist + char* pNewBuf; + APIRET nRet = DosAllocSharedMem( (void**)&pNewBuf,(PSZ)NULL,nNewSize, + PAG_READ | PAG_WRITE | PAG_COMMIT | + OBJ_GIVEABLE | OBJ_GETTABLE | OBJ_ANY); + DBG_ASSERT(!nRet,"DosAllocSharedMem failed"); + + if( !nRet ) + { + bRetVal = TRUE; // Success! + if( nNewSize < nSize ) // Verkleinern ? + { + memcpy( pNewBuf, pBuf, (size_t)nNewSize ); + if( nPos > nNewSize ) + nPos = 0L; + if( nEndOfData >= nNewSize ) + nEndOfData = nNewSize-1L; + } + else + memcpy( pNewBuf, pBuf, (size_t)nSize ); + + FreeMemory(); // den alten Block loeschen ... + + // und den neuen Block in Dienst stellen + pBuf = (sal_uInt8*)pNewBuf; + nSize = nNewSize; + } + } + else + { + bRetVal = TRUE; + FreeMemory(); + pBuf = 0; + nSize = 0; + nEndOfData = 0; + } + return bRetVal; +} + +void SvSharedMemoryStream::FreeMemory() +{ + DBG_ASSERT(aHandle==0,"Keine Handles unter OS/2"); + DosFreeMem( pBuf ); +} + +/************************************************************************* +|* +|* SvSharedMemoryStream::SetHandle() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 05.10.95 +|* Letzte Aenderung OV 05.10.95 +|* +*************************************************************************/ + +void* SvSharedMemoryStream::SetHandle( void* aNewHandle, sal_Size nSize, + sal_Bool bOwnsData, sal_Size nEOF ) +{ + DBG_ERROR("OS/2 does not support memory handles"); + // return SetBuffer(aNewHandle, nSize, bOwnsData, nEOF ); + return 0; +} + + +#endif // 0 diff --git a/tools/source/stream/strmsys.cxx b/tools/source/stream/strmsys.cxx new file mode 100644 index 000000000000..2b6ea54ffb3b --- /dev/null +++ b/tools/source/stream/strmsys.cxx @@ -0,0 +1,40 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: strmsys.cxx,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#if defined WNT +#include "strmwnt.cxx" +#elif defined UNX +#include "strmunx.cxx" +#elif defined OS2 +#include "strmos2.cxx" +#endif diff --git a/tools/source/stream/strmunx.cxx b/tools/source/stream/strmunx.cxx new file mode 100644 index 000000000000..d27fe1f7f2c0 --- /dev/null +++ b/tools/source/stream/strmunx.cxx @@ -0,0 +1,923 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: strmunx.cxx,v $ + * $Revision: 1.15 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// no include "precompiled_tools.hxx" because this file is included in strmsys.cxx + +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <limits.h> +#include <stdlib.h> // fuer getenv() + +#include <tools/debug.hxx> +#include <tools/fsys.hxx> +#include <tools/stream.hxx> + +#include <vos/mutex.hxx> +#include <osl/thread.h> // osl_getThreadTextEncoding + +// class FileBase +#include <osl/file.hxx> +#include <rtl/instance.hxx> + +using namespace osl; + +// ----------------------------------------------------------------------- + +// ---------------- +// - InternalLock - +// ---------------- + +class InternalStreamLock; +DECLARE_LIST( InternalStreamLockList, InternalStreamLock* ) +namespace { struct LockList : public rtl::Static< InternalStreamLockList, LockList > {}; } + +#ifndef BOOTSTRAP +namespace { struct LockMutex : public rtl::Static< NAMESPACE_VOS(OMutex), LockMutex > {}; } +#endif + +class InternalStreamLock +{ + sal_Size m_nStartPos; + sal_Size m_nEndPos; + SvFileStream* m_pStream; + struct stat m_aStat; + + InternalStreamLock( sal_Size, sal_Size, SvFileStream* ); + ~InternalStreamLock(); +public: + static sal_Bool LockFile( sal_Size nStart, sal_Size nEnd, SvFileStream* ); + static void UnlockFile( sal_Size nStart, sal_Size nEnd, SvFileStream* ); +}; + +InternalStreamLock::InternalStreamLock( + sal_Size nStart, + sal_Size nEnd, + SvFileStream* pStream ) : + m_nStartPos( nStart ), + m_nEndPos( nEnd ), + m_pStream( pStream ) +{ + ByteString aFileName(m_pStream->GetFileName(), osl_getThreadTextEncoding()); + stat( aFileName.GetBuffer(), &m_aStat ); + LockList::get().Insert( this, LIST_APPEND ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "locked %s", aFileName.GetBuffer() ); + if( m_nStartPos || m_nEndPos ) + fprintf(stderr, " [ %d ... %d ]", m_nStartPos, m_nEndPos ); + fprintf( stderr, "\n" ); +#endif +} + +InternalStreamLock::~InternalStreamLock() +{ + LockList::get().Remove( this ); +#if OSL_DEBUG_LEVEL > 1 + ByteString aFileName(m_pStream->GetFileName(), osl_getThreadTextEncoding()); + fprintf( stderr, "unlocked %s", aFileName.GetBuffer() ); + if( m_nStartPos || m_nEndPos ) + fprintf(stderr, " [ %d ... %d ]", m_nStartPos, m_nEndPos ); + fprintf( stderr, "\n" ); +#endif +} + +sal_Bool InternalStreamLock::LockFile( sal_Size nStart, sal_Size nEnd, SvFileStream* pStream ) +{ +#ifndef BOOTSTRAP + NAMESPACE_VOS( OGuard ) aGuard( LockMutex::get() ); +#endif + ByteString aFileName(pStream->GetFileName(), osl_getThreadTextEncoding()); + struct stat aStat; + if( stat( aFileName.GetBuffer(), &aStat ) ) + return sal_False; + + if( S_ISDIR( aStat.st_mode ) ) + return sal_True; + + InternalStreamLock* pLock = NULL; + InternalStreamLockList &rLockList = LockList::get(); + for( ULONG i = 0; i < rLockList.Count(); ++i ) + { + pLock = rLockList.GetObject( i ); + if( aStat.st_ino == pLock->m_aStat.st_ino ) + { + sal_Bool bDenyByOptions = sal_False; + StreamMode nLockMode = pLock->m_pStream->GetStreamMode(); + StreamMode nNewMode = pStream->GetStreamMode(); + + if( nLockMode & STREAM_SHARE_DENYALL ) + bDenyByOptions = sal_True; + else if( ( nLockMode & STREAM_SHARE_DENYWRITE ) && + ( nNewMode & STREAM_WRITE ) ) + bDenyByOptions = sal_True; + else if( ( nLockMode & STREAM_SHARE_DENYREAD ) && + ( nNewMode & STREAM_READ ) ) + bDenyByOptions = sal_True; + + if( bDenyByOptions ) + { + if( pLock->m_nStartPos == 0 && pLock->m_nEndPos == 0 ) // whole file is already locked + return sal_False; + if( nStart == 0 && nEnd == 0) // cannot lock whole file + return sal_False; + + if( ( nStart < pLock->m_nStartPos && nEnd > pLock->m_nStartPos ) || + ( nStart < pLock->m_nEndPos && nEnd > pLock->m_nEndPos ) ) + return sal_False; + } + } + } + pLock = new InternalStreamLock( nStart, nEnd, pStream ); + return sal_True; +} + +void InternalStreamLock::UnlockFile( sal_Size nStart, sal_Size nEnd, SvFileStream* pStream ) +{ +#ifndef BOOTSTRAP + NAMESPACE_VOS( OGuard ) aGuard( LockMutex::get() ); +#endif + InternalStreamLock* pLock = NULL; + InternalStreamLockList &rLockList = LockList::get(); + if( nStart == 0 && nEnd == 0 ) + { + for( ULONG i = 0; i < rLockList.Count(); ++i ) + { + if( ( pLock = rLockList.GetObject( i ) )->m_pStream == pStream ) + { + delete pLock; + i--; + } + } + return; + } + for( ULONG i = 0; i < rLockList.Count(); ++i ) + { + if( ( pLock = rLockList.GetObject( i ) )->m_pStream == pStream && + nStart == pLock->m_nStartPos && nEnd == pLock->m_nEndPos ) + { + delete pLock; + return; + } + } +} + +// -------------- +// - StreamData - +// -------------- + +class StreamData +{ +public: + int nHandle; + + StreamData() { nHandle = 0; } +}; + +// ----------------------------------------------------------------------- + +static sal_uInt32 GetSvError( int nErrno ) +{ + static struct { int nErr; sal_uInt32 sv; } errArr[] = + { + { 0, SVSTREAM_OK }, + { EACCES, SVSTREAM_ACCESS_DENIED }, + { EBADF, SVSTREAM_INVALID_HANDLE }, +#if defined( RS6000 ) || defined( ALPHA ) || defined( HP9000 ) || defined( NETBSD ) || defined(FREEBSD) || defined(MACOSX) + { 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( RS6000 ) && ! defined( ALPHA ) && ! defined( NETBSD ) && ! defined (FREEBSD) && ! defined (MACOSX) + { 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 } + }; + + sal_uInt32 nRetVal = SVSTREAM_GENERALERROR; // Standardfehler + int i=0; + do + { + if ( errArr[i].nErr == nErrno ) + { + nRetVal = errArr[i].sv; + break; + } + ++i; + } + while( errArr[i].nErr != 0xFFFF ); + return nRetVal; +} + +/************************************************************************* +|* +|* SvFileStream::SvFileStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 08.06.94 +|* Letzte Aenderung OV 08.06.94 +|* +*************************************************************************/ + +SvFileStream::SvFileStream( const String& rFileName, StreamMode nOpenMode ) +{ + bIsOpen = sal_False; + nLockCounter = 0; + bIsWritable = sal_False; + pInstanceData = new StreamData; + + SetBufferSize( 1024 ); + // convert URL to SystemPath, if necessary + ::rtl::OUString aSystemFileName; + if( FileBase::getSystemPathFromFileURL( rFileName , aSystemFileName ) + != FileBase::E_None ) + { + aSystemFileName = rFileName; + } + Open( aSystemFileName, nOpenMode ); +} + +/************************************************************************* +|* +|* SvFileStream::SvFileStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 22.11.94 +|* Letzte Aenderung OV 22.11.94 +|* +*************************************************************************/ + +SvFileStream::SvFileStream() +{ + bIsOpen = sal_False; + nLockCounter = 0; + bIsWritable = sal_False; + pInstanceData = new StreamData; + SetBufferSize( 1024 ); +} + +/************************************************************************* +|* +|* SvFileStream::~SvFileStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 22.11.94 +|* Letzte Aenderung OV 22.11.94 +|* +*************************************************************************/ + +SvFileStream::~SvFileStream() +{ + Close(); + + InternalStreamLock::UnlockFile( 0, 0, this ); + + if (pInstanceData) + delete pInstanceData; +} + +/************************************************************************* +|* +|* SvFileStream::GetFileHandle() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 22.11.94 +|* Letzte Aenderung OV 22.11.94 +|* +*************************************************************************/ + +sal_uInt32 SvFileStream::GetFileHandle() const +{ + return (sal_uInt32)pInstanceData->nHandle; +} + +/************************************************************************* +|* +|* SvFileStream::IsA() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 14.06.94 +|* Letzte Aenderung OV 14.06.94 +|* +*************************************************************************/ + +sal_uInt16 SvFileStream::IsA() const +{ + return ID_FILESTREAM; +} + +/************************************************************************* +|* +|* SvFileStream::GetData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +sal_Size SvFileStream::GetData( void* pData, sal_Size nSize ) +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "SvFileStream::GetData(): " ); + aTraceStr += ByteString::CreateFromInt64(nSize); + aTraceStr += " Bytes from "; + aTraceStr += ByteString(aFilename, osl_getThreadTextEncoding()); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + int nRead = 0; + if ( IsOpen() ) + { + nRead = read(pInstanceData->nHandle,pData,(unsigned)nSize); + if ( nRead == -1 ) + SetError( ::GetSvError( errno )); + } + return (sal_Size)nRead; +} + +/************************************************************************* +|* +|* SvFileStream::PutData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +sal_Size SvFileStream::PutData( const void* pData, sal_Size nSize ) +{ +#ifdef DBG_UTIL + ByteString aTraceStr( "SvFileStrean::PutData: " ); + aTraceStr += ByteString::CreateFromInt64(nSize); + aTraceStr += " Bytes to "; + aTraceStr += ByteString(aFilename, osl_getThreadTextEncoding()); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + int nWrite = 0; + if ( IsOpen() ) + { + nWrite = write(pInstanceData->nHandle,pData,(unsigned)nSize); + if ( nWrite == -1 ) + SetError( ::GetSvError( errno ) ); + else if( !nWrite ) + SetError( SVSTREAM_DISK_FULL ); + } + return (sal_Size)nWrite; +} + +/************************************************************************* +|* +|* SvFileStream::SeekPos() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +sal_Size SvFileStream::SeekPos( sal_Size nPos ) +{ + if ( IsOpen() ) + { + long nNewPos; + if ( nPos != STREAM_SEEK_TO_END ) + nNewPos = lseek( pInstanceData->nHandle, (long)nPos, SEEK_SET ); + else + nNewPos = lseek( pInstanceData->nHandle, 0L, SEEK_END ); + + if ( nNewPos == -1 ) + { + SetError( SVSTREAM_SEEK_ERROR ); + return 0L; + } + // langsam aber sicherer als return nNewPos + return lseek(pInstanceData->nHandle,0L,SEEK_CUR); + // return nNewPos; + } + SetError( SVSTREAM_GENERALERROR ); + return 0L; +} + + +/************************************************************************* +|* +|* SvFileStream::FlushData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::FlushData() +{ +// lokal gibt es nicht +} + +static char *pFileLockEnvVar = (char*)1; + +/************************************************************************* +|* +|* SvFileStream::LockRange() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +sal_Bool SvFileStream::LockRange( sal_Size nByteOffset, sal_Size nBytes ) +{ + struct flock aflock; + aflock.l_start = nByteOffset; + aflock.l_whence = SEEK_SET; + aflock.l_len = nBytes; + + int nLockMode = 0; + + if ( ! IsOpen() ) + return sal_False; + + if ( eStreamMode & STREAM_SHARE_DENYALL ) + { + if (bIsWritable) + nLockMode = F_WRLCK; + else + nLockMode = F_RDLCK; + } + + if ( eStreamMode & STREAM_SHARE_DENYREAD ) + { + if (bIsWritable) + nLockMode = F_WRLCK; + else + { + SetError(SVSTREAM_LOCKING_VIOLATION); + return sal_False; + } + } + + if ( eStreamMode & STREAM_SHARE_DENYWRITE ) + { + if (bIsWritable) + nLockMode = F_WRLCK; + else + nLockMode = F_RDLCK; + } + + if (!nLockMode) + return sal_True; + + if( ! InternalStreamLock::LockFile( nByteOffset, nByteOffset+nBytes, this ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "InternalLock on %s [ %d ... %d ] failed\n", + ByteString(aFilename, osl_getThreadTextEncoding()).GetBuffer(), nByteOffset, nByteOffset+nBytes ); +#endif + return sal_False; + } + + // HACK: File-Locking nur via Environmentvariable einschalten + // um einen Haenger im Zusammenspiel mit einem Linux + // NFS-2-Server (kein Lockdaemon) zu verhindern. + // File-Locking ?ber NFS ist generell ein Performancekiller. + // HR, 22.10.1997 fuer SOLARIS + // CP, 30.11.1997 fuer HPUX + // ER, 18.12.1997 fuer IRIX + // HR, 18.05.1998 Environmentvariable + + if ( pFileLockEnvVar == (char*)1 ) + pFileLockEnvVar = getenv("STAR_ENABLE_FILE_LOCKING"); + if ( ! pFileLockEnvVar ) + return sal_True; + + aflock.l_type = nLockMode; + if (fcntl(pInstanceData->nHandle, F_GETLK, &aflock) == -1) + { + #if ( defined HPUX && defined BAD_UNION ) + #ifdef DBG_UTIL + fprintf( stderr, "***** FCNTL(lock):errno = %d\n", errno ); + #endif + if ( errno == EINVAL || errno == ENOSYS ) + return sal_True; + #endif + #if defined SINIX + if (errno == EINVAL) + return sal_True; + #endif + #if defined SOLARIS + if (errno == ENOSYS) + return sal_True; + #endif + SetError( ::GetSvError( errno )); + return sal_False; + } + if (aflock.l_type != F_UNLCK) + { + SetError(SVSTREAM_LOCKING_VIOLATION); + return sal_False; + } + + aflock.l_type = nLockMode; + if (fcntl(pInstanceData->nHandle, F_SETLK, &aflock) == -1) + { + SetError( ::GetSvError( errno )); + return sal_False; + } + return sal_True; +} + +/************************************************************************* +|* +|* SvFileStream::UnlockRange() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +sal_Bool SvFileStream::UnlockRange( sal_Size nByteOffset, sal_Size nBytes ) +{ + + struct flock aflock; + aflock.l_type = F_UNLCK; + aflock.l_start = nByteOffset; + aflock.l_whence = SEEK_SET; + aflock.l_len = nBytes; + + if ( ! IsOpen() ) + return sal_False; + + InternalStreamLock::UnlockFile( nByteOffset, nByteOffset+nBytes, this ); + + if ( ! (eStreamMode & + (STREAM_SHARE_DENYALL | STREAM_SHARE_DENYREAD | STREAM_SHARE_DENYWRITE))) + return sal_True; + + // wenn File Locking ausgeschaltet, siehe SvFileStream::LockRange + if ( ! pFileLockEnvVar ) + return sal_True; + + if (fcntl(pInstanceData->nHandle, F_SETLK, &aflock) != -1) + return sal_True; + +#if ( defined HPUX && defined BAD_UNION ) +#ifdef DBG_UTIL + fprintf( stderr, "***** FCNTL(unlock):errno = %d\n", errno ); +#endif + if ( errno == EINVAL || errno == ENOSYS ) + return sal_True; +#endif +#if ( defined SINIX ) + if (errno == EINVAL) + return sal_True; +#endif + + SetError( ::GetSvError( errno )); + return sal_False; +} + +/************************************************************************* +|* +|* SvFileStream::LockFile() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +sal_Bool SvFileStream::LockFile() +{ + return LockRange( 0UL, 0UL ); +} + +/************************************************************************* +|* +|* SvFileStream::UnlockFile() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +sal_Bool SvFileStream::UnlockFile() +{ + return UnlockRange( 0UL, 0UL ); +} + +/************************************************************************* +|* +|* SvFileStream::Open() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::Open( const String& rFilename, StreamMode nOpenMode ) +{ + int nAccess, nAccessRW; + int nMode; + int nHandleTmp; + struct stat buf; + sal_Bool bStatValid = sal_False; + + Close(); + errno = 0; + eStreamMode = nOpenMode; + eStreamMode &= ~STREAM_TRUNC; // beim ReOpen nicht cutten + +// !!! NoOp: Ansonsten ToAbs() verwendern +// !!! DirEntry aDirEntry( rFilename ); +// !!! aFilename = aDirEntry.GetFull(); + aFilename = rFilename; +#ifndef BOOTSTRAP + FSysRedirector::DoRedirect( aFilename ); +#endif + ByteString aLocalFilename(aFilename, osl_getThreadTextEncoding()); + +#ifdef DBG_UTIL + ByteString aTraceStr( "SvFileStream::Open(): " ); + aTraceStr += aLocalFilename; + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + if ( lstat( aLocalFilename.GetBuffer(), &buf ) == 0 ) + { + bStatValid = sal_True; + // SvFileStream soll kein Directory oeffnen + if( S_ISDIR( buf.st_mode ) ) + { + SetError( ::GetSvError( EISDIR ) ); + return; + } + } + + + if ( !( nOpenMode & STREAM_WRITE ) ) + nAccessRW = O_RDONLY; + else if ( !( nOpenMode & STREAM_READ ) ) + nAccessRW = O_WRONLY; + else + nAccessRW = O_RDWR; + + nAccess = 0; + // Fix (MDA, 18.01.95): Bei RD_ONLY nicht mit O_CREAT oeffnen + // Wichtig auf Read-Only-Dateisystemen (wie CDROM) + if ( (!( nOpenMode & STREAM_NOCREATE )) && ( nAccessRW != O_RDONLY ) ) + nAccess |= O_CREAT; + if ( nOpenMode & STREAM_TRUNC ) + nAccess |= O_TRUNC; + + nMode = S_IREAD | S_IROTH | S_IRGRP; + if ( nOpenMode & STREAM_WRITE) + { + nMode |= (S_IWRITE | S_IWOTH | S_IWGRP); + + if ( nOpenMode & STREAM_COPY_ON_SYMLINK ) + { + if ( bStatValid && S_ISLNK( buf.st_mode ) < 0 ) + { + char *pBuf = new char[ 1024+1 ]; + if ( readlink( aLocalFilename.GetBuffer(), pBuf, 1024 ) > 0 ) + { + if ( unlink(aLocalFilename.GetBuffer()) == 0 ) + { +#ifdef DBG_UTIL + fprintf( stderr, + "Copying file on symbolic link (%s).\n", + aLocalFilename.GetBuffer() ); +#endif + String aTmpString( pBuf, osl_getThreadTextEncoding() ); + const DirEntry aSourceEntry( aTmpString ); + const DirEntry aTargetEntry( aFilename ); + FileCopier aFileCopier( aSourceEntry, aTargetEntry ); + aFileCopier.Execute(); + } + } + delete [] pBuf; + } + } + } + + + nHandleTmp = open(aLocalFilename.GetBuffer(),nAccessRW|nAccess, nMode ); + + if ( nHandleTmp == -1 ) + { + if ( nAccessRW != O_RDONLY ) + { + // auf Lesen runterschalten + nAccessRW = O_RDONLY; + nAccess = 0; + nMode = S_IREAD | S_IROTH | S_IRGRP; + nHandleTmp =open( aLocalFilename.GetBuffer(), + nAccessRW|nAccess, + nMode ); + } + } + if ( nHandleTmp != -1 ) + { + pInstanceData->nHandle = nHandleTmp; + bIsOpen = sal_True; + if ( nAccessRW != O_RDONLY ) + bIsWritable = sal_True; + + if ( !LockFile() ) // ganze Datei + { + close( nHandleTmp ); + bIsOpen = sal_False; + bIsWritable = sal_False; + pInstanceData->nHandle = 0; + } + } + else + SetError( ::GetSvError( errno ) ); +} + +/************************************************************************* +|* +|* SvFileStream::ReOpen() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::ReOpen() +{ + if ( !bIsOpen && aFilename.Len() ) + Open( aFilename, eStreamMode ); +} + +/************************************************************************* +|* +|* SvFileStream::Close() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::Close() +{ + InternalStreamLock::UnlockFile( 0, 0, this ); + + if ( IsOpen() ) + { +#ifdef DBG_UTIL + ByteString aTraceStr( "SvFileStream::Close(): " ); + aTraceStr += ByteString(aFilename, osl_getThreadTextEncoding()); + DBG_TRACE( aTraceStr.GetBuffer() ); +#endif + + Flush(); + close( pInstanceData->nHandle ); + pInstanceData->nHandle = 0; + } + + bIsOpen = sal_False; + bIsWritable = sal_False; + SvStream::ClearBuffer(); + SvStream::ClearError(); +} + +/************************************************************************* +|* +|* SvFileStream::ResetError() +|* +|* Beschreibung STREAM.SDW; Setzt Filepointer auf Dateianfang +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::ResetError() +{ + SvStream::ClearError(); +} + + +/************************************************************************* +|* +|* SvFileStream::SetSize() +|* +|* Beschreibung STREAM.SDW; +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::SetSize (sal_Size nSize) +{ + if (IsOpen()) + { + int fd = pInstanceData->nHandle; + if (::ftruncate (fd, (off_t)nSize) < 0) + { + // Save original error. + sal_uInt32 nErr = ::GetSvError (errno); + + // Check against current size. Fail upon 'shrink'. + struct stat aStat; + if (::fstat (fd, &aStat) < 0) + { + SetError (nErr); + return; + } + if ((sal::static_int_cast< sal_sSize >(nSize) <= aStat.st_size)) + { + // Failure upon 'shrink'. Return original error. + SetError (nErr); + return; + } + + // Save current position. + sal_Size nCurPos = (sal_Size)::lseek (fd, (off_t)0, SEEK_CUR); + if (nCurPos == (sal_Size)(-1)) + { + SetError (nErr); + return; + } + + // Try 'expand' via 'lseek()' and 'write()'. + if (::lseek (fd, (off_t)(nSize - 1), SEEK_SET) < 0) + { + SetError (nErr); + return; + } + if (::write (fd, (char*)"", (size_t)1) < 0) + { + // Failure. Restore saved position. + if (::lseek (fd, (off_t)nCurPos, SEEK_SET) < 0) + { + // Double failure. + } + + SetError (nErr); + return; + } + + // Success. Restore saved position. + if (::lseek (fd, (off_t)nCurPos, SEEK_SET) < 0) + { + SetError (nErr); + return; + } + } + } +} + + diff --git a/tools/source/stream/strmwnt.cxx b/tools/source/stream/strmwnt.cxx new file mode 100644 index 000000000000..865bf665c05f --- /dev/null +++ b/tools/source/stream/strmwnt.cxx @@ -0,0 +1,692 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: strmwnt.cxx,v $ + * $Revision: 1.9 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// no include "precompiled_tools.hxx" because this file is included in strmsys.cxx + +/* + Todo: StreamMode <-> AllocateMemory +*/ + +#include <string.h> +#include <limits.h> + +#include <tools/svwin.h> + +#include <tools/debug.hxx> +#include <tools/fsys.hxx> +#include <tools/stream.hxx> + +// class FileBase +#include <osl/file.hxx> +using namespace osl; + +// ----------------------------------------------------------------------- + +// -------------- +// - StreamData - +// -------------- + +class StreamData +{ +public: + HANDLE hFile; + + StreamData() + { + hFile = 0; + } +}; + +// ----------------------------------------------------------------------- + +static ULONG GetSvError( DWORD nWntError ) +{ + static struct { DWORD wnt; ULONG sv; } errArr[] = + { + { ERROR_SUCCESS, SVSTREAM_OK }, + { 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 } + }; + + ULONG nRetVal = SVSTREAM_GENERALERROR; // Standardfehler + 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() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 17.06.94 +|* Letzte Aenderung TPF 15.07.98 +|* +*************************************************************************/ + +SvFileStream::SvFileStream( const String& rFileName, StreamMode nMode ) +{ + bIsOpen = FALSE; + nLockCounter = 0; + bIsWritable = FALSE; + pInstanceData = new StreamData; + + SetBufferSize( 8192 ); + // convert URL to SystemPath, if necessary + ::rtl::OUString aFileName, aNormPath; + + if ( FileBase::getSystemPathFromFileURL( rFileName, aFileName ) != FileBase::E_None ) + aFileName = rFileName; + Open( aFileName, nMode ); +} + +/************************************************************************* +|* +|* SvFileStream::SvFileStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 22.11.94 +|* Letzte Aenderung TPF 15.07.98 +|* +*************************************************************************/ + +SvFileStream::SvFileStream() +{ + bIsOpen = FALSE; + nLockCounter = 0; + bIsWritable = FALSE; + pInstanceData = new StreamData; + + SetBufferSize( 8192 ); +} + +/************************************************************************* +|* +|* SvFileStream::~SvFileStream() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 14.06.94 +|* Letzte Aenderung OV 14.06.94 +|* +*************************************************************************/ + +SvFileStream::~SvFileStream() +{ + Close(); + if (pInstanceData) + delete pInstanceData; +} + +/************************************************************************* +|* +|* SvFileStream::GetFileHandle() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 14.06.94 +|* Letzte Aenderung OV 14.06.94 +|* +*************************************************************************/ + +ULONG SvFileStream::GetFileHandle() const +{ + return (ULONG)pInstanceData->hFile; +} + +/************************************************************************* +|* +|* SvFileStream::IsA() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 14.06.94 +|* Letzte Aenderung OV 14.06.94 +|* +*************************************************************************/ + +USHORT SvFileStream::IsA() const +{ + return ID_FILESTREAM; +} + +/************************************************************************* +|* +|* SvFileStream::GetData() +|* +|* Beschreibung STREAM.SDW, Prueft nicht Eof; IsEof danach rufbar +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung TPF 15.07.98 +|* +*************************************************************************/ + +ULONG SvFileStream::GetData( void* pData, ULONG nSize ) +{ + DWORD nCount = 0; + if( IsOpen() ) + { + bool bResult = ReadFile(pInstanceData->hFile,(LPVOID)pData,nSize,&nCount,NULL); + if( !bResult ) + { + ULONG nTestError = GetLastError(); + SetError(::GetSvError( nTestError ) ); + } + } + return (DWORD)nCount; +} + +/************************************************************************* +|* +|* SvFileStream::PutData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung TPF 15.07.98 +|* +*************************************************************************/ + +ULONG SvFileStream::PutData( const void* pData, ULONG nSize ) +{ + DWORD nCount = 0; + if( IsOpen() ) + { + if(!WriteFile(pInstanceData->hFile,(LPVOID)pData,nSize,&nCount,NULL)) + SetError(::GetSvError( GetLastError() ) ); + } + return nCount; +} + +/************************************************************************* +|* +|* SvFileStream::SeekPos() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung TPF 15.07.98 +|* +*************************************************************************/ + +ULONG SvFileStream::SeekPos( ULONG nPos ) +{ + DWORD nNewPos = 0; + if( IsOpen() ) + { + if( nPos != STREAM_SEEK_TO_END ) + // 64-Bit files werden nicht unterstuetzt + nNewPos=SetFilePointer(pInstanceData->hFile,nPos,NULL,FILE_BEGIN); + else + nNewPos=SetFilePointer(pInstanceData->hFile,0L,NULL,FILE_END); + + if( nNewPos == 0xFFFFFFFF ) + { + SetError(::GetSvError( GetLastError() ) ); + nNewPos = 0L; + } + } + else + SetError( SVSTREAM_GENERALERROR ); + return (ULONG)nNewPos; +} + +/************************************************************************* +|* +|* SvFileStream::Tell() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ +/* +ULONG SvFileStream::Tell() +{ + ULONG nPos = 0L; + + if( IsOpen() ) + { + DWORD nPos; + nPos = SetFilePointer(pInstanceData->hFile,0L,NULL,FILE_CURRENT); + if( nPos = 0xFFFFFFFF ) + { + SetError( ::GetSvError( GetLastError() ) ); + nPos = 0L; + } + } + return nPos; +} +*/ + +/************************************************************************* +|* +|* SvFileStream::FlushData() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung TPF 15.07.98 +|* +*************************************************************************/ + +void SvFileStream::FlushData() +{ + if( IsOpen() ) + { + if( !FlushFileBuffers(pInstanceData->hFile) ) + SetError(::GetSvError(GetLastError())); + } +} + +/************************************************************************* +|* +|* SvFileStream::LockRange() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung TPF 15.07.98 +|* +*************************************************************************/ + +BOOL SvFileStream::LockRange( ULONG nByteOffset, ULONG nBytes ) +{ + bool bRetVal = false; + if( IsOpen() ) + { + bRetVal = ::LockFile(pInstanceData->hFile,nByteOffset,0L,nBytes,0L ); + if( !bRetVal ) + SetError(::GetSvError(GetLastError())); + } + return bRetVal; +} + +/************************************************************************* +|* +|* SvFileStream::UnlockRange() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung TPF 15.07.98 +|* +*************************************************************************/ + +BOOL SvFileStream::UnlockRange( ULONG nByteOffset, ULONG nBytes ) +{ + bool bRetVal = false; + if( IsOpen() ) + { + bRetVal = ::UnlockFile(pInstanceData->hFile,nByteOffset,0L,nBytes,0L ); + if( !bRetVal ) + SetError(::GetSvError(GetLastError())); + } + return bRetVal; +} + +/************************************************************************* +|* +|* SvFileStream::LockFile() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +BOOL SvFileStream::LockFile() +{ + BOOL bRetVal = FALSE; + if( !nLockCounter ) + { + if( LockRange( 0L, LONG_MAX ) ) + { + nLockCounter = 1; + bRetVal = TRUE; + } + } + else + { + nLockCounter++; + bRetVal = TRUE; + } + return bRetVal; +} + +/************************************************************************* +|* +|* SvFileStream::UnlockFile() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +BOOL SvFileStream::UnlockFile() +{ + BOOL bRetVal = FALSE; + if( nLockCounter > 0) + { + if( nLockCounter == 1) + { + if( UnlockRange( 0L, LONG_MAX ) ) + { + nLockCounter = 0; + bRetVal = TRUE; + } + } + else + { + nLockCounter--; + bRetVal = TRUE; + } + } + return bRetVal; +} + + +/************************************************************************* +|* +|* SvFileStream::Open() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung TPF 15.07.98 +|* +*************************************************************************/ +/* + 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 String& rFilename, StreamMode nMode ) +{ + String aParsedFilename(rFilename); + + SetLastError( ERROR_SUCCESS ); + Close(); + SvStream::ClearBuffer(); + + eStreamMode = nMode; + eStreamMode &= ~STREAM_TRUNC; // beim ReOpen nicht cutten + + // !!! NoOp: Ansonsten ToAbs() verwendern + // !!! DirEntry aDirEntry( rFilename ); + // !!! aFilename = aDirEntry.GetFull(); + aFilename = aParsedFilename; +#ifdef BOOTSTRAP + ByteString aFileNameA( aFilename, gsl_getSystemTextEncoding()); +#else + ByteString aFileNameA( aFilename, osl_getThreadTextEncoding()); + FSysRedirector::DoRedirect( aFilename ); +#endif + SetLastError( ERROR_SUCCESS ); // ggf. durch Redirector geaendert! + + /* + #ifdef DBG_UTIL + String aTraceStr( "SvFileStream::Open(): " ); + aTraceStr += aFilename; + DBG_TRACE( aTraceStr ); + #endif + */ + + DWORD nOpenAction; + DWORD nShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + DWORD nAccessMode = 0L; + UINT nOldErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX ); + + if( nMode & STREAM_SHARE_DENYREAD) + nShareMode &= ~FILE_SHARE_READ; + + if( nMode & STREAM_SHARE_DENYWRITE) + nShareMode &= ~FILE_SHARE_WRITE; + + if( nMode & STREAM_SHARE_DENYALL) + nShareMode = 0; + + if( (nMode & STREAM_READ) ) + nAccessMode |= GENERIC_READ; + if( (nMode & STREAM_WRITE) ) + nAccessMode |= GENERIC_WRITE; + + if( nAccessMode == GENERIC_READ ) // ReadOnly ? + nMode |= STREAM_NOCREATE; // wenn ja, nicht erzeugen + + // Zuordnung siehe obige Wahrheitstafel + if( !(nMode & STREAM_NOCREATE) ) + { + if( nMode & STREAM_TRUNC ) + nOpenAction = CREATE_ALWAYS; + else + nOpenAction = OPEN_ALWAYS; + } + else + { + if( nMode & STREAM_TRUNC ) + nOpenAction = TRUNCATE_EXISTING; + else + nOpenAction = OPEN_EXISTING; + } + + pInstanceData->hFile = CreateFile( + aFileNameA.GetBuffer(), + nAccessMode, + nShareMode, + (LPSECURITY_ATTRIBUTES)NULL, + nOpenAction, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, + (HANDLE) NULL + ); + + if( pInstanceData->hFile!=INVALID_HANDLE_VALUE && ( + // Hat Create Always eine existierende Datei ueberschrieben ? + GetLastError() == ERROR_ALREADY_EXISTS || + // Hat Open Always eine neue Datei angelegt ? + GetLastError() == ERROR_FILE_NOT_FOUND )) + { + // wenn ja, dann alles OK + if( nOpenAction == OPEN_ALWAYS || nOpenAction == CREATE_ALWAYS ) + SetLastError( ERROR_SUCCESS ); + } + + // Bei Fehler pruefen, ob wir lesen duerfen + if( (pInstanceData->hFile==INVALID_HANDLE_VALUE) && + (nAccessMode & GENERIC_WRITE)) + { + ULONG nErr = ::GetSvError( GetLastError() ); + if(nErr==SVSTREAM_ACCESS_DENIED || nErr==SVSTREAM_SHARING_VIOLATION) + { + nMode &= (~STREAM_WRITE); + nAccessMode = GENERIC_READ; + // OV, 28.1.97: Win32 setzt die Datei auf 0-Laenge, wenn + // die Openaction CREATE_ALWAYS ist!!!! + nOpenAction = OPEN_EXISTING; + SetLastError( ERROR_SUCCESS ); + pInstanceData->hFile = CreateFile( + aFileNameA.GetBuffer(), + GENERIC_READ, + nShareMode, + (LPSECURITY_ATTRIBUTES)NULL, + nOpenAction, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, + (HANDLE) NULL + ); + 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 ) + bIsWritable = TRUE; + } + SetErrorMode( nOldErrorMode ); +} + +/************************************************************************* +|* +|* SvFileStream::ReOpen() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::ReOpen() +{ + if( !bIsOpen && aFilename.Len() ) + Open( aFilename, eStreamMode ); +} + +/************************************************************************* +|* +|* SvFileStream::Close() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung TPF 15.07.98 +|* +*************************************************************************/ + +void SvFileStream::Close() +{ + if( IsOpen() ) + { + if( nLockCounter ) + { + nLockCounter = 1; + UnlockFile(); + } + Flush(); + CloseHandle( pInstanceData->hFile ); + } + bIsOpen = FALSE; + nLockCounter= 0; + bIsWritable = FALSE; + SvStream::ClearBuffer(); + SvStream::ClearError(); +} + +/************************************************************************* +|* +|* SvFileStream::ResetError() +|* +|* Beschreibung STREAM.SDW; Setzt Filepointer auf Dateianfang +|* Ersterstellung OV 15.06.94 +|* Letzte Aenderung OV 15.06.94 +|* +*************************************************************************/ + +void SvFileStream::ResetError() +{ + SvStream::ClearError(); +} + +/************************************************************************* +|* +|* SvFileStream::SetSize() +|* +|* Beschreibung STREAM.SDW +|* Ersterstellung OV 19.10.95 +|* Letzte Aenderung TPF 15.07.98 +|* +*************************************************************************/ + +void SvFileStream::SetSize( ULONG nSize ) +{ + + if( IsOpen() ) + { + int bError = FALSE; + HANDLE hFile = pInstanceData->hFile; + ULONG nOld = SetFilePointer( hFile, 0L, NULL, FILE_CURRENT ); + if( nOld != 0xffffffff ) + { + if( SetFilePointer(hFile,nSize,NULL,FILE_BEGIN ) != 0xffffffff) + { + bool bSucc = SetEndOfFile( hFile ); + if( !bSucc ) + bError = TRUE; + } + if( SetFilePointer( hFile,nOld,NULL,FILE_BEGIN ) == 0xffffffff) + bError = TRUE; + } + if( bError ) + SetError(::GetSvError( GetLastError() ) ); + } +} + diff --git a/tools/source/stream/vcompat.cxx b/tools/source/stream/vcompat.cxx new file mode 100644 index 000000000000..f275db60281a --- /dev/null +++ b/tools/source/stream/vcompat.cxx @@ -0,0 +1,83 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: vcompat.cxx,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#define _VCOMPAT_CXX +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> + +// ----------------- +// - VersionCompat - +// ----------------- + +VersionCompat::VersionCompat( SvStream& rStm, USHORT nStreamMode, USHORT nVersion ) : + mpRWStm ( &rStm ), + mnStmMode ( nStreamMode ), + mnVersion ( nVersion ) +{ + if( !mpRWStm->GetError() ) + { + if( STREAM_WRITE == mnStmMode ) + { + *mpRWStm << mnVersion; + mnTotalSize = ( mnCompatPos = mpRWStm->Tell() ) + 4UL; + mpRWStm->SeekRel( 4L ); + } + else + { + *mpRWStm >> mnVersion; + *mpRWStm >> mnTotalSize; + mnCompatPos = mpRWStm->Tell(); + } + } +} + +// ------------------------------------------------------------------------ + +VersionCompat::~VersionCompat() +{ + if( STREAM_WRITE == mnStmMode ) + { + const UINT32 nEndPos = mpRWStm->Tell(); + + mpRWStm->Seek( mnCompatPos ); + *mpRWStm << ( nEndPos - mnTotalSize ); + mpRWStm->Seek( nEndPos ); + } + else + { + const UINT32 nReadSize = mpRWStm->Tell() - mnCompatPos; + + if( mnTotalSize > nReadSize ) + mpRWStm->SeekRel( mnTotalSize - nReadSize ); + } +} diff --git a/tools/source/string/debugprint.cxx b/tools/source/string/debugprint.cxx new file mode 100644 index 000000000000..19e663208498 --- /dev/null +++ b/tools/source/string/debugprint.cxx @@ -0,0 +1,51 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: debugprint.cxx,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +// ======================================================================= + +#include <tools/string.hxx> + +const sal_Char *dbg_dump(const ByteString &rStr) +{ + static ByteString aStr; + aStr = rStr; + aStr.Append(static_cast<char>(0)); + return aStr.GetBuffer(); +} + +const sal_Char *dbg_dump(const UniString &rStr) +{ + return dbg_dump(ByteString(rStr, RTL_TEXTENCODING_UTF8)); +} + +/* vi:set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/tools/source/string/makefile.mk b/tools/source/string/makefile.mk new file mode 100644 index 000000000000..1bb9b0fdf6bc --- /dev/null +++ b/tools/source/string/makefile.mk @@ -0,0 +1,83 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.8 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=str + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +ALWAYSDBGFILES = $(SLO)$/debugprint.obj + +.IF "$(ALWAYSDBGFILES)" != "" +ALWAYSDBGTARGET=do_it_alwaysdebug +.ENDIF + +SLOFILES= $(SLO)$/tstring.obj \ + $(SLO)$/tustring.obj \ + $(SLO)$/tenccvt.obj \ + $(SLO)$/debugprint.obj + +OBJFILES= $(OBJ)$/tstring.obj \ + $(OBJ)$/tustring.obj \ + $(OBJ)$/tenccvt.obj \ + $(OBJ)$/debugprint.obj + +# --- Targets ------------------------------------------------------ + +.IF "$(ALWAYSDBG_FLAG)"=="" +TARGETDEPS+=$(ALWAYSDBGTARGET) +.ENDIF + +.INCLUDE : target.mk + +.IF "$(ALWAYSDBGTARGET)" != "" +.IF "$(ALWAYSDBG_FLAG)" == "" +# -------------------------------------------------- +# - ALWAYSDBG - files always compiled with debugging +# -------------------------------------------------- +$(ALWAYSDBGTARGET): + @echo --- ALWAYSDBGFILES --- + @dmake $(MFLAGS) $(MAKEFILE) debug=true $(ALWAYSDBGFILES) ALWAYSDBG_FLAG=TRUE $(CALLMACROS) + @echo --- ALWAYSDBGFILES OVER --- + +$(ALWAYSDBGFILES): + @echo --- ALWAYSDBG --- + @dmake $(MFLAGS) $(MAKEFILE) debug=true ALWAYSDBG_FLAG=TRUE $(CALLMACROS) $@ + @echo --- ALWAYSDBG OVER --- +.ENDIF +.ENDIF diff --git a/tools/source/string/strascii.cxx b/tools/source/string/strascii.cxx new file mode 100644 index 000000000000..6ce35241c552 --- /dev/null +++ b/tools/source/string/strascii.cxx @@ -0,0 +1,640 @@ +#/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: strascii.cxx,v $ + * $Revision: 1.9 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// no include "precompiled_tools.hxx" because this is included in other cxx files. + +// ======================================================================= + +#ifdef DBG_UTIL + +static BOOL ImplDbgCheckAsciiStr( const sal_Char* pAsciiStr, sal_Int32 nLen ) +{ + while ( nLen && *pAsciiStr ) + { + if ( ((unsigned char)*pAsciiStr) > 127 ) + return FALSE; + ++pAsciiStr, + --nLen; + } + + return TRUE; +} + +#endif + +// ======================================================================= + +static void ImplCopyAsciiStr( sal_Unicode* pDest, const sal_Char* pSrc, + sal_Int32 nLen ) +{ + DBG_ASSERT( ImplDbgCheckAsciiStr( pSrc, nLen ), + "UniString::CopyAsciiStr() - pAsciiStr include characters > 127" ); + + while ( nLen ) + { + *pDest = (unsigned char)*pSrc; + ++pDest, + ++pSrc, + --nLen; + } +} + +// ======================================================================= + +static sal_Int32 ImplStringCompareAscii( const sal_Unicode* pStr1, const sal_Char* pStr2 ) +{ + sal_Int32 nRet; + while ( ((nRet = ((sal_Int32)*pStr1)-((sal_Int32)((unsigned char)*pStr2))) == 0) && + *pStr2 ) + { + ++pStr1, + ++pStr2; + } + + return nRet; +} + +// ----------------------------------------------------------------------- + +static sal_Int32 ImplStringCompareAscii( const sal_Unicode* pStr1, const sal_Char* pStr2, + xub_StrLen nCount ) +{ + sal_Int32 nRet = 0; + while ( nCount && + ((nRet = ((sal_Int32)*pStr1)-((sal_Int32)((unsigned char)*pStr2))) == 0) && + *pStr2 ) + { + ++pStr1, + ++pStr2, + --nCount; + } + + return nRet; +} + +// ----------------------------------------------------------------------- + +static sal_Int32 ImplStringCompareWithoutZeroAscii( const sal_Unicode* pStr1, const sal_Char* pStr2, + xub_StrLen nCount ) +{ + sal_Int32 nRet = 0; + while ( nCount && + ((nRet = ((sal_Int32)*pStr1)-((sal_Int32)((unsigned char)*pStr2))) == 0) ) + { + ++pStr1, + ++pStr2, + --nCount; + } + + return nRet; +} + +// ----------------------------------------------------------------------- + +static sal_Int32 ImplStringICompareAscii( const sal_Unicode* pStr1, const sal_Char* pStr2 ) +{ + sal_Int32 nRet; + sal_Unicode c1; + sal_Char c2; + do + { + // Ist das Zeichen zwischen 'A' und 'Z' dann umwandeln + c1 = *pStr1; + c2 = *pStr2; + if ( (c1 >= 65) && (c1 <= 90) ) + c1 += 32; + if ( (c2 >= 65) && (c2 <= 90) ) + c2 += 32; + nRet = ((sal_Int32)c1)-((sal_Int32)((unsigned char)c2)); + if ( nRet != 0 ) + break; + + ++pStr1, + ++pStr2; + } + while ( c2 ); + + return nRet; +} + +// ----------------------------------------------------------------------- + +static sal_Int32 ImplStringICompareAscii( const sal_Unicode* pStr1, const sal_Char* pStr2, + xub_StrLen nCount ) +{ + sal_Int32 nRet = 0; + sal_Unicode c1; + sal_Char c2; + do + { + if ( !nCount ) + break; + + // Ist das Zeichen zwischen 'A' und 'Z' dann umwandeln + c1 = *pStr1; + c2 = *pStr2; + if ( (c1 >= 65) && (c1 <= 90) ) + c1 += 32; + if ( (c2 >= 65) && (c2 <= 90) ) + c2 += 32; + nRet = ((sal_Int32)c1)-((sal_Int32)((unsigned char)c2)); + if ( nRet != 0 ) + break; + + ++pStr1, + ++pStr2, + --nCount; + } + while ( c2 ); + + return nRet; +} + +// ======================================================================= + +UniString UniString::CreateFromAscii( const sal_Char* pAsciiStr ) +{ + DBG_ASSERT( pAsciiStr, "UniString::CreateFromAscii() - pAsciiStr is NULL" ); + + // Stringlaenge ermitteln + xub_StrLen nLen = ImplStringLen( pAsciiStr ); + + UniString aTempStr; + if ( nLen ) + { + ImplCopyAsciiStr( aTempStr.AllocBuffer( nLen ), pAsciiStr, nLen ); + } + return aTempStr; +} + +// ----------------------------------------------------------------------- + +UniString UniString::CreateFromAscii( const sal_Char* pAsciiStr, xub_StrLen nLen ) +{ + DBG_ASSERT( pAsciiStr, "UniString::CreateFromAscii() - pAsciiStr is NULL" ); + + // Stringlaenge ermitteln + if ( nLen == STRING_LEN ) + nLen = ImplStringLen( pAsciiStr ); + + UniString aTempStr; + + if ( nLen ) + { + ImplCopyAsciiStr( aTempStr.AllocBuffer( nLen ), pAsciiStr, nLen ); + } + return aTempStr; +} + +// ----------------------------------------------------------------------- + +UniString& UniString::AssignAscii( const sal_Char* pAsciiStr ) +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( pAsciiStr, "UniString::AssignAscii() - pAsciiStr is NULL" ); + + // Stringlaenge ermitteln + xub_StrLen nLen = ImplStringLen( pAsciiStr ); + + if ( !nLen ) + { + STRING_NEW((STRING_TYPE **)&mpData); + } + else + { + // Wenn String genauso lang ist, wie der String, dann direkt kopieren + if ( (nLen == mpData->mnLen) && (mpData->mnRefCount == 1) ) + ImplCopyAsciiStr( mpData->maStr, pAsciiStr, nLen ); + else + { + // Alte Daten loeschen + STRING_RELEASE((STRING_TYPE *)mpData); + + // Daten initialisieren und String kopieren + mpData = ImplAllocData( nLen ); + ImplCopyAsciiStr( mpData->maStr, pAsciiStr, nLen ); + } + } + + return *this; +} + +// ----------------------------------------------------------------------- + +UniString& UniString::AssignAscii( const sal_Char* pAsciiStr, xub_StrLen nLen ) +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( pAsciiStr, "UniString::AssignAscii() - pAsciiStr is NULL" ); + + if ( nLen == STRING_LEN ) + nLen = ImplStringLen( pAsciiStr ); + +#ifdef DBG_UTIL + if ( DbgIsAssert() ) + { + for ( xub_StrLen i = 0; i < nLen; ++i ) + { + if ( !pAsciiStr[i] ) + { + DBG_ERROR( "UniString::AssignAscii() : nLen is wrong" ); + } + } + } +#endif + + if ( !nLen ) + { + STRING_NEW((STRING_TYPE **)&mpData); + } + else + { + // Wenn String genauso lang ist, wie der String, dann direkt kopieren + if ( (nLen == mpData->mnLen) && (mpData->mnRefCount == 1) ) + ImplCopyAsciiStr( mpData->maStr, pAsciiStr, nLen ); + else + { + // Alte Daten loeschen + STRING_RELEASE((STRING_TYPE *)mpData); + + // Daten initialisieren und String kopieren + mpData = ImplAllocData( nLen ); + ImplCopyAsciiStr( mpData->maStr, pAsciiStr, nLen ); + } + } + + return *this; +} + +// ----------------------------------------------------------------------- + +UniString& UniString::AppendAscii( const sal_Char* pAsciiStr ) +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( pAsciiStr, "UniString::AppendAscii() - pAsciiStr is NULL" ); + + // Stringlaenge ermitteln + sal_Int32 nCopyLen = ImplStringLen( pAsciiStr ); + + // Ueberlauf abfangen + nCopyLen = ImplGetCopyLen( mpData->mnLen, nCopyLen ); + + // Ist es kein leerer String + if ( nCopyLen ) + { + // Neue Datenstruktur und neuen String erzeugen + UniStringData* pNewData = ImplAllocData( mpData->mnLen+nCopyLen ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, mpData->mnLen*sizeof( sal_Unicode ) ); + ImplCopyAsciiStr( pNewData->maStr+mpData->mnLen, pAsciiStr, nCopyLen ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + } + + return *this; +} + +// ----------------------------------------------------------------------- + +UniString& UniString::AppendAscii( const sal_Char* pAsciiStr, xub_StrLen nLen ) +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( pAsciiStr, "UniString::AppendAscii() - pAsciiStr is NULL" ); + + if ( nLen == STRING_LEN ) + nLen = ImplStringLen( pAsciiStr ); + +#ifdef DBG_UTIL + if ( DbgIsAssert() ) + { + for ( xub_StrLen i = 0; i < nLen; ++i ) + { + if ( !pAsciiStr[i] ) + { + DBG_ERROR( "UniString::AppendAscii() : nLen is wrong" ); + } + } + } +#endif + + // Ueberlauf abfangen + sal_Int32 nCopyLen = ImplGetCopyLen( mpData->mnLen, nLen ); + + // Ist es kein leerer String + if ( nCopyLen ) + { + // Neue Datenstruktur und neuen String erzeugen + UniStringData* pNewData = ImplAllocData( mpData->mnLen+nCopyLen ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, mpData->mnLen*sizeof( sal_Unicode ) ); + ImplCopyAsciiStr( pNewData->maStr+mpData->mnLen, pAsciiStr, nCopyLen ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + } + + return *this; +} + +// ----------------------------------------------------------------------- + +UniString& UniString::InsertAscii( const char* pAsciiStr, xub_StrLen nIndex ) +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( pAsciiStr, "UniString::InsertAscii() - pAsciiStr is NULL" ); + + // Stringlaenge ermitteln + sal_Int32 nCopyLen = ImplStringLen( pAsciiStr ); + + // Ueberlauf abfangen + nCopyLen = ImplGetCopyLen( mpData->mnLen, nCopyLen ); + + // Ist der einzufuegende String ein Leerstring + if ( !nCopyLen ) + return *this; + + // Index groesser als Laenge + if ( nIndex > mpData->mnLen ) + nIndex = static_cast< xub_StrLen >(mpData->mnLen); + + // Neue Laenge ermitteln und neuen String anlegen + UniStringData* pNewData = ImplAllocData( mpData->mnLen+nCopyLen ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nIndex*sizeof( sal_Unicode ) ); + ImplCopyAsciiStr( pNewData->maStr+nIndex, pAsciiStr, nCopyLen ); + memcpy( pNewData->maStr+nIndex+nCopyLen, mpData->maStr+nIndex, + (mpData->mnLen-nIndex)*sizeof( sal_Unicode ) ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + + return *this; +} + +// ----------------------------------------------------------------------- + +UniString& UniString::ReplaceAscii( xub_StrLen nIndex, xub_StrLen nCount, + const sal_Char* pAsciiStr, xub_StrLen nStrLen ) +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( pAsciiStr, "UniString::ReplaceAscii() - pAsciiStr is NULL" ); + + // Wenn Index groessergleich Laenge ist, dann ist es ein Append + if ( nIndex >= mpData->mnLen ) + { + AppendAscii( pAsciiStr, nStrLen ); + return *this; + } + + // Ist es eine Zuweisung + if ( (nIndex == 0) && (nCount >= mpData->mnLen) ) + { + AssignAscii( pAsciiStr, nStrLen ); + return *this; + } + + // Reicht ein Erase + if ( nStrLen == STRING_LEN ) + nStrLen = ImplStringLen( pAsciiStr ); + if ( !nStrLen ) + return Erase( nIndex, nCount ); + + // nCount darf nicht ueber das Stringende hinnausgehen + if ( nCount > mpData->mnLen - nIndex ) + nCount = static_cast< xub_StrLen >(mpData->mnLen-nIndex); + + // Reicht eine zeichenweise Zuweisung + if ( nCount == nStrLen ) + { + ImplCopyData(); + ImplCopyAsciiStr( mpData->maStr+nIndex, pAsciiStr, nStrLen ); + return *this; + } + + // Ueberlauf abfangen + sal_Int32 n = ImplGetCopyLen( mpData->mnLen-nCount, nStrLen ); + + // Neue Daten anlegen + STRINGDATA* pNewData = ImplAllocData( mpData->mnLen-nCount+n ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nIndex*sizeof( STRCODE ) ); + ImplCopyAsciiStr( pNewData->maStr+nIndex, pAsciiStr, n ); + memcpy( pNewData->maStr+nIndex+n, mpData->maStr+nIndex+nCount, + (mpData->mnLen-nIndex-nCount+1)*sizeof( STRCODE ) ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + + return *this; +} + +// ----------------------------------------------------------------------- + +StringCompare UniString::CompareToAscii( const sal_Char* pAsciiStr, + xub_StrLen nLen ) const +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( ImplDbgCheckAsciiStr( pAsciiStr, nLen ), + "UniString::CompareToAscii() - pAsciiStr include characters > 127" ); + + // String vergleichen + sal_Int32 nCompare = ImplStringCompareAscii( mpData->maStr, pAsciiStr, nLen ); + + // Rueckgabewert anpassen + if ( nCompare == 0 ) + return COMPARE_EQUAL; + else if ( nCompare < 0 ) + return COMPARE_LESS; + else + return COMPARE_GREATER; +} + +// ----------------------------------------------------------------------- + +StringCompare UniString::CompareIgnoreCaseToAscii( const sal_Char* pAsciiStr, + xub_StrLen nLen ) const +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( ImplDbgCheckAsciiStr( pAsciiStr, nLen ), + "UniString::CompareIgnoreCaseToAscii() - pAsciiStr include characters > 127" ); + + // String vergleichen + sal_Int32 nCompare = ImplStringICompareAscii( mpData->maStr, pAsciiStr, nLen ); + + // Rueckgabewert anpassen + if ( nCompare == 0 ) + return COMPARE_EQUAL; + else if ( nCompare < 0 ) + return COMPARE_LESS; + else + return COMPARE_GREATER; +} + +// ----------------------------------------------------------------------- + +BOOL UniString::EqualsAscii( const sal_Char* pAsciiStr ) const +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( ImplDbgCheckAsciiStr( pAsciiStr, STRING_LEN ), + "UniString::EqualsAscii() - pAsciiStr include characters > 127" ); + + return (ImplStringCompareAscii( mpData->maStr, pAsciiStr ) == 0); +} + +// ----------------------------------------------------------------------- + +BOOL UniString::EqualsIgnoreCaseAscii( const sal_Char* pAsciiStr ) const +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( ImplDbgCheckAsciiStr( pAsciiStr, STRING_LEN ), + "UniString::EqualsIgnoreCaseAscii() - pAsciiStr include characters > 127" ); + + return (ImplStringICompareAscii( mpData->maStr, pAsciiStr ) == 0); +} + +// ----------------------------------------------------------------------- + +BOOL UniString::EqualsAscii( const sal_Char* pAsciiStr, + xub_StrLen nIndex, xub_StrLen nLen ) const +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( ImplDbgCheckAsciiStr( pAsciiStr, nLen ), + "UniString::EqualsAscii() - pAsciiStr include characters > 127" ); + + // Are there enough codes for comparing? + if ( nIndex > mpData->mnLen ) + return (*pAsciiStr == 0); + + return (ImplStringCompareAscii( mpData->maStr+nIndex, pAsciiStr, nLen ) == 0); +} + +// ----------------------------------------------------------------------- + +BOOL UniString::EqualsIgnoreCaseAscii( const sal_Char* pAsciiStr, + xub_StrLen nIndex, xub_StrLen nLen ) const +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( ImplDbgCheckAsciiStr( pAsciiStr, nLen ), + "UniString::EqualsIgnoreCaseAscii() - pAsciiStr include characters > 127" ); + + // Are there enough codes for comparing? + if ( nIndex > mpData->mnLen ) + return (*pAsciiStr == 0); + + return (ImplStringICompareAscii( mpData->maStr+nIndex, pAsciiStr, nLen ) == 0); +} + +// ----------------------------------------------------------------------- + +xub_StrLen UniString::SearchAscii( const sal_Char* pAsciiStr, xub_StrLen nIndex ) const +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( ImplDbgCheckAsciiStr( pAsciiStr, STRING_LEN ), + "UniString::SearchAscii() - pAsciiStr include characters > 127" ); + + sal_Int32 nLen = mpData->mnLen; + xub_StrLen nStrLen = ImplStringLen( pAsciiStr ); + + // Falls die Laenge des uebergebenen Strings 0 ist oder der Index + // hinter dem String liegt, dann wurde der String nicht gefunden + if ( !nStrLen || (nIndex >= nLen) ) + return STRING_NOTFOUND; + + const sal_Unicode* pStr = mpData->maStr; + pStr += nIndex; + + if ( nStrLen == 1 ) + { + sal_Unicode cSearch = (unsigned char)*pAsciiStr; + while ( nIndex < nLen ) + { + if ( *pStr == cSearch ) + return nIndex; + ++pStr, + ++nIndex; + } + } + else + { + // Nur innerhalb des Strings suchen + while ( nLen - nIndex >= nStrLen ) + { + // Stimmt der String ueberein + if ( ImplStringCompareWithoutZeroAscii( pStr, pAsciiStr, nStrLen ) == 0 ) + return nIndex; + ++pStr, + ++nIndex; + } + } + + return STRING_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +xub_StrLen UniString::SearchAndReplaceAscii( const sal_Char* pAsciiStr, const UniString& rRepStr, + xub_StrLen nIndex ) +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( ImplDbgCheckAsciiStr( pAsciiStr, STRING_LEN ), + "UniString::SearchAndReplaceAscii() - pAsciiStr include characters > 127" ); + + xub_StrLen nSPos = SearchAscii( pAsciiStr, nIndex ); + if ( nSPos != STRING_NOTFOUND ) + Replace( nSPos, ImplStringLen( pAsciiStr ), rRepStr ); + + return nSPos; +} + +// ----------------------------------------------------------------------- + +void UniString::SearchAndReplaceAllAscii( const sal_Char* pAsciiStr, const UniString& rRepStr ) +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + DBG_ASSERT( ImplDbgCheckAsciiStr( pAsciiStr, STRING_LEN ), + "UniString::SearchAndReplaceAllAscii() - pAsciiStr include characters > 127" ); + + xub_StrLen nCharLen = ImplStringLen( pAsciiStr ); + xub_StrLen nSPos = SearchAscii( pAsciiStr, 0 ); + while ( nSPos != STRING_NOTFOUND ) + { + Replace( nSPos, nCharLen, rRepStr ); + nSPos = nSPos + rRepStr.Len(); + nSPos = SearchAscii( pAsciiStr, nSPos ); + } +} diff --git a/tools/source/string/strcvt.cxx b/tools/source/string/strcvt.cxx new file mode 100644 index 000000000000..958c5bff76d6 --- /dev/null +++ b/tools/source/string/strcvt.cxx @@ -0,0 +1,616 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: strcvt.cxx,v $ + * $Revision: 1.12 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// no include "precompiled_tools.hxx" because this is included in other cxx files. + +// ----------------------------------------------------------------------- + +void ByteString::ImplUpdateStringFromUniString( + const sal_Unicode* pUniStr, sal_Size nUniLen, + rtl_TextEncoding eTextEncoding, sal_uInt32 nCvtFlags ) +{ + ByteStringData* pNewStringData = NULL; + rtl_uString2String( (rtl_String **)(&pNewStringData), + pUniStr, nUniLen, + eTextEncoding, nCvtFlags ); + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewStringData; +} + +// ======================================================================= + +ByteString::ByteString( const UniString& rUniStr, rtl_TextEncoding eTextEncoding, sal_uInt32 nCvtFlags ) +{ + DBG_CTOR( ByteString, DbgCheckByteString ); + DBG_CHKOBJ( &rUniStr, UniString, DbgCheckUniString ); + + mpData = NULL; + rtl_uString2String( (rtl_String **)(&mpData), + rUniStr.mpData->maStr, rUniStr.mpData->mnLen, + eTextEncoding, nCvtFlags ); +} + +// ----------------------------------------------------------------------- + +ByteString::ByteString( const UniString& rUniStr, xub_StrLen nPos, xub_StrLen nLen, + rtl_TextEncoding eTextEncoding, sal_uInt32 nCvtFlags ) +{ + DBG_CTOR( ByteString, DbgCheckByteString ); + DBG_CHKOBJ( &rUniStr, UniString, DbgCheckUniString ); + + // Stringlaenge ermitteln + if ( nPos > rUniStr.mpData->mnLen ) + nLen = 0; + else + { + // Laenge korrigieren, wenn noetig + sal_Int32 nMaxLen = rUniStr.mpData->mnLen-nPos; + if ( nLen > nMaxLen ) + nLen = static_cast< xub_StrLen >(nMaxLen); + } + + mpData = NULL; + rtl_uString2String( (rtl_String **)(&mpData), + rUniStr.mpData->maStr+nPos, nLen, + eTextEncoding, nCvtFlags ); +} + +// ----------------------------------------------------------------------- + +ByteString::ByteString( const sal_Unicode* pUniStr, + rtl_TextEncoding eTextEncoding, sal_uInt32 nCvtFlags ) +{ + DBG_CTOR( ByteString, DbgCheckByteString ); + DBG_ASSERT( pUniStr, "ByteString::ByteString() - pUniStr is NULL" ); + + mpData = NULL; + rtl_uString2String( (rtl_String **)(&mpData), + pUniStr, ImplStringLen( pUniStr ), + eTextEncoding, nCvtFlags ); +} + +// ----------------------------------------------------------------------- + +ByteString::ByteString( const sal_Unicode* pUniStr, xub_StrLen nLen, + rtl_TextEncoding eTextEncoding, sal_uInt32 nCvtFlags ) +{ + DBG_CTOR( ByteString, DbgCheckByteString ); + DBG_ASSERT( pUniStr, "ByteString::ByteString() - pUniStr is NULL" ); + + if ( nLen == STRING_LEN ) + nLen = ImplStringLen( pUniStr ); + + mpData = NULL; + rtl_uString2String( (rtl_String **)(&mpData), + pUniStr, nLen, + eTextEncoding, nCvtFlags ); +} + +// ======================================================================= + +static sal_uChar aImplByteTab[256] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255 +}; + +// ======================================================================= + +struct Impl1ByteUnicodeTabData +{ + rtl_TextEncoding meTextEncoding; + sal_Unicode maUniTab[256]; + Impl1ByteUnicodeTabData* mpNext; +}; + +// ----------------------------------------------------------------------- + +struct Impl1ByteConvertTabData +{ + rtl_TextEncoding meSrcTextEncoding; + rtl_TextEncoding meDestTextEncoding; + sal_uChar maConvertTab[256]; + sal_uChar maRepConvertTab[256]; + Impl1ByteConvertTabData* mpNext; +}; + +// ======================================================================= + +sal_Unicode* ImplGet1ByteUnicodeTab( rtl_TextEncoding eTextEncoding ) +{ +#ifndef BOOTSTRAP + TOOLSINDATA* pToolsData = ImplGetToolsInData(); +#else + TOOLSINDATA* pToolsData = 0x0; +#endif + Impl1ByteUnicodeTabData* pTab = pToolsData->mpFirstUniTabData; + + while ( pTab ) + { + if ( pTab->meTextEncoding == eTextEncoding ) + return pTab->maUniTab; + pTab = pTab->mpNext; + } + + // get TextEncodingInfo + rtl_TextEncodingInfo aTextEncInfo; + aTextEncInfo.StructSize = sizeof( aTextEncInfo ); + rtl_getTextEncodingInfo( eTextEncoding, &aTextEncInfo ); + + if ( aTextEncInfo.MaximumCharSize == 1 ) + { + pTab = new Impl1ByteUnicodeTabData; + pTab->meTextEncoding = eTextEncoding; + pTab->mpNext = pToolsData->mpFirstUniTabData; + + rtl_TextToUnicodeConverter hConverter; + sal_uInt32 nInfo; + sal_Size nSrcBytes; + sal_Size nDestChars; + hConverter = rtl_createTextToUnicodeConverter( eTextEncoding ); + nDestChars = rtl_convertTextToUnicode( hConverter, 0, + (const sal_Char*)aImplByteTab, 256, + pTab->maUniTab, 256, + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_MAPTOPRIVATE | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT, + &nInfo, &nSrcBytes ); + rtl_destroyTextToUnicodeConverter( hConverter ); + + if ( (nSrcBytes != 256) || (nDestChars != 256) ) + delete pTab; + else + { + pToolsData->mpFirstUniTabData = pTab; + return pTab->maUniTab; + } + } + + return NULL; +} + +// ----------------------------------------------------------------------- + +static sal_uChar* ImplGet1ByteConvertTab( rtl_TextEncoding eSrcTextEncoding, + rtl_TextEncoding eDestTextEncoding, + BOOL bReplace ) +{ +#ifndef BOOTSTRAP + TOOLSINDATA* pToolsData = ImplGetToolsInData(); +#else + TOOLSINDATA* pToolsData = 0x0; +#endif + Impl1ByteConvertTabData* pTab = pToolsData->mpFirstConvertTabData; + + while ( pTab ) + { + if ( (pTab->meSrcTextEncoding == eSrcTextEncoding) && + (pTab->meDestTextEncoding == eDestTextEncoding) ) + { + if ( bReplace ) + return pTab->maRepConvertTab; + else + return pTab->maConvertTab; + } + pTab = pTab->mpNext; + } + + // get TextEncodingInfo + rtl_TextEncodingInfo aTextEncInfo1; + aTextEncInfo1.StructSize = sizeof( aTextEncInfo1 ); + rtl_getTextEncodingInfo( eSrcTextEncoding, &aTextEncInfo1 ); + rtl_TextEncodingInfo aTextEncInfo2; + aTextEncInfo2.StructSize = sizeof( aTextEncInfo2 ); + rtl_getTextEncodingInfo( eDestTextEncoding, &aTextEncInfo2 ); + + if ( (aTextEncInfo1.MaximumCharSize == 1) && + (aTextEncInfo2.MaximumCharSize == 1) ) + { + pTab = new Impl1ByteConvertTabData; + pTab->meSrcTextEncoding = eSrcTextEncoding; + pTab->meDestTextEncoding = eDestTextEncoding; + pTab->mpNext = pToolsData->mpFirstConvertTabData; + + rtl_TextToUnicodeConverter hConverter; + rtl_UnicodeToTextConverter hConverter2; + sal_uInt32 nInfo; + sal_Size nSrcBytes; + sal_Size nDestChars; + sal_Size nSrcChars; + sal_Size nDestBytes; + sal_Unicode aTempBuf[256]; + hConverter = rtl_createTextToUnicodeConverter( eSrcTextEncoding ); + nDestChars = rtl_convertTextToUnicode( hConverter, 0, + (const sal_Char*)aImplByteTab, 256, + aTempBuf, 256, + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT, + &nInfo, &nSrcBytes ); + rtl_destroyTextToUnicodeConverter( hConverter ); + if ( (nSrcBytes != 256) || (nDestChars != 256) ) + delete pTab; + else + { + hConverter2 = rtl_createUnicodeToTextConverter( eDestTextEncoding ); + nDestBytes = rtl_convertUnicodeToText( hConverter2, 0, + aTempBuf, 256, + (sal_Char*)pTab->maConvertTab, 256, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_0 | + RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT, + &nInfo, &nSrcChars ); + if ( (nDestBytes == 256) || (nSrcChars == 256) ) + { + nDestBytes = rtl_convertUnicodeToText( hConverter2, 0, + aTempBuf, 256, + (sal_Char*)pTab->maRepConvertTab, 256, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT | + RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT | + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE, + &nInfo, &nSrcChars ); + } + rtl_destroyUnicodeToTextConverter( hConverter2 ); + if ( (nDestBytes != 256) || (nSrcChars != 256) ) + delete pTab; + else + { + pToolsData->mpFirstConvertTabData = pTab; + if ( bReplace ) + return pTab->maRepConvertTab; + else + return pTab->maConvertTab; + } + } + } + + return NULL; +} + +// ======================================================================= + +void ImplDeleteCharTabData() +{ +#ifndef BOOTSTRAP + TOOLSINDATA* pToolsData = ImplGetToolsInData(); +#else + TOOLSINDATA* pToolsData = 0x0; +#endif + Impl1ByteUnicodeTabData* pTempUniTab; + Impl1ByteUnicodeTabData* pUniTab = pToolsData->mpFirstUniTabData; + while ( pUniTab ) + { + pTempUniTab = pUniTab->mpNext; + delete pUniTab; + pUniTab = pTempUniTab; + } + pToolsData->mpFirstUniTabData = NULL; + + Impl1ByteConvertTabData* pTempConvertTab; + Impl1ByteConvertTabData* pConvertTab = pToolsData->mpFirstConvertTabData; + while ( pConvertTab ) + { + pTempConvertTab = pConvertTab->mpNext; + delete pConvertTab; + pConvertTab = pTempConvertTab; + } + pToolsData->mpFirstConvertTabData = NULL; +} + +// ======================================================================= + +void ByteString::ImplStringConvert( + rtl_TextEncoding eSource, rtl_TextEncoding eTarget, BOOL bReplace ) +{ + sal_uChar* pConvertTab = ImplGet1ByteConvertTab( eSource, eTarget, bReplace ); + if ( pConvertTab ) + { + char* pStr = mpData->maStr; + while ( *pStr ) + { + sal_uChar c = (sal_uChar)*pStr; + sal_uChar cConv = pConvertTab[c]; + if ( c != cConv ) + { + pStr = ImplCopyStringData( pStr ); + *pStr = (char)cConv; + } + + pStr++; + } + } + else + { + rtl_UnicodeToTextConverter hSrcConverter = rtl_createTextToUnicodeConverter( eSource ); + sal_uInt32 nInfo; + sal_Size nSrcBytes; + sal_Size nDestChars; + sal_Size nTempLen; + sal_Unicode* pTempBuf; + nTempLen = mpData->mnLen; + pTempBuf = new sal_Unicode[nTempLen]; + nDestChars = rtl_convertTextToUnicode( hSrcConverter, 0, + mpData->maStr, mpData->mnLen, + pTempBuf, nTempLen, + RTL_TEXTTOUNICODE_FLAGS_FLUSH | + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_MAPTOPRIVATE | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT, + &nInfo, &nSrcBytes ); + rtl_destroyTextToUnicodeConverter( hSrcConverter ); + // Hier werten wir bReplace nicht aus, da fuer MultiByte-Textencodings + // sowieso keine Ersatzdarstellung moeglich ist. Da sich der String + // sowieso in der Laenge aendern kann, nehmen wir auch sonst keine + // Ruecksicht darauf, das die Laenge erhalten bleibt. + ImplUpdateStringFromUniString( pTempBuf, nDestChars, eTarget, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT | + RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT | + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE | + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACESTR | + RTL_UNICODETOTEXT_FLAGS_PRIVATE_MAPTO0 | + RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE | + RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE ); + delete [] pTempBuf; + } +} + +// ======================================================================= + +ByteString& ByteString::Convert( rtl_TextEncoding eSource, rtl_TextEncoding eTarget, BOOL bReplace ) +{ + DBG_CHKTHIS( ByteString, DbgCheckByteString ); + + // rtl_TextEncoding Dontknow kann nicht konvertiert werden + if ( (eSource == RTL_TEXTENCODING_DONTKNOW) || (eTarget == RTL_TEXTENCODING_DONTKNOW) ) + return *this; + + // Wenn Source und Target gleich sind, muss nicht konvertiert werden + if ( eSource == eTarget ) + return *this; + + // rtl_TextEncoding Symbol nur nach Unicode oder von Unicode wandeln, ansonsten + // wollen wir die Zeichencodes beibehalten + if ( (eSource == RTL_TEXTENCODING_SYMBOL) && + (eTarget != RTL_TEXTENCODING_UTF7) && + (eTarget != RTL_TEXTENCODING_UTF8) ) + return *this; + if ( (eTarget == RTL_TEXTENCODING_SYMBOL) && + (eSource != RTL_TEXTENCODING_UTF7) && + (eSource != RTL_TEXTENCODING_UTF8) ) + return *this; + + // Zeichensatz umwandeln + ImplStringConvert( eSource, eTarget, bReplace ); + + return *this; +} + +// ======================================================================= + +char ByteString::Convert( char c, + rtl_TextEncoding eSource, rtl_TextEncoding eTarget, + BOOL bReplace ) +{ + // TextEncoding Dontknow kann nicht konvertiert werden + if ( (eSource == RTL_TEXTENCODING_DONTKNOW) || (eTarget == RTL_TEXTENCODING_DONTKNOW) ) + return '\0'; + + // Wenn Source und Target gleich sind, muss nicht konvertiert werden + if ( eSource == eTarget ) + return c; + + // TextEncoding Symbol nur nach Unicode oder von Unicode wandeln, ansonsten + // wollen wir die Zeichencodes beibehalten + if ( (eSource == RTL_TEXTENCODING_SYMBOL) && + (eTarget != RTL_TEXTENCODING_UTF7) && + (eTarget != RTL_TEXTENCODING_UTF8) ) + return '\0'; + if ( (eTarget == RTL_TEXTENCODING_SYMBOL) && + (eSource != RTL_TEXTENCODING_UTF7) && + (eSource != RTL_TEXTENCODING_UTF8) ) + return '\0'; + + sal_uChar* pConvertTab = ImplGet1ByteConvertTab( eSource, eTarget, bReplace ); + if ( pConvertTab ) + return (char)pConvertTab[(sal_uChar)c]; + else + return '\0'; +} + +// ======================================================================= + +sal_Unicode ByteString::ConvertToUnicode( char c, rtl_TextEncoding eTextEncoding ) +{ + sal_Size nLen = 1; + return ConvertToUnicode( &c, &nLen, eTextEncoding ); +} + +// ----------------------------------------------------------------------- + +char ByteString::ConvertFromUnicode( sal_Unicode c, rtl_TextEncoding eTextEncoding, BOOL bReplace ) +{ + sal_Size nLen; + char aBuf[30]; + nLen = ConvertFromUnicode( c, aBuf, sizeof( aBuf ), eTextEncoding, bReplace ); + if ( nLen == 1 ) + return aBuf[0]; + else + return 0; +} + +// ----------------------------------------------------------------------- + +sal_Unicode ByteString::ConvertToUnicode( const char* pChar, sal_Size* pLen, rtl_TextEncoding eTextEncoding ) +{ + // TextEncoding Dontknow wird nicht konvertiert + if ( eTextEncoding == RTL_TEXTENCODING_DONTKNOW ) + return 0; + + rtl_TextToUnicodeConverter hConverter; + sal_uInt32 nInfo; + sal_Size nSrcBytes; + sal_Size nDestChars; + sal_Unicode nConvChar; + hConverter = rtl_createTextToUnicodeConverter( eTextEncoding ); + nDestChars = rtl_convertTextToUnicode( hConverter, 0, + (const sal_Char*)pChar, *pLen, + &nConvChar, 1, + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_FLUSH, + &nInfo, &nSrcBytes ); + rtl_destroyTextToUnicodeConverter( hConverter ); + + if ( nDestChars == 1 ) + { + *pLen = nSrcBytes; + return nConvChar; + } + else + { + *pLen = 0; + return 0; + } +} + +// ----------------------------------------------------------------------- + +sal_Size ByteString::ConvertFromUnicode( sal_Unicode c, char* pBuf, sal_Size nBufLen, rtl_TextEncoding eTextEncoding, + BOOL bReplace ) +{ + // TextEncoding Dontknow wird nicht konvertiert + if ( eTextEncoding == RTL_TEXTENCODING_DONTKNOW ) + return '\0'; + + rtl_UnicodeToTextConverter hConverter; + sal_uInt32 nInfo; + sal_Size nSrcChars; + sal_Size nDestBytes; + sal_Unicode cUni = c; + sal_uInt32 nFlags = RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE | + RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE | + RTL_UNICODETOTEXT_FLAGS_FLUSH; + if ( bReplace ) + { + nFlags |= RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT | + RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT; + nFlags |= RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE; + if ( nBufLen > 1 ) + nFlags |= RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACESTR; + } + else + { + nFlags |= RTL_UNICODETOTEXT_FLAGS_UNDEFINED_0 | + RTL_UNICODETOTEXT_FLAGS_INVALID_0; + } + + hConverter = rtl_createUnicodeToTextConverter( eTextEncoding ); + nDestBytes = rtl_convertUnicodeToText( hConverter, 0, + &cUni, 1, + (sal_Char*)pBuf, nBufLen, + nFlags, + &nInfo, &nSrcChars ); + rtl_destroyUnicodeToTextConverter( hConverter ); + return nDestBytes; +} + +// ======================================================================= + +ByteString::ByteString( const rtl::OString& rStr ) + : mpData(NULL) +{ + DBG_CTOR( ByteString, DbgCheckByteString ); + + OSL_ENSURE(rStr.pData->length < STRING_MAXLEN, + "Overflowing rtl::OString -> ByteString cut to zero length"); + + if (rStr.pData->length < STRING_MAXLEN) + { + mpData = reinterpret_cast< ByteStringData * >(const_cast< rtl::OString & >(rStr).pData); + STRING_ACQUIRE((STRING_TYPE *)mpData); + } + else + { + STRING_NEW((STRING_TYPE **)&mpData); + } +} + +// ----------------------------------------------------------------------- + +ByteString& ByteString::Assign( const rtl::OString& rStr ) +{ + DBG_CHKTHIS( ByteString, DbgCheckByteString ); + + OSL_ENSURE(rStr.pData->length < STRING_MAXLEN, + "Overflowing rtl::OString -> ByteString cut to zero length"); + + if (rStr.pData->length < STRING_MAXLEN) + { + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = reinterpret_cast< ByteStringData * >(const_cast< rtl::OString & >(rStr).pData); + STRING_ACQUIRE((STRING_TYPE *)mpData); + } + else + { + STRING_NEW((STRING_TYPE **)&mpData); + } + + return *this; +} diff --git a/tools/source/string/strimp.cxx b/tools/source/string/strimp.cxx new file mode 100644 index 000000000000..f6273aa2632f --- /dev/null +++ b/tools/source/string/strimp.cxx @@ -0,0 +1,2118 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: strimp.cxx,v $ + * $Revision: 1.12 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// no include "precompiled_tools.hxx" because this is included in other cxx files. + +// ======================================================================= + +static sal_Int32 ImplStringCompare( const STRCODE* pStr1, const STRCODE* pStr2 ) +{ + sal_Int32 nRet; + while ( ((nRet = ((sal_Int32)((STRCODEU)*pStr1))-((sal_Int32)((STRCODEU)*pStr2))) == 0) && + *pStr2 ) + { + ++pStr1, + ++pStr2; + } + + return nRet; +} + +// ----------------------------------------------------------------------- + +static sal_Int32 ImplStringCompare( const STRCODE* pStr1, const STRCODE* pStr2, + xub_StrLen nCount ) +{ + sal_Int32 nRet = 0; + while ( nCount && + ((nRet = ((sal_Int32)((STRCODEU)*pStr1))-((sal_Int32)((STRCODEU)*pStr2))) == 0) && + *pStr2 ) + { + ++pStr1, + ++pStr2, + --nCount; + } + + return nRet; +} + +// ----------------------------------------------------------------------- + +static sal_Int32 ImplStringCompareWithoutZero( const STRCODE* pStr1, const STRCODE* pStr2, + sal_Int32 nCount ) +{ + sal_Int32 nRet = 0; + while ( nCount && + ((nRet = ((sal_Int32)((STRCODEU)*pStr1))-((sal_Int32)((STRCODEU)*pStr2))) == 0) ) + { + ++pStr1, + ++pStr2, + --nCount; + } + + return nRet; +} + +// ----------------------------------------------------------------------- + +static sal_Int32 ImplStringICompare( const STRCODE* pStr1, const STRCODE* pStr2 ) +{ + sal_Int32 nRet; + STRCODE c1; + STRCODE c2; + do + { + // Ist das Zeichen zwischen 'A' und 'Z' dann umwandeln + c1 = *pStr1; + c2 = *pStr2; + if ( (c1 >= 65) && (c1 <= 90) ) + c1 += 32; + if ( (c2 >= 65) && (c2 <= 90) ) + c2 += 32; + nRet = ((sal_Int32)((STRCODEU)c1))-((sal_Int32)((STRCODEU)c2)); + if ( nRet != 0 ) + break; + + ++pStr1, + ++pStr2; + } + while ( c2 ); + + return nRet; +} + +// ----------------------------------------------------------------------- + +static sal_Int32 ImplStringICompare( const STRCODE* pStr1, const STRCODE* pStr2, + xub_StrLen nCount ) +{ + sal_Int32 nRet = 0; + STRCODE c1; + STRCODE c2; + do + { + if ( !nCount ) + break; + + // Ist das Zeichen zwischen 'A' und 'Z' dann umwandeln + c1 = *pStr1; + c2 = *pStr2; + if ( (c1 >= 65) && (c1 <= 90) ) + c1 += 32; + if ( (c2 >= 65) && (c2 <= 90) ) + c2 += 32; + nRet = ((sal_Int32)((STRCODEU)c1))-((sal_Int32)((STRCODEU)c2)); + if ( nRet != 0 ) + break; + + ++pStr1, + ++pStr2, + --nCount; + } + while ( c2 ); + + return nRet; +} + +// ----------------------------------------------------------------------- + +static sal_Int32 ImplStringICompareWithoutZero( const STRCODE* pStr1, const STRCODE* pStr2, + sal_Int32 nCount ) +{ + sal_Int32 nRet = 0; + STRCODE c1; + STRCODE c2; + do + { + if ( !nCount ) + break; + + // Ist das Zeichen zwischen 'A' und 'Z' dann umwandeln + c1 = *pStr1; + c2 = *pStr2; + if ( (c1 >= 65) && (c1 <= 90) ) + c1 += 32; + if ( (c2 >= 65) && (c2 <= 90) ) + c2 += 32; + nRet = ((sal_Int32)((STRCODEU)c1))-((sal_Int32)((STRCODEU)c2)); + + ++pStr1, + ++pStr2, + --nCount; + } + while ( nRet == 0 ); + + return nRet; +} + +// ======================================================================= + +#ifdef DBG_UTIL +const char* DBGCHECKSTRING( const void* pString ) +{ + STRING* p = (STRING*)pString; + + if ( p->GetBuffer()[p->Len()] != 0 ) + return "String damaged: aStr[nLen] != 0"; + + return NULL; +} +#endif + +// ======================================================================= + +static STRINGDATA* ImplAllocData( sal_Int32 nLen ) +{ + // Dann kopiere die Daten + STRINGDATA* pData = (STRINGDATA*)rtl_allocateMemory( sizeof(STRINGDATA)+(nLen*sizeof( STRCODE )) ); + pData->mnRefCount = 1; + pData->mnLen = nLen; + pData->maStr[nLen] = 0; + return pData; +} + +// ----------------------------------------------------------------------- + +static STRINGDATA* _ImplCopyData( STRINGDATA* pData ) +{ + unsigned int nSize = sizeof(STRINGDATA)+(pData->mnLen*sizeof( STRCODE )); + STRINGDATA* pNewData = (STRINGDATA*)rtl_allocateMemory( nSize ); + memcpy( pNewData, pData, nSize ); + pNewData->mnRefCount = 1; + STRING_RELEASE((STRING_TYPE *)pData); + return pNewData; +} + +// ----------------------------------------------------------------------- + +inline void STRING::ImplCopyData() +{ + DBG_ASSERT( (mpData->mnRefCount != 0), "String::ImplCopyData() - RefCount == 0" ); + + // ist es ein referenzierter String, dann die Daten abkoppeln + if ( mpData->mnRefCount != 1 ) + mpData = _ImplCopyData( mpData ); +} + +// ----------------------------------------------------------------------- + +inline STRCODE* STRING::ImplCopyStringData( STRCODE* pStr ) +{ + // Ist der Referenzzaehler groesser 0 + if ( mpData->mnRefCount != 1 ) { + DBG_ASSERT( (pStr >= mpData->maStr) && + ((pStr-mpData->maStr) < mpData->mnLen), + "ImplCopyStringData - pStr from other String-Instanz" ); + unsigned int nIndex = (unsigned int)(pStr-mpData->maStr); + mpData = _ImplCopyData( mpData ); + pStr = mpData->maStr + nIndex; + } + return pStr; +} + +// ----------------------------------------------------------------------- + +inline sal_Int32 ImplGetCopyLen( sal_Int32 nStrLen, sal_Int32 nCopyLen ) +{ + OSL_ASSERT(nStrLen <= STRING_MAXLEN && nCopyLen <= STRING_MAXLEN); + if ( nCopyLen > STRING_MAXLEN-nStrLen ) + nCopyLen = STRING_MAXLEN-nStrLen; + return nCopyLen; +} + +// ======================================================================= + +STRING::STRING() + : mpData(NULL) +{ + DBG_CTOR( STRING, DBGCHECKSTRING ); + + STRING_NEW((STRING_TYPE **)&mpData); +} + +// ----------------------------------------------------------------------- + +STRING::STRING( const STRING& rStr ) +{ + DBG_CTOR( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Pointer auf die Daten des uebergebenen Strings setzen und + // Referenzzaehler erhoehen + STRING_ACQUIRE((STRING_TYPE *)rStr.mpData); + mpData = rStr.mpData; +} + +// ----------------------------------------------------------------------- + +STRING::STRING( const STRING& rStr, xub_StrLen nPos, xub_StrLen nLen ) +: mpData( NULL ) +{ + DBG_CTOR( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Stringlaenge ermitteln + if ( nPos > rStr.mpData->mnLen ) + nLen = 0; + else + { + // Laenge korrigieren, wenn noetig + sal_Int32 nMaxLen = rStr.mpData->mnLen-nPos; + if ( nLen > nMaxLen ) + nLen = static_cast< xub_StrLen >(nMaxLen); + } + + // Ist es kein leerer String + if ( nLen ) + { + // Reicht ein einfaches erhoehen des Referenzcounters + if ( (nPos == 0) && (nLen == rStr.mpData->mnLen) ) + { + STRING_ACQUIRE((STRING_TYPE *)rStr.mpData); + mpData = rStr.mpData; + } + else + { + // Verwaltungsdaten anlegen und String kopieren + mpData = ImplAllocData( nLen ); + memcpy( mpData->maStr, rStr.mpData->maStr+nPos, nLen*sizeof( STRCODE ) ); + } + } + else + { + STRING_NEW((STRING_TYPE **)&mpData); + } +} + +// ----------------------------------------------------------------------- + +STRING::STRING( const STRCODE* pCharStr ) + : mpData(NULL) +{ + DBG_CTOR( STRING, DBGCHECKSTRING ); + + // Stringlaenge ermitteln + // Bei diesem Ctor darf NULL uebergeben werden + xub_StrLen nLen; + if ( pCharStr ) + nLen = ImplStringLen( pCharStr ); + else + nLen = 0; + + // Ist es kein leerer String + if ( nLen ) + { + // Verwaltungsdaten anlegen und String kopieren + mpData = ImplAllocData( nLen ); + memcpy( mpData->maStr, pCharStr, nLen*sizeof( STRCODE ) ); + } + else + { + STRING_NEW((STRING_TYPE **)&mpData); + } +} + +// ----------------------------------------------------------------------- + +STRING::STRING( const STRCODE* pCharStr, xub_StrLen nLen ) +: mpData(NULL) +{ + DBG_CTOR( STRING, DBGCHECKSTRING ); + DBG_ASSERT( pCharStr, "String::String() - pCharStr is NULL" ); + + if ( nLen == STRING_LEN ) + nLen = ImplStringLen( pCharStr ); + +#ifdef DBG_UTIL + if ( DbgIsAssert() ) + { + for ( xub_StrLen i = 0; i < nLen; i++ ) + { + if ( !pCharStr[i] ) + { + DBG_ERROR( "String::String() : nLen is wrong" ); + } + } + } +#endif + + // Ist es kein leerer String + if ( nLen ) + { + // Verwaltungsdaten anlegen und String kopieren + mpData = ImplAllocData( nLen ); + memcpy( mpData->maStr, pCharStr, nLen*sizeof( STRCODE ) ); + } + else + { + STRING_NEW((STRING_TYPE **)&mpData); + } +} + +// ----------------------------------------------------------------------- + +STRING::STRING( STRCODE c ) +{ + DBG_CTOR( STRING, DBGCHECKSTRING ); + DBG_ASSERT( c, "String::String() - c is 0" ); + + // Verwaltungsdaten anlegen und initialisieren + mpData = ImplAllocData( 1 ); + mpData->maStr[0] = c; +} + +// ----------------------------------------------------------------------- + +STRING::~STRING() +{ + DBG_DTOR( STRING, DBGCHECKSTRING ); + + // Daten loeschen + STRING_RELEASE((STRING_TYPE *)mpData); +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Assign( const STRING& rStr ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + STRING_ACQUIRE((STRING_TYPE *)rStr.mpData); + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = rStr.mpData; + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Assign( const STRCODE* pCharStr ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_ASSERT( pCharStr, "String::Assign() - pCharStr is NULL" ); + + // Stringlaenge ermitteln + xub_StrLen nLen = ImplStringLen( pCharStr ); + + if ( !nLen ) + { + STRING_NEW((STRING_TYPE **)&mpData); + } + else + { + // Wenn String genauso lang ist, wie der String, dann direkt kopieren + if ( (nLen == mpData->mnLen) && (mpData->mnRefCount == 1) ) + memcpy( mpData->maStr, pCharStr, nLen*sizeof( STRCODE ) ); + else + { + // Alte Daten loeschen + STRING_RELEASE((STRING_TYPE *)mpData); + + // Daten initialisieren und String kopieren + mpData = ImplAllocData( nLen ); + memcpy( mpData->maStr, pCharStr, nLen*sizeof( STRCODE ) ); + } + } + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Assign( const STRCODE* pCharStr, xub_StrLen nLen ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_ASSERT( pCharStr, "String::Assign() - pCharStr is NULL" ); + + if ( nLen == STRING_LEN ) + nLen = ImplStringLen( pCharStr ); + +#ifdef DBG_UTIL + if ( DbgIsAssert() ) + { + for ( xub_StrLen i = 0; i < nLen; i++ ) + { + if ( !pCharStr[i] ) + { + DBG_ERROR( "String::Assign() : nLen is wrong" ); + } + } + } +#endif + + if ( !nLen ) + { + STRING_NEW((STRING_TYPE **)&mpData); + } + else + { + // Wenn String genauso lang ist, wie der String, dann direkt kopieren + if ( (nLen == mpData->mnLen) && (mpData->mnRefCount == 1) ) + memcpy( mpData->maStr, pCharStr, nLen*sizeof( STRCODE ) ); + else + { + // Alte Daten loeschen + STRING_RELEASE((STRING_TYPE *)mpData); + + // Daten initialisieren und String kopieren + mpData = ImplAllocData( nLen ); + memcpy( mpData->maStr, pCharStr, nLen*sizeof( STRCODE ) ); + } + } + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Assign( STRCODE c ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_ASSERT( c, "String::Assign() - c is 0" ); + + // Verwaltungsdaten anlegen und initialisieren + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = ImplAllocData( 1 ); + mpData->maStr[0] = c; + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Append( const STRING& rStr ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Wenn String leer, dann reicht eine Zuweisung + sal_Int32 nLen = mpData->mnLen; + if ( !nLen ) + { + STRING_ACQUIRE((STRING_TYPE *)rStr.mpData); + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = rStr.mpData; + } + else + { + // Ueberlauf abfangen + sal_Int32 nCopyLen = ImplGetCopyLen( nLen, rStr.mpData->mnLen ); + + // Ist der uebergebene String kein Leerstring + if ( nCopyLen ) + { + // Neue Datenstruktur und neuen String erzeugen + STRINGDATA* pNewData = ImplAllocData( nLen+nCopyLen ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nLen*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nLen, rStr.mpData->maStr, nCopyLen*sizeof( STRCODE ) ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + } + } + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Append( const STRCODE* pCharStr ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_ASSERT( pCharStr, "String::Append() - pCharStr is NULL" ); + + // Stringlaenge ermitteln + sal_Int32 nLen = mpData->mnLen; + sal_Int32 nCopyLen = ImplStringLen( pCharStr ); + + // Ueberlauf abfangen + nCopyLen = ImplGetCopyLen( nLen, nCopyLen ); + + // Ist es kein leerer String + if ( nCopyLen ) + { + // Neue Datenstruktur und neuen String erzeugen + STRINGDATA* pNewData = ImplAllocData( nLen+nCopyLen ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nLen*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nLen, pCharStr, nCopyLen*sizeof( STRCODE ) ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + } + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Append( const STRCODE* pCharStr, xub_StrLen nCharLen ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_ASSERT( pCharStr, "String::Append() - pCharStr is NULL" ); + + if ( nCharLen == STRING_LEN ) + nCharLen = ImplStringLen( pCharStr ); + +#ifdef DBG_UTIL + if ( DbgIsAssert() ) + { + for ( xub_StrLen i = 0; i < nCharLen; i++ ) + { + if ( !pCharStr[i] ) + { + DBG_ERROR( "String::Append() : nLen is wrong" ); + } + } + } +#endif + + // Ueberlauf abfangen + sal_Int32 nLen = mpData->mnLen; + sal_Int32 nCopyLen = ImplGetCopyLen( nLen, nCharLen ); + + // Ist es kein leerer String + if ( nCopyLen ) + { + // Neue Datenstruktur und neuen String erzeugen + STRINGDATA* pNewData = ImplAllocData( nLen+nCopyLen ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nLen*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nLen, pCharStr, nCopyLen*sizeof( STRCODE ) ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + } + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Append( STRCODE c ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // kein 0-Character und maximale Stringlaenge nicht ueberschreiten + sal_Int32 nLen = mpData->mnLen; + if ( c && (nLen < STRING_MAXLEN) ) + { + // Neue Datenstruktur und neuen String erzeugen + STRINGDATA* pNewData = ImplAllocData( nLen+1 ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nLen*sizeof( STRCODE ) ); + pNewData->maStr[nLen] = c; + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + } + + return *this; +} + +// ----------------------------------------------------------------------- + +void STRING::SetChar( xub_StrLen nIndex, STRCODE c ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_ASSERT( nIndex < mpData->mnLen, "String::SetChar() - nIndex > String.Len()" ); + + // Daten kopieren, wenn noetig und Character zuweisen + ImplCopyData(); + mpData->maStr[nIndex] = c; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Insert( const STRING& rStr, xub_StrLen nIndex ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Ueberlauf abfangen + sal_Int32 nCopyLen = ImplGetCopyLen( mpData->mnLen, rStr.mpData->mnLen ); + + // Ist der einzufuegende String ein Leerstring + if ( !nCopyLen ) + return *this; + + // Index groesser als Laenge + if ( nIndex > mpData->mnLen ) + nIndex = static_cast< xub_StrLen >(mpData->mnLen); + + // Neue Laenge ermitteln und neuen String anlegen + STRINGDATA* pNewData = ImplAllocData( mpData->mnLen+nCopyLen ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nIndex*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nIndex, rStr.mpData->maStr, nCopyLen*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nIndex+nCopyLen, mpData->maStr+nIndex, + (mpData->mnLen-nIndex)*sizeof( STRCODE ) ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Insert( const STRING& rStr, xub_StrLen nPos, xub_StrLen nLen, + xub_StrLen nIndex ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Stringlaenge ermitteln + if ( nPos > rStr.mpData->mnLen ) + nLen = 0; + else + { + // Laenge korrigieren, wenn noetig + sal_Int32 nMaxLen = rStr.mpData->mnLen-nPos; + if ( nLen > nMaxLen ) + nLen = static_cast< xub_StrLen >(nMaxLen); + } + + // Ueberlauf abfangen + sal_Int32 nCopyLen = ImplGetCopyLen( mpData->mnLen, nLen ); + + // Ist der einzufuegende String ein Leerstring + if ( !nCopyLen ) + return *this; + + // Index groesser als Laenge + if ( nIndex > mpData->mnLen ) + nIndex = static_cast< xub_StrLen >(mpData->mnLen); + + // Neue Laenge ermitteln und neuen String anlegen + STRINGDATA* pNewData = ImplAllocData( mpData->mnLen+nCopyLen ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nIndex*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nIndex, rStr.mpData->maStr+nPos, nCopyLen*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nIndex+nCopyLen, mpData->maStr+nIndex, + (mpData->mnLen-nIndex)*sizeof( STRCODE ) ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Insert( const STRCODE* pCharStr, xub_StrLen nIndex ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_ASSERT( pCharStr, "String::Insert() - pCharStr is NULL" ); + + // Stringlaenge ermitteln + sal_Int32 nCopyLen = ImplStringLen( pCharStr ); + + // Ueberlauf abfangen + nCopyLen = ImplGetCopyLen( mpData->mnLen, nCopyLen ); + + // Ist der einzufuegende String ein Leerstring + if ( !nCopyLen ) + return *this; + + // Index groesser als Laenge + if ( nIndex > mpData->mnLen ) + nIndex = static_cast< xub_StrLen >(mpData->mnLen); + + // Neue Laenge ermitteln und neuen String anlegen + STRINGDATA* pNewData = ImplAllocData( mpData->mnLen+nCopyLen ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nIndex*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nIndex, pCharStr, nCopyLen*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nIndex+nCopyLen, mpData->maStr+nIndex, + (mpData->mnLen-nIndex)*sizeof( STRCODE ) ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Insert( STRCODE c, xub_StrLen nIndex ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // Ist es kein 0-Character + if ( !c || (mpData->mnLen == STRING_MAXLEN) ) + return *this; + + // Index groesser als Laenge + if ( nIndex > mpData->mnLen ) + nIndex = static_cast< xub_StrLen >(mpData->mnLen); + + // Neue Laenge ermitteln und neuen String anlegen + STRINGDATA* pNewData = ImplAllocData( mpData->mnLen+1 ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nIndex*sizeof( STRCODE ) ); + pNewData->maStr[nIndex] = c; + memcpy( pNewData->maStr+nIndex+1, mpData->maStr+nIndex, + (mpData->mnLen-nIndex)*sizeof( STRCODE ) ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Replace( xub_StrLen nIndex, xub_StrLen nCount, const STRING& rStr ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Wenn Index groessergleich Laenge ist, dann ist es ein Append + if ( nIndex >= mpData->mnLen ) + { + Append( rStr ); + return *this; + } + + // Ist es eine Zuweisung + if ( (nIndex == 0) && (nCount >= mpData->mnLen) ) + { + Assign( rStr ); + return *this; + } + + // Reicht ein Erase + sal_Int32 nStrLen = rStr.mpData->mnLen; + if ( !nStrLen ) + return Erase( nIndex, nCount ); + + // nCount darf nicht ueber das Stringende hinnausgehen + if ( nCount > mpData->mnLen - nIndex ) + nCount = static_cast< xub_StrLen >(mpData->mnLen-nIndex); + + // Reicht ein Insert + if ( !nCount ) + return Insert( rStr, nIndex ); + + // Reicht eine zeichenweise Zuweisung + if ( nCount == nStrLen ) + { + ImplCopyData(); + memcpy( mpData->maStr+nIndex, rStr.mpData->maStr, nCount*sizeof( STRCODE ) ); + return *this; + } + + // Ueberlauf abfangen + nStrLen = ImplGetCopyLen( mpData->mnLen-nCount, nStrLen ); + + // Neue Daten anlegen + STRINGDATA* pNewData = ImplAllocData( mpData->mnLen-nCount+nStrLen ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nIndex*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nIndex, rStr.mpData->maStr, nStrLen*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nIndex+nStrLen, mpData->maStr+nIndex+nCount, + (mpData->mnLen-nIndex-nCount+1)*sizeof( STRCODE ) ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Erase( xub_StrLen nIndex, xub_StrLen nCount ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // Ist der Index ausserhalb des Strings oder ist nCount == 0 + if ( (nIndex >= mpData->mnLen) || !nCount ) + return *this; + + // nCount darf nicht ueber das Stringende hinnausgehen + if ( nCount > mpData->mnLen - nIndex ) + nCount = static_cast< xub_StrLen >(mpData->mnLen-nIndex); + + // Ist das Ergebnis kein Leerstring + if ( mpData->mnLen - nCount ) + { + // Neue Daten anlegen + STRINGDATA* pNewData = ImplAllocData( mpData->mnLen-nCount ); + + // String kopieren + memcpy( pNewData->maStr, mpData->maStr, nIndex*sizeof( STRCODE ) ); + memcpy( pNewData->maStr+nIndex, mpData->maStr+nIndex+nCount, + (mpData->mnLen-nIndex-nCount+1)*sizeof( STRCODE ) ); + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + } + else + { + STRING_NEW((STRING_TYPE **)&mpData); + } + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Fill( xub_StrLen nCount, STRCODE cFillChar ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + if ( !nCount ) + return *this; + + // Ist nCount groesser wie der jetzige String, dann verlaengern + if ( nCount > mpData->mnLen ) + { + // dann neuen String mit der neuen Laenge anlegen + STRINGDATA* pNewData = ImplAllocData( nCount ); + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + } + else + ImplCopyData(); + + STRCODE* pStr = mpData->maStr; + do + { + *pStr = cFillChar; + ++pStr, + --nCount; + } + while ( nCount ); + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Expand( xub_StrLen nCount, STRCODE cExpandChar ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // Muss der String erweitert werden + sal_Int32 nLen = mpData->mnLen; + if ( nCount <= nLen ) + return *this; + + // Neuen String anlegen + STRINGDATA* pNewData = ImplAllocData( nCount ); + + // Alten String kopieren + memcpy( pNewData->maStr, mpData->maStr, nLen*sizeof( STRCODE ) ); + + // und initialisieren + STRCODE* pStr = pNewData->maStr; + pStr += nLen; + for (sal_Int32 i = nCount - nLen; i > 0; --i) { + *pStr++ = cExpandChar; + } + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::EraseLeadingChars( STRCODE c ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + if ( mpData->maStr[0] != c ) + return *this; + + xub_StrLen nStart = 0; + while ( mpData->maStr[nStart] == c ) + ++nStart; + + return Erase( 0, nStart ); +} + +// ----------------------------------------------------------------------- + +STRING& STRING::EraseTrailingChars( STRCODE c ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + sal_Int32 nEnd = mpData->mnLen; + while ( nEnd && (mpData->maStr[nEnd-1] == c) ) + nEnd--; + + if ( nEnd != mpData->mnLen ) + Erase( static_cast< xub_StrLen >(nEnd) ); + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::EraseLeadingAndTrailingChars( STRCODE c ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + xub_StrLen nStart = 0; + while ( mpData->maStr[nStart] == c ) + ++nStart; + if ( nStart ) + Erase( 0, nStart ); + + sal_Int32 nEnd = mpData->mnLen; + while ( nEnd && (mpData->maStr[nEnd-1] == c) ) + nEnd--; + if ( nEnd != mpData->mnLen ) + Erase( static_cast< xub_StrLen >(nEnd) ); + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::EraseAllChars( STRCODE c ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + sal_Int32 nCount = 0; + for (sal_Int32 i = 0; i < mpData->mnLen; ++i) { + if ( mpData->maStr[i] == c ) + ++nCount; + } + + if ( nCount ) + { + if ( nCount == mpData->mnLen ) + { + STRING_NEW((STRING_TYPE **)&mpData); + } + else + { + // Neuen String anlegen + STRINGDATA* pNewData = ImplAllocData( mpData->mnLen-nCount ); + + // Alten String kopieren und initialisieren + nCount = 0; + for( xub_StrLen j = 0; j < mpData->mnLen; ++j ) + { + if ( mpData->maStr[j] != c ) + { + pNewData->maStr[nCount] = mpData->maStr[j]; + ++nCount; + } + } + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + } + } + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::Reverse() +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + if ( !mpData->mnLen ) + return *this; + + // Daten kopieren, wenn noetig + ImplCopyData(); + + // Reverse + sal_Int32 nCount = mpData->mnLen / 2; + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + STRCODE cTemp = mpData->maStr[i]; + mpData->maStr[i] = mpData->maStr[mpData->mnLen-i-1]; + mpData->maStr[mpData->mnLen-i-1] = cTemp; + } + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::ToLowerAscii() +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + sal_Int32 nIndex = 0; + sal_Int32 nLen = mpData->mnLen; + STRCODE* pStr = mpData->maStr; + while ( nIndex < nLen ) + { + // Ist das Zeichen zwischen 'A' und 'Z' dann umwandeln + if ( (*pStr >= 65) && (*pStr <= 90) ) + { + // Daten kopieren, wenn noetig + pStr = ImplCopyStringData( pStr ); + *pStr += 32; + } + + ++pStr, + ++nIndex; + } + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::ToUpperAscii() +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + sal_Int32 nIndex = 0; + sal_Int32 nLen = mpData->mnLen; + STRCODE* pStr = mpData->maStr; + while ( nIndex < nLen ) + { + // Ist das Zeichen zwischen 'a' und 'z' dann umwandeln + if ( (*pStr >= 97) && (*pStr <= 122) ) + { + // Daten kopieren, wenn noetig + pStr = ImplCopyStringData( pStr ); + *pStr -= 32; + } + + ++pStr, + ++nIndex; + } + + return *this; +} + +// ----------------------------------------------------------------------- + +STRING& STRING::ConvertLineEnd( LineEnd eLineEnd ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // Zeilenumbrueche ermitteln und neue Laenge berechnen + BOOL bConvert = FALSE; // Muss konvertiert werden + const STRCODE* pStr = mpData->maStr; // damit es schneller geht + xub_StrLen nLineEndLen = (eLineEnd == LINEEND_CRLF) ? 2 : 1; + xub_StrLen nLen = 0; // Ziel-Laenge + xub_StrLen i = 0; // Source-Zaehler + + while ( i < mpData->mnLen ) + { + // Bei \r oder \n gibt es neuen Zeilenumbruch + if ( (pStr[i] == _CR) || (pStr[i] == _LF) ) + { + nLen = nLen + nLineEndLen; + + // Wenn schon gesetzt, dann brauchen wir keine aufwendige Abfrage + if ( !bConvert ) + { + // Muessen wir Konvertieren + if ( ((eLineEnd != LINEEND_LF) && (pStr[i] == _LF)) || + ((eLineEnd == LINEEND_CRLF) && (pStr[i+1] != _LF)) || + ((eLineEnd == LINEEND_LF) && + ((pStr[i] == _CR) || (pStr[i+1] == _CR))) || + ((eLineEnd == LINEEND_CR) && + ((pStr[i] == _LF) || (pStr[i+1] == _LF))) ) + bConvert = TRUE; + } + + // \r\n oder \n\r, dann Zeichen ueberspringen + if ( ((pStr[i+1] == _CR) || (pStr[i+1] == _LF)) && + (pStr[i] != pStr[i+1]) ) + ++i; + } + else + ++nLen; + ++i; + + // Wenn String zu lang, dann konvertieren wir nicht + if ( nLen >= STRING_MAXLEN ) + return *this; + } + + // Zeilenumbrueche konvertieren + if ( bConvert ) + { + // Neuen String anlegen + STRINGDATA* pNewData = ImplAllocData( nLen ); + xub_StrLen j = 0; + i = 0; + while ( i < mpData->mnLen ) + { + // Bei \r oder \n gibt es neuen Zeilenumbruch + if ( (pStr[i] == _CR) || (pStr[i] == _LF) ) + { + if ( eLineEnd == LINEEND_CRLF ) + { + pNewData->maStr[j] = _CR; + pNewData->maStr[j+1] = _LF; + j += 2; + } + else + { + if ( eLineEnd == LINEEND_CR ) + pNewData->maStr[j] = _CR; + else + pNewData->maStr[j] = _LF; + ++j; + } + + if ( ((pStr[i+1] == _CR) || (pStr[i+1] == _LF)) && + (pStr[i] != pStr[i+1]) ) + ++i; + } + else + { + pNewData->maStr[j] = mpData->maStr[i]; + ++j; + } + + ++i; + } + + // Alte Daten loeschen und Neue zuweisen + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + } + + return *this; +} + +// ----------------------------------------------------------------------- + +StringCompare STRING::CompareTo( const STRING& rStr, xub_StrLen nLen ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Auf Gleichheit der Pointer testen + if ( mpData == rStr.mpData ) + return COMPARE_EQUAL; + + // Maximale Laenge ermitteln + if ( mpData->mnLen < nLen ) + nLen = static_cast< xub_StrLen >(mpData->mnLen+1); + if ( rStr.mpData->mnLen < nLen ) + nLen = static_cast< xub_StrLen >(rStr.mpData->mnLen+1); + + // String vergleichen + sal_Int32 nCompare = ImplStringCompareWithoutZero( mpData->maStr, rStr.mpData->maStr, nLen ); + + // Rueckgabewert anpassen + if ( nCompare == 0 ) + return COMPARE_EQUAL; + else if ( nCompare < 0 ) + return COMPARE_LESS; + else + return COMPARE_GREATER; +} + +// ----------------------------------------------------------------------- + +StringCompare STRING::CompareTo( const STRCODE* pCharStr, xub_StrLen nLen ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // String vergleichen + sal_Int32 nCompare = ImplStringCompare( mpData->maStr, pCharStr, nLen ); + + // Rueckgabewert anpassen + if ( nCompare == 0 ) + return COMPARE_EQUAL; + else if ( nCompare < 0 ) + return COMPARE_LESS; + else + return COMPARE_GREATER; +} + +// ----------------------------------------------------------------------- + +StringCompare STRING::CompareIgnoreCaseToAscii( const STRING& rStr, + xub_StrLen nLen ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Auf Gleichheit der Pointer testen + if ( mpData == rStr.mpData ) + return COMPARE_EQUAL; + + // Maximale Laenge ermitteln + if ( mpData->mnLen < nLen ) + nLen = static_cast< xub_StrLen >(mpData->mnLen+1); + if ( rStr.mpData->mnLen < nLen ) + nLen = static_cast< xub_StrLen >(rStr.mpData->mnLen+1); + + // String vergleichen + sal_Int32 nCompare = ImplStringICompareWithoutZero( mpData->maStr, rStr.mpData->maStr, nLen ); + + // Rueckgabewert anpassen + if ( nCompare == 0 ) + return COMPARE_EQUAL; + else if ( nCompare < 0 ) + return COMPARE_LESS; + else + return COMPARE_GREATER; +} + +// ----------------------------------------------------------------------- + +StringCompare STRING::CompareIgnoreCaseToAscii( const STRCODE* pCharStr, + xub_StrLen nLen ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // String vergleichen + sal_Int32 nCompare = ImplStringICompare( mpData->maStr, pCharStr, nLen ); + + // Rueckgabewert anpassen + if ( nCompare == 0 ) + return COMPARE_EQUAL; + else if ( nCompare < 0 ) + return COMPARE_LESS; + else + return COMPARE_GREATER; +} + +// ----------------------------------------------------------------------- + +BOOL STRING::Equals( const STRING& rStr ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Sind die Daten gleich + if ( mpData == rStr.mpData ) + return TRUE; + + // Gleiche Laenge + if ( mpData->mnLen != rStr.mpData->mnLen ) + return FALSE; + + // String vergleichen + return (ImplStringCompareWithoutZero( mpData->maStr, rStr.mpData->maStr, mpData->mnLen ) == 0); +} + +// ----------------------------------------------------------------------- + +BOOL STRING::Equals( const STRCODE* pCharStr ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + return (ImplStringCompare( mpData->maStr, pCharStr ) == 0); +} + +// ----------------------------------------------------------------------- + +BOOL STRING::EqualsIgnoreCaseAscii( const STRING& rStr ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Sind die Daten gleich + if ( mpData == rStr.mpData ) + return TRUE; + + // Gleiche Laenge + if ( mpData->mnLen != rStr.mpData->mnLen ) + return FALSE; + + // String vergleichen + return (ImplStringICompareWithoutZero( mpData->maStr, rStr.mpData->maStr, mpData->mnLen ) == 0); +} + +// ----------------------------------------------------------------------- + +BOOL STRING::EqualsIgnoreCaseAscii( const STRCODE* pCharStr ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + return (ImplStringICompare( mpData->maStr, pCharStr ) == 0); +} + +// ----------------------------------------------------------------------- + +BOOL STRING::Equals( const STRING& rStr, xub_StrLen nIndex, xub_StrLen nLen ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Are there enough codes for comparing? + if ( nIndex > mpData->mnLen ) + return (rStr.mpData->mnLen == 0); + sal_Int32 nMaxLen = mpData->mnLen-nIndex; + if ( nMaxLen < nLen ) + { + if ( rStr.mpData->mnLen != nMaxLen ) + return FALSE; + nLen = static_cast< xub_StrLen >(nMaxLen); + } + + // String vergleichen + return (ImplStringCompareWithoutZero( mpData->maStr+nIndex, rStr.mpData->maStr, nLen ) == 0); +} + +// ----------------------------------------------------------------------- + +BOOL STRING::Equals( const STRCODE* pCharStr, xub_StrLen nIndex, xub_StrLen nLen ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // Are there enough codes for comparing? + if ( nIndex > mpData->mnLen ) + return (*pCharStr == 0); + + return (ImplStringCompare( mpData->maStr+nIndex, pCharStr, nLen ) == 0); +} + +// ----------------------------------------------------------------------- + +BOOL STRING::EqualsIgnoreCaseAscii( const STRING& rStr, xub_StrLen nIndex, xub_StrLen nLen ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Are there enough codes for comparing? + if ( nIndex > mpData->mnLen ) + return (rStr.mpData->mnLen == 0); + sal_Int32 nMaxLen = mpData->mnLen-nIndex; + if ( nMaxLen < nLen ) + { + if ( rStr.mpData->mnLen != nMaxLen ) + return FALSE; + nLen = static_cast< xub_StrLen >(nMaxLen); + } + + // String vergleichen + return (ImplStringICompareWithoutZero( mpData->maStr+nIndex, rStr.mpData->maStr, nLen ) == 0); +} + +// ----------------------------------------------------------------------- + +BOOL STRING::EqualsIgnoreCaseAscii( const STRCODE* pCharStr, xub_StrLen nIndex, xub_StrLen nLen ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // Are there enough codes for comparing? + if ( nIndex > mpData->mnLen ) + return (*pCharStr == 0); + + return (ImplStringICompare( mpData->maStr+nIndex, pCharStr, nLen ) == 0); +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::Match( const STRING& rStr ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + // Ist dieser String leer + if ( !mpData->mnLen ) + return STRING_MATCH; + + // Suche bis Stringende nach dem ersten nicht uebereinstimmenden Zeichen + const STRCODE* pStr1 = mpData->maStr; + const STRCODE* pStr2 = rStr.mpData->maStr; + xub_StrLen i = 0; + while ( i < mpData->mnLen ) + { + // Stimmt das Zeichen nicht ueberein, dann abbrechen + if ( *pStr1 != *pStr2 ) + return i; + ++pStr1, + ++pStr2, + ++i; + } + + return STRING_MATCH; +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::Match( const STRCODE* pCharStr ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // Ist dieser String leer + if ( !mpData->mnLen ) + return STRING_MATCH; + + // Suche bis Stringende nach dem ersten nicht uebereinstimmenden Zeichen + const STRCODE* pStr = mpData->maStr; + xub_StrLen i = 0; + while ( i < mpData->mnLen ) + { + // Stimmt das Zeichen nicht ueberein, dann abbrechen + if ( *pStr != *pCharStr ) + return i; + ++pStr, + ++pCharStr, + ++i; + } + + return STRING_MATCH; +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::Search( STRCODE c, xub_StrLen nIndex ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + sal_Int32 nLen = mpData->mnLen; + const STRCODE* pStr = mpData->maStr; + pStr += nIndex; + while ( nIndex < nLen ) + { + if ( *pStr == c ) + return nIndex; + ++pStr, + ++nIndex; + } + + return STRING_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::Search( const STRING& rStr, xub_StrLen nIndex ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + sal_Int32 nLen = mpData->mnLen; + sal_Int32 nStrLen = rStr.mpData->mnLen; + + // Falls die Laenge des uebergebenen Strings 0 ist oder der Index + // hinter dem String liegt, dann wurde der String nicht gefunden + if ( !nStrLen || (nIndex >= nLen) ) + return STRING_NOTFOUND; + + const STRCODE* pStr1 = mpData->maStr; + pStr1 += nIndex; + + if ( nStrLen == 1 ) + { + STRCODE cSearch = rStr.mpData->maStr[0]; + while ( nIndex < nLen ) + { + if ( *pStr1 == cSearch ) + return nIndex; + ++pStr1, + ++nIndex; + } + } + else + { + const STRCODE* pStr2 = rStr.mpData->maStr; + + // Nur innerhalb des Strings suchen + while ( nLen - nIndex >= nStrLen ) + { + // Stimmt der String ueberein + if ( ImplStringCompareWithoutZero( pStr1, pStr2, nStrLen ) == 0 ) + return nIndex; + ++pStr1, + ++nIndex; + } + } + + return STRING_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::Search( const STRCODE* pCharStr, xub_StrLen nIndex ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + sal_Int32 nLen = mpData->mnLen; + xub_StrLen nStrLen = ImplStringLen( pCharStr ); + + // Falls die Laenge des uebergebenen Strings 0 ist oder der Index + // hinter dem String liegt, dann wurde der String nicht gefunden + if ( !nStrLen || (nIndex >= nLen) ) + return STRING_NOTFOUND; + + const STRCODE* pStr = mpData->maStr; + pStr += nIndex; + + if ( nStrLen == 1 ) + { + STRCODE cSearch = *pCharStr; + while ( nIndex < nLen ) + { + if ( *pStr == cSearch ) + return nIndex; + ++pStr, + ++nIndex; + } + } + else + { + // Nur innerhalb des Strings suchen + while ( nLen - nIndex >= nStrLen ) + { + // Stimmt der String ueberein + if ( ImplStringCompareWithoutZero( pStr, pCharStr, nStrLen ) == 0 ) + return nIndex; + ++pStr, + ++nIndex; + } + } + + return STRING_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::SearchBackward( STRCODE c, xub_StrLen nIndex ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + if ( nIndex > mpData->mnLen ) + nIndex = (xub_StrLen)mpData->mnLen; + + const STRCODE* pStr = mpData->maStr; + pStr += nIndex; + + while ( nIndex ) + { + nIndex--; + pStr--; + if ( *pStr == c ) + return nIndex; + } + + return STRING_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::SearchChar( const STRCODE* pChars, xub_StrLen nIndex ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + sal_Int32 nLen = mpData->mnLen; + const STRCODE* pStr = mpData->maStr; + pStr += nIndex; + while ( nIndex < nLen ) + { + STRCODE c = *pStr; + const STRCODE* pCompStr = pChars; + while ( *pCompStr ) + { + if ( *pCompStr == c ) + return nIndex; + ++pCompStr; + } + ++pStr, + ++nIndex; + } + + return STRING_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::SearchCharBackward( const STRCODE* pChars, xub_StrLen nIndex ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + if ( nIndex > mpData->mnLen ) + nIndex = (xub_StrLen)mpData->mnLen; + + const STRCODE* pStr = mpData->maStr; + pStr += nIndex; + + while ( nIndex ) + { + nIndex--; + pStr--; + + STRCODE c =*pStr; + const STRCODE* pCompStr = pChars; + while ( *pCompStr ) + { + if ( *pCompStr == c ) + return nIndex; + ++pCompStr; + } + } + + return STRING_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::SearchAndReplace( STRCODE c, STRCODE cRep, xub_StrLen nIndex ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + sal_Int32 nLen = mpData->mnLen; + const STRCODE* pStr = mpData->maStr; + pStr += nIndex; + while ( nIndex < nLen ) + { + if ( *pStr == c ) + { + ImplCopyData(); + mpData->maStr[nIndex] = cRep; + return nIndex; + } + ++pStr, + ++nIndex; + } + + return STRING_NOTFOUND; +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::SearchAndReplace( const STRING& rStr, const STRING& rRepStr, + xub_StrLen nIndex ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rRepStr, STRING, DBGCHECKSTRING ); + + xub_StrLen nSPos = Search( rStr, nIndex ); + if ( nSPos != STRING_NOTFOUND ) + Replace( nSPos, rStr.Len(), rRepStr ); + + return nSPos; +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::SearchAndReplace( const STRCODE* pCharStr, const STRING& rRepStr, + xub_StrLen nIndex ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rRepStr, STRING, DBGCHECKSTRING ); + + xub_StrLen nSPos = Search( pCharStr, nIndex ); + if ( nSPos != STRING_NOTFOUND ) + Replace( nSPos, ImplStringLen( pCharStr ), rRepStr ); + + return nSPos; +} + +// ----------------------------------------------------------------------- + +void STRING::SearchAndReplaceAll( STRCODE c, STRCODE cRep ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + sal_Int32 nLen = mpData->mnLen; + const STRCODE* pStr = mpData->maStr; + sal_Int32 nIndex = 0; + while ( nIndex < nLen ) + { + if ( *pStr == c ) + { + ImplCopyData(); + mpData->maStr[nIndex] = cRep; + } + ++pStr, + ++nIndex; + } +} + +// ----------------------------------------------------------------------- + +void STRING::SearchAndReplaceAll( const STRCODE* pCharStr, const STRING& rRepStr ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rRepStr, STRING, DBGCHECKSTRING ); + + xub_StrLen nCharLen = ImplStringLen( pCharStr ); + xub_StrLen nSPos = Search( pCharStr, 0 ); + while ( nSPos != STRING_NOTFOUND ) + { + Replace( nSPos, nCharLen, rRepStr ); + nSPos = nSPos + rRepStr.Len(); + nSPos = Search( pCharStr, nSPos ); + } +} + +// ----------------------------------------------------------------------- + +void STRING::SearchAndReplaceAll( const STRING& rStr, const STRING& rRepStr ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rRepStr, STRING, DBGCHECKSTRING ); + + xub_StrLen nSPos = Search( rStr, 0 ); + while ( nSPos != STRING_NOTFOUND ) + { + Replace( nSPos, rStr.Len(), rRepStr ); + nSPos = nSPos + rRepStr.Len(); + nSPos = Search( rStr, nSPos ); + } +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::GetTokenCount( STRCODE cTok ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // Leerer String: TokenCount per Definition 0 + if ( !mpData->mnLen ) + return 0; + + xub_StrLen nTokCount = 1; + sal_Int32 nLen = mpData->mnLen; + const STRCODE* pStr = mpData->maStr; + sal_Int32 nIndex = 0; + while ( nIndex < nLen ) + { + // Stimmt das Tokenzeichen ueberein, dann erhoehe TokCount + if ( *pStr == cTok ) + ++nTokCount; + ++pStr, + ++nIndex; + } + + return nTokCount; +} + +// ----------------------------------------------------------------------- + +void STRING::SetToken( xub_StrLen nToken, STRCODE cTok, const STRING& rStr, + xub_StrLen nIndex ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rStr, STRING, DBGCHECKSTRING ); + + const STRCODE* pStr = mpData->maStr; + xub_StrLen nLen = (xub_StrLen)mpData->mnLen; + xub_StrLen nTok = 0; + xub_StrLen nFirstChar = nIndex; + xub_StrLen i = nFirstChar; + + // Bestimme die Token-Position und Laenge + pStr += i; + while ( i < nLen ) + { + // Stimmt das Tokenzeichen ueberein, dann erhoehe TokCount + if ( *pStr == cTok ) + { + ++nTok; + + if ( nTok == nToken ) + nFirstChar = i+1; + else + { + if ( nTok > nToken ) + break; + } + } + + ++pStr, + ++i; + } + + if ( nTok >= nToken ) + Replace( nFirstChar, i-nFirstChar, rStr ); +} + +// ----------------------------------------------------------------------- + +STRING STRING::GetToken( xub_StrLen nToken, STRCODE cTok, xub_StrLen& rIndex ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + const STRCODE* pStr = mpData->maStr; + xub_StrLen nLen = (xub_StrLen)mpData->mnLen; + xub_StrLen nTok = 0; + xub_StrLen nFirstChar = rIndex; + xub_StrLen i = nFirstChar; + + // Bestimme die Token-Position und Laenge + pStr += i; + while ( i < nLen ) + { + // Stimmt das Tokenzeichen ueberein, dann erhoehe TokCount + if ( *pStr == cTok ) + { + ++nTok; + + if ( nTok == nToken ) + nFirstChar = i+1; + else + { + if ( nTok > nToken ) + break; + } + } + + ++pStr, + ++i; + } + + if ( nTok >= nToken ) + { + if ( i < nLen ) + rIndex = i+1; + else + rIndex = STRING_NOTFOUND; + return Copy( nFirstChar, i-nFirstChar ); + } + else + { + rIndex = STRING_NOTFOUND; + return STRING(); + } +} + +// ----------------------------------------------------------------------- + +xub_StrLen STRING::GetQuotedTokenCount( const STRING& rQuotedPairs, STRCODE cTok ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rQuotedPairs, STRING, DBGCHECKSTRING ); + DBG_ASSERT( !(rQuotedPairs.Len()%2), "String::GetQuotedTokenCount() - QuotedString%2 != 0" ); + DBG_ASSERT( rQuotedPairs.Search(cTok) == STRING_NOTFOUND, "String::GetQuotedTokenCount() - cTok in QuotedString" ); + + // Leerer String: TokenCount per Definition 0 + if ( !mpData->mnLen ) + return 0; + + xub_StrLen nTokCount = 1; + sal_Int32 nLen = mpData->mnLen; + xub_StrLen nQuotedLen = rQuotedPairs.Len(); + STRCODE cQuotedEndChar = 0; + const STRCODE* pQuotedStr = rQuotedPairs.mpData->maStr; + const STRCODE* pStr = mpData->maStr; + sal_Int32 nIndex = 0; + while ( nIndex < nLen ) + { + STRCODE c = *pStr; + if ( cQuotedEndChar ) + { + // Ende des Quotes erreicht ? + if ( c == cQuotedEndChar ) + cQuotedEndChar = 0; + } + else + { + // Ist das Zeichen ein Quote-Anfang-Zeichen ? + xub_StrLen nQuoteIndex = 0; + while ( nQuoteIndex < nQuotedLen ) + { + if ( pQuotedStr[nQuoteIndex] == c ) + { + cQuotedEndChar = pQuotedStr[nQuoteIndex+1]; + break; + } + else + nQuoteIndex += 2; + } + + // Stimmt das Tokenzeichen ueberein, dann erhoehe TokCount + if ( c == cTok ) + ++nTokCount; + } + + ++pStr, + ++nIndex; + } + + return nTokCount; +} + +// ----------------------------------------------------------------------- + +STRING STRING::GetQuotedToken( xub_StrLen nToken, const STRING& rQuotedPairs, + STRCODE cTok, xub_StrLen& rIndex ) const +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + DBG_CHKOBJ( &rQuotedPairs, STRING, DBGCHECKSTRING ); + DBG_ASSERT( !(rQuotedPairs.Len()%2), "String::GetQuotedToken() - QuotedString%2 != 0" ); + DBG_ASSERT( rQuotedPairs.Search(cTok) == STRING_NOTFOUND, "String::GetQuotedToken() - cTok in QuotedString" ); + + const STRCODE* pStr = mpData->maStr; + const STRCODE* pQuotedStr = rQuotedPairs.mpData->maStr; + STRCODE cQuotedEndChar = 0; + xub_StrLen nQuotedLen = rQuotedPairs.Len(); + xub_StrLen nLen = (xub_StrLen)mpData->mnLen; + xub_StrLen nTok = 0; + xub_StrLen nFirstChar = rIndex; + xub_StrLen i = nFirstChar; + + // Bestimme die Token-Position und Laenge + pStr += i; + while ( i < nLen ) + { + STRCODE c = *pStr; + if ( cQuotedEndChar ) + { + // Ende des Quotes erreicht ? + if ( c == cQuotedEndChar ) + cQuotedEndChar = 0; + } + else + { + // Ist das Zeichen ein Quote-Anfang-Zeichen ? + xub_StrLen nQuoteIndex = 0; + while ( nQuoteIndex < nQuotedLen ) + { + if ( pQuotedStr[nQuoteIndex] == c ) + { + cQuotedEndChar = pQuotedStr[nQuoteIndex+1]; + break; + } + else + nQuoteIndex += 2; + } + + // Stimmt das Tokenzeichen ueberein, dann erhoehe TokCount + if ( c == cTok ) + { + ++nTok; + + if ( nTok == nToken ) + nFirstChar = i+1; + else + { + if ( nTok > nToken ) + break; + } + } + } + + ++pStr, + ++i; + } + + if ( nTok >= nToken ) + { + if ( i < nLen ) + rIndex = i+1; + else + rIndex = STRING_NOTFOUND; + return Copy( nFirstChar, i-nFirstChar ); + } + else + { + rIndex = STRING_NOTFOUND; + return STRING(); + } +} + +// ----------------------------------------------------------------------- + +STRCODE* STRING::GetBufferAccess() +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + // Daten kopieren, wenn noetig + if ( mpData->mnLen ) + ImplCopyData(); + + // Pointer auf den String zurueckgeben + return mpData->maStr; +} + +// ----------------------------------------------------------------------- + +void STRING::ReleaseBufferAccess( xub_StrLen nLen ) +{ + // Hier ohne Funktionstest, da String nicht konsistent + DBG_CHKTHIS( STRING, NULL ); + DBG_ASSERT( mpData->mnRefCount == 1, "String::ReleaseCharStr() called for String with RefCount" ); + + if ( nLen > mpData->mnLen ) + nLen = ImplStringLen( mpData->maStr ); + OSL_ASSERT(nLen <= mpData->mnLen); + if ( !nLen ) + { + STRING_NEW((STRING_TYPE **)&mpData); + } + // Bei mehr als 8 Zeichen unterschied, kuerzen wir den Buffer + else if ( mpData->mnLen - nLen > 8 ) + { + STRINGDATA* pNewData = ImplAllocData( nLen ); + memcpy( pNewData->maStr, mpData->maStr, nLen*sizeof( STRCODE ) ); + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = pNewData; + } + else + mpData->mnLen = nLen; +} + +// ----------------------------------------------------------------------- + +STRCODE* STRING::AllocBuffer( xub_StrLen nLen ) +{ + DBG_CHKTHIS( STRING, DBGCHECKSTRING ); + + STRING_RELEASE((STRING_TYPE *)mpData); + if ( nLen ) + mpData = ImplAllocData( nLen ); + else + { + mpData = NULL; + STRING_NEW((STRING_TYPE **)&mpData); + } + + return mpData->maStr; +} diff --git a/tools/source/string/strucvt.cxx b/tools/source/string/strucvt.cxx new file mode 100644 index 000000000000..d2b79e7c4e3f --- /dev/null +++ b/tools/source/string/strucvt.cxx @@ -0,0 +1,214 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: strucvt.cxx,v $ + * $Revision: 1.16 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// no include "precompiled_tools.hxx" because this is included in other cxx files. + +// ======================================================================= + +void UniString::InitStringRes( const char* pUTF8Str, sal_Int32 nLen ) +{ + DBG_CTOR( UniString, DbgCheckUniString ); + OSL_ENSURE(nLen <= STRING_MAXLEN, "Overflowing UniString"); + + mpData = NULL; + rtl_string2UString( (rtl_uString **)(&mpData), + pUTF8Str, nLen, + RTL_TEXTENCODING_UTF8, + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_MAPTOPRIVATE | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT ); +} + +// ======================================================================= + +UniString::UniString( const ByteString& rByteStr, rtl_TextEncoding eTextEncoding, sal_uInt32 nCvtFlags ) +{ + DBG_CTOR( UniString, DbgCheckUniString ); + DBG_CHKOBJ( &rByteStr, ByteString, DbgCheckByteString ); + + mpData = NULL; + rtl_string2UString( (rtl_uString **)(&mpData), + rByteStr.mpData->maStr, rByteStr.mpData->mnLen, + eTextEncoding, nCvtFlags ); +} + +// ----------------------------------------------------------------------- + +UniString::UniString( const ByteString& rByteStr, xub_StrLen nPos, xub_StrLen nLen, + rtl_TextEncoding eTextEncoding, sal_uInt32 nCvtFlags ) +{ + DBG_CTOR( UniString, DbgCheckUniString ); + DBG_CHKOBJ( &rByteStr, ByteString, DbgCheckByteString ); + + // Stringlaenge ermitteln + if ( nPos > rByteStr.mpData->mnLen ) + nLen = 0; + else + { + // Laenge korrigieren, wenn noetig + sal_Int32 nMaxLen = rByteStr.mpData->mnLen-nPos; + if ( nLen > nMaxLen ) + nLen = static_cast< xub_StrLen >(nMaxLen); + } + + mpData = NULL; + rtl_string2UString( (rtl_uString **)(&mpData), + rByteStr.mpData->maStr+nPos, nLen, + eTextEncoding, nCvtFlags ); +} + +// ----------------------------------------------------------------------- + +UniString::UniString( const char* pByteStr, + rtl_TextEncoding eTextEncoding, sal_uInt32 nCvtFlags ) +{ + DBG_CTOR( UniString, DbgCheckUniString ); + DBG_ASSERT( pByteStr, "UniString::UniString() - pByteStr is NULL" ); + + mpData = NULL; + rtl_string2UString( (rtl_uString **)(&mpData), + pByteStr, ImplStringLen( pByteStr ), + eTextEncoding, nCvtFlags ); +} + +// ----------------------------------------------------------------------- + +UniString::UniString( const char* pByteStr, xub_StrLen nLen, + rtl_TextEncoding eTextEncoding, sal_uInt32 nCvtFlags ) +{ + DBG_CTOR( UniString, DbgCheckUniString ); + DBG_ASSERT( pByteStr, "UniString::UniString() - pByteStr is NULL" ); + + if ( nLen == STRING_LEN ) + nLen = ImplStringLen( pByteStr ); + + mpData = NULL; + rtl_string2UString( (rtl_uString **)(&mpData), + pByteStr, nLen, + eTextEncoding, nCvtFlags ); +} + +// ======================================================================= + +UniString::UniString( const rtl::OUString& rStr ) + : mpData(NULL) +{ + DBG_CTOR( UniString, DbgCheckUniString ); + + OSL_ENSURE(rStr.pData->length < STRING_MAXLEN, + "Overflowing rtl::OUString -> UniString cut to zero length"); + + + if (rStr.pData->length < STRING_MAXLEN) + { + mpData = reinterpret_cast< UniStringData * >(const_cast< rtl::OUString & >(rStr).pData); + STRING_ACQUIRE((STRING_TYPE *)mpData); + } + else + { + STRING_NEW((STRING_TYPE **)&mpData); + } +} + +// ----------------------------------------------------------------------- + +UniString& UniString::Assign( const rtl::OUString& rStr ) +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + + OSL_ENSURE(rStr.pData->length < STRING_MAXLEN, + "Overflowing rtl::OUString -> UniString cut to zero length"); + + + if (rStr.pData->length < STRING_MAXLEN) + { + STRING_RELEASE((STRING_TYPE *)mpData); + mpData = reinterpret_cast< UniStringData * >(const_cast< rtl::OUString & >(rStr).pData); + STRING_ACQUIRE((STRING_TYPE *)mpData); + } + else + { + STRING_NEW((STRING_TYPE **)&mpData); + } + + return *this; +} + +UniString UniString::intern() const +{ + UniString aStr; + + rtl_uString_intern( reinterpret_cast<rtl_uString **>(&aStr.mpData), + (rtl_uString *)(mpData) ); + + return aStr; +} + +// ======================================================================= + +#include <tools/rc.hxx> +#include <tools/rcid.h> + +UniString::UniString( const ResId& rResId ) +{ + rResId.SetRT( RSC_STRING ); + ResMgr* pResMgr = rResId.GetResMgr(); + mpData = NULL; + if ( pResMgr && pResMgr->GetResource( rResId ) ) + { + // String laden + RSHEADER_TYPE * pResHdr = (RSHEADER_TYPE*)pResMgr->GetClass(); + //sal_uInt32 nLen = pResHdr->GetLocalOff() - sizeof( RSHEADER_TYPE ); + + sal_Int32 nStringLen = rtl_str_getLength( (char*)(pResHdr+1) ); + InitStringRes( (const char*)(pResHdr+1), nStringLen ); + + sal_uInt32 nSize = sizeof( RSHEADER_TYPE ) + + sal::static_int_cast< sal_uInt32 >(nStringLen) + 1; + nSize += nSize % 2; + pResMgr->Increment( nSize ); + } + else + { + STRING_NEW((STRING_TYPE **)&mpData); + +#if OSL_DEBUG_LEVEL > 0 + *this = UniString::CreateFromAscii( "<resource id " ); + Append( UniString::CreateFromInt32( rResId.GetId() ) ); + AppendAscii( " not found>" ); +#endif + } + + + ResHookProc pImplResHookProc = ResMgr::GetReadStringHook(); + if ( pImplResHookProc ) + pImplResHookProc( *this ); +} + diff --git a/tools/source/string/tenccvt.cxx b/tools/source/string/tenccvt.cxx new file mode 100644 index 000000000000..91b0c3810c58 --- /dev/null +++ b/tools/source/string/tenccvt.cxx @@ -0,0 +1,100 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: tenccvt.cxx,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" +#include <rtl/tencinfo.h> +#include <tools/tenccvt.hxx> + +// ======================================================================= + +rtl_TextEncoding GetExtendedCompatibilityTextEncoding( rtl_TextEncoding eEncoding ) +{ + // Latin1 + if ( eEncoding == RTL_TEXTENCODING_ISO_8859_1 ) + return RTL_TEXTENCODING_MS_1252; + // Turkey + else if ( eEncoding == RTL_TEXTENCODING_ISO_8859_9 ) + return RTL_TEXTENCODING_MS_1254; + else + return eEncoding; +} + +// ----------------------------------------------------------------------- + +rtl_TextEncoding GetExtendedTextEncoding( rtl_TextEncoding eEncoding ) +{ + // Cyr + if ( eEncoding == RTL_TEXTENCODING_ISO_8859_5 ) + return RTL_TEXTENCODING_MS_1251; + // Greek (2 Characters different: A1 (0x2018/0x0385), A2 (0x2019/0x0386) - + // so it is handled in this function and not in GetExtendedCompatibilityTextEncoding) + else if ( eEncoding == RTL_TEXTENCODING_ISO_8859_7 ) + return RTL_TEXTENCODING_MS_1253; + // East-Europe - Latin2 + else if ( eEncoding == RTL_TEXTENCODING_ISO_8859_2 ) + return RTL_TEXTENCODING_MS_1250; + // Latin-15 - Latin 1 mit Euro-Sign + else if ( eEncoding == RTL_TEXTENCODING_ISO_8859_15 ) + return RTL_TEXTENCODING_MS_1252; + else + return GetExtendedCompatibilityTextEncoding( eEncoding ); +} + +// ----------------------------------------------------------------------- + +rtl_TextEncoding GetOneByteTextEncoding( rtl_TextEncoding eEncoding ) +{ + rtl_TextEncodingInfo aTextEncInfo; + aTextEncInfo.StructSize = sizeof( aTextEncInfo ); + if ( rtl_getTextEncodingInfo( eEncoding, &aTextEncInfo ) ) + { + if ( aTextEncInfo.MaximumCharSize > 1 ) + return RTL_TEXTENCODING_MS_1252; + else + return eEncoding; + } + else + return RTL_TEXTENCODING_MS_1252; +} + +// ----------------------------------------------------------------------- + +rtl_TextEncoding GetSOLoadTextEncoding( rtl_TextEncoding eEncoding, USHORT /* nVersion = SOFFICE_FILEFORMAT_50 */ ) +{ + return GetExtendedCompatibilityTextEncoding( GetOneByteTextEncoding( eEncoding ) ); +} + +// ----------------------------------------------------------------------- + +rtl_TextEncoding GetSOStoreTextEncoding( rtl_TextEncoding eEncoding, USHORT /* nVersion = SOFFICE_FILEFORMAT_50 */ ) +{ + return GetExtendedTextEncoding( GetOneByteTextEncoding( eEncoding ) ); +} diff --git a/tools/source/string/tstring.cxx b/tools/source/string/tstring.cxx new file mode 100644 index 000000000000..9c6b3daac02c --- /dev/null +++ b/tools/source/string/tstring.cxx @@ -0,0 +1,298 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: tstring.cxx,v $ + * $Revision: 1.11 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <string.h> + +#include "boost/static_assert.hpp" + +#include "osl/diagnose.h" +#ifndef _OSL_INTERLCK_H +#include <osl/interlck.h> +#endif +#ifndef _RTL_ALLOC_H +#include <rtl/alloc.h> +#endif +#ifndef _RTL_MEMORY_H +#include <rtl/memory.h> +#endif +#include <rtl/tencinfo.h> +#include <rtl/instance.hxx> + +#include <tools/string.hxx> +#include <impstrg.hxx> + +// For shared byte convert tables +#ifndef _TOOLS_TOOLSIN_HXX +#include <toolsin.hxx> +#endif + +#include <tools/debug.hxx> + +// ======================================================================= + +DBG_NAME( ByteString ) +DBG_NAMEEX( UniString ) + +// ----------------------------------------------------------------------- + +#define STRCODE sal_Char +#define STRCODEU unsigned char +#define STRING ByteString +#define STRINGDATA ByteStringData +#define DBGCHECKSTRING DbgCheckByteString +#define STRING_TYPE rtl_String +#define STRING_ACQUIRE rtl_string_acquire +#define STRING_RELEASE rtl_string_release +#define STRING_NEW rtl_string_new + + +// ----------------------------------------------------------------------- + +xub_StrLen ImplStringLen( const sal_Char* pStr ) +{ + const sal_Char* pTempStr = pStr; + while( *pTempStr ) + ++pTempStr; + return (xub_StrLen)(pTempStr-pStr); +} + +// ----------------------------------------------------------------------- + +xub_StrLen ImplStringLen( const sal_Unicode* pStr ) +{ + const sal_Unicode* pTempStr = pStr; + while( *pTempStr ) + ++pTempStr; + return (xub_StrLen)(pTempStr-pStr); +} + +// ----------------------------------------------------------------------- + +#include <strimp.cxx> +#include <strcvt.cxx> + +// ----------------------------------------------------------------------- + +ByteString ByteString::CreateFromInt32( sal_Int32 n, sal_Int16 nRadix ) +{ + sal_Char aBuf[RTL_STR_MAX_VALUEOFINT32]; + BOOST_STATIC_ASSERT(RTL_STR_MAX_VALUEOFINT32 <= STRING_MAXLEN); + return ByteString( + aBuf, + static_cast< xub_StrLen >(rtl_str_valueOfInt32( aBuf, n, nRadix )) ); +} + +// ----------------------------------------------------------------------- + +ByteString ByteString::CreateFromInt64( sal_Int64 n, sal_Int16 nRadix ) +{ + sal_Char aBuf[RTL_STR_MAX_VALUEOFINT64]; + BOOST_STATIC_ASSERT(RTL_STR_MAX_VALUEOFINT64 <= STRING_MAXLEN); + return ByteString( + aBuf, + static_cast< xub_StrLen >(rtl_str_valueOfInt64( aBuf, n, nRadix )) ); +} + +// ----------------------------------------------------------------------- + +ByteString ByteString::CreateFromFloat( float f ) +{ + sal_Char aBuf[RTL_STR_MAX_VALUEOFFLOAT]; + BOOST_STATIC_ASSERT(RTL_STR_MAX_VALUEOFFLOAT <= STRING_MAXLEN); + return ByteString( + aBuf, static_cast< xub_StrLen >(rtl_str_valueOfFloat( aBuf, f )) ); +} + +// ----------------------------------------------------------------------- + +ByteString ByteString::CreateFromDouble( double d ) +{ + sal_Char aBuf[RTL_STR_MAX_VALUEOFDOUBLE]; + BOOST_STATIC_ASSERT(RTL_STR_MAX_VALUEOFDOUBLE <= STRING_MAXLEN); + return ByteString( + aBuf, static_cast< xub_StrLen >(rtl_str_valueOfDouble( aBuf, d )) ); +} + +// ----------------------------------------------------------------------- + +namespace { struct Empty : public rtl::Static< const ByteString, Empty> {}; } +const ByteString& ByteString::EmptyString() +{ + return Empty::get(); +} + +// ----------------------------------------------------------------------- + +sal_Int32 ByteString::ToInt32() const +{ + DBG_CHKTHIS( ByteString, DbgCheckByteString ); + + return atoi( mpData->maStr ); +} + +// ----------------------------------------------------------------------- + +sal_Int64 ByteString::ToInt64() const +{ + DBG_CHKTHIS( ByteString, DbgCheckByteString ); + + return atoi( mpData->maStr ); +} + +// ----------------------------------------------------------------------- + +float ByteString::ToFloat() const +{ + DBG_CHKTHIS( ByteString, DbgCheckByteString ); + + OSL_ENSURE(false, "ByteString::ToFloat unusable"); + return 0; +} + +// ----------------------------------------------------------------------- + +double ByteString::ToDouble() const +{ + DBG_CHKTHIS( ByteString, DbgCheckByteString ); + + OSL_ENSURE(false, "ByteString::ToDouble unusable"); + return 0; +} + +// ----------------------------------------------------------------------- + +BOOL ByteString::IsLowerAscii() const +{ + DBG_CHKTHIS( ByteString, DbgCheckByteString ); + + sal_Int32 nIndex = 0; + sal_Int32 nLen = mpData->mnLen; + const sal_Char* pStr = mpData->maStr; + while ( nIndex < nLen ) + { + if ( (*pStr >= 65) && (*pStr <= 90) ) + return FALSE; + + ++pStr, + ++nIndex; + } + + return TRUE; +} + +// ----------------------------------------------------------------------- + +BOOL ByteString::IsUpperAscii() const +{ + DBG_CHKTHIS( ByteString, DbgCheckByteString ); + + sal_Int32 nIndex = 0; + sal_Int32 nLen = mpData->mnLen; + const sal_Char* pStr = mpData->maStr; + while ( nIndex < nLen ) + { + if ( (*pStr >= 97) && (*pStr <= 122) ) + return FALSE; + + ++pStr, + ++nIndex; + } + + return TRUE; +} + +// ----------------------------------------------------------------------- + +BOOL ByteString::IsAlphaAscii() const +{ + DBG_CHKTHIS( ByteString, DbgCheckByteString ); + + sal_Int32 nIndex = 0; + sal_Int32 nLen = mpData->mnLen; + const sal_Char* pStr = mpData->maStr; + while ( nIndex < nLen ) + { + if ( !(((*pStr >= 97) && (*pStr <= 122)) || + ((*pStr >= 65) && (*pStr <= 90))) ) + return FALSE; + + ++pStr, + ++nIndex; + } + + return TRUE; +} + +// ----------------------------------------------------------------------- + +BOOL ByteString::IsNumericAscii() const +{ + DBG_CHKTHIS( ByteString, DbgCheckByteString ); + + sal_Int32 nIndex = 0; + sal_Int32 nLen = mpData->mnLen; + const sal_Char* pStr = mpData->maStr; + while ( nIndex < nLen ) + { + if ( !((*pStr >= 48) && (*pStr <= 57)) ) + return FALSE; + + ++pStr, + ++nIndex; + } + + return TRUE; +} + +// ----------------------------------------------------------------------- + +BOOL ByteString::IsAlphaNumericAscii() const +{ + DBG_CHKTHIS( ByteString, DbgCheckByteString ); + + sal_Int32 nIndex = 0; + sal_Int32 nLen = mpData->mnLen; + const sal_Char* pStr = mpData->maStr; + while ( nIndex < nLen ) + { + if ( !(((*pStr >= 97) && (*pStr <= 122)) || + ((*pStr >= 65) && (*pStr <= 90)) || + ((*pStr >= 48) && (*pStr <= 57))) ) + return FALSE; + + ++pStr, + ++nIndex; + } + + return TRUE; +} diff --git a/tools/source/string/tustring.cxx b/tools/source/string/tustring.cxx new file mode 100644 index 000000000000..eaa5fab02ec7 --- /dev/null +++ b/tools/source/string/tustring.cxx @@ -0,0 +1,165 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: tustring.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include <string.h> + +#include "boost/static_assert.hpp" + +#ifndef _OSL_INTERLCK_H +#include <osl/interlck.h> +#endif +#ifndef _RTL_ALLOC_H +#include <rtl/alloc.h> +#endif +#ifndef _RTL_MEMORY_H +#include <rtl/memory.h> +#endif +#include <rtl/tencinfo.h> +#include <rtl/instance.hxx> + +#include <tools/string.hxx> +#include <impstrg.hxx> + +#include <tools/debug.hxx> + +// ======================================================================= + +DBG_NAME( UniString ) +DBG_NAMEEX( ByteString ) + +// ----------------------------------------------------------------------- + +#define STRCODE sal_Unicode +#define STRCODEU sal_Unicode +#define STRING UniString +#define STRINGDATA UniStringData +#define DBGCHECKSTRING DbgCheckUniString +#define STRING_TYPE rtl_uString +#define STRING_ACQUIRE rtl_uString_acquire +#define STRING_RELEASE rtl_uString_release +#define STRING_NEW rtl_uString_new + +// ----------------------------------------------------------------------- + +#include <strimp.cxx> +#include <strucvt.cxx> +#include <strascii.cxx> + +UniString::UniString(char c): mpData(ImplAllocData(1)) { mpData->maStr[0] = c; } + +// ----------------------------------------------------------------------- + +UniString UniString::CreateFromInt32( sal_Int32 n, sal_Int16 nRadix ) +{ + sal_Unicode aBuf[RTL_USTR_MAX_VALUEOFINT32]; + BOOST_STATIC_ASSERT(RTL_USTR_MAX_VALUEOFINT32 <= STRING_MAXLEN); + return UniString( + aBuf, + static_cast< xub_StrLen >(rtl_ustr_valueOfInt32( aBuf, n, nRadix )) ); +} + +// ----------------------------------------------------------------------- + +UniString UniString::CreateFromInt64( sal_Int64 n, sal_Int16 nRadix ) +{ + sal_Unicode aBuf[RTL_USTR_MAX_VALUEOFINT64]; + BOOST_STATIC_ASSERT(RTL_USTR_MAX_VALUEOFINT64 <= STRING_MAXLEN); + return UniString( + aBuf, + static_cast< xub_StrLen >(rtl_ustr_valueOfInt64( aBuf, n, nRadix )) ); +} + +// ----------------------------------------------------------------------- + +UniString UniString::CreateFromFloat( float f ) +{ + sal_Unicode aBuf[RTL_USTR_MAX_VALUEOFFLOAT]; + BOOST_STATIC_ASSERT(RTL_USTR_MAX_VALUEOFFLOAT <= STRING_MAXLEN); + return UniString( + aBuf, static_cast< xub_StrLen >(rtl_ustr_valueOfFloat( aBuf, f )) ); +} + +// ----------------------------------------------------------------------- + +UniString UniString::CreateFromDouble( double d ) +{ + sal_Unicode aBuf[RTL_USTR_MAX_VALUEOFDOUBLE]; + BOOST_STATIC_ASSERT(RTL_USTR_MAX_VALUEOFDOUBLE <= STRING_MAXLEN); + return UniString( + aBuf, static_cast< xub_StrLen >(rtl_ustr_valueOfDouble( aBuf, d )) ); +} + +// ----------------------------------------------------------------------- + +namespace { struct Empty : public rtl::Static< const UniString, Empty> {}; } +const UniString& UniString::EmptyString() +{ + return Empty::get(); +} + +// ----------------------------------------------------------------------- + +sal_Int32 UniString::ToInt32() const +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + + return rtl_ustr_toInt32( mpData->maStr, 10 ); +} + +// ----------------------------------------------------------------------- + +sal_Int64 UniString::ToInt64() const +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + + return rtl_ustr_toInt64( mpData->maStr, 10 ); +} + +// ----------------------------------------------------------------------- + +float UniString::ToFloat() const +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + + return rtl_ustr_toFloat( mpData->maStr ); +} + +// ----------------------------------------------------------------------- + +double UniString::ToDouble() const +{ + DBG_CHKTHIS( UniString, DbgCheckUniString ); + + return rtl_ustr_toDouble( mpData->maStr ); +} + diff --git a/tools/source/testtoolloader/makefile.mk b/tools/source/testtoolloader/makefile.mk new file mode 100644 index 000000000000..0ed54c43f843 --- /dev/null +++ b/tools/source/testtoolloader/makefile.mk @@ -0,0 +1,49 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.6 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=TOOLS +TARGET=testtoolloader + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES= \ + $(SLO)$/testtoolloader.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/tools/source/testtoolloader/testtoolloader.cxx b/tools/source/testtoolloader/testtoolloader.cxx new file mode 100644 index 000000000000..f0263c001e29 --- /dev/null +++ b/tools/source/testtoolloader/testtoolloader.cxx @@ -0,0 +1,188 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: testtoolloader.cxx,v $ + * $Revision: 1.12 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" + +#include "tools/testtoolloader.hxx" +#include <osl/module.h> +#include <rtl/logfile.hxx> +#include <vos/process.hxx> +#include "tools/solar.h" +#include "tools/string.hxx" +#include "tools/debug.hxx" + +#include <comphelper/uieventslogger.hxx> + +using namespace rtl; + +namespace tools +{ + typedef void ( *pfunc_CreateRemoteControl)(); + typedef void ( *pfunc_DestroyRemoteControl)(); + + typedef void ( *pfunc_CreateEventLogger)(); + typedef void ( *pfunc_DestroyEventLogger)(); + +static oslModule aTestToolModule = 0; +// are we to be automated at all? +static bool bAutomate = false; +static bool bLoggerStarted = false; + + +sal_uInt32 GetCommandLineParamCount() +{ + NAMESPACE_VOS( OStartupInfo ) aStartInfo; + return aStartInfo.getCommandArgCount(); +} + +String GetCommandLineParam( sal_uInt32 nParam ) +{ + NAMESPACE_VOS( OStartupInfo ) aStartInfo; + ::rtl::OUString aParam; + NAMESPACE_VOS( OStartupInfo )::TStartupError eError = aStartInfo.getCommandArg( nParam, aParam ); + if ( eError == NAMESPACE_VOS( OStartupInfo )::E_None ) + return String( aParam ); + else + { + DBG_ERROR( "Unable to get CommandLineParam" ); + return String(); + } +} + +extern "C" { static void SAL_CALL thisModule() {} } + +void LoadLib() +{ + if ( !aTestToolModule ) + { + aTestToolModule = osl_loadModuleRelative( + &thisModule, + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(SVLIBRARY("sts"))).pData, + SAL_LOADMODULE_GLOBAL ); + } +} + +void InitTestToolLib() +{ + RTL_LOGFILE_CONTEXT( aLog, "desktop (cd100003) ::InitTestToolLib" ); + + sal_uInt32 i; + + for ( i = 0 ; i < GetCommandLineParamCount() ; i++ ) + { + if ( GetCommandLineParam( i ).EqualsIgnoreCaseAscii("/enableautomation") + || GetCommandLineParam( i ).EqualsIgnoreCaseAscii("-enableautomation")) + { + bAutomate = true; + break; + } + } + + if ( bAutomate ) + { + OUString aFuncName( RTL_CONSTASCII_USTRINGPARAM( "CreateRemoteControl" )); + + LoadLib(); + if ( aTestToolModule ) + { + oslGenericFunction pInitFunc = osl_getFunctionSymbol( + aTestToolModule, aFuncName.pData ); + if ( pInitFunc ) + (reinterpret_cast< pfunc_CreateRemoteControl >(pInitFunc))(); + else + { + DBG_ERROR1( "Unable to get Symbol 'CreateRemoteControl' from library %s while loading testtool support.", SVLIBRARY( "sts" ) ); + } + } + else + { + DBG_ERROR1( "Unable to access library %s while loading testtool support.", SVLIBRARY( "sts" ) ); + } + } + + if ( ::comphelper::UiEventsLogger::isEnabled() ) + { + OUString aFuncName( RTL_CONSTASCII_USTRINGPARAM( "CreateEventLogger" )); + + LoadLib(); + if ( aTestToolModule ) + { + oslGenericFunction pInitFunc = osl_getFunctionSymbol( + aTestToolModule, aFuncName.pData ); + if ( pInitFunc ) + { + (reinterpret_cast< pfunc_CreateEventLogger >(pInitFunc))(); + bLoggerStarted = TRUE; + } + else + { + DBG_ERROR1( "Unable to get Symbol 'CreateEventLogger' from library %s while loading testtool support.", SVLIBRARY( "sts" ) ); + } + } + else + { + DBG_ERROR1( "Unable to access library %s while loading testtool support.", SVLIBRARY( "sts" ) ); + } + } +} + +void DeInitTestToolLib() +{ + if ( aTestToolModule ) + { + if ( bAutomate ) + { + OUString aFuncName( RTL_CONSTASCII_USTRINGPARAM( "DestroyRemoteControl" )); + + oslGenericFunction pDeInitFunc = osl_getFunctionSymbol( + aTestToolModule, aFuncName.pData ); + if ( pDeInitFunc ) + (reinterpret_cast< pfunc_DestroyRemoteControl >(pDeInitFunc))(); + } + + if ( bLoggerStarted /*::comphelper::UiEventsLogger::isEnabled()*/ ) + { + OUString aFuncName( RTL_CONSTASCII_USTRINGPARAM( "DestroyEventLogger" )); + + oslGenericFunction pDeInitFunc = osl_getFunctionSymbol( + aTestToolModule, aFuncName.pData ); + if ( pDeInitFunc ) + { + (reinterpret_cast< pfunc_DestroyEventLogger >(pDeInitFunc))(); + bLoggerStarted = FALSE; + } + } + + osl_unloadModule( aTestToolModule ); + } +} + +} // namespace tools diff --git a/tools/source/zcodec/makefile.mk b/tools/source/zcodec/makefile.mk new file mode 100644 index 000000000000..6f9e530a5fec --- /dev/null +++ b/tools/source/zcodec/makefile.mk @@ -0,0 +1,51 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.6 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=tools +TARGET=zcodec + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +.IF "$(SYSTEM_ZLIB)" == "YES" +CFLAGS+=-DSYSTEM_ZLIB +.ENDIF +SLOFILES= $(SLO)$/zcodec.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/tools/source/zcodec/zcodec.cxx b/tools/source/zcodec/zcodec.cxx new file mode 100644 index 000000000000..ab3e5508ef82 --- /dev/null +++ b/tools/source/zcodec/zcodec.cxx @@ -0,0 +1,491 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: zcodec.cxx,v $ + * $Revision: 1.11 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_tools.hxx" +#include <tools/stream.hxx> +#ifndef _ZLIB_H +#ifdef SYSTEM_ZLIB +#include "zlib.h" +#else +#include "zlib/zlib.h" +#endif +#endif +#include <tools/zcodec.hxx> +#include <rtl/crc.h> +#include <osl/endian.h> + +// ----------- +// - Defines - +// ----------- + +#define PZSTREAM ((z_stream*) mpsC_Stream) + +/* gzip flag byte */ +#define GZ_ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define GZ_HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define GZ_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define GZ_ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define GZ_COMMENT 0x10 /* bit 4 set: file comment present */ +#define GZ_RESERVED 0xE0 /* bits 5..7: reserved */ + +static int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */ + + +// ---------- +// - ZCodec - +// ---------- + +ZCodec::ZCodec( ULONG nInBufSize, ULONG nOutBufSize, ULONG nMemUsage ) + : mnCRC(0) +{ + mnMemUsage = nMemUsage; + mnInBufSize = nInBufSize; + mnOutBufSize = nOutBufSize; + mpsC_Stream = new z_stream; +} + +ZCodec::ZCodec( void ) + : mnCRC(0) +{ + mnMemUsage = MAX_MEM_USAGE; + mnInBufSize = DEFAULT_IN_BUFSIZE; + mnOutBufSize = DEFAULT_OUT_BUFSIZE; + mpsC_Stream = new z_stream; +} + +// ------------------------------------------------------------------------ + +ZCodec::~ZCodec() +{ + delete (z_stream*) mpsC_Stream; +} + +// ------------------------------------------------------------------------ + +void ZCodec::BeginCompression( ULONG nCompressMethod ) +{ + mbInit = 0; + mbStatus = TRUE; + mbFinish = FALSE; + mpIStm = mpOStm = NULL; + mnInToRead = 0xffffffff; + mpInBuf = mpOutBuf = NULL; + PZSTREAM->total_out = PZSTREAM->total_in = 0; + mnCompressMethod = nCompressMethod; + PZSTREAM->zalloc = ( alloc_func )0; + PZSTREAM->zfree = ( free_func )0; + PZSTREAM->opaque = ( voidpf )0; + PZSTREAM->avail_out = PZSTREAM->avail_in = 0; +} + +// ------------------------------------------------------------------------ + +long ZCodec::EndCompression() +{ + long retvalue = 0; + + if ( mbInit != 0 ) + { + if ( mbInit & 2 ) // 1->decompress, 3->compress + { + do + { + ImplWriteBack(); + } + while ( deflate( PZSTREAM, Z_FINISH ) != Z_STREAM_END ); + + ImplWriteBack(); + + retvalue = PZSTREAM->total_in; + deflateEnd( PZSTREAM ); + } + else + { + retvalue = PZSTREAM->total_out; + inflateEnd( PZSTREAM ); + } + delete[] mpOutBuf; + delete[] mpInBuf; + } + return ( mbStatus ) ? retvalue : -1; +} + + +// ------------------------------------------------------------------------ + +long ZCodec::Compress( SvStream& rIStm, SvStream& rOStm ) +{ + long nOldTotal_In = PZSTREAM->total_in; + + if ( mbInit == 0 ) + { + mpIStm = &rIStm; + mpOStm = &rOStm; + ImplInitBuf( FALSE ); + mpInBuf = new BYTE[ mnInBufSize ]; + } + while (( PZSTREAM->avail_in = mpIStm->Read( PZSTREAM->next_in = mpInBuf, mnInBufSize )) != 0 ) + { + if ( PZSTREAM->avail_out == 0 ) + ImplWriteBack(); + if ( deflate( PZSTREAM, Z_NO_FLUSH ) < 0 ) + { + mbStatus = FALSE; + break; + } + }; + return ( mbStatus ) ? (long)(PZSTREAM->total_in - nOldTotal_In) : -1; +} + +// ------------------------------------------------------------------------ + +long ZCodec::Decompress( SvStream& rIStm, SvStream& rOStm ) +{ + int err; + ULONG nInToRead; + long nOldTotal_Out = PZSTREAM->total_out; + + if ( mbFinish ) + return PZSTREAM->total_out - nOldTotal_Out; + + if ( mbInit == 0 ) + { + mpIStm = &rIStm; + mpOStm = &rOStm; + ImplInitBuf( TRUE ); + PZSTREAM->next_out = mpOutBuf = new BYTE[ PZSTREAM->avail_out = mnOutBufSize ]; + } + do + { + if ( PZSTREAM->avail_out == 0 ) ImplWriteBack(); + if ( PZSTREAM->avail_in == 0 && mnInToRead ) + { + nInToRead = ( mnInBufSize > mnInToRead ) ? mnInToRead : mnInBufSize; + PZSTREAM->avail_in = mpIStm->Read( PZSTREAM->next_in = mpInBuf, nInToRead ); + mnInToRead -= nInToRead; + + if ( mnCompressMethod & ZCODEC_UPDATE_CRC ) + mnCRC = UpdateCRC( mnCRC, mpInBuf, nInToRead ); + + } + err = inflate( PZSTREAM, Z_NO_FLUSH ); + if ( err < 0 ) + { + mbStatus = FALSE; + break; + } + + } + while ( ( err != Z_STREAM_END) && ( PZSTREAM->avail_in || mnInToRead ) ); + ImplWriteBack(); + + if ( err == Z_STREAM_END ) + mbFinish = TRUE; + return ( mbStatus ) ? (long)(PZSTREAM->total_out - nOldTotal_Out) : -1; +} + +// ------------------------------------------------------------------------ + +long ZCodec::Write( SvStream& rOStm, const BYTE* pData, ULONG nSize ) +{ + if ( mbInit == 0 ) + { + mpOStm = &rOStm; + ImplInitBuf( FALSE ); + } + + PZSTREAM->avail_in = nSize; + PZSTREAM->next_in = (unsigned char*)pData; + + while ( PZSTREAM->avail_in || ( PZSTREAM->avail_out == 0 ) ) + { + if ( PZSTREAM->avail_out == 0 ) + ImplWriteBack(); + + if ( deflate( PZSTREAM, Z_NO_FLUSH ) < 0 ) + { + mbStatus = FALSE; + break; + } + } + return ( mbStatus ) ? (long)nSize : -1; +} + +// ------------------------------------------------------------------------ + +long ZCodec::Read( SvStream& rIStm, BYTE* pData, ULONG nSize ) +{ + int err; + ULONG nInToRead; + + if ( mbFinish ) + return 0; // PZSTREAM->total_out; + + mpIStm = &rIStm; + if ( mbInit == 0 ) + { + ImplInitBuf( TRUE ); + } + PZSTREAM->avail_out = nSize; + PZSTREAM->next_out = pData; + do + { + if ( PZSTREAM->avail_in == 0 && mnInToRead ) + { + nInToRead = (mnInBufSize > mnInToRead) ? mnInToRead : mnInBufSize; + PZSTREAM->avail_in = mpIStm->Read ( + PZSTREAM->next_in = mpInBuf, nInToRead); + mnInToRead -= nInToRead; + + if ( mnCompressMethod & ZCODEC_UPDATE_CRC ) + mnCRC = UpdateCRC( mnCRC, mpInBuf, nInToRead ); + + } + err = inflate( PZSTREAM, Z_NO_FLUSH ); + if ( err < 0 ) + { + // Accept Z_BUF_ERROR as EAGAIN or EWOULDBLOCK. + mbStatus = (err == Z_BUF_ERROR); + break; + } + } + while ( (err != Z_STREAM_END) && + (PZSTREAM->avail_out != 0) && + (PZSTREAM->avail_in || mnInToRead) ); + if ( err == Z_STREAM_END ) + mbFinish = TRUE; + + return (mbStatus ? (long)(nSize - PZSTREAM->avail_out) : -1); +} + +// ------------------------------------------------------------------------ + +long ZCodec::ReadAsynchron( SvStream& rIStm, BYTE* pData, ULONG nSize ) +{ + int err = 0; + ULONG nInToRead; + + if ( mbFinish ) + return 0; // PZSTREAM->total_out; + + if ( mbInit == 0 ) + { + mpIStm = &rIStm; + ImplInitBuf( TRUE ); + } + PZSTREAM->avail_out = nSize; + PZSTREAM->next_out = pData; + do + { + if ( PZSTREAM->avail_in == 0 && mnInToRead ) + { + nInToRead = (mnInBufSize > mnInToRead) ? mnInToRead : mnInBufSize; + + ULONG nStreamPos = rIStm.Tell(); + rIStm.Seek( STREAM_SEEK_TO_END ); + ULONG nMaxPos = rIStm.Tell(); + rIStm.Seek( nStreamPos ); + if ( ( nMaxPos - nStreamPos ) < nInToRead ) + { + rIStm.SetError( ERRCODE_IO_PENDING ); + err= ! Z_STREAM_END; // TODO What is appropriate code for this? + break; + } + + PZSTREAM->avail_in = mpIStm->Read ( + PZSTREAM->next_in = mpInBuf, nInToRead); + mnInToRead -= nInToRead; + + if ( mnCompressMethod & ZCODEC_UPDATE_CRC ) + mnCRC = UpdateCRC( mnCRC, mpInBuf, nInToRead ); + + } + err = inflate( PZSTREAM, Z_NO_FLUSH ); + if ( err < 0 ) + { + // Accept Z_BUF_ERROR as EAGAIN or EWOULDBLOCK. + mbStatus = (err == Z_BUF_ERROR); + break; + } + } + while ( (err != Z_STREAM_END) && + (PZSTREAM->avail_out != 0) && + (PZSTREAM->avail_in || mnInToRead) ); + if ( err == Z_STREAM_END ) + mbFinish = TRUE; + + return (mbStatus ? (long)(nSize - PZSTREAM->avail_out) : -1); +} + +// ------------------------------------------------------------------------ + +void ZCodec::ImplWriteBack() +{ + ULONG nAvail = mnOutBufSize - PZSTREAM->avail_out; + + if ( nAvail ) + { + if ( mbInit & 2 && ( mnCompressMethod & ZCODEC_UPDATE_CRC ) ) + mnCRC = UpdateCRC( mnCRC, mpOutBuf, nAvail ); + mpOStm->Write( PZSTREAM->next_out = mpOutBuf, nAvail ); + PZSTREAM->avail_out = mnOutBufSize; + } +} + +// ------------------------------------------------------------------------ + +void ZCodec::SetBreak( ULONG nInToRead ) +{ + mnInToRead = nInToRead; +} + +// ------------------------------------------------------------------------ + +ULONG ZCodec::GetBreak( void ) +{ + return ( mnInToRead + PZSTREAM->avail_in ); +} + +// ------------------------------------------------------------------------ + +void ZCodec::SetCRC( ULONG nCRC ) +{ + mnCRC = nCRC; +} + +// ------------------------------------------------------------------------ + +ULONG ZCodec::GetCRC() +{ + return mnCRC; +} + +// ------------------------------------------------------------------------ + +void ZCodec::ImplInitBuf ( BOOL nIOFlag ) +{ + if ( mbInit == 0 ) + { + if ( nIOFlag ) + { + mbInit = 1; + if ( mbStatus && ( mnCompressMethod & ZCODEC_GZ_LIB ) ) + { + BYTE n1, n2, j, nMethod, nFlags; + for ( int i = 0; i < 2; i++ ) // gz - magic number + { + *mpIStm >> j; + if ( j != gz_magic[ i ] ) + mbStatus = FALSE; + } + *mpIStm >> nMethod; + *mpIStm >> nFlags; + if ( nMethod != Z_DEFLATED ) + mbStatus = FALSE; + if ( ( nFlags & GZ_RESERVED ) != 0 ) + mbStatus = FALSE; + /* Discard time, xflags and OS code: */ + mpIStm->SeekRel( 6 ); + /* skip the extra field */ + if ( nFlags & GZ_EXTRA_FIELD ) + { + *mpIStm >> n1 >> n2; + mpIStm->SeekRel( n1 + ( n2 << 8 ) ); + } + /* skip the original file name */ + if ( nFlags & GZ_ORIG_NAME) + { + do + { + *mpIStm >> j; + } + while ( j && !mpIStm->IsEof() ); + } + /* skip the .gz file comment */ + if ( nFlags & GZ_COMMENT ) + { + do + { + *mpIStm >> j; + } + while ( j && !mpIStm->IsEof() ); + } + /* skip the header crc */ + if ( nFlags & GZ_HEAD_CRC ) + mpIStm->SeekRel( 2 ); + if ( mbStatus ) + mbStatus = ( inflateInit2( PZSTREAM, -MAX_WBITS) != Z_OK ) ? FALSE : TRUE; + } + else + { + mbStatus = ( inflateInit( PZSTREAM ) >= 0 ); + } + mpInBuf = new BYTE[ mnInBufSize ]; + } + else + { + mbInit = 3; + + mbStatus = ( deflateInit2_( PZSTREAM, mnCompressMethod & 0xff, Z_DEFLATED, + MAX_WBITS, mnMemUsage, ( mnCompressMethod >> 8 ) & 0xff, + ZLIB_VERSION, sizeof( z_stream ) ) >= 0 ); + + PZSTREAM->next_out = mpOutBuf = new BYTE[ PZSTREAM->avail_out = mnOutBufSize ]; + } + } +} + +// ------------------------------------------------------------------------ + +ULONG ZCodec::UpdateCRC ( ULONG nLatestCRC, ULONG nNumber ) +{ + +#ifdef OSL_LITENDIAN + nNumber = SWAPLONG( nNumber ); +#endif + return rtl_crc32( nLatestCRC, &nNumber, 4 ); +} + +// ------------------------------------------------------------------------ + +ULONG ZCodec::UpdateCRC ( ULONG nLatestCRC, BYTE* pSource, long nDatSize) +{ + return rtl_crc32( nLatestCRC, pSource, nDatSize ); +} + +// ------------------------------------------------------------------------ + +void GZCodec::BeginCompression( ULONG nCompressMethod ) +{ + ZCodec::BeginCompression( nCompressMethod | ZCODEC_GZ_LIB ); +}; + + |