/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * 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 * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_svtools.hxx" #include #include #include #include #include #include #include #ifndef _SVSTDARR_HXX #define _SVSTDARR_ULONGS #include #endif #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; const sal_Int32 MAX_LEN( 1024L ); //static sal_Unicode sTmpBuffer[ MAX_LEN+1 ]; const sal_Int32 MAX_MACRO_LEN( 1024 ); const sal_Int32 MAX_ENTITY_LEN( 8L ); /* */ // Tabellen zum Umwandeln von Options-Werten in Strings // static HTMLOptionEnum __READONLY_DATA aInputTypeOptEnums[] = { { OOO_STRING_SVTOOLS_HTML_IT_text, HTML_IT_TEXT }, { OOO_STRING_SVTOOLS_HTML_IT_password, HTML_IT_PASSWORD }, { OOO_STRING_SVTOOLS_HTML_IT_checkbox, HTML_IT_CHECKBOX }, { OOO_STRING_SVTOOLS_HTML_IT_radio, HTML_IT_RADIO }, { OOO_STRING_SVTOOLS_HTML_IT_range, HTML_IT_RANGE }, { OOO_STRING_SVTOOLS_HTML_IT_scribble, HTML_IT_SCRIBBLE }, { OOO_STRING_SVTOOLS_HTML_IT_file, HTML_IT_FILE }, { OOO_STRING_SVTOOLS_HTML_IT_hidden, HTML_IT_HIDDEN }, { OOO_STRING_SVTOOLS_HTML_IT_submit, HTML_IT_SUBMIT }, { OOO_STRING_SVTOOLS_HTML_IT_image, HTML_IT_IMAGE }, { OOO_STRING_SVTOOLS_HTML_IT_reset, HTML_IT_RESET }, { OOO_STRING_SVTOOLS_HTML_IT_button, HTML_IT_BUTTON }, { 0, 0 } }; // static HTMLOptionEnum __READONLY_DATA aTableFrameOptEnums[] = { { OOO_STRING_SVTOOLS_HTML_TF_void, HTML_TF_VOID }, { OOO_STRING_SVTOOLS_HTML_TF_above, HTML_TF_ABOVE }, { OOO_STRING_SVTOOLS_HTML_TF_below, HTML_TF_BELOW }, { OOO_STRING_SVTOOLS_HTML_TF_hsides, HTML_TF_HSIDES }, { OOO_STRING_SVTOOLS_HTML_TF_lhs, HTML_TF_LHS }, { OOO_STRING_SVTOOLS_HTML_TF_rhs, HTML_TF_RHS }, { OOO_STRING_SVTOOLS_HTML_TF_vsides, HTML_TF_VSIDES }, { OOO_STRING_SVTOOLS_HTML_TF_box, HTML_TF_BOX }, { OOO_STRING_SVTOOLS_HTML_TF_border, HTML_TF_BOX }, { 0, 0 } }; //
static HTMLOptionEnum __READONLY_DATA aTableRulesOptEnums[] = { { OOO_STRING_SVTOOLS_HTML_TR_none, HTML_TR_NONE }, { OOO_STRING_SVTOOLS_HTML_TR_groups, HTML_TR_GROUPS }, { OOO_STRING_SVTOOLS_HTML_TR_rows, HTML_TR_ROWS }, { OOO_STRING_SVTOOLS_HTML_TR_cols, HTML_TR_COLS }, { OOO_STRING_SVTOOLS_HTML_TR_all, HTML_TR_ALL }, { 0, 0 } }; SV_IMPL_PTRARR(HTMLOptions,HTMLOptionPtr) /* */ USHORT HTMLOption::GetEnum( const HTMLOptionEnum *pOptEnums, USHORT nDflt ) const { USHORT nValue = nDflt; while( pOptEnums->pName ) if( aValue.EqualsIgnoreCaseAscii( pOptEnums->pName ) ) break; else pOptEnums++; if( pOptEnums->pName ) nValue = pOptEnums->nValue; return nValue; } BOOL HTMLOption::GetEnum( USHORT &rEnum, const HTMLOptionEnum *pOptEnums ) const { while( pOptEnums->pName ) { if( aValue.EqualsIgnoreCaseAscii( pOptEnums->pName ) ) break; else pOptEnums++; } const sal_Char *pName = pOptEnums->pName; if( pName ) rEnum = pOptEnums->nValue; return (pName != 0); } HTMLOption::HTMLOption( USHORT nTok, const String& rToken, const String& rValue ) : aValue(rValue) , aToken(rToken) , nToken( nTok ) { DBG_ASSERT( nToken>=HTML_OPTION_START && nToken=HTML_OPTION_NUMBER_START && nToken=HTML_OPTION_CONTEXT_START && nToken= 0 ? (sal_uInt32)nTmp : 0; } INT32 HTMLOption::GetSNumber() const { DBG_ASSERT( (nToken>=HTML_OPTION_NUMBER_START && nToken=HTML_OPTION_CONTEXT_START && nToken='0' && c<='9' ) { nNum *= 10; nNum += (c - '0'); bInNum = TRUE; } else if( bInNum ) { rLongs.Insert( nNum, rLongs.Count() ); bInNum = FALSE; nNum = 0; } } if( bInNum ) { rLongs.Insert( nNum, rLongs.Count() ); } } else { // hier wird auf die korrekte Trennung der Zahlen durch ',' geachtet // und auch mal eine 0 eingefuegt xub_StrLen nPos = 0; while( nPos < aValue.Len() ) { register sal_Unicode c; while( nPos < aValue.Len() && ((c=aValue.GetChar(nPos)) == ' ' || c == '\t' || c == '\n' || c== '\r' ) ) nPos++; if( nPos==aValue.Len() ) rLongs.Insert( ULONG(0), rLongs.Count() ); else { xub_StrLen nEnd = aValue.Search( (sal_Unicode)',', nPos ); if( STRING_NOTFOUND==nEnd ) { sal_Int32 nTmp = aValue.Copy(nPos).ToInt32(); rLongs.Insert( nTmp >= 0 ? (sal_uInt32)nTmp : 0, rLongs.Count() ); nPos = aValue.Len(); } else { sal_Int32 nTmp = aValue.Copy(nPos,nEnd-nPos).ToInt32(); rLongs.Insert( nTmp >= 0 ? (sal_uInt32)nTmp : 0, rLongs.Count() ); nPos = nEnd+1; } } } } } void HTMLOption::GetColor( Color& rColor ) const { DBG_ASSERT( (nToken>=HTML_OPTION_COLOR_START && nToken= '0' && c <= '9' ) nColor += (c - 48); else if( c >= 'A' && c <= 'F' ) nColor += (c - 55); } } rColor.SetRed( (BYTE)((nColor & 0x00ff0000) >> 16) ); rColor.SetGreen( (BYTE)((nColor & 0x0000ff00) >> 8)); rColor.SetBlue( (BYTE)(nColor & 0x000000ff) ); } HTMLInputType HTMLOption::GetInputType() const { DBG_ASSERT( nToken==HTML_O_TYPE, "GetInputType: Option nicht TYPE" ); return (HTMLInputType)GetEnum( aInputTypeOptEnums, HTML_IT_TEXT ); } HTMLTableFrame HTMLOption::GetTableFrame() const { DBG_ASSERT( nToken==HTML_O_FRAME, "GetTableFrame: Option nicht FRAME" ); return (HTMLTableFrame)GetEnum( aTableFrameOptEnums, HTML_TF_VOID ); } HTMLTableRules HTMLOption::GetTableRules() const { DBG_ASSERT( nToken==HTML_O_RULES, "GetTableRules: Option nicht RULES" ); return (HTMLTableRules)GetEnum( aTableRulesOptEnums, HTML_TR_NONE ); } /* */ HTMLParser::HTMLParser( SvStream& rIn, int bReadNewDoc ) : SvParser( rIn ) { bNewDoc = bReadNewDoc; bReadListing = bReadXMP = bReadPRE = bReadTextArea = bReadScript = bReadStyle = bEndTokenFound = bIsInBody = bReadNextChar = bReadComment = FALSE; bIsInHeader = TRUE; pOptions = new HTMLOptions; } HTMLParser::~HTMLParser() { if( pOptions && pOptions->Count() ) pOptions->DeleteAndDestroy( 0, pOptions->Count() ); delete pOptions; } SvParserState __EXPORT HTMLParser::CallParser() { eState = SVPAR_WORKING; nNextCh = GetNextChar(); SaveState( 0 ); nPre_LinePos = 0; bPre_IgnoreNewPara = FALSE; AddRef(); Continue( 0 ); if( SVPAR_PENDING != eState ) ReleaseRef(); // dann brauchen wir den Parser nicht mehr! return eState; } void HTMLParser::Continue( int nToken ) { if( !nToken ) nToken = GetNextToken(); while( IsParserWorking() ) { SaveState( nToken ); nToken = FilterToken( nToken ); if( nToken ) NextToken( nToken ); if( IsParserWorking() ) SaveState( 0 ); // bis hierhin abgearbeitet, // weiter mit neuem Token! nToken = GetNextToken(); } } int HTMLParser::FilterToken( int nToken ) { switch( nToken ) { case sal_Unicode(EOF): nToken = 0; break; // nicht verschicken case HTML_HEAD_OFF: bIsInBody = TRUE; case HTML_HEAD_ON: bIsInHeader = HTML_HEAD_ON == nToken; break; case HTML_BODY_ON: case HTML_FRAMESET_ON: bIsInHeader = FALSE; bIsInBody = HTML_BODY_ON == nToken; break; case HTML_BODY_OFF: bIsInBody = bReadPRE = bReadListing = bReadXMP = FALSE; break; case HTML_HTML_OFF: nToken = 0; bReadPRE = bReadListing = bReadXMP = FALSE; break; // HTML_ON wurde auch nicht verschickt ! case HTML_PREFORMTXT_ON: StartPRE(); break; case HTML_PREFORMTXT_OFF: FinishPRE(); break; case HTML_LISTING_ON: StartListing(); break; case HTML_LISTING_OFF: FinishListing(); break; case HTML_XMP_ON: StartXMP(); break; case HTML_XMP_OFF: FinishXMP(); break; default: if( bReadPRE ) nToken = FilterPRE( nToken ); else if( bReadListing ) nToken = FilterListing( nToken ); else if( bReadXMP ) nToken = FilterXMP( nToken ); break; } return nToken; } #define HTML_ISDIGIT( c ) (c >= '0' && c <= '9') #define HTML_ISALPHA( c ) ( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ) #define HTML_ISALNUM( c ) ( HTML_ISALPHA(c) || HTML_ISDIGIT(c) ) #define HTML_ISSPACE( c ) ( ' ' == c || (c >= 0x09 && c <= 0x0d) ) #define HTML_ISPRINTABLE( c ) ( c >= 32 && c != 127) // --> OD 2006-07-26 #138464# #define HTML_ISHEXDIGIT( c ) ( HTML_ISDIGIT(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') ) // <-- int HTMLParser::ScanText( const sal_Unicode cBreak ) { ::rtl::OUStringBuffer sTmpBuffer( MAX_LEN ); int bWeiter = TRUE; int bEqSignFound = FALSE; sal_Unicode cQuote = 0U; while( bWeiter && IsParserWorking() ) { int bNextCh = TRUE; switch( nNextCh ) { case '&': bEqSignFound = FALSE; if( bReadXMP ) sTmpBuffer.append( (sal_Unicode)'&' ); else { ULONG nStreamPos = rInput.Tell(); ULONG nLinePos = GetLinePos(); sal_Unicode cChar = 0U; if( '#' == (nNextCh = GetNextChar()) ) { nNextCh = GetNextChar(); // --> OD 2006-07-26 #138464# // consider hexadecimal digits const sal_Bool bIsHex( 'x' == nNextCh ); const sal_Bool bIsDecOrHex( bIsHex || HTML_ISDIGIT(nNextCh) ); if ( bIsDecOrHex ) { if ( bIsHex ) { nNextCh = GetNextChar(); while ( HTML_ISHEXDIGIT(nNextCh) ) { cChar = cChar * 16U + ( nNextCh <= '9' ? sal_Unicode( nNextCh - '0' ) : ( nNextCh <= 'F' ? sal_Unicode( nNextCh - 'A' + 10 ) : sal_Unicode( nNextCh - 'a' + 10 ) ) ); nNextCh = GetNextChar(); } } else { do { cChar = cChar * 10U + sal_Unicode( nNextCh - '0'); nNextCh = GetNextChar(); } while( HTML_ISDIGIT(nNextCh) ); } if( RTL_TEXTENCODING_DONTKNOW != eSrcEnc && RTL_TEXTENCODING_UCS2 != eSrcEnc && RTL_TEXTENCODING_UTF8 != eSrcEnc && cChar < 256 ) { sal_Unicode cOrig = cChar; cChar = ByteString::ConvertToUnicode( (sal_Char)cChar, eSrcEnc ); if( 0U == cChar ) { // #73398#: If the character could not be // converted, because a conversion is not // available, do no conversion at all. cChar = cOrig; } } } // <-- else nNextCh = 0U; } else if( HTML_ISALPHA( nNextCh ) ) { ::rtl::OUStringBuffer sEntityBuffer( MAX_ENTITY_LEN ); xub_StrLen nPos = 0L; do { sEntityBuffer.append( nNextCh ); nPos++; nNextCh = GetNextChar(); } while( nPos < MAX_ENTITY_LEN && HTML_ISALNUM( nNextCh ) && !rInput.IsEof() ); if( IsParserWorking() && !rInput.IsEof() ) { String sEntity( sEntityBuffer.getStr(), nPos ); cChar = GetHTMLCharName( sEntity ); // nicht gefunden ( == 0 ), dann Klartext // oder ein Zeichen das als Attribut eingefuegt // wird if( 0U == cChar && ';' != nNextCh ) { DBG_ASSERT( rInput.Tell() - nStreamPos == (ULONG)(nPos+1L)*GetCharSize(), "UTF-8 geht hier schief" ); for( xub_StrLen i=nPos-1L; i>1L; i-- ) { nNextCh = sEntityBuffer[i]; sEntityBuffer.setLength( i ); sEntity.Assign( sEntityBuffer.getStr(), i ); cChar = GetHTMLCharName( sEntity ); if( cChar ) { rInput.SeekRel( -(long) ((nPos-i)*GetCharSize()) ); nlLinePos -= sal_uInt32(nPos-i); nPos = i; ClearTxtConvContext(); break; } } } if( !cChar ) // unbekanntes Zeichen? { // dann im Stream zurueck, das '&' als Zeichen // einfuegen und mit dem nachfolgenden Zeichen // wieder aufsetzen sTmpBuffer.append( (sal_Unicode)'&' ); // rInput.SeekRel( -(long)(++nPos*GetCharSize()) ); // nlLinePos -= nPos; DBG_ASSERT( rInput.Tell()-nStreamPos == (ULONG)(nPos+1)*GetCharSize(), "Falsche Stream-Position" ); DBG_ASSERT( nlLinePos-nLinePos == (ULONG)(nPos+1), "Falsche Zeilen-Position" ); rInput.Seek( nStreamPos ); nlLinePos = nLinePos; ClearTxtConvContext(); break; } // 1 == Non Breaking Space // 2 == SoftHyphen if( cChar < 3U ) { if( '>' == cBreak ) { // Wenn der Inhalt eines Tags gelesen wird, // muessen wir ein Space bzw. - daraus machen switch( cChar ) { case 1U: cChar = ' '; break; case 2U: cChar = '-'; break; default: DBG_ASSERT( cChar==1U, "\0x00 sollte doch schon laengt abgefangen sein!" ); break; } } else { // Wenn kein Tag gescannt wird, enstprechendes // Token zurueckgeben aToken += String( sTmpBuffer.makeStringAndClear() ); if( cChar ) { if( aToken.Len() ) { // mit dem Zeichen wieder aufsetzen nNextCh = '&'; // rInput.SeekRel( -(long)(++nPos*GetCharSize()) ); // nlLinePos -= nPos; DBG_ASSERT( rInput.Tell()-nStreamPos == (ULONG)(nPos+1)*GetCharSize(), "Falsche Stream-Position" ); DBG_ASSERT( nlLinePos-nLinePos == (ULONG)(nPos+1), "Falsche Zeilen-Position" ); rInput.Seek( nStreamPos ); nlLinePos = nLinePos; ClearTxtConvContext(); return HTML_TEXTTOKEN; } // Hack: _GetNextChar soll nicht das // naechste Zeichen lesen if( ';' != nNextCh ) aToken += ' '; if( 1U == cChar ) return HTML_NONBREAKSPACE; if( 2U == cChar ) return HTML_SOFTHYPH; } aToken += (sal_Unicode)'&'; aToken += String(sEntityBuffer.makeStringAndClear()); break; } } } else nNextCh = 0U; } // MIB 03/02/2000: &{...};-JavaScript-Macros are not // supported any longer. else if( IsParserWorking() ) { sTmpBuffer.append( (sal_Unicode)'&' ); bNextCh = FALSE; break; } bNextCh = (';' == nNextCh); if( cBreak=='>' && (cChar=='\\' || cChar=='\'' || cChar=='\"' || cChar==' ') ) { // ' und " mussen innerhalb von Tags mit einem // gekennzeichnet werden, um sie von ' und " als Klammern // um Optionen zu unterscheiden. Logischerweise muss // deshalb auch ein \ gekeenzeichnet werden. Ausserdem // schuetzen wir ein Space, weil es kein Trennzeichen // zwischen Optionen ist. sTmpBuffer.append( (sal_Unicode)'\\' ); if( MAX_LEN == sTmpBuffer.getLength() ) aToken += String(sTmpBuffer.makeStringAndClear()); } if( IsParserWorking() ) { if( cChar ) sTmpBuffer.append( cChar ); } else if( SVPAR_PENDING==eState && '>'!=cBreak ) { // Mit dem '&' Zeichen wieder aufsetzen, der Rest // wird als Texttoken zurueckgegeben. if( aToken.Len() || sTmpBuffer.getLength() ) { // Der bisherige Text wird von _GetNextChar() // zurueckgegeben und beim naechsten Aufruf wird // ein neues Zeichen gelesen. Also muessen wir uns // noch vor das & stellen. nNextCh = 0U; rInput.Seek( nStreamPos-(sal_uInt32)GetCharSize() ); nlLinePos = nLinePos-1; ClearTxtConvContext(); bReadNextChar = TRUE; } bNextCh = FALSE; } } break; case '=': if( '>'==cBreak && !cQuote ) bEqSignFound = TRUE; sTmpBuffer.append( nNextCh ); break; case '\\': if( '>'==cBreak ) { // Innerhalb von Tags kennzeichnen sTmpBuffer.append( (sal_Unicode)'\\' ); if( MAX_LEN == sTmpBuffer.getLength() ) aToken += String(sTmpBuffer.makeStringAndClear()); } sTmpBuffer.append( (sal_Unicode)'\\' ); break; case '\"': case '\'': if( '>'==cBreak ) { if( bEqSignFound ) cQuote = nNextCh; else if( cQuote && (cQuote==nNextCh ) ) cQuote = 0U; } sTmpBuffer.append( nNextCh ); bEqSignFound = FALSE; break; case sal_Unicode(EOF): if( rInput.IsEof() ) { // MIB 20.11.98: Das macht hier keinen Sinn, oder doch: Zumindest wird // abcä nicht angezeigt, also lassen wir das in Zukunft. // if( '>' != cBreak ) // eState = SVPAR_ACCEPTED; bWeiter = FALSE; } else { sTmpBuffer.append( nNextCh ); } break; case '<': bEqSignFound = FALSE; if( '>'==cBreak ) sTmpBuffer.append( nNextCh ); else bWeiter = FALSE; // Abbrechen, String zusammen break; case '\f': if( '>' == cBreak ) { // Beim Scannen von Optionen wie ein Space behandeln sTmpBuffer.append( (sal_Unicode)' ' ); } else { // sonst wird es ein eigenes Token bWeiter = FALSE; } break; case '\r': case '\n': if( '>'==cBreak ) { // #26979# cr/lf in Tag wird in _GetNextToken() behandeln sTmpBuffer.append( nNextCh ); break; } else if( bReadListing || bReadXMP || bReadPRE || bReadTextArea ) { bWeiter = FALSE; break; } // Bug 18984: CR-LF -> Blank // Folge von CR/LF/BLANK/TAB nur in ein Blank wandeln // kein break!! case '\t': if( '\t'==nNextCh && bReadPRE && '>'!=cBreak ) { // In
: Tabs nach oben durchreichen
                bWeiter = FALSE;
                break;
            }
            // kein break
        case '\x0b':
            if( '\x0b'==nNextCh && (bReadPRE || bReadXMP ||bReadListing) &&
                '>'!=cBreak )
            {
                break;
            }
            nNextCh = ' ';
            // kein break;
        case ' ':
            sTmpBuffer.append( nNextCh );
            if( '>'!=cBreak && (!bReadListing && !bReadXMP &&
                                !bReadPRE && !bReadTextArea) )
            {
                // alle Folgen von Blanks/Tabs/CR/LF zu einem Blank umwandeln
                do {
                    if( sal_Unicode(EOF) == (nNextCh = GetNextChar()) &&
                        rInput.IsEof() )
                    {
                        if( aToken.Len() || sTmpBuffer.getLength() > 1L )
                        {
                            // ausser den Blanks wurde noch etwas geselen
                            aToken += String(sTmpBuffer.makeStringAndClear());
                            return HTML_TEXTTOKEN;
                        }
                        else
                            // nur Blanks gelesen: dann darf kein Text
                            // mehr zurueckgegeben werden und _GetNextToken
                            // muss auf EOF laufen
                            return 0;
                    }
                } while ( ' ' == nNextCh || '\t' == nNextCh ||
                          '\r' == nNextCh || '\n' == nNextCh ||
                          '\x0b' == nNextCh );
                bNextCh = FALSE;
            }
            break;

        default:
            bEqSignFound = FALSE;
            if( (nNextCh==cBreak && !cQuote) ||
                (ULONG(aToken.Len()) + MAX_LEN) > ULONG(STRING_MAXLEN & ~1 ))
                bWeiter = FALSE;
            else
            {
                do {
                    // alle anderen Zeichen kommen in den Text
                    sTmpBuffer.append( nNextCh );
                    if( MAX_LEN == sTmpBuffer.getLength() )
                    {
                        aToken += String(sTmpBuffer.makeStringAndClear());
                        if( (ULONG(aToken.Len()) + MAX_LEN) >
                                ULONG(STRING_MAXLEN & ~1 ) )
                        {
                            nNextCh = GetNextChar();
                            return HTML_TEXTTOKEN;
                        }
                    }
                    if( ( sal_Unicode(EOF) == (nNextCh = GetNextChar()) &&
                          rInput.IsEof() ) ||
                        !IsParserWorking() )
                    {
                        if( sTmpBuffer.getLength() )
                            aToken += String(sTmpBuffer.makeStringAndClear());
                        return HTML_TEXTTOKEN;
                    }
                } while( HTML_ISALPHA( nNextCh ) || HTML_ISDIGIT( nNextCh ) );
                bNextCh = FALSE;
            }
        }

        if( MAX_LEN == sTmpBuffer.getLength() )
            aToken += String(sTmpBuffer.makeStringAndClear());

        if( bWeiter && bNextCh )
            nNextCh = GetNextChar();
    }

    if( sTmpBuffer.getLength() )
        aToken += String(sTmpBuffer.makeStringAndClear());

    return HTML_TEXTTOKEN;
}

int HTMLParser::_GetNextRawToken()
{
    ::rtl::OUStringBuffer sTmpBuffer( MAX_LEN );

    if( bEndTokenFound )
    {
        // beim letzten Aufruf haben wir das End-Token bereits gefunden,
        // deshalb muessen wir es nicht noch einmal suchen
        bReadScript = FALSE;
        bReadStyle = FALSE;
        aEndToken.Erase();
        bEndTokenFound = FALSE;

        return 0;
    }

    // per default geben wir HTML_RAWDATA zurueck
    int bWeiter = TRUE;
    int nToken = HTML_RAWDATA;
    SaveState( 0 );
    while( bWeiter && IsParserWorking() )
    {
        int bNextCh = TRUE;
        switch( nNextCh )
        {
        case '<':
            {
                // Vielleicht haben wir das Ende erreicht

                // das bisher gelesene erstmal retten
                aToken += String(sTmpBuffer.makeStringAndClear());

                // und die Position im Stream merken
                ULONG nStreamPos = rInput.Tell();
                ULONG nLineNr = GetLineNr();
                ULONG nLinePos = GetLinePos();

                // Start eines End-Token?
                int bOffState = FALSE;
                if( '/' == (nNextCh = GetNextChar()) )
                {
                    bOffState = TRUE;
                    nNextCh = GetNextChar();
                }
                else if( '!' == nNextCh )
                {
                    sTmpBuffer.append( nNextCh );
                    nNextCh = GetNextChar();
                }

                // jetzt die Buchstaben danach lesen
                while( (HTML_ISALPHA(nNextCh) || '-'==nNextCh) &&
                       IsParserWorking() && sTmpBuffer.getLength() < MAX_LEN )
                {
                    sTmpBuffer.append( nNextCh );
                    nNextCh = GetNextChar();
                }

                String aTok( sTmpBuffer.getStr(),
                             sal::static_int_cast< xub_StrLen >(
                                 sTmpBuffer.getLength()) );
                aTok.ToUpperAscii();
                BOOL bDone = FALSE;
                if( bReadScript || aEndToken.Len() )
                {
                    if( !bReadComment )
                    {
                        if( aTok.CompareToAscii( OOO_STRING_SVTOOLS_HTML_comment, 3 )
                                == COMPARE_EQUAL )
                        {
                            bReadComment = TRUE;
                        }
                        else
                        {
                            // ein Script muss mit "" aufhoehren, wobei
                            // wir es mit dem ">" aus sicherheitsgruenden
                            // erstmal nicht so genau nehmen
                            bDone = bOffState && // '>'==nNextCh &&
                            COMPARE_EQUAL == ( bReadScript
                                ? aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_script)
                                : aTok.CompareTo(aEndToken) );
                        }
                    }
                    if( bReadComment && '>'==nNextCh && aTok.Len() >= 2 &&
                        aTok.Copy( aTok.Len()-2 ).EqualsAscii( "--" ) )
                    {
                        // hier ist ein Kommentar der Art  zuende
                        bReadComment = FALSE;
                    }
                }
                else
                {
                    // ein Style-Sheet kann mit ,  oder
                    //  aughoehren
                    if( bOffState )
                        bDone = aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_style)
                                    == COMPARE_EQUAL ||
                                aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_head)
                                    == COMPARE_EQUAL;
                    else
                        bDone =
                            aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_body) == COMPARE_EQUAL;
                }

                if( bDone )
                {
                    // das war's, jetzt muessen wir gegebenenfalls den
                    // bisher gelesenen String zurueckgeben und dnach normal
                    // weitermachen

                    bWeiter = FALSE;

                    // nToken==0 heisst, dass _GetNextToken gleich weiterliest
                    if( !aToken.Len() && (bReadStyle || bReadScript) )
                    {
                        // wir koennen sofort die Umgebung beeden und
                        // das End-Token parsen
                        bReadScript = FALSE;
                        bReadStyle = FALSE;
                        aEndToken.Erase();
                        nToken = 0;
                    }
                    else
                    {
                        // wir muessen bReadScript/bReadStyle noch am
                        // Leben lassen und koennen erst beim naechsten
                        // mal das End-Token Parsen
                        bEndTokenFound = TRUE;
                    }

                    // jetzt fahren wir im Stream auf das '<' zurueck
                    rInput.Seek( nStreamPos );
                    SetLineNr( nLineNr );
                    SetLinePos( nLinePos );
                    ClearTxtConvContext();
                    nNextCh = '<';

                    // den String wollen wir nicht an das Token haengen
                    sTmpBuffer.setLength( 0L );
                }
                else
                {
                    // "' == nNextCh && IsParserWorking() && bTwoMinus )
                    bReadComment = FALSE;

                bNextCh = FALSE;
            }
            break;

        case '\r':
            // \r\n? beendet das aktuelle Text-Token (auch wenn es leer ist)
            nNextCh = GetNextChar();
            if( nNextCh=='\n' )
                nNextCh = GetNextChar();
            bWeiter = FALSE;
            break;
        case '\n':
            // \n beendet das aktuelle Text-Token (auch wenn es leer ist)
            nNextCh = GetNextChar();
            bWeiter = FALSE;
            break;
        case sal_Unicode(EOF):
            // eof beendet das aktuelle Text-Token und tut so, als ob
            // ein End-Token gelesen wurde
            if( rInput.IsEof() )
            {
                bWeiter = FALSE;
                if( aToken.Len() || sTmpBuffer.getLength() )
                {
                    bEndTokenFound = TRUE;
                }
                else
                {
                    bReadScript = FALSE;
                    bReadStyle = FALSE;
                    aEndToken.Erase();
                    nToken = 0;
                }
                break;
            }
            // kein break
        default:
            // alle anderen Zeichen landen im Buffer
            sTmpBuffer.append( nNextCh );
            break;
        }

        if( (!bWeiter && sTmpBuffer.getLength() > 0L) ||
            MAX_LEN == sTmpBuffer.getLength() )
            aToken += String(sTmpBuffer.makeStringAndClear());

        if( bWeiter && bNextCh )
            nNextCh = GetNextChar();
    }

    if( IsParserWorking() )
        SaveState( 0 );
    else
        nToken = 0;

    return nToken;
}

// scanne das naechste Token,
int __EXPORT HTMLParser::_GetNextToken()
{
    int nRet = 0;
    sSaveToken.Erase();

    // die Optionen loeschen
    if( pOptions->Count() )
        pOptions->DeleteAndDestroy( 0, pOptions->Count() );

    if( !IsParserWorking() )        // wenn schon Fehler, dann nicht weiter!
        return 0;

    BOOL bReadNextCharSave = bReadNextChar;
    if( bReadNextChar )
    {
        DBG_ASSERT( !bEndTokenFound,
                    " gelesen und trotzdem noch ein Zeichen lesen?" );
        nNextCh = GetNextChar();
        if( !IsParserWorking() )        // wenn schon Fehler, dann nicht weiter!
            return 0;
        bReadNextChar = FALSE;
    }

    if( bReadScript || bReadStyle || aEndToken.Len() )
    {
        nRet = _GetNextRawToken();
        if( nRet || !IsParserWorking() )
            return nRet;
    }

    do {
        int bNextCh = TRUE;
        switch( nNextCh )
        {
        case '<':
            {
                ULONG nStreamPos = rInput.Tell();
                ULONG nLineNr = GetLineNr();
                ULONG nLinePos = GetLinePos();

                int bOffState = FALSE;
                if( '/' == (nNextCh = GetNextChar()) )
                {
                    bOffState = TRUE;
                    nNextCh = GetNextChar();
                }
                if( HTML_ISALPHA( nNextCh ) || '!'==nNextCh ) // fix #26984#
                {
                    ::rtl::OUStringBuffer sTmpBuffer;
                    do {
                        sTmpBuffer.append( nNextCh );
                        if( MAX_LEN == sTmpBuffer.getLength() )
                            aToken += String(sTmpBuffer.makeStringAndClear());
                        nNextCh = GetNextChar();
                    } while( '>' != nNextCh && !HTML_ISSPACE( nNextCh ) &&
                             IsParserWorking() && !rInput.IsEof() );

                    if( sTmpBuffer.getLength() )
                        aToken += String(sTmpBuffer.makeStringAndClear());

                    // Blanks ueberlesen
                    while( HTML_ISSPACE( nNextCh ) && IsParserWorking() )
                        nNextCh = GetNextChar();

                    if( !IsParserWorking() )
                    {
                        if( SVPAR_PENDING == eState )
                            bReadNextChar = bReadNextCharSave;
                        break;
                    }

                    // suche das Token in der Tabelle:
                    sSaveToken = aToken;
                    aToken.ToUpperAscii();
                    if( 0 == (nRet = GetHTMLToken( aToken )) )
                        // Unknown Control
                        nRet = HTML_UNKNOWNCONTROL_ON;

                    // Wenn es ein Token zum ausschalten ist ...
                    if( bOffState )
                    {
                         if( HTML_TOKEN_ONOFF & nRet )
                         {
                            // und es ein Off-Token gibt, das daraus machen
                            ++nRet;
                         }
                         else if( HTML_LINEBREAK!=nRet )
                         {
                            // und es kein Off-Token gibt, ein unbekanntes
                            // Token daraus machen (ausser 
, das wird // wie
behandelt nRet = HTML_UNKNOWNCONTROL_OFF; } } if( nRet == HTML_COMMENT ) { // fix: sSaveToken wegen Gross-/Kleinschreibung // als Anfang des Kommentars benutzen und ein // Space anhaengen. aToken = sSaveToken; if( '>'!=nNextCh ) aToken += (sal_Unicode)' '; ULONG nCStreamPos = 0; ULONG nCLineNr = 0; ULONG nCLinePos = 0; xub_StrLen nCStrLen = 0; BOOL bDone = FALSE; // bis zum schliessenden --> lesen. wenn keins gefunden // wurde beim der ersten > wieder aufsetzen while( !bDone && !rInput.IsEof() && IsParserWorking() ) { if( '>'==nNextCh ) { if( !nCStreamPos ) { nCStreamPos = rInput.Tell(); nCStrLen = aToken.Len(); nCLineNr = GetLineNr(); nCLinePos = GetLinePos(); } bDone = aToken.Len() >= 2 && aToken.Copy(aToken.Len()-2,2). EqualsAscii( "--" ); if( !bDone ) aToken += nNextCh; } else aToken += nNextCh; if( !bDone ) nNextCh = GetNextChar(); } if( !bDone && IsParserWorking() && nCStreamPos ) { rInput.Seek( nCStreamPos ); SetLineNr( nCLineNr ); SetLinePos( nCLinePos ); ClearTxtConvContext(); aToken.Erase( nCStrLen ); nNextCh = '>'; } } else { // den TokenString koennen wir jetzt verwerfen aToken.Erase(); } // dann lesen wir mal alles bis zur schliessenden '>' if( '>' != nNextCh && IsParserWorking() ) { ScanText( '>' ); if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() ) { // zurueck hinter die < gehen und dort neu // aufsetzen, das < als Text zurueckgeben rInput.Seek( nStreamPos ); SetLineNr( nLineNr ); SetLinePos( nLinePos ); ClearTxtConvContext(); aToken = '<'; nRet = HTML_TEXTTOKEN; nNextCh = GetNextChar(); bNextCh = FALSE; break; } } if( SVPAR_PENDING == eState ) bReadNextChar = bReadNextCharSave; } else { if( bOffState ) { // einfach alles wegschmeissen ScanText( '>' ); if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() ) { // zurueck hinter die < gehen und dort neu // aufsetzen, das < als Text zurueckgeben rInput.Seek( nStreamPos ); SetLineNr( nLineNr ); SetLinePos( nLinePos ); ClearTxtConvContext(); aToken = '<'; nRet = HTML_TEXTTOKEN; nNextCh = GetNextChar(); bNextCh = FALSE; break; } if( SVPAR_PENDING == eState ) bReadNextChar = bReadNextCharSave; aToken.Erase(); } else if( '%' == nNextCh ) { nRet = HTML_UNKNOWNCONTROL_ON; ULONG nCStreamPos = rInput.Tell(); ULONG nCLineNr = GetLineNr(), nCLinePos = GetLinePos(); BOOL bDone = FALSE; // bis zum schliessenden %> lesen. wenn keins gefunden // wurde beim der ersten > wieder aufsetzen while( !bDone && !rInput.IsEof() && IsParserWorking() ) { bDone = '>'==nNextCh && aToken.Len() >= 1 && '%' == aToken.GetChar( aToken.Len()-1 ); if( !bDone ) { aToken += nNextCh; nNextCh = GetNextChar(); } } if( !bDone && IsParserWorking() ) { rInput.Seek( nCStreamPos ); SetLineNr( nCLineNr ); SetLinePos( nCLinePos ); ClearTxtConvContext(); aToken.AssignAscii( "<%", 2 ); nRet = HTML_TEXTTOKEN; break; } if( IsParserWorking() ) { sSaveToken = aToken; aToken.Erase(); } } else { aToken = '<'; nRet = HTML_TEXTTOKEN; bNextCh = FALSE; break; } } if( IsParserWorking() ) { bNextCh = '>' == nNextCh; switch( nRet ) { case HTML_TEXTAREA_ON: bReadTextArea = TRUE; break; case HTML_TEXTAREA_OFF: bReadTextArea = FALSE; break; case HTML_SCRIPT_ON: if( !bReadTextArea ) bReadScript = TRUE; break; case HTML_SCRIPT_OFF: if( !bReadTextArea ) { bReadScript = FALSE; // JavaScript kann den Stream veraendern // also muss das letzte Zeichen nochmals // gelesen werden bReadNextChar = TRUE; bNextCh = FALSE; } break; case HTML_STYLE_ON: bReadStyle = TRUE; break; case HTML_STYLE_OFF: bReadStyle = FALSE; break; } } } break; case sal_Unicode(EOF): if( rInput.IsEof() ) { eState = SVPAR_ACCEPTED; nRet = nNextCh; } else { // normalen Text lesen goto scan_text; } break; case '\f': // Form-Feeds werden jetzt extra nach oben gereicht nRet = HTML_LINEFEEDCHAR; // !!! eigentlich FORMFEEDCHAR break; case '\n': case '\r': if( bReadListing || bReadXMP || bReadPRE || bReadTextArea ) { sal_Unicode c = GetNextChar(); if( ( '\n' != nNextCh || '\r' != c ) && ( '\r' != nNextCh || '\n' != c ) ) { bNextCh = FALSE; nNextCh = c; } nRet = HTML_NEWPARA; break; } // kein break ! case '\t': if( bReadPRE ) { nRet = HTML_TABCHAR; break; } // kein break ! case ' ': // kein break ! default: scan_text: // es folgt "normaler" Text nRet = ScanText(); bNextCh = 0 == aToken.Len(); // der Text sollte noch verarbeitet werden if( !bNextCh && eState == SVPAR_PENDING ) { eState = SVPAR_WORKING; bReadNextChar = TRUE; } break; } if( bNextCh && SVPAR_WORKING == eState ) { nNextCh = GetNextChar(); if( SVPAR_PENDING == eState && nRet && HTML_TEXTTOKEN != nRet ) { bReadNextChar = TRUE; eState = SVPAR_WORKING; } } } while( !nRet && SVPAR_WORKING == eState ); if( SVPAR_PENDING == eState ) nRet = -1; // irgendwas ungueltiges return nRet; } void HTMLParser::UnescapeToken() { xub_StrLen nPos=0; BOOL bEscape = FALSE; while( nPos < aToken.Len() ) { BOOL bOldEscape = bEscape; bEscape = FALSE; if( '\\'==aToken.GetChar(nPos) && !bOldEscape ) { aToken.Erase( nPos, 1 ); bEscape = TRUE; } else { nPos++; } } } // hole die Optionen const HTMLOptions *HTMLParser::GetOptions( USHORT *pNoConvertToken ) const { // wenn die Option fuer das aktuelle Token schon einmal // geholt wurden, geben wir sie noch einmal zurueck if( pOptions->Count() ) return pOptions; xub_StrLen nPos = 0; while( nPos < aToken.Len() ) { // ein Zeichen ? Dann faengt hier eine Option an if( HTML_ISALPHA( aToken.GetChar(nPos) ) ) { int nToken; String aValue; xub_StrLen nStt = nPos; sal_Unicode cChar = 0; // Eigentlich sind hier nur ganz bestimmte Zeichen erlaubt. // Netscape achtet aber nur auf "=" und Leerzeichen (siehe // Mozilla: PA_FetchRequestedNameValues in // lipparse/pa_mdl.c // while( nPos < aToken.Len() && // ( '-'==(c=aToken[nPos]) || isalnum(c) || '.'==c || '_'==c) ) while( nPos < aToken.Len() && '=' != (cChar=aToken.GetChar(nPos)) && HTML_ISPRINTABLE(cChar) && !HTML_ISSPACE(cChar) ) nPos++; String sName( aToken.Copy( nStt, nPos-nStt ) ); //JP 23.03.97: die PlugIns wollen die TokenName im "Original" haben // also nur fuers Suchen in UpperCase wandeln String sNameUpperCase( sName ); sNameUpperCase.ToUpperAscii(); nToken = GetHTMLOption( sNameUpperCase ); // der Name ist fertig DBG_ASSERTWARNING( nToken!=HTML_O_UNKNOWN, "GetOption: unbekannte HTML-Option" ); BOOL bStripCRLF = (nToken < HTML_OPTION_SCRIPT_START || nToken >= HTML_OPTION_SCRIPT_END) && (!pNoConvertToken || nToken != *pNoConvertToken); while( nPos < aToken.Len() && ( !HTML_ISPRINTABLE( (cChar=aToken.GetChar(nPos)) ) || HTML_ISSPACE(cChar) ) ) nPos++; // hat die Option auch einen Wert? if( nPos!=aToken.Len() && '='==cChar ) { nPos++; while( nPos < aToken.Len() && ( !HTML_ISPRINTABLE( (cChar=aToken.GetChar(nPos)) ) || ' '==cChar || '\t'==cChar || '\r'==cChar || '\n'==cChar ) ) nPos++; if( nPos != aToken.Len() ) { xub_StrLen nLen = 0; nStt = nPos; if( ('"'==cChar) || ('\'')==cChar ) { sal_Unicode cEnd = cChar; nPos++; nStt++; BOOL bDone = FALSE; BOOL bEscape = FALSE; while( nPos < aToken.Len() && !bDone ) { BOOL bOldEscape = bEscape; bEscape = FALSE; cChar = aToken.GetChar(nPos); switch( cChar ) { case '\r': case '\n': if( bStripCRLF ) ((String &)aToken).Erase( nPos, 1 ); else nPos++, nLen++; break; case '\\': if( bOldEscape ) { nPos++, nLen++; } else { ((String &)aToken).Erase( nPos, 1 ); bEscape = TRUE; } break; case '"': case '\'': bDone = !bOldEscape && cChar==cEnd; if( !bDone ) nPos++, nLen++; break; default: nPos++, nLen++; break; } } if( nPos!=aToken.Len() ) nPos++; } else { // hier sind wir etwas laxer als der // Standard und erlauben alles druckbare BOOL bEscape = FALSE; BOOL bDone = FALSE; while( nPos < aToken.Len() && !bDone ) { BOOL bOldEscape = bEscape; bEscape = FALSE; sal_Unicode c = aToken.GetChar(nPos); switch( c ) { case ' ': bDone = !bOldEscape; if( !bDone ) nPos++, nLen++; break; case '\t': case '\r': case '\n': bDone = TRUE; break; case '\\': if( bOldEscape ) { nPos++, nLen++; } else { ((String &)aToken).Erase( nPos, 1 ); bEscape = TRUE; } break; default: if( HTML_ISPRINTABLE( c ) ) nPos++, nLen++; else bDone = TRUE; break; } } } if( nLen ) aValue = aToken.Copy( nStt, nLen ); } } // Wir kennen das Token und koennen es Speichern HTMLOption *pOption = new HTMLOption( sal::static_int_cast< sal_uInt16 >(nToken), sName, aValue ); pOptions->Insert( pOption, pOptions->Count() ); } else // white space un unerwartete Zeichen ignorieren wie nPos++; } return pOptions; } int HTMLParser::FilterPRE( int nToken ) { switch( nToken ) { #ifdef HTML_BEHAVIOUR // diese werden laut Definition zu LFs case HTML_PARABREAK_ON: case HTML_LINEBREAK: nToken = HTML_NEWPARA; #else // in Netscape zeigen sie aber nur in nicht-leeren Absaetzen Wirkung case HTML_PARABREAK_ON: nToken = HTML_LINEBREAK; case HTML_LINEBREAK: #endif case HTML_NEWPARA: nPre_LinePos = 0; if( bPre_IgnoreNewPara ) nToken = 0; break; case HTML_TABCHAR: { xub_StrLen nSpaces = sal::static_int_cast< xub_StrLen >( 8 - (nPre_LinePos % 8)); DBG_ASSERT( !aToken.Len(), "Wieso ist das Token nicht leer?" ); aToken.Expand( nSpaces, ' ' ); nPre_LinePos += nSpaces; nToken = HTML_TEXTTOKEN; } break; // diese bleiben erhalten case HTML_TEXTTOKEN: nPre_LinePos += aToken.Len(); break; case HTML_SELECT_ON: case HTML_SELECT_OFF: case HTML_BODY_ON: case HTML_FORM_ON: case HTML_FORM_OFF: case HTML_INPUT: case HTML_OPTION: case HTML_TEXTAREA_ON: case HTML_TEXTAREA_OFF: case HTML_IMAGE: case HTML_APPLET_ON: case HTML_APPLET_OFF: case HTML_PARAM: case HTML_EMBED: case HTML_HEAD1_ON: case HTML_HEAD1_OFF: case HTML_HEAD2_ON: case HTML_HEAD2_OFF: case HTML_HEAD3_ON: case HTML_HEAD3_OFF: case HTML_HEAD4_ON: case HTML_HEAD4_OFF: case HTML_HEAD5_ON: case HTML_HEAD5_OFF: case HTML_HEAD6_ON: case HTML_HEAD6_OFF: case HTML_BLOCKQUOTE_ON: case HTML_BLOCKQUOTE_OFF: case HTML_ADDRESS_ON: case HTML_ADDRESS_OFF: case HTML_HORZRULE: case HTML_CENTER_ON: case HTML_CENTER_OFF: case HTML_DIVISION_ON: case HTML_DIVISION_OFF: case HTML_SCRIPT_ON: case HTML_SCRIPT_OFF: case HTML_RAWDATA: case HTML_TABLE_ON: case HTML_TABLE_OFF: case HTML_CAPTION_ON: case HTML_CAPTION_OFF: case HTML_COLGROUP_ON: case HTML_COLGROUP_OFF: case HTML_COL_ON: case HTML_COL_OFF: case HTML_THEAD_ON: case HTML_THEAD_OFF: case HTML_TFOOT_ON: case HTML_TFOOT_OFF: case HTML_TBODY_ON: case HTML_TBODY_OFF: case HTML_TABLEROW_ON: case HTML_TABLEROW_OFF: case HTML_TABLEDATA_ON: case HTML_TABLEDATA_OFF: case HTML_TABLEHEADER_ON: case HTML_TABLEHEADER_OFF: case HTML_ANCHOR_ON: case HTML_ANCHOR_OFF: case HTML_BOLD_ON: case HTML_BOLD_OFF: case HTML_ITALIC_ON: case HTML_ITALIC_OFF: case HTML_STRIKE_ON: case HTML_STRIKE_OFF: case HTML_STRIKETHROUGH_ON: case HTML_STRIKETHROUGH_OFF: case HTML_UNDERLINE_ON: case HTML_UNDERLINE_OFF: case HTML_BASEFONT_ON: case HTML_BASEFONT_OFF: case HTML_FONT_ON: case HTML_FONT_OFF: case HTML_BLINK_ON: case HTML_BLINK_OFF: case HTML_SPAN_ON: case HTML_SPAN_OFF: case HTML_SUBSCRIPT_ON: case HTML_SUBSCRIPT_OFF: case HTML_SUPERSCRIPT_ON: case HTML_SUPERSCRIPT_OFF: case HTML_BIGPRINT_ON: case HTML_BIGPRINT_OFF: case HTML_SMALLPRINT_OFF: case HTML_SMALLPRINT_ON: case HTML_EMPHASIS_ON: case HTML_EMPHASIS_OFF: case HTML_CITIATION_ON: case HTML_CITIATION_OFF: case HTML_STRONG_ON: case HTML_STRONG_OFF: case HTML_CODE_ON: case HTML_CODE_OFF: case HTML_SAMPLE_ON: case HTML_SAMPLE_OFF: case HTML_KEYBOARD_ON: case HTML_KEYBOARD_OFF: case HTML_VARIABLE_ON: case HTML_VARIABLE_OFF: case HTML_DEFINSTANCE_ON: case HTML_DEFINSTANCE_OFF: case HTML_SHORTQUOTE_ON: case HTML_SHORTQUOTE_OFF: case HTML_LANGUAGE_ON: case HTML_LANGUAGE_OFF: case HTML_AUTHOR_ON: case HTML_AUTHOR_OFF: case HTML_PERSON_ON: case HTML_PERSON_OFF: case HTML_ACRONYM_ON: case HTML_ACRONYM_OFF: case HTML_ABBREVIATION_ON: case HTML_ABBREVIATION_OFF: case HTML_INSERTEDTEXT_ON: case HTML_INSERTEDTEXT_OFF: case HTML_DELETEDTEXT_ON: case HTML_DELETEDTEXT_OFF: case HTML_TELETYPE_ON: case HTML_TELETYPE_OFF: break; // der Rest wird als unbekanntes Token behandelt default: if( nToken ) { nToken = ( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken)) ? HTML_UNKNOWNCONTROL_OFF : HTML_UNKNOWNCONTROL_ON ); } break; } bPre_IgnoreNewPara = FALSE; return nToken; } int HTMLParser::FilterXMP( int nToken ) { switch( nToken ) { case HTML_NEWPARA: if( bPre_IgnoreNewPara ) nToken = 0; case HTML_TEXTTOKEN: case HTML_NONBREAKSPACE: case HTML_SOFTHYPH: break; // bleiben erhalten default: if( nToken ) { if( (HTML_TOKEN_ONOFF & nToken) && (1 & nToken) ) { sSaveToken.Insert( '<', 0 ); sSaveToken.Insert( '/', 1 ); } else sSaveToken.Insert( '<', 0 ); if( aToken.Len() ) { UnescapeToken(); sSaveToken += (sal_Unicode)' '; aToken.Insert( sSaveToken, 0 ); } else aToken = sSaveToken; aToken += (sal_Unicode)'>'; nToken = HTML_TEXTTOKEN; } break; } bPre_IgnoreNewPara = FALSE; return nToken; } int HTMLParser::FilterListing( int nToken ) { switch( nToken ) { case HTML_NEWPARA: if( bPre_IgnoreNewPara ) nToken = 0; case HTML_TEXTTOKEN: case HTML_NONBREAKSPACE: case HTML_SOFTHYPH: break; // bleiben erhalten default: if( nToken ) { nToken = ( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken)) ? HTML_UNKNOWNCONTROL_OFF : HTML_UNKNOWNCONTROL_ON ); } break; } bPre_IgnoreNewPara = FALSE; return nToken; } FASTBOOL HTMLParser::IsHTMLFormat( const sal_Char* pHeader, BOOL bSwitchToUCS2, rtl_TextEncoding eEnc ) { // Einer der folgenden regulaeren Ausdrucke muss sich auf den String // anwenden lassen, damit das Dok ein HTML-Dokument ist. // // ^[^<]*<[^ \t]*[> \t] // ------- // ^' kommen sal_Char c; xub_StrLen nPos; for( nPos = nStart; nPos'==(c=sCmp.GetChar(nPos)) || HTML_ISSPACE(c) ) break; } // wenn das Dokeument hinter dem < aufhoert ist es wohl kein HTML if( nPos==nStart ) return FALSE; // die Zeichenkette nach dem '<' muss ausserdem ein bekanntes // HTML Token sein. Damit die Ausgabe eines DOS-dir-Befehls nicht // als HTML interpretiert wird, wird ein jedoch nicht als HTML // interpretiert. String sTest( sCmp.Copy( nStart, nPos-nStart ), RTL_TEXTENCODING_ASCII_US ); int nTok = GetHTMLToken( sTest ); if( 0 != nTok && HTML_DIRLIST_ON != nTok ) return TRUE; // oder es handelt sich um ein " in den ersten 80 Zeichen nStart = sCmp.Search( OOO_STRING_SVTOOLS_HTML_html ); if( nStart!=STRING_NOTFOUND && nStart>0 && '<'==sCmp.GetChar(nStart-1) && nStart+4 < sCmp.Len() && '>'==sCmp.GetChar(nStart+4) ) return TRUE; // sonst ist es wohl doch eher kein HTML-Dokument return FALSE; } BOOL HTMLParser::InternalImgToPrivateURL( String& rURL ) { if( rURL.Len() < 19 || 'i' != rURL.GetChar(0) || rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_gopher, 9 ) != COMPARE_EQUAL ) return FALSE; BOOL bFound = FALSE; if( rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_gopher,16) == COMPARE_EQUAL ) { String aName( rURL.Copy(16) ); switch( aName.GetChar(0) ) { case 'b': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_binary ); break; case 'i': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_image ) || aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_index ); break; case 'm': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_menu ) || aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_movie ); break; case 's': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_sound ); break; case 't': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_telnet ) || aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_text ); break; case 'u': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_unknown ); break; } } else if( rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_icon,14) == COMPARE_EQUAL ) { String aName( rURL.Copy(14) ); switch( aName.GetChar(0) ) { case 'b': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_baddata ); break; case 'd': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_delayed ); break; case 'e': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_embed ); break; case 'i': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_insecure ); break; case 'n': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_notfound ); break; } } if( bFound ) { String sTmp ( rURL ); rURL.AssignAscii( OOO_STRING_SVTOOLS_HTML_private_image ); rURL.Append( sTmp ); } return bFound; } #ifdef USED void HTMLParser::SaveState( int nToken ) { SvParser::SaveState( nToken ); } void HTMLParser::RestoreState() { SvParser::RestoreState(); } #endif enum eHtmlMetas { HTML_META_NONE = 0, HTML_META_AUTHOR, HTML_META_DESCRIPTION, HTML_META_KEYWORDS, HTML_META_REFRESH, HTML_META_CLASSIFICATION, HTML_META_CREATED, HTML_META_CHANGEDBY, HTML_META_CHANGED, HTML_META_GENERATOR, HTML_META_SDFOOTNOTE, HTML_META_SDENDNOTE, HTML_META_CONTENT_TYPE }; // static HTMLOptionEnum __READONLY_DATA aHTMLMetaNameTable[] = { { OOO_STRING_SVTOOLS_HTML_META_author, HTML_META_AUTHOR }, { OOO_STRING_SVTOOLS_HTML_META_changed, HTML_META_CHANGED }, { OOO_STRING_SVTOOLS_HTML_META_changedby, HTML_META_CHANGEDBY }, { OOO_STRING_SVTOOLS_HTML_META_classification,HTML_META_CLASSIFICATION}, { OOO_STRING_SVTOOLS_HTML_META_content_type, HTML_META_CONTENT_TYPE }, { OOO_STRING_SVTOOLS_HTML_META_created, HTML_META_CREATED }, { OOO_STRING_SVTOOLS_HTML_META_description, HTML_META_DESCRIPTION }, { OOO_STRING_SVTOOLS_HTML_META_keywords, HTML_META_KEYWORDS }, { OOO_STRING_SVTOOLS_HTML_META_generator, HTML_META_GENERATOR }, { OOO_STRING_SVTOOLS_HTML_META_refresh, HTML_META_REFRESH }, { OOO_STRING_SVTOOLS_HTML_META_sdendnote, HTML_META_SDENDNOTE }, { OOO_STRING_SVTOOLS_HTML_META_sdfootnote, HTML_META_SDFOOTNOTE }, { 0, 0 } }; void HTMLParser::AddMetaUserDefined( ::rtl::OUString const & ) { } bool HTMLParser::ParseMetaOptionsImpl( const uno::Reference & i_xDocProps, SvKeyValueIterator *i_pHTTPHeader, const HTMLOptions *i_pOptions, rtl_TextEncoding& o_rEnc ) { String aName, aContent; USHORT nAction = HTML_META_NONE; bool bHTTPEquiv = false, bChanged = false; for ( USHORT i = i_pOptions->Count(); i; ) { const HTMLOption *pOption = (*i_pOptions)[ --i ]; switch ( pOption->GetToken() ) { case HTML_O_NAME: aName = pOption->GetString(); if ( HTML_META_NONE==nAction ) { pOption->GetEnum( nAction, aHTMLMetaNameTable ); } break; case HTML_O_HTTPEQUIV: aName = pOption->GetString(); pOption->GetEnum( nAction, aHTMLMetaNameTable ); bHTTPEquiv = true; break; case HTML_O_CONTENT: aContent = pOption->GetString(); break; } } if ( bHTTPEquiv || HTML_META_DESCRIPTION != nAction ) { // if it is not a Description, remove CRs and LFs from CONTENT aContent.EraseAllChars( _CR ); aContent.EraseAllChars( _LF ); } else { // convert line endings for Description aContent.ConvertLineEnd(); } if ( bHTTPEquiv && i_pHTTPHeader ) { // #57232#: Netscape seems to just ignore a closing ", so we do too if ( aContent.Len() && '"' == aContent.GetChar( aContent.Len()-1 ) ) { aContent.Erase( aContent.Len() - 1 ); } SvKeyValue aKeyValue( aName, aContent ); i_pHTTPHeader->Append( aKeyValue ); } switch ( nAction ) { case HTML_META_AUTHOR: if (i_xDocProps.is()) { i_xDocProps->setAuthor( aContent ); bChanged = true; } break; case HTML_META_DESCRIPTION: if (i_xDocProps.is()) { i_xDocProps->setDescription( aContent ); bChanged = true; } break; case HTML_META_KEYWORDS: if (i_xDocProps.is()) { i_xDocProps->setKeywords( ::comphelper::string::convertCommaSeparated(aContent)); bChanged = true; } break; case HTML_META_CLASSIFICATION: if (i_xDocProps.is()) { i_xDocProps->setSubject( aContent ); bChanged = true; } break; case HTML_META_CHANGEDBY: if (i_xDocProps.is()) { i_xDocProps->setModifiedBy( aContent ); } break; case HTML_META_CREATED: case HTML_META_CHANGED: if ( i_xDocProps.is() && aContent.Len() && aContent.GetTokenCount() == 2 ) { Date aDate( (ULONG)aContent.GetToken(0).ToInt32() ); Time aTime( (ULONG)aContent.GetToken(1).ToInt32() ); DateTime aDateTime( aDate, aTime ); ::util::DateTime uDT(aDateTime.Get100Sec(), aDateTime.GetSec(), aDateTime.GetMin(), aDateTime.GetHour(), aDateTime.GetDay(), aDateTime.GetMonth(), aDateTime.GetYear()); if ( HTML_META_CREATED==nAction ) i_xDocProps->setCreationDate( uDT ); else i_xDocProps->setModificationDate( uDT ); bChanged = true; } break; case HTML_META_REFRESH: DBG_ASSERT( !bHTTPEquiv || i_pHTTPHeader, "Reload-URL aufgrund unterlassener MUSS-Aenderung verlorengegangen" ); break; case HTML_META_CONTENT_TYPE: if ( aContent.Len() ) { o_rEnc = GetEncodingByMIME( aContent ); } break; case HTML_META_NONE: if ( !bHTTPEquiv ) { if (i_xDocProps.is()) { uno::Reference xUDProps = i_xDocProps->getUserDefinedProperties(); try { xUDProps->addProperty(aName, beans::PropertyAttribute::REMOVEABLE, uno::makeAny(::rtl::OUString(aContent))); AddMetaUserDefined(aName); bChanged = true; } catch (uno::Exception &) { // ignore } } } break; default: break; } return bChanged; } bool HTMLParser::ParseMetaOptions( const uno::Reference & i_xDocProps, SvKeyValueIterator *i_pHeader ) { USHORT nContentOption = HTML_O_CONTENT; rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW; bool bRet = ParseMetaOptionsImpl( i_xDocProps, i_pHeader, GetOptions(&nContentOption), eEnc ); // If the encoding is set by a META tag, it may only overwrite the // current encoding if both, the current and the new encoding, are 1-BYTE // encodings. Everything else cannot lead to reasonable results. if (RTL_TEXTENCODING_DONTKNOW != eEnc && rtl_isOctetTextEncoding( eEnc ) && rtl_isOctetTextEncoding( GetSrcEncoding() ) ) { eEnc = GetExtendedCompatibilityTextEncoding( eEnc ); // #89973# SetSrcEncoding( eEnc ); } return bRet; } rtl_TextEncoding HTMLParser::GetEncodingByMIME( const String& rMime ) { ByteString sType; ByteString sSubType; INetContentTypeParameterList aParameters; ByteString sMime( rMime, RTL_TEXTENCODING_ASCII_US ); if (INetContentTypes::parse(sMime, sType, sSubType, &aParameters)) { const INetContentTypeParameter * pCharset = aParameters.find("charset"); if (pCharset != 0) { ByteString sValue( pCharset->m_sValue, RTL_TEXTENCODING_ASCII_US ); return GetExtendedCompatibilityTextEncoding( rtl_getTextEncodingFromMimeCharset( sValue.GetBuffer() ) ); } } return RTL_TEXTENCODING_DONTKNOW; } rtl_TextEncoding HTMLParser::GetEncodingByHttpHeader( SvKeyValueIterator *pHTTPHeader ) { rtl_TextEncoding eRet = RTL_TEXTENCODING_DONTKNOW; if( pHTTPHeader ) { SvKeyValue aKV; for( BOOL bCont = pHTTPHeader->GetFirst( aKV ); bCont; bCont = pHTTPHeader->GetNext( aKV ) ) { if( aKV.GetKey().EqualsIgnoreCaseAscii( OOO_STRING_SVTOOLS_HTML_META_content_type ) ) { if( aKV.GetValue().Len() ) { eRet = HTMLParser::GetEncodingByMIME( aKV.GetValue() ); } } } } return eRet; } BOOL HTMLParser::SetEncodingByHTTPHeader( SvKeyValueIterator *pHTTPHeader ) { BOOL bRet = FALSE; rtl_TextEncoding eEnc = HTMLParser::GetEncodingByHttpHeader( pHTTPHeader ); if(RTL_TEXTENCODING_DONTKNOW != eEnc) { SetSrcEncoding( eEnc ); bRet = TRUE; } return bRet; }