xref: /AOO41X/main/svtools/source/svhtml/parhtml.cxx (revision 5900e8ec128faec89519683efce668ccd8cc6084)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svtools.hxx"
26 
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <tools/stream.hxx>
30 #include <tools/debug.hxx>
31 #include <tools/color.hxx>
32 #include <rtl/ustrbuf.hxx>
33 #include <rtl/strbuf.hxx>
34 #ifndef _SVSTDARR_HXX
35 #define _SVSTDARR_ULONGS
36 #include <svl/svstdarr.hxx>
37 #endif
38 
39 #include <tools/tenccvt.hxx>
40 #include <tools/datetime.hxx>
41 #include <svl/inettype.hxx>
42 #include <comphelper/string.hxx>
43 #include <com/sun/star/beans/PropertyAttribute.hpp>
44 #include <com/sun/star/document/XDocumentProperties.hpp>
45 
46 #include <svtools/parhtml.hxx>
47 #include <svtools/htmltokn.h>
48 #include <svtools/htmlkywd.hxx>
49 
50 
51 using namespace ::com::sun::star;
52 
53 
54 const sal_Int32 MAX_LEN( 1024L );
55 //static sal_Unicode sTmpBuffer[ MAX_LEN+1 ];
56 const sal_Int32 MAX_MACRO_LEN( 1024 );
57 
58 const sal_Int32 MAX_ENTITY_LEN( 8L );
59 
60 /*  */
61 
62 // Tabellen zum Umwandeln von Options-Werten in Strings
63 
64 // <INPUT TYPE=xxx>
65 static HTMLOptionEnum __READONLY_DATA aInputTypeOptEnums[] =
66 {
67     { OOO_STRING_SVTOOLS_HTML_IT_text,      HTML_IT_TEXT        },
68     { OOO_STRING_SVTOOLS_HTML_IT_password,  HTML_IT_PASSWORD    },
69     { OOO_STRING_SVTOOLS_HTML_IT_checkbox,  HTML_IT_CHECKBOX    },
70     { OOO_STRING_SVTOOLS_HTML_IT_radio,     HTML_IT_RADIO       },
71     { OOO_STRING_SVTOOLS_HTML_IT_range,     HTML_IT_RANGE       },
72     { OOO_STRING_SVTOOLS_HTML_IT_scribble,  HTML_IT_SCRIBBLE    },
73     { OOO_STRING_SVTOOLS_HTML_IT_file,      HTML_IT_FILE        },
74     { OOO_STRING_SVTOOLS_HTML_IT_hidden,    HTML_IT_HIDDEN      },
75     { OOO_STRING_SVTOOLS_HTML_IT_submit,    HTML_IT_SUBMIT      },
76     { OOO_STRING_SVTOOLS_HTML_IT_image,     HTML_IT_IMAGE       },
77     { OOO_STRING_SVTOOLS_HTML_IT_reset,     HTML_IT_RESET       },
78     { OOO_STRING_SVTOOLS_HTML_IT_button,    HTML_IT_BUTTON      },
79     { 0,                    0                   }
80 };
81 
82 // <TABLE FRAME=xxx>
83 static HTMLOptionEnum __READONLY_DATA aTableFrameOptEnums[] =
84 {
85     { OOO_STRING_SVTOOLS_HTML_TF_void,  HTML_TF_VOID    },
86     { OOO_STRING_SVTOOLS_HTML_TF_above, HTML_TF_ABOVE   },
87     { OOO_STRING_SVTOOLS_HTML_TF_below, HTML_TF_BELOW   },
88     { OOO_STRING_SVTOOLS_HTML_TF_hsides,    HTML_TF_HSIDES  },
89     { OOO_STRING_SVTOOLS_HTML_TF_lhs,       HTML_TF_LHS     },
90     { OOO_STRING_SVTOOLS_HTML_TF_rhs,       HTML_TF_RHS     },
91     { OOO_STRING_SVTOOLS_HTML_TF_vsides,    HTML_TF_VSIDES  },
92     { OOO_STRING_SVTOOLS_HTML_TF_box,       HTML_TF_BOX     },
93     { OOO_STRING_SVTOOLS_HTML_TF_border,    HTML_TF_BOX     },
94     { 0,                0               }
95 };
96 
97 // <TABLE RULES=xxx>
98 static HTMLOptionEnum __READONLY_DATA aTableRulesOptEnums[] =
99 {
100     { OOO_STRING_SVTOOLS_HTML_TR_none,  HTML_TR_NONE    },
101     { OOO_STRING_SVTOOLS_HTML_TR_groups,    HTML_TR_GROUPS  },
102     { OOO_STRING_SVTOOLS_HTML_TR_rows,  HTML_TR_ROWS    },
103     { OOO_STRING_SVTOOLS_HTML_TR_cols,  HTML_TR_COLS    },
104     { OOO_STRING_SVTOOLS_HTML_TR_all,       HTML_TR_ALL     },
105     { 0,                0               }
106 };
107 
108 
SV_IMPL_PTRARR(HTMLOptions,HTMLOptionPtr)109 SV_IMPL_PTRARR(HTMLOptions,HTMLOptionPtr)
110 
111 /*  */
112 
113 sal_uInt16 HTMLOption::GetEnum( const HTMLOptionEnum *pOptEnums, sal_uInt16 nDflt ) const
114 {
115     sal_uInt16 nValue = nDflt;
116 
117     while( pOptEnums->pName )
118         if( aValue.EqualsIgnoreCaseAscii( pOptEnums->pName ) )
119             break;
120         else
121             pOptEnums++;
122 
123     if( pOptEnums->pName )
124         nValue = pOptEnums->nValue;
125 
126     return nValue;
127 }
128 
GetEnum(sal_uInt16 & rEnum,const HTMLOptionEnum * pOptEnums) const129 sal_Bool HTMLOption::GetEnum( sal_uInt16 &rEnum, const HTMLOptionEnum *pOptEnums ) const
130 {
131     while( pOptEnums->pName )
132     {
133         if( aValue.EqualsIgnoreCaseAscii( pOptEnums->pName ) )
134             break;
135         else
136             pOptEnums++;
137     }
138 
139     const sal_Char *pName = pOptEnums->pName;
140     if( pName )
141         rEnum = pOptEnums->nValue;
142 
143     return (pName != 0);
144 }
145 
HTMLOption(sal_uInt16 nTok,const String & rToken,const String & rValue)146 HTMLOption::HTMLOption( sal_uInt16 nTok, const String& rToken,
147                         const String& rValue )
148     : aValue(rValue)
149     , aToken(rToken)
150     , nToken( nTok )
151 {
152     DBG_ASSERT( nToken>=HTML_OPTION_START && nToken<HTML_OPTION_END,
153         "HTMLOption: unbekanntes Token" );
154 }
155 
GetNumber() const156 sal_uInt32 HTMLOption::GetNumber() const
157 {
158     DBG_ASSERT( (nToken>=HTML_OPTION_NUMBER_START &&
159                  nToken<HTML_OPTION_NUMBER_END) ||
160                 (nToken>=HTML_OPTION_CONTEXT_START &&
161                  nToken<HTML_OPTION_CONTEXT_END) ||
162                 nToken==HTML_O_VALUE,
163         "GetNumber: Option ist nicht numerisch" );
164     String aTmp( aValue );
165     aTmp.EraseLeadingChars();
166     sal_Int32 nTmp = aTmp.ToInt32();
167     return nTmp >= 0 ? (sal_uInt32)nTmp : 0;
168 }
169 
GetSNumber() const170 sal_Int32 HTMLOption::GetSNumber() const
171 {
172     DBG_ASSERT( (nToken>=HTML_OPTION_NUMBER_START && nToken<HTML_OPTION_NUMBER_END) ||
173                 (nToken>=HTML_OPTION_CONTEXT_START && nToken<HTML_OPTION_CONTEXT_END),
174         "GetSNumber: Option ist nicht numerisch" );
175     String aTmp( aValue );
176     aTmp.EraseLeadingChars();
177     return aTmp.ToInt32();
178 }
179 
GetNumbers(SvULongs & rLongs,sal_Bool bSpaceDelim) const180 void HTMLOption::GetNumbers( SvULongs &rLongs, sal_Bool bSpaceDelim ) const
181 {
182     if( rLongs.Count() )
183         rLongs.Remove( 0, rLongs.Count() );
184 
185     if( bSpaceDelim )
186     {
187         // das ist ein sehr stark vereinfachter Scanner. Er sucht einfach
188         // alle Tiffern aus dem String
189         sal_Bool bInNum = sal_False;
190         sal_uLong nNum = 0;
191         for( xub_StrLen i=0; i<aValue.Len(); i++ )
192         {
193             register sal_Unicode c = aValue.GetChar( i );
194             if( c>='0' && c<='9' )
195             {
196                 nNum *= 10;
197                 nNum += (c - '0');
198                 bInNum = sal_True;
199             }
200             else if( bInNum )
201             {
202                 rLongs.Insert( nNum, rLongs.Count() );
203                 bInNum = sal_False;
204                 nNum = 0;
205             }
206         }
207         if( bInNum )
208         {
209             rLongs.Insert( nNum, rLongs.Count() );
210         }
211     }
212     else
213     {
214         // hier wird auf die korrekte Trennung der Zahlen durch ',' geachtet
215         // und auch mal eine 0 eingefuegt
216         xub_StrLen nPos = 0;
217         while( nPos < aValue.Len() )
218         {
219             register sal_Unicode c;
220             while( nPos < aValue.Len() &&
221                    ((c=aValue.GetChar(nPos)) == ' ' || c == '\t' ||
222                    c == '\n' || c== '\r' ) )
223                 nPos++;
224 
225             if( nPos==aValue.Len() )
226                 rLongs.Insert( sal_uLong(0), rLongs.Count() );
227             else
228             {
229                 xub_StrLen nEnd = aValue.Search( (sal_Unicode)',', nPos );
230                 if( STRING_NOTFOUND==nEnd )
231                 {
232                     sal_Int32 nTmp = aValue.Copy(nPos).ToInt32();
233                     rLongs.Insert( nTmp >= 0 ? (sal_uInt32)nTmp : 0,
234                                    rLongs.Count() );
235                     nPos = aValue.Len();
236                 }
237                 else
238                 {
239                     sal_Int32 nTmp =
240                         aValue.Copy(nPos,nEnd-nPos).ToInt32();
241                     rLongs.Insert( nTmp >= 0 ? (sal_uInt32)nTmp : 0,
242                                    rLongs.Count() );
243                     nPos = nEnd+1;
244                 }
245             }
246         }
247     }
248 }
249 
GetColor(Color & rColor) const250 void HTMLOption::GetColor( Color& rColor ) const
251 {
252     DBG_ASSERT( (nToken>=HTML_OPTION_COLOR_START && nToken<HTML_OPTION_COLOR_END) || nToken==HTML_O_SIZE,
253         "GetColor: Option spezifiziert keine Farbe" );
254 
255     String aTmp( aValue );
256     aTmp.ToUpperAscii();
257     sal_uLong nColor = ULONG_MAX;
258     if( '#'!=aTmp.GetChar( 0 ) )
259         nColor = GetHTMLColor( aTmp );
260 
261     if( ULONG_MAX == nColor )
262     {
263         nColor = 0;
264         xub_StrLen nPos = 0;
265         for( sal_uInt32 i=0; i<6; i++ )
266         {
267             // MIB 26.06.97: Wie auch immer Netscape Farbwerte ermittelt,
268             // maximal drei Zeichen, die kleiner als '0' sind werden
269             // ignoriert. Bug #40901# stimmt damit. Mal schauen, was sich
270             // irgendwelche HTML-Autoren noch so einfallen lassen...
271             register sal_Unicode c = nPos<aTmp.Len() ? aTmp.GetChar( nPos++ )
272                                                      : '0';
273             if( c < '0' )
274             {
275                 c = nPos<aTmp.Len() ? aTmp.GetChar(nPos++) : '0';
276                 if( c < '0' )
277                     c = nPos<aTmp.Len() ? aTmp.GetChar(nPos++) : '0';
278             }
279             nColor *= 16;
280             if( c >= '0' && c <= '9' )
281                 nColor += (c - 48);
282             else if( c >= 'A' && c <= 'F' )
283                 nColor += (c - 55);
284         }
285     }
286 
287     rColor.SetRed(   (sal_uInt8)((nColor & 0x00ff0000) >> 16) );
288     rColor.SetGreen( (sal_uInt8)((nColor & 0x0000ff00) >> 8));
289     rColor.SetBlue(  (sal_uInt8)(nColor & 0x000000ff) );
290 }
291 
GetInputType() const292 HTMLInputType HTMLOption::GetInputType() const
293 {
294     DBG_ASSERT( nToken==HTML_O_TYPE, "GetInputType: Option nicht TYPE" );
295     return (HTMLInputType)GetEnum( aInputTypeOptEnums, HTML_IT_TEXT );
296 }
297 
GetTableFrame() const298 HTMLTableFrame HTMLOption::GetTableFrame() const
299 {
300     DBG_ASSERT( nToken==HTML_O_FRAME, "GetTableFrame: Option nicht FRAME" );
301     return (HTMLTableFrame)GetEnum( aTableFrameOptEnums, HTML_TF_VOID );
302 }
303 
GetTableRules() const304 HTMLTableRules HTMLOption::GetTableRules() const
305 {
306     DBG_ASSERT( nToken==HTML_O_RULES, "GetTableRules: Option nicht RULES" );
307     return (HTMLTableRules)GetEnum( aTableRulesOptEnums, HTML_TR_NONE );
308 }
309 
310 /*  */
311 
HTMLParser(SvStream & rIn,int bReadNewDoc)312 HTMLParser::HTMLParser( SvStream& rIn, int bReadNewDoc )
313     : SvParser( rIn )
314 {
315     bNewDoc = bReadNewDoc;
316     bReadListing = bReadXMP = bReadPRE = bReadTextArea =
317         bReadScript = bReadStyle =
318         bEndTokenFound = bIsInBody = bReadNextChar =
319         bReadComment = sal_False;
320     bIsInHeader = sal_True;
321     pOptions = new HTMLOptions;
322 
323     //#i76649, default to UTF-8 for HTML unless we know differently
324     SetSrcEncoding(RTL_TEXTENCODING_UTF8);
325 }
326 
~HTMLParser()327 HTMLParser::~HTMLParser()
328 {
329     if( pOptions && pOptions->Count() )
330         pOptions->DeleteAndDestroy( 0, pOptions->Count() );
331     delete pOptions;
332 }
333 
CallParser()334 SvParserState __EXPORT HTMLParser::CallParser()
335 {
336     eState = SVPAR_WORKING;
337     nNextCh = GetNextChar();
338     SaveState( 0 );
339 
340     nPre_LinePos = 0;
341     bPre_IgnoreNewPara = sal_False;
342 
343     AddRef();
344     Continue( 0 );
345     if( SVPAR_PENDING != eState )
346         ReleaseRef();       // dann brauchen wir den Parser nicht mehr!
347 
348     return eState;
349 }
350 
Continue(int nToken)351 void HTMLParser::Continue( int nToken )
352 {
353     if( !nToken )
354         nToken = GetNextToken();
355 
356     while( IsParserWorking() )
357     {
358         SaveState( nToken );
359         nToken = FilterToken( nToken );
360 
361         if( nToken )
362             NextToken( nToken );
363 
364         if( IsParserWorking() )
365             SaveState( 0 );         // bis hierhin abgearbeitet,
366                                     // weiter mit neuem Token!
367         nToken = GetNextToken();
368     }
369 }
370 
FilterToken(int nToken)371 int HTMLParser::FilterToken( int nToken )
372 {
373     switch( nToken )
374     {
375     case sal_Unicode(EOF):
376         nToken = 0;
377         break;          // nicht verschicken
378 
379     case HTML_HEAD_OFF:
380         bIsInBody = sal_True;
381     case HTML_HEAD_ON:
382         bIsInHeader = HTML_HEAD_ON == nToken;
383         break;
384 
385     case HTML_BODY_ON:
386     case HTML_FRAMESET_ON:
387         bIsInHeader = sal_False;
388         bIsInBody = HTML_BODY_ON == nToken;
389         break;
390 
391     case HTML_BODY_OFF:
392         bIsInBody = bReadPRE = bReadListing = bReadXMP = sal_False;
393         break;
394 
395     case HTML_HTML_OFF:
396         nToken = 0;
397         bReadPRE = bReadListing = bReadXMP = sal_False;
398         break;      // HTML_ON wurde auch nicht verschickt !
399 
400     case HTML_PREFORMTXT_ON:
401         StartPRE();
402         break;
403 
404     case HTML_PREFORMTXT_OFF:
405         FinishPRE();
406         break;
407 
408     case HTML_LISTING_ON:
409         StartListing();
410         break;
411 
412     case HTML_LISTING_OFF:
413         FinishListing();
414         break;
415 
416     case HTML_XMP_ON:
417         StartXMP();
418         break;
419 
420     case HTML_XMP_OFF:
421         FinishXMP();
422         break;
423 
424     default:
425         if( bReadPRE )
426             nToken = FilterPRE( nToken );
427         else if( bReadListing )
428             nToken = FilterListing( nToken );
429         else if( bReadXMP )
430             nToken = FilterXMP( nToken );
431 
432         break;
433     }
434 
435     return nToken;
436 }
437 
438 #define HTML_ISDIGIT( c ) (c >= '0' && c <= '9')
439 #define HTML_ISALPHA( c ) ( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') )
440 #define HTML_ISALNUM( c ) ( HTML_ISALPHA(c) || HTML_ISDIGIT(c) )
441 #define HTML_ISSPACE( c ) ( ' ' == c || (c >= 0x09 && c <= 0x0d) )
442 #define HTML_ISPRINTABLE( c ) ( c >= 32 && c != 127)
443 // --> OD 2006-07-26 #138464#
444 #define HTML_ISHEXDIGIT( c ) ( HTML_ISDIGIT(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') )
445 // <--
446 
ScanText(const sal_Unicode cBreak)447 int HTMLParser::ScanText( const sal_Unicode cBreak )
448 {
449     ::rtl::OUStringBuffer sTmpBuffer( MAX_LEN );
450     int bWeiter = sal_True;
451     int bEqSignFound = sal_False;
452     sal_Unicode cQuote = 0U;
453 
454     while( bWeiter && IsParserWorking() )
455     {
456         int bNextCh = sal_True;
457         switch( nNextCh )
458         {
459         case '&':
460             bEqSignFound = sal_False;
461             if( bReadXMP )
462                 sTmpBuffer.append( (sal_Unicode)'&' );
463             else
464             {
465                 sal_uLong nStreamPos = rInput.Tell();
466                 sal_uLong nLinePos = GetLinePos();
467 
468                 sal_Unicode cChar = 0U;
469                 if( '#' == (nNextCh = GetNextChar()) )
470                 {
471                     nNextCh = GetNextChar();
472                     // --> OD 2006-07-26 #138464#
473                     // consider hexadecimal digits
474                     const sal_Bool bIsHex( 'x' == nNextCh );
475                     const sal_Bool bIsDecOrHex( bIsHex || HTML_ISDIGIT(nNextCh) );
476                     if ( bIsDecOrHex )
477                     {
478                         if ( bIsHex )
479                         {
480                             nNextCh = GetNextChar();
481                             while ( HTML_ISHEXDIGIT(nNextCh) )
482                             {
483                                 cChar = cChar * 16U +
484                                         ( nNextCh <= '9'
485                                           ? sal_Unicode( nNextCh - '0' )
486                                           : ( nNextCh <= 'F'
487                                               ? sal_Unicode( nNextCh - 'A' + 10 )
488                                               : sal_Unicode( nNextCh - 'a' + 10 ) ) );
489                                 nNextCh = GetNextChar();
490                             }
491                         }
492                         else
493                         {
494                             do
495                             {
496                                 cChar = cChar * 10U + sal_Unicode( nNextCh - '0');
497                                 nNextCh = GetNextChar();
498                             }
499                             while( HTML_ISDIGIT(nNextCh) );
500                         }
501 
502                         if( RTL_TEXTENCODING_DONTKNOW != eSrcEnc &&
503                             RTL_TEXTENCODING_UCS2 != eSrcEnc &&
504                             RTL_TEXTENCODING_UTF8 != eSrcEnc &&
505                             cChar < 256 )
506                         {
507                             sal_Unicode cOrig = cChar;
508                             cChar = ByteString::ConvertToUnicode(
509                                             (sal_Char)cChar, eSrcEnc );
510                             if( 0U == cChar )
511                             {
512                                 // #73398#: If the character could not be
513                                 // converted, because a conversion is not
514                                 // available, do no conversion at all.
515                                 cChar = cOrig;
516                             }
517                         }
518                     }
519                     // <--
520                     else
521                         nNextCh = 0U;
522                 }
523                 else if( HTML_ISALPHA( nNextCh ) )
524                 {
525                     ::rtl::OUStringBuffer sEntityBuffer( MAX_ENTITY_LEN );
526                     xub_StrLen nPos = 0L;
527                     do
528                     {
529                         sEntityBuffer.append( nNextCh );
530                         nPos++;
531                         nNextCh = GetNextChar();
532                     }
533                     while( nPos < MAX_ENTITY_LEN && HTML_ISALNUM( nNextCh ) &&
534                            !rInput.IsEof() );
535 
536                     if( IsParserWorking() && !rInput.IsEof() )
537                     {
538                         String sEntity( sEntityBuffer.getStr(), nPos );
539                         cChar = GetHTMLCharName( sEntity );
540 
541                         // nicht gefunden ( == 0 ), dann Klartext
542                         // oder ein Zeichen das als Attribut eingefuegt
543                         // wird
544                         if( 0U == cChar && ';' != nNextCh )
545                         {
546                             DBG_ASSERT( rInput.Tell() - nStreamPos ==
547                                         (sal_uLong)(nPos+1L)*GetCharSize(),
548                                         "UTF-8 geht hier schief" );
549                             for( xub_StrLen i=nPos-1L; i>1L; i-- )
550                             {
551                                 nNextCh = sEntityBuffer[i];
552                                 sEntityBuffer.setLength( i );
553                                 sEntity.Assign( sEntityBuffer.getStr(), i );
554                                 cChar = GetHTMLCharName( sEntity );
555                                 if( cChar )
556                                 {
557                                     rInput.SeekRel( -(long)
558                                             ((nPos-i)*GetCharSize()) );
559                                     nlLinePos -= sal_uInt32(nPos-i);
560                                     nPos = i;
561                                     ClearTxtConvContext();
562                                     break;
563                                 }
564                             }
565                         }
566 
567                         if( !cChar )        // unbekanntes Zeichen?
568                         {
569                             // dann im Stream zurueck, das '&' als Zeichen
570                             // einfuegen und mit dem nachfolgenden Zeichen
571                             // wieder aufsetzen
572                             sTmpBuffer.append( (sal_Unicode)'&' );
573 
574 //                          rInput.SeekRel( -(long)(++nPos*GetCharSize()) );
575 //                          nlLinePos -= nPos;
576                             DBG_ASSERT( rInput.Tell()-nStreamPos ==
577                                         (sal_uLong)(nPos+1)*GetCharSize(),
578                                         "Falsche Stream-Position" );
579                             DBG_ASSERT( nlLinePos-nLinePos ==
580                                         (sal_uLong)(nPos+1),
581                                         "Falsche Zeilen-Position" );
582                             rInput.Seek( nStreamPos );
583                             nlLinePos = nLinePos;
584                             ClearTxtConvContext();
585                             break;
586                         }
587 
588                         // 1 == Non Breaking Space
589                         // 2 == SoftHyphen
590 
591                         if( cChar < 3U )
592                         {
593                             if( '>' == cBreak )
594                             {
595                                 // Wenn der Inhalt eines Tags gelesen wird,
596                                 // muessen wir ein Space bzw. - daraus machen
597                                 switch( cChar )
598                                 {
599                                 case 1U: cChar = ' '; break;
600                                 case 2U: cChar = '-'; break;
601                                 default:
602                                     DBG_ASSERT( cChar==1U,
603                             "\0x00 sollte doch schon laengt abgefangen sein!" );
604                                     break;
605                                 }
606                             }
607                             else
608                             {
609                                 // Wenn kein Tag gescannt wird, enstprechendes
610                                 // Token zurueckgeben
611                                 aToken +=
612                                     String( sTmpBuffer.makeStringAndClear() );
613                                 if( cChar )
614                                 {
615                                     if( aToken.Len() )
616                                     {
617                                         // mit dem Zeichen wieder aufsetzen
618                                         nNextCh = '&';
619 //                                      rInput.SeekRel( -(long)(++nPos*GetCharSize()) );
620 //                                      nlLinePos -= nPos;
621                                         DBG_ASSERT( rInput.Tell()-nStreamPos ==
622                                                     (sal_uLong)(nPos+1)*GetCharSize(),
623                                                     "Falsche Stream-Position" );
624                                         DBG_ASSERT( nlLinePos-nLinePos ==
625                                                     (sal_uLong)(nPos+1),
626                                                     "Falsche Zeilen-Position" );
627                                         rInput.Seek( nStreamPos );
628                                         nlLinePos = nLinePos;
629                                         ClearTxtConvContext();
630                                         return HTML_TEXTTOKEN;
631                                     }
632 
633                                     // Hack: _GetNextChar soll nicht das
634                                     // naechste Zeichen lesen
635                                     if( ';' != nNextCh )
636                                         aToken += ' ';
637                                     if( 1U == cChar )
638                                         return HTML_NONBREAKSPACE;
639                                     if( 2U == cChar )
640                                         return HTML_SOFTHYPH;
641                                 }
642                                 aToken += (sal_Unicode)'&';
643                                 aToken +=
644                                     String(sEntityBuffer.makeStringAndClear());
645                                 break;
646                             }
647                         }
648                     }
649                     else
650                         nNextCh = 0U;
651                 }
652                 // MIB 03/02/2000: &{...};-JavaScript-Macros are not
653                 // supported any longer.
654                 else if( IsParserWorking() )
655                 {
656                     sTmpBuffer.append( (sal_Unicode)'&' );
657                     bNextCh = sal_False;
658                     break;
659                 }
660 
661                 bNextCh = (';' == nNextCh);
662                 if( cBreak=='>' && (cChar=='\\' || cChar=='\'' ||
663                                     cChar=='\"' || cChar==' ') )
664                 {
665                     // ' und " mussen innerhalb von Tags mit einem
666                     // gekennzeichnet werden, um sie von ' und " als Klammern
667                     // um Optionen zu unterscheiden. Logischerweise muss
668                     // deshalb auch ein \ gekeenzeichnet werden. Ausserdem
669                     // schuetzen wir ein Space, weil es kein Trennzeichen
670                     // zwischen Optionen ist.
671                     sTmpBuffer.append( (sal_Unicode)'\\' );
672                     if( MAX_LEN == sTmpBuffer.getLength() )
673                         aToken += String(sTmpBuffer.makeStringAndClear());
674                 }
675                 if( IsParserWorking() )
676                 {
677                     if( cChar )
678                         sTmpBuffer.append( cChar );
679                 }
680                 else if( SVPAR_PENDING==eState && '>'!=cBreak )
681                 {
682                     // Mit dem '&' Zeichen wieder aufsetzen, der Rest
683                     // wird als Texttoken zurueckgegeben.
684                     if( aToken.Len() || sTmpBuffer.getLength() )
685                     {
686                         // Der bisherige Text wird von _GetNextChar()
687                         // zurueckgegeben und beim naechsten Aufruf wird
688                         // ein neues Zeichen gelesen. Also muessen wir uns
689                         // noch vor das & stellen.
690                         nNextCh = 0U;
691                         rInput.Seek( nStreamPos-(sal_uInt32)GetCharSize() );
692                         nlLinePos = nLinePos-1;
693                         ClearTxtConvContext();
694                         bReadNextChar = sal_True;
695                     }
696                     bNextCh = sal_False;
697                 }
698             }
699             break;
700         case '=':
701             if( '>'==cBreak && !cQuote )
702                 bEqSignFound = sal_True;
703             sTmpBuffer.append( nNextCh );
704             break;
705 
706         case '\\':
707             if( '>'==cBreak )
708             {
709                 // Innerhalb von Tags kennzeichnen
710                 sTmpBuffer.append( (sal_Unicode)'\\' );
711                 if( MAX_LEN == sTmpBuffer.getLength() )
712                     aToken += String(sTmpBuffer.makeStringAndClear());
713             }
714             sTmpBuffer.append( (sal_Unicode)'\\' );
715             break;
716 
717         case '\"':
718         case '\'':
719             if( '>'==cBreak )
720             {
721                 if( bEqSignFound )
722                     cQuote = nNextCh;
723                 else if( cQuote && (cQuote==nNextCh ) )
724                     cQuote = 0U;
725             }
726             sTmpBuffer.append( nNextCh );
727             bEqSignFound = sal_False;
728             break;
729 
730         case sal_Unicode(EOF):
731             if( rInput.IsEof() )
732             {
733 // MIB 20.11.98: Das macht hier keinen Sinn, oder doch: Zumindest wird
734 // abc&auml;<EOF> nicht angezeigt, also lassen wir das in Zukunft.
735 //              if( '>' != cBreak )
736 //                  eState = SVPAR_ACCEPTED;
737                 bWeiter = sal_False;
738             }
739             else
740             {
741                 sTmpBuffer.append( nNextCh );
742             }
743             break;
744 
745         case '<':
746             bEqSignFound = sal_False;
747             if( '>'==cBreak )
748                 sTmpBuffer.append( nNextCh );
749             else
750                 bWeiter = sal_False;        // Abbrechen, String zusammen
751             break;
752 
753         case '\f':
754             if( '>' == cBreak )
755             {
756                 // Beim Scannen von Optionen wie ein Space behandeln
757                 sTmpBuffer.append( (sal_Unicode)' ' );
758             }
759             else
760             {
761                 // sonst wird es ein eigenes Token
762                 bWeiter = sal_False;
763             }
764             break;
765 
766         case '\r':
767         case '\n':
768             if( '>'==cBreak )
769             {
770                 // #26979# cr/lf in Tag wird in _GetNextToken() behandeln
771                 sTmpBuffer.append( nNextCh );
772                 break;
773             }
774             else if( bReadListing || bReadXMP || bReadPRE || bReadTextArea )
775             {
776                 bWeiter = sal_False;
777                 break;
778             }
779             // Bug 18984: CR-LF -> Blank
780             //      Folge von CR/LF/BLANK/TAB nur in ein Blank wandeln
781             // kein break!!
782         case '\t':
783             if( '\t'==nNextCh && bReadPRE && '>'!=cBreak )
784             {
785                 // In <PRE>: Tabs nach oben durchreichen
786                 bWeiter = sal_False;
787                 break;
788             }
789             // kein break
790         case '\x0b':
791             if( '\x0b'==nNextCh && (bReadPRE || bReadXMP ||bReadListing) &&
792                 '>'!=cBreak )
793             {
794                 break;
795             }
796             nNextCh = ' ';
797             // kein break;
798         case ' ':
799             sTmpBuffer.append( nNextCh );
800             if( '>'!=cBreak && (!bReadListing && !bReadXMP &&
801                                 !bReadPRE && !bReadTextArea) )
802             {
803                 // alle Folgen von Blanks/Tabs/CR/LF zu einem Blank umwandeln
804                 do {
805                     if( sal_Unicode(EOF) == (nNextCh = GetNextChar()) &&
806                         rInput.IsEof() )
807                     {
808                         if( aToken.Len() || sTmpBuffer.getLength() > 1L )
809                         {
810                             // ausser den Blanks wurde noch etwas geselen
811                             aToken += String(sTmpBuffer.makeStringAndClear());
812                             return HTML_TEXTTOKEN;
813                         }
814                         else
815                             // nur Blanks gelesen: dann darf kein Text
816                             // mehr zurueckgegeben werden und _GetNextToken
817                             // muss auf EOF laufen
818                             return 0;
819                     }
820                 } while ( ' ' == nNextCh || '\t' == nNextCh ||
821                           '\r' == nNextCh || '\n' == nNextCh ||
822                           '\x0b' == nNextCh );
823                 bNextCh = sal_False;
824             }
825             break;
826 
827         default:
828             bEqSignFound = sal_False;
829             if( (nNextCh==cBreak && !cQuote) ||
830                 (sal_uLong(aToken.Len()) + MAX_LEN) > sal_uLong(STRING_MAXLEN & ~1 ))
831                 bWeiter = sal_False;
832             else
833             {
834                 do {
835                     // alle anderen Zeichen kommen in den Text
836                     sTmpBuffer.append( nNextCh );
837                     if( MAX_LEN == sTmpBuffer.getLength() )
838                     {
839                         aToken += String(sTmpBuffer.makeStringAndClear());
840                         if( (sal_uLong(aToken.Len()) + MAX_LEN) >
841                                 sal_uLong(STRING_MAXLEN & ~1 ) )
842                         {
843                             nNextCh = GetNextChar();
844                             return HTML_TEXTTOKEN;
845                         }
846                     }
847                     if( ( sal_Unicode(EOF) == (nNextCh = GetNextChar()) &&
848                           rInput.IsEof() ) ||
849                         !IsParserWorking() )
850                     {
851                         if( sTmpBuffer.getLength() )
852                             aToken += String(sTmpBuffer.makeStringAndClear());
853                         return HTML_TEXTTOKEN;
854                     }
855                 } while( HTML_ISALPHA( nNextCh ) || HTML_ISDIGIT( nNextCh ) );
856                 bNextCh = sal_False;
857             }
858         }
859 
860         if( MAX_LEN == sTmpBuffer.getLength() )
861             aToken += String(sTmpBuffer.makeStringAndClear());
862 
863         if( bWeiter && bNextCh )
864             nNextCh = GetNextChar();
865     }
866 
867     if( sTmpBuffer.getLength() )
868         aToken += String(sTmpBuffer.makeStringAndClear());
869 
870     return HTML_TEXTTOKEN;
871 }
872 
_GetNextRawToken()873 int HTMLParser::_GetNextRawToken()
874 {
875     ::rtl::OUStringBuffer sTmpBuffer( MAX_LEN );
876 
877     if( bEndTokenFound )
878     {
879         // beim letzten Aufruf haben wir das End-Token bereits gefunden,
880         // deshalb muessen wir es nicht noch einmal suchen
881         bReadScript = sal_False;
882         bReadStyle = sal_False;
883         aEndToken.Erase();
884         bEndTokenFound = sal_False;
885 
886         return 0;
887     }
888 
889     // per default geben wir HTML_RAWDATA zurueck
890     int bWeiter = sal_True;
891     int nToken = HTML_RAWDATA;
892     SaveState( 0 );
893     while( bWeiter && IsParserWorking() )
894     {
895         int bNextCh = sal_True;
896         switch( nNextCh )
897         {
898         case '<':
899             {
900                 // Vielleicht haben wir das Ende erreicht
901 
902                 // das bisher gelesene erstmal retten
903                 aToken += String(sTmpBuffer.makeStringAndClear());
904 
905                 // und die Position im Stream merken
906                 sal_uLong nStreamPos = rInput.Tell();
907                 sal_uLong nLineNr = GetLineNr();
908                 sal_uLong nLinePos = GetLinePos();
909 
910                 // Start eines End-Token?
911                 int bOffState = sal_False;
912                 if( '/' == (nNextCh = GetNextChar()) )
913                 {
914                     bOffState = sal_True;
915                     nNextCh = GetNextChar();
916                 }
917                 else if( '!' == nNextCh )
918                 {
919                     sTmpBuffer.append( nNextCh );
920                     nNextCh = GetNextChar();
921                 }
922 
923                 // jetzt die Buchstaben danach lesen
924                 while( (HTML_ISALPHA(nNextCh) || '-'==nNextCh) &&
925                        IsParserWorking() && sTmpBuffer.getLength() < MAX_LEN )
926                 {
927                     sTmpBuffer.append( nNextCh );
928                     nNextCh = GetNextChar();
929                 }
930 
931                 String aTok( sTmpBuffer.getStr(),
932                              sal::static_int_cast< xub_StrLen >(
933                                  sTmpBuffer.getLength()) );
934                 aTok.ToUpperAscii();
935                 sal_Bool bDone = sal_False;
936                 if( bReadScript || aEndToken.Len() )
937                 {
938                     if( !bReadComment )
939                     {
940                         if( aTok.CompareToAscii( OOO_STRING_SVTOOLS_HTML_comment, 3 )
941                                 == COMPARE_EQUAL )
942                         {
943                             bReadComment = sal_True;
944                         }
945                         else
946                         {
947                             // ein Script muss mit "</SCRIPT>" aufhoehren, wobei
948                             // wir es mit dem ">" aus sicherheitsgruenden
949                             // erstmal nicht so genau nehmen
950                             bDone = bOffState && // '>'==nNextCh &&
951                             COMPARE_EQUAL == ( bReadScript
952                                 ? aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_script)
953                                 : aTok.CompareTo(aEndToken) );
954                         }
955                     }
956                     if( bReadComment && '>'==nNextCh && aTok.Len() >= 2 &&
957                         aTok.Copy( aTok.Len()-2 ).EqualsAscii( "--" ) )
958                     {
959                         // hier ist ein Kommentar der Art <!-----> zuende
960                         bReadComment = sal_False;
961                     }
962                 }
963                 else
964                 {
965                     // ein Style-Sheet kann mit </STYLE>, </HEAD> oder
966                     // <BODY> aughoehren
967                     if( bOffState )
968                         bDone = aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_style)
969                                     == COMPARE_EQUAL ||
970                                 aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_head)
971                                     == COMPARE_EQUAL;
972                     else
973                         bDone =
974                             aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_body) == COMPARE_EQUAL;
975                 }
976 
977                 if( bDone )
978                 {
979                     // das war's, jetzt muessen wir gegebenenfalls den
980                     // bisher gelesenen String zurueckgeben und dnach normal
981                     // weitermachen
982 
983                     bWeiter = sal_False;
984 
985                     // nToken==0 heisst, dass _GetNextToken gleich weiterliest
986                     if( !aToken.Len() && (bReadStyle || bReadScript) )
987                     {
988                         // wir koennen sofort die Umgebung beeden und
989                         // das End-Token parsen
990                         bReadScript = sal_False;
991                         bReadStyle = sal_False;
992                         aEndToken.Erase();
993                         nToken = 0;
994                     }
995                     else
996                     {
997                         // wir muessen bReadScript/bReadStyle noch am
998                         // Leben lassen und koennen erst beim naechsten
999                         // mal das End-Token Parsen
1000                         bEndTokenFound = sal_True;
1001                     }
1002 
1003                     // jetzt fahren wir im Stream auf das '<' zurueck
1004                     rInput.Seek( nStreamPos );
1005                     SetLineNr( nLineNr );
1006                     SetLinePos( nLinePos );
1007                     ClearTxtConvContext();
1008                     nNextCh = '<';
1009 
1010                     // den String wollen wir nicht an das Token haengen
1011                     sTmpBuffer.setLength( 0L );
1012                 }
1013                 else
1014                 {
1015                     // "</" merken, alles andere steht noch im buffer
1016                     aToken += (sal_Unicode)'<';
1017                     if( bOffState )
1018                         aToken += (sal_Unicode)'/';
1019 
1020                     bNextCh = sal_False;
1021                 }
1022             }
1023             break;
1024         case '-':
1025             sTmpBuffer.append( nNextCh );
1026             if( bReadComment )
1027             {
1028                 sal_Bool bTwoMinus = sal_False;
1029                 nNextCh = GetNextChar();
1030                 while( '-' == nNextCh && IsParserWorking() )
1031                 {
1032                     bTwoMinus = sal_True;
1033 
1034                     if( MAX_LEN == sTmpBuffer.getLength() )
1035                         aToken += String(sTmpBuffer.makeStringAndClear());
1036                     sTmpBuffer.append( nNextCh );
1037                     nNextCh = GetNextChar();
1038                 }
1039 
1040                 if( '>' == nNextCh && IsParserWorking() && bTwoMinus )
1041                     bReadComment = sal_False;
1042 
1043                 bNextCh = sal_False;
1044             }
1045             break;
1046 
1047         case '\r':
1048             // \r\n? beendet das aktuelle Text-Token (auch wenn es leer ist)
1049             nNextCh = GetNextChar();
1050             if( nNextCh=='\n' )
1051                 nNextCh = GetNextChar();
1052             bWeiter = sal_False;
1053             break;
1054         case '\n':
1055             // \n beendet das aktuelle Text-Token (auch wenn es leer ist)
1056             nNextCh = GetNextChar();
1057             bWeiter = sal_False;
1058             break;
1059         case sal_Unicode(EOF):
1060             // eof beendet das aktuelle Text-Token und tut so, als ob
1061             // ein End-Token gelesen wurde
1062             if( rInput.IsEof() )
1063             {
1064                 bWeiter = sal_False;
1065                 if( aToken.Len() || sTmpBuffer.getLength() )
1066                 {
1067                     bEndTokenFound = sal_True;
1068                 }
1069                 else
1070                 {
1071                     bReadScript = sal_False;
1072                     bReadStyle = sal_False;
1073                     aEndToken.Erase();
1074                     nToken = 0;
1075                 }
1076                 break;
1077             }
1078             // kein break
1079         default:
1080             // alle anderen Zeichen landen im Buffer
1081             sTmpBuffer.append( nNextCh );
1082             break;
1083         }
1084 
1085         if( (!bWeiter && sTmpBuffer.getLength() > 0L) ||
1086             MAX_LEN == sTmpBuffer.getLength() )
1087             aToken += String(sTmpBuffer.makeStringAndClear());
1088 
1089         if( bWeiter && bNextCh )
1090             nNextCh = GetNextChar();
1091     }
1092 
1093     if( IsParserWorking() )
1094         SaveState( 0 );
1095     else
1096         nToken = 0;
1097 
1098     return nToken;
1099 }
1100 
1101 // scanne das naechste Token,
_GetNextToken()1102 int __EXPORT HTMLParser::_GetNextToken()
1103 {
1104     int nRet = 0;
1105     sSaveToken.Erase();
1106 
1107     // die Optionen loeschen
1108     if( pOptions->Count() )
1109         pOptions->DeleteAndDestroy( 0, pOptions->Count() );
1110 
1111     if( !IsParserWorking() )        // wenn schon Fehler, dann nicht weiter!
1112         return 0;
1113 
1114     sal_Bool bReadNextCharSave = bReadNextChar;
1115     if( bReadNextChar )
1116     {
1117         DBG_ASSERT( !bEndTokenFound,
1118                     "</SCRIPT> gelesen und trotzdem noch ein Zeichen lesen?" );
1119         nNextCh = GetNextChar();
1120         if( !IsParserWorking() )        // wenn schon Fehler, dann nicht weiter!
1121             return 0;
1122         bReadNextChar = sal_False;
1123     }
1124 
1125     if( bReadScript || bReadStyle || aEndToken.Len() )
1126     {
1127         nRet = _GetNextRawToken();
1128         if( nRet || !IsParserWorking() )
1129             return nRet;
1130     }
1131 
1132     do {
1133         int bNextCh = sal_True;
1134         switch( nNextCh )
1135         {
1136         case '<':
1137             {
1138                 sal_uLong nStreamPos = rInput.Tell();
1139                 sal_uLong nLineNr = GetLineNr();
1140                 sal_uLong nLinePos = GetLinePos();
1141 
1142                 int bOffState = sal_False;
1143                 if( '/' == (nNextCh = GetNextChar()) )
1144                 {
1145                     bOffState = sal_True;
1146                     nNextCh = GetNextChar();
1147                 }
1148                 if( HTML_ISALPHA( nNextCh ) || '!'==nNextCh ) // fix #26984#
1149                 {
1150                     ::rtl::OUStringBuffer sTmpBuffer;
1151                     do {
1152                         sTmpBuffer.append( nNextCh );
1153                         if( MAX_LEN == sTmpBuffer.getLength() )
1154                             aToken += String(sTmpBuffer.makeStringAndClear());
1155                         nNextCh = GetNextChar();
1156                     } while( '>' != nNextCh && !HTML_ISSPACE( nNextCh ) &&
1157                              IsParserWorking() && !rInput.IsEof() );
1158 
1159                     if( sTmpBuffer.getLength() )
1160                         aToken += String(sTmpBuffer.makeStringAndClear());
1161 
1162                     // Blanks ueberlesen
1163                     while( HTML_ISSPACE( nNextCh ) && IsParserWorking() )
1164                         nNextCh = GetNextChar();
1165 
1166                     if( !IsParserWorking() )
1167                     {
1168                         if( SVPAR_PENDING == eState )
1169                             bReadNextChar = bReadNextCharSave;
1170                         break;
1171                     }
1172 
1173                     // suche das Token in der Tabelle:
1174                     sSaveToken = aToken;
1175                     aToken.ToUpperAscii();
1176                     if( 0 == (nRet = GetHTMLToken( aToken )) )
1177                         // Unknown Control
1178                         nRet = HTML_UNKNOWNCONTROL_ON;
1179 
1180                     // Wenn es ein Token zum ausschalten ist ...
1181                     if( bOffState )
1182                     {
1183                          if( HTML_TOKEN_ONOFF & nRet )
1184                          {
1185                             // und es ein Off-Token gibt, das daraus machen
1186                             ++nRet;
1187                          }
1188                          else if( HTML_LINEBREAK!=nRet )
1189                          {
1190                             // und es kein Off-Token gibt, ein unbekanntes
1191                             // Token daraus machen (ausser </BR>, das wird
1192                             // wie <BR> behandelt
1193                             nRet = HTML_UNKNOWNCONTROL_OFF;
1194                          }
1195                     }
1196 
1197                     if( nRet == HTML_COMMENT )
1198                     {
1199                         // fix: sSaveToken wegen Gross-/Kleinschreibung
1200                         // als Anfang des Kommentars benutzen und ein
1201                         // Space anhaengen.
1202                         aToken = sSaveToken;
1203                         if( '>'!=nNextCh )
1204                             aToken += (sal_Unicode)' ';
1205                         sal_uLong nCStreamPos = 0;
1206                         sal_uLong nCLineNr = 0;
1207                         sal_uLong nCLinePos = 0;
1208                         xub_StrLen nCStrLen = 0;
1209 
1210                         sal_Bool bDone = sal_False;
1211                         // bis zum schliessenden --> lesen. wenn keins gefunden
1212                         // wurde beim der ersten > wieder aufsetzen
1213                         while( !bDone && !rInput.IsEof() && IsParserWorking() )
1214                         {
1215                             if( '>'==nNextCh )
1216                             {
1217                                 if( !nCStreamPos )
1218                                 {
1219                                     nCStreamPos = rInput.Tell();
1220                                     nCStrLen = aToken.Len();
1221                                     nCLineNr = GetLineNr();
1222                                     nCLinePos = GetLinePos();
1223                                 }
1224                                 bDone = aToken.Len() >= 2 &&
1225                                         aToken.Copy(aToken.Len()-2,2).
1226                                                         EqualsAscii( "--" );
1227                                 if( !bDone )
1228                                 aToken += nNextCh;
1229                             }
1230                             else
1231                                 aToken += nNextCh;
1232                             if( !bDone )
1233                                 nNextCh = GetNextChar();
1234                         }
1235                         if( !bDone && IsParserWorking() && nCStreamPos )
1236                         {
1237                             rInput.Seek( nCStreamPos );
1238                             SetLineNr( nCLineNr );
1239                             SetLinePos( nCLinePos );
1240                             ClearTxtConvContext();
1241                             aToken.Erase( nCStrLen );
1242                             nNextCh = '>';
1243                         }
1244                     }
1245                     else
1246                     {
1247                         // den TokenString koennen wir jetzt verwerfen
1248                         aToken.Erase();
1249                     }
1250 
1251                     // dann lesen wir mal alles bis zur schliessenden '>'
1252                     if( '>' != nNextCh && IsParserWorking() )
1253                     {
1254                         ScanText( '>' );
1255                         if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() )
1256                         {
1257                             // zurueck hinter die < gehen  und dort neu
1258                             // aufsetzen, das < als Text zurueckgeben
1259                             rInput.Seek( nStreamPos );
1260                             SetLineNr( nLineNr );
1261                             SetLinePos( nLinePos );
1262                             ClearTxtConvContext();
1263 
1264                             aToken = '<';
1265                             nRet = HTML_TEXTTOKEN;
1266                             nNextCh = GetNextChar();
1267                             bNextCh = sal_False;
1268                             break;
1269                         }
1270                     }
1271                     if( SVPAR_PENDING == eState )
1272                         bReadNextChar = bReadNextCharSave;
1273                 }
1274                 else
1275                 {
1276                     if( bOffState )
1277                     {
1278                         // einfach alles wegschmeissen
1279                         ScanText( '>' );
1280                         if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() )
1281                         {
1282                             // zurueck hinter die < gehen  und dort neu
1283                             // aufsetzen, das < als Text zurueckgeben
1284                             rInput.Seek( nStreamPos );
1285                             SetLineNr( nLineNr );
1286                             SetLinePos( nLinePos );
1287                             ClearTxtConvContext();
1288 
1289                             aToken = '<';
1290                             nRet = HTML_TEXTTOKEN;
1291                             nNextCh = GetNextChar();
1292                             bNextCh = sal_False;
1293                             break;
1294                         }
1295                         if( SVPAR_PENDING == eState )
1296                             bReadNextChar = bReadNextCharSave;
1297                         aToken.Erase();
1298                     }
1299                     else if( '%' == nNextCh )
1300                     {
1301                         nRet = HTML_UNKNOWNCONTROL_ON;
1302 
1303                         sal_uLong nCStreamPos = rInput.Tell();
1304                         sal_uLong nCLineNr = GetLineNr(), nCLinePos = GetLinePos();
1305 
1306                         sal_Bool bDone = sal_False;
1307                         // bis zum schliessenden %> lesen. wenn keins gefunden
1308                         // wurde beim der ersten > wieder aufsetzen
1309                         while( !bDone && !rInput.IsEof() && IsParserWorking() )
1310                         {
1311                             bDone = '>'==nNextCh && aToken.Len() >= 1 &&
1312                                     '%' == aToken.GetChar( aToken.Len()-1 );
1313                             if( !bDone )
1314                             {
1315                                 aToken += nNextCh;
1316                                 nNextCh = GetNextChar();
1317                             }
1318                         }
1319                         if( !bDone && IsParserWorking() )
1320                         {
1321                             rInput.Seek( nCStreamPos );
1322                             SetLineNr( nCLineNr );
1323                             SetLinePos( nCLinePos );
1324                             ClearTxtConvContext();
1325                             aToken.AssignAscii( "<%", 2 );
1326                             nRet = HTML_TEXTTOKEN;
1327                             break;
1328                         }
1329                         if( IsParserWorking() )
1330                         {
1331                             sSaveToken = aToken;
1332                             aToken.Erase();
1333                         }
1334                     }
1335                     else
1336                     {
1337                         aToken = '<';
1338                         nRet = HTML_TEXTTOKEN;
1339                         bNextCh = sal_False;
1340                         break;
1341                     }
1342                 }
1343 
1344                 if( IsParserWorking() )
1345                 {
1346                     bNextCh = '>' == nNextCh;
1347                     switch( nRet )
1348                     {
1349                     case HTML_TEXTAREA_ON:
1350                         bReadTextArea = sal_True;
1351                         break;
1352                     case HTML_TEXTAREA_OFF:
1353                         bReadTextArea = sal_False;
1354                         break;
1355                     case HTML_SCRIPT_ON:
1356                         if( !bReadTextArea )
1357                             bReadScript = sal_True;
1358                         break;
1359                     case HTML_SCRIPT_OFF:
1360                         if( !bReadTextArea )
1361                         {
1362                             bReadScript = sal_False;
1363                             // JavaScript kann den Stream veraendern
1364                             // also muss das letzte Zeichen nochmals
1365                             // gelesen werden
1366                             bReadNextChar = sal_True;
1367                             bNextCh = sal_False;
1368                         }
1369                         break;
1370 
1371                     case HTML_STYLE_ON:
1372                         bReadStyle = sal_True;
1373                         break;
1374                     case HTML_STYLE_OFF:
1375                         bReadStyle = sal_False;
1376                         break;
1377                     }
1378 
1379                 }
1380             }
1381             break;
1382 
1383         case sal_Unicode(EOF):
1384             if( rInput.IsEof() )
1385             {
1386                 eState = SVPAR_ACCEPTED;
1387                 nRet = nNextCh;
1388             }
1389             else
1390             {
1391                 // normalen Text lesen
1392                 goto scan_text;
1393             }
1394             break;
1395 
1396         case '\f':
1397             // Form-Feeds werden jetzt extra nach oben gereicht
1398             nRet = HTML_LINEFEEDCHAR; // !!! eigentlich FORMFEEDCHAR
1399             break;
1400 
1401         case '\n':
1402         case '\r':
1403             if( bReadListing || bReadXMP || bReadPRE || bReadTextArea )
1404             {
1405                 sal_Unicode c = GetNextChar();
1406                 if( ( '\n' != nNextCh || '\r' != c ) &&
1407                     ( '\r' != nNextCh || '\n' != c ) )
1408                 {
1409                     bNextCh = sal_False;
1410                     nNextCh = c;
1411                 }
1412                 nRet = HTML_NEWPARA;
1413                 break;
1414             }
1415             // kein break !
1416         case '\t':
1417             if( bReadPRE )
1418             {
1419                 nRet = HTML_TABCHAR;
1420                 break;
1421             }
1422             // kein break !
1423         case ' ':
1424             // kein break !
1425         default:
1426 
1427 scan_text:
1428             // es folgt "normaler" Text
1429             nRet = ScanText();
1430             bNextCh = 0 == aToken.Len();
1431 
1432             // der Text sollte noch verarbeitet werden
1433             if( !bNextCh && eState == SVPAR_PENDING )
1434             {
1435                 eState = SVPAR_WORKING;
1436                 bReadNextChar = sal_True;
1437             }
1438 
1439             break;
1440         }
1441 
1442         if( bNextCh && SVPAR_WORKING == eState )
1443         {
1444             nNextCh = GetNextChar();
1445             if( SVPAR_PENDING == eState && nRet && HTML_TEXTTOKEN != nRet )
1446             {
1447                 bReadNextChar = sal_True;
1448                 eState = SVPAR_WORKING;
1449             }
1450         }
1451 
1452     } while( !nRet && SVPAR_WORKING == eState );
1453 
1454     if( SVPAR_PENDING == eState )
1455         nRet = -1;      // irgendwas ungueltiges
1456 
1457     return nRet;
1458 }
1459 
UnescapeToken()1460 void HTMLParser::UnescapeToken()
1461 {
1462     xub_StrLen nPos=0;
1463 
1464     sal_Bool bEscape = sal_False;
1465     while( nPos < aToken.Len() )
1466     {
1467         sal_Bool bOldEscape = bEscape;
1468         bEscape = sal_False;
1469         if( '\\'==aToken.GetChar(nPos) && !bOldEscape )
1470         {
1471             aToken.Erase( nPos, 1 );
1472             bEscape = sal_True;
1473         }
1474         else
1475         {
1476             nPos++;
1477         }
1478     }
1479 }
1480 
1481 // hole die Optionen
GetOptions(sal_uInt16 * pNoConvertToken) const1482 const HTMLOptions *HTMLParser::GetOptions( sal_uInt16 *pNoConvertToken ) const
1483 {
1484     // wenn die Option fuer das aktuelle Token schon einmal
1485     // geholt wurden, geben wir sie noch einmal zurueck
1486     if( pOptions->Count() )
1487         return pOptions;
1488 
1489     xub_StrLen nPos = 0;
1490     while( nPos < aToken.Len() )
1491     {
1492         // ein Zeichen ? Dann faengt hier eine Option an
1493         if( HTML_ISALPHA( aToken.GetChar(nPos) ) )
1494         {
1495             int nToken;
1496             String aValue;
1497             xub_StrLen nStt = nPos;
1498             sal_Unicode cChar = 0;
1499 
1500             // Eigentlich sind hier nur ganz bestimmte Zeichen erlaubt.
1501             // Netscape achtet aber nur auf "=" und Leerzeichen (siehe
1502             // Mozilla: PA_FetchRequestedNameValues in
1503             // lipparse/pa_mdl.c
1504 //          while( nPos < aToken.Len() &&
1505 //                  ( '-'==(c=aToken[nPos]) || isalnum(c) || '.'==c || '_'==c) )
1506             while( nPos < aToken.Len() && '=' != (cChar=aToken.GetChar(nPos)) &&
1507                    HTML_ISPRINTABLE(cChar) && !HTML_ISSPACE(cChar) )
1508                 nPos++;
1509 
1510             String sName( aToken.Copy( nStt, nPos-nStt ) );
1511 
1512 //JP 23.03.97: die PlugIns wollen die TokenName im "Original" haben
1513 //              also nur fuers Suchen in UpperCase wandeln
1514             String sNameUpperCase( sName );
1515             sNameUpperCase.ToUpperAscii();
1516 
1517             nToken = GetHTMLOption( sNameUpperCase ); // der Name ist fertig
1518             DBG_ASSERTWARNING( nToken!=HTML_O_UNKNOWN,
1519                         "GetOption: unbekannte HTML-Option" );
1520             sal_Bool bStripCRLF = (nToken < HTML_OPTION_SCRIPT_START ||
1521                                nToken >= HTML_OPTION_SCRIPT_END) &&
1522                               (!pNoConvertToken || nToken != *pNoConvertToken);
1523 
1524             while( nPos < aToken.Len() &&
1525                    ( !HTML_ISPRINTABLE( (cChar=aToken.GetChar(nPos)) ) ||
1526                      HTML_ISSPACE(cChar) ) )
1527                 nPos++;
1528 
1529             // hat die Option auch einen Wert?
1530             if( nPos!=aToken.Len() && '='==cChar )
1531             {
1532                 nPos++;
1533 
1534                 while( nPos < aToken.Len() &&
1535                         ( !HTML_ISPRINTABLE( (cChar=aToken.GetChar(nPos)) ) ||
1536                           ' '==cChar || '\t'==cChar || '\r'==cChar || '\n'==cChar ) )
1537                     nPos++;
1538 
1539                 if( nPos != aToken.Len() )
1540                 {
1541                     xub_StrLen nLen = 0;
1542                     nStt = nPos;
1543                     if( ('"'==cChar) || ('\'')==cChar )
1544                     {
1545                         sal_Unicode cEnd = cChar;
1546                         nPos++; nStt++;
1547                         sal_Bool bDone = sal_False;
1548                         sal_Bool bEscape = sal_False;
1549                         while( nPos < aToken.Len() && !bDone )
1550                         {
1551                             sal_Bool bOldEscape = bEscape;
1552                             bEscape = sal_False;
1553                             cChar = aToken.GetChar(nPos);
1554                             switch( cChar )
1555                             {
1556                             case '\r':
1557                             case '\n':
1558                                 if( bStripCRLF )
1559                                     ((String &)aToken).Erase( nPos, 1 );
1560                                 else
1561                                     nPos++, nLen++;
1562                                 break;
1563                             case '\\':
1564                                 if( bOldEscape )
1565                                 {
1566                                     nPos++, nLen++;
1567                                 }
1568                                 else
1569                                 {
1570                                     ((String &)aToken).Erase( nPos, 1 );
1571                                     bEscape = sal_True;
1572                                 }
1573                                 break;
1574                             case '"':
1575                             case '\'':
1576                                 bDone = !bOldEscape && cChar==cEnd;
1577                                 if( !bDone )
1578                                     nPos++, nLen++;
1579                                 break;
1580                             default:
1581                                 nPos++, nLen++;
1582                                 break;
1583                             }
1584                         }
1585                         if( nPos!=aToken.Len() )
1586                             nPos++;
1587                     }
1588                     else
1589                     {
1590                         // hier sind wir etwas laxer als der
1591                         // Standard und erlauben alles druckbare
1592                         sal_Bool bEscape = sal_False;
1593                         sal_Bool bDone = sal_False;
1594                         while( nPos < aToken.Len() && !bDone )
1595                         {
1596                             sal_Bool bOldEscape = bEscape;
1597                             bEscape = sal_False;
1598                             sal_Unicode c = aToken.GetChar(nPos);
1599                             switch( c )
1600                             {
1601                             case ' ':
1602                                 bDone = !bOldEscape;
1603                                 if( !bDone )
1604                                     nPos++, nLen++;
1605                                 break;
1606 
1607                             case '\t':
1608                             case '\r':
1609                             case '\n':
1610                                 bDone = sal_True;
1611                                 break;
1612 
1613                             case '\\':
1614                                 if( bOldEscape )
1615                                 {
1616                                     nPos++, nLen++;
1617                                 }
1618                                 else
1619                                 {
1620                                     ((String &)aToken).Erase( nPos, 1 );
1621                                     bEscape = sal_True;
1622                                 }
1623                                 break;
1624 
1625                             default:
1626                                 if( HTML_ISPRINTABLE( c ) )
1627                                     nPos++, nLen++;
1628                                 else
1629                                     bDone = sal_True;
1630                                 break;
1631                             }
1632                         }
1633                     }
1634 
1635                     if( nLen )
1636                         aValue = aToken.Copy( nStt, nLen );
1637                 }
1638             }
1639 
1640             // Wir kennen das Token und koennen es Speichern
1641             HTMLOption *pOption =
1642                 new HTMLOption(
1643                     sal::static_int_cast< sal_uInt16 >(nToken), sName, aValue );
1644 
1645             pOptions->Insert( pOption, pOptions->Count() );
1646 
1647         }
1648         else
1649             // white space un unerwartete Zeichen ignorieren wie
1650             nPos++;
1651     }
1652 
1653     return pOptions;
1654 }
1655 
FilterPRE(int nToken)1656 int HTMLParser::FilterPRE( int nToken )
1657 {
1658     switch( nToken )
1659     {
1660 #ifdef HTML_BEHAVIOUR
1661     // diese werden laut Definition zu LFs
1662     case HTML_PARABREAK_ON:
1663     case HTML_LINEBREAK:
1664         nToken = HTML_NEWPARA;
1665 #else
1666     // in Netscape zeigen sie aber nur in nicht-leeren Absaetzen Wirkung
1667     case HTML_PARABREAK_ON:
1668         nToken = HTML_LINEBREAK;
1669     case HTML_LINEBREAK:
1670 #endif
1671     case HTML_NEWPARA:
1672         nPre_LinePos = 0;
1673         if( bPre_IgnoreNewPara )
1674             nToken = 0;
1675         break;
1676 
1677     case HTML_TABCHAR:
1678         {
1679             xub_StrLen nSpaces = sal::static_int_cast< xub_StrLen >(
1680                 8 - (nPre_LinePos % 8));
1681             DBG_ASSERT( !aToken.Len(), "Wieso ist das Token nicht leer?" );
1682             aToken.Expand( nSpaces, ' ' );
1683             nPre_LinePos += nSpaces;
1684             nToken = HTML_TEXTTOKEN;
1685         }
1686         break;
1687     // diese bleiben erhalten
1688     case HTML_TEXTTOKEN:
1689         nPre_LinePos += aToken.Len();
1690         break;
1691 
1692     case HTML_SELECT_ON:
1693     case HTML_SELECT_OFF:
1694     case HTML_BODY_ON:
1695     case HTML_FORM_ON:
1696     case HTML_FORM_OFF:
1697     case HTML_INPUT:
1698     case HTML_OPTION:
1699     case HTML_TEXTAREA_ON:
1700     case HTML_TEXTAREA_OFF:
1701 
1702     case HTML_IMAGE:
1703     case HTML_APPLET_ON:
1704     case HTML_APPLET_OFF:
1705     case HTML_PARAM:
1706     case HTML_EMBED:
1707 
1708     case HTML_HEAD1_ON:
1709     case HTML_HEAD1_OFF:
1710     case HTML_HEAD2_ON:
1711     case HTML_HEAD2_OFF:
1712     case HTML_HEAD3_ON:
1713     case HTML_HEAD3_OFF:
1714     case HTML_HEAD4_ON:
1715     case HTML_HEAD4_OFF:
1716     case HTML_HEAD5_ON:
1717     case HTML_HEAD5_OFF:
1718     case HTML_HEAD6_ON:
1719     case HTML_HEAD6_OFF:
1720     case HTML_BLOCKQUOTE_ON:
1721     case HTML_BLOCKQUOTE_OFF:
1722     case HTML_ADDRESS_ON:
1723     case HTML_ADDRESS_OFF:
1724     case HTML_HORZRULE:
1725 
1726     case HTML_CENTER_ON:
1727     case HTML_CENTER_OFF:
1728     case HTML_DIVISION_ON:
1729     case HTML_DIVISION_OFF:
1730 
1731     case HTML_SCRIPT_ON:
1732     case HTML_SCRIPT_OFF:
1733     case HTML_RAWDATA:
1734 
1735     case HTML_TABLE_ON:
1736     case HTML_TABLE_OFF:
1737     case HTML_CAPTION_ON:
1738     case HTML_CAPTION_OFF:
1739     case HTML_COLGROUP_ON:
1740     case HTML_COLGROUP_OFF:
1741     case HTML_COL_ON:
1742     case HTML_COL_OFF:
1743     case HTML_THEAD_ON:
1744     case HTML_THEAD_OFF:
1745     case HTML_TFOOT_ON:
1746     case HTML_TFOOT_OFF:
1747     case HTML_TBODY_ON:
1748     case HTML_TBODY_OFF:
1749     case HTML_TABLEROW_ON:
1750     case HTML_TABLEROW_OFF:
1751     case HTML_TABLEDATA_ON:
1752     case HTML_TABLEDATA_OFF:
1753     case HTML_TABLEHEADER_ON:
1754     case HTML_TABLEHEADER_OFF:
1755 
1756     case HTML_ANCHOR_ON:
1757     case HTML_ANCHOR_OFF:
1758     case HTML_BOLD_ON:
1759     case HTML_BOLD_OFF:
1760     case HTML_ITALIC_ON:
1761     case HTML_ITALIC_OFF:
1762     case HTML_STRIKE_ON:
1763     case HTML_STRIKE_OFF:
1764     case HTML_STRIKETHROUGH_ON:
1765     case HTML_STRIKETHROUGH_OFF:
1766     case HTML_UNDERLINE_ON:
1767     case HTML_UNDERLINE_OFF:
1768     case HTML_BASEFONT_ON:
1769     case HTML_BASEFONT_OFF:
1770     case HTML_FONT_ON:
1771     case HTML_FONT_OFF:
1772     case HTML_BLINK_ON:
1773     case HTML_BLINK_OFF:
1774     case HTML_SPAN_ON:
1775     case HTML_SPAN_OFF:
1776     case HTML_SUBSCRIPT_ON:
1777     case HTML_SUBSCRIPT_OFF:
1778     case HTML_SUPERSCRIPT_ON:
1779     case HTML_SUPERSCRIPT_OFF:
1780     case HTML_BIGPRINT_ON:
1781     case HTML_BIGPRINT_OFF:
1782     case HTML_SMALLPRINT_OFF:
1783     case HTML_SMALLPRINT_ON:
1784 
1785     case HTML_EMPHASIS_ON:
1786     case HTML_EMPHASIS_OFF:
1787     case HTML_CITIATION_ON:
1788     case HTML_CITIATION_OFF:
1789     case HTML_STRONG_ON:
1790     case HTML_STRONG_OFF:
1791     case HTML_CODE_ON:
1792     case HTML_CODE_OFF:
1793     case HTML_SAMPLE_ON:
1794     case HTML_SAMPLE_OFF:
1795     case HTML_KEYBOARD_ON:
1796     case HTML_KEYBOARD_OFF:
1797     case HTML_VARIABLE_ON:
1798     case HTML_VARIABLE_OFF:
1799     case HTML_DEFINSTANCE_ON:
1800     case HTML_DEFINSTANCE_OFF:
1801     case HTML_SHORTQUOTE_ON:
1802     case HTML_SHORTQUOTE_OFF:
1803     case HTML_LANGUAGE_ON:
1804     case HTML_LANGUAGE_OFF:
1805     case HTML_AUTHOR_ON:
1806     case HTML_AUTHOR_OFF:
1807     case HTML_PERSON_ON:
1808     case HTML_PERSON_OFF:
1809     case HTML_ACRONYM_ON:
1810     case HTML_ACRONYM_OFF:
1811     case HTML_ABBREVIATION_ON:
1812     case HTML_ABBREVIATION_OFF:
1813     case HTML_INSERTEDTEXT_ON:
1814     case HTML_INSERTEDTEXT_OFF:
1815     case HTML_DELETEDTEXT_ON:
1816     case HTML_DELETEDTEXT_OFF:
1817     case HTML_TELETYPE_ON:
1818     case HTML_TELETYPE_OFF:
1819 
1820         break;
1821 
1822     // der Rest wird als unbekanntes Token behandelt
1823     default:
1824         if( nToken )
1825         {
1826             nToken =
1827                 ( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken))
1828                     ? HTML_UNKNOWNCONTROL_OFF
1829                     : HTML_UNKNOWNCONTROL_ON );
1830         }
1831         break;
1832     }
1833 
1834     bPre_IgnoreNewPara = sal_False;
1835 
1836     return nToken;
1837 }
1838 
FilterXMP(int nToken)1839 int HTMLParser::FilterXMP( int nToken )
1840 {
1841     switch( nToken )
1842     {
1843     case HTML_NEWPARA:
1844         if( bPre_IgnoreNewPara )
1845             nToken = 0;
1846     case HTML_TEXTTOKEN:
1847     case HTML_NONBREAKSPACE:
1848     case HTML_SOFTHYPH:
1849         break;              // bleiben erhalten
1850 
1851     default:
1852         if( nToken )
1853         {
1854             if( (HTML_TOKEN_ONOFF & nToken) && (1 & nToken) )
1855             {
1856                 sSaveToken.Insert( '<', 0 );
1857                 sSaveToken.Insert( '/', 1 );
1858             }
1859             else
1860                 sSaveToken.Insert( '<', 0 );
1861             if( aToken.Len() )
1862             {
1863                 UnescapeToken();
1864                 sSaveToken += (sal_Unicode)' ';
1865                 aToken.Insert( sSaveToken, 0 );
1866             }
1867             else
1868                 aToken = sSaveToken;
1869             aToken += (sal_Unicode)'>';
1870             nToken = HTML_TEXTTOKEN;
1871         }
1872         break;
1873     }
1874 
1875     bPre_IgnoreNewPara = sal_False;
1876 
1877     return nToken;
1878 }
1879 
FilterListing(int nToken)1880 int HTMLParser::FilterListing( int nToken )
1881 {
1882     switch( nToken )
1883     {
1884     case HTML_NEWPARA:
1885         if( bPre_IgnoreNewPara )
1886             nToken = 0;
1887     case HTML_TEXTTOKEN:
1888     case HTML_NONBREAKSPACE:
1889     case HTML_SOFTHYPH:
1890         break;      // bleiben erhalten
1891 
1892     default:
1893         if( nToken )
1894         {
1895             nToken =
1896                 ( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken))
1897                     ? HTML_UNKNOWNCONTROL_OFF
1898                     : HTML_UNKNOWNCONTROL_ON );
1899         }
1900         break;
1901     }
1902 
1903     bPre_IgnoreNewPara = sal_False;
1904 
1905     return nToken;
1906 }
1907 
IsHTMLFormat(const sal_Char * pHeader,sal_Bool bSwitchToUCS2,rtl_TextEncoding eEnc)1908 FASTBOOL HTMLParser::IsHTMLFormat( const sal_Char* pHeader,
1909                                    sal_Bool bSwitchToUCS2,
1910                                    rtl_TextEncoding eEnc )
1911 {
1912     // Einer der folgenden regulaeren Ausdrucke muss sich auf den String
1913     // anwenden lassen, damit das Dok ein HTML-Dokument ist.
1914     //
1915     // ^[^<]*<[^ \t]*[> \t]
1916     //        -------
1917     // ^<!
1918     //
1919     // wobei der unterstrichene Teilausdruck einem HTML-Token
1920     // ensprechen muss
1921 
1922     ByteString sCmp;
1923     sal_Bool bUCS2B = sal_False;
1924     if( bSwitchToUCS2 )
1925     {
1926         if( 0xfeU == (sal_uChar)pHeader[0] &&
1927             0xffU == (sal_uChar)pHeader[1] )
1928         {
1929             eEnc = RTL_TEXTENCODING_UCS2;
1930             bUCS2B = sal_True;
1931         }
1932         else if( 0xffU == (sal_uChar)pHeader[0] &&
1933                  0xfeU == (sal_uChar)pHeader[1] )
1934         {
1935             eEnc = RTL_TEXTENCODING_UCS2;
1936         }
1937     }
1938     if
1939        (
1940         RTL_TEXTENCODING_UCS2 == eEnc &&
1941         (
1942          (0xfe == (sal_uChar)pHeader[0] && 0xff == (sal_uChar)pHeader[1]) ||
1943          (0xff == (sal_uChar)pHeader[0] && 0xfe == (sal_uChar)pHeader[1])
1944         )
1945        )
1946     {
1947         if( 0xfe == (sal_uChar)pHeader[0] )
1948             bUCS2B = sal_True;
1949 
1950         xub_StrLen nLen;
1951         for( nLen = 2;
1952              pHeader[nLen] != 0 || pHeader[nLen+1] != 0;
1953              nLen+=2 )
1954             ;
1955 
1956         ::rtl::OStringBuffer sTmp( (nLen - 2)/2 );
1957         for( xub_StrLen nPos = 2; nPos < nLen; nPos += 2 )
1958         {
1959             sal_Unicode cUC;
1960             if( bUCS2B )
1961                 cUC = (sal_Unicode(pHeader[nPos]) << 8) | pHeader[nPos+1];
1962             else
1963                 cUC = (sal_Unicode(pHeader[nPos+1]) << 8) | pHeader[nPos];
1964             if( 0U == cUC )
1965                 break;
1966 
1967             sTmp.append( cUC < 256U ? (sal_Char)cUC : '.' );
1968         }
1969         sCmp = ByteString( sTmp.makeStringAndClear() );
1970     }
1971     else
1972     {
1973         sCmp = (sal_Char *)pHeader;
1974     }
1975 
1976     sCmp.ToUpperAscii();
1977 
1978     // Ein HTML-Dokument muss in der ersten Zeile ein '<' besitzen
1979     xub_StrLen nStart = sCmp.Search( '<' );
1980     if( STRING_NOTFOUND  == nStart )
1981         return sal_False;
1982     nStart++;
1983 
1984     // danach duerfen beliebige andere Zeichen bis zu einem blank oder
1985     // '>' kommen
1986     sal_Char c;
1987     xub_StrLen nPos;
1988     for( nPos = nStart; nPos<sCmp.Len(); nPos++ )
1989     {
1990         if( '>'==(c=sCmp.GetChar(nPos)) || HTML_ISSPACE(c) )
1991             break;
1992     }
1993 
1994     // wenn das Dokeument hinter dem < aufhoert ist es wohl kein HTML
1995     if( nPos==nStart )
1996         return sal_False;
1997 
1998     // die Zeichenkette nach dem '<' muss ausserdem ein bekanntes
1999     // HTML Token sein. Damit die Ausgabe eines DOS-dir-Befehls nicht
2000     // als HTML interpretiert wird, wird ein <DIR> jedoch nicht als HTML
2001     // interpretiert.
2002     String sTest( sCmp.Copy( nStart, nPos-nStart ), RTL_TEXTENCODING_ASCII_US );
2003     int nTok = GetHTMLToken( sTest );
2004     if( 0 != nTok && HTML_DIRLIST_ON != nTok )
2005         return sal_True;
2006 
2007     // oder es handelt sich um ein "<!" ganz am Anfang der Datei (fix #27092#)
2008     if( nStart == 1 && '!' == sCmp.GetChar( 1 ) )
2009         return sal_True;
2010 
2011     // oder wir finden irgendwo ein <HTML> in den ersten 80 Zeichen
2012     nStart = sCmp.Search( OOO_STRING_SVTOOLS_HTML_html );
2013     if( nStart!=STRING_NOTFOUND &&
2014         nStart>0 && '<'==sCmp.GetChar(nStart-1) &&
2015         nStart+4 < sCmp.Len() && '>'==sCmp.GetChar(nStart+4) )
2016         return sal_True;
2017 
2018     // sonst ist es wohl doch eher kein HTML-Dokument
2019     return sal_False;
2020 }
2021 
InternalImgToPrivateURL(String & rURL)2022 sal_Bool HTMLParser::InternalImgToPrivateURL( String& rURL )
2023 {
2024     if( rURL.Len() < 19 || 'i' != rURL.GetChar(0) ||
2025         rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_gopher, 9 ) != COMPARE_EQUAL )
2026         return sal_False;
2027 
2028     sal_Bool bFound = sal_False;
2029 
2030     if( rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_gopher,16) == COMPARE_EQUAL )
2031     {
2032         String aName( rURL.Copy(16) );
2033         switch( aName.GetChar(0) )
2034         {
2035         case 'b':
2036             bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_binary );
2037             break;
2038         case 'i':
2039             bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_image ) ||
2040                      aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_index );
2041             break;
2042         case 'm':
2043             bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_menu ) ||
2044                      aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_movie );
2045             break;
2046         case 's':
2047             bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_sound );
2048             break;
2049         case 't':
2050             bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_telnet ) ||
2051                      aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_text );
2052             break;
2053         case 'u':
2054             bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_unknown );
2055             break;
2056         }
2057     }
2058     else if( rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_icon,14) == COMPARE_EQUAL )
2059     {
2060         String aName( rURL.Copy(14) );
2061         switch( aName.GetChar(0) )
2062         {
2063         case 'b':
2064             bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_baddata );
2065             break;
2066         case 'd':
2067             bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_delayed );
2068             break;
2069         case 'e':
2070             bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_embed );
2071             break;
2072         case 'i':
2073             bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_insecure );
2074             break;
2075         case 'n':
2076             bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_notfound );
2077             break;
2078         }
2079     }
2080     if( bFound )
2081     {
2082         String sTmp ( rURL );
2083         rURL.AssignAscii( OOO_STRING_SVTOOLS_HTML_private_image );
2084         rURL.Append( sTmp );
2085     }
2086 
2087     return bFound;
2088 }
2089 
2090 #ifdef USED
SaveState(int nToken)2091 void HTMLParser::SaveState( int nToken )
2092 {
2093     SvParser::SaveState( nToken );
2094 }
2095 
RestoreState()2096 void HTMLParser::RestoreState()
2097 {
2098     SvParser::RestoreState();
2099 }
2100 #endif
2101 
2102 
2103 enum eHtmlMetas {
2104     HTML_META_NONE = 0,
2105     HTML_META_AUTHOR,
2106     HTML_META_DESCRIPTION,
2107     HTML_META_KEYWORDS,
2108     HTML_META_REFRESH,
2109     HTML_META_CLASSIFICATION,
2110     HTML_META_CREATED,
2111     HTML_META_CHANGEDBY,
2112     HTML_META_CHANGED,
2113     HTML_META_GENERATOR,
2114     HTML_META_SDFOOTNOTE,
2115     HTML_META_SDENDNOTE,
2116     HTML_META_CONTENT_TYPE
2117 };
2118 
2119 // <META NAME=xxx>
2120 static HTMLOptionEnum __READONLY_DATA aHTMLMetaNameTable[] =
2121 {
2122     { OOO_STRING_SVTOOLS_HTML_META_author,        HTML_META_AUTHOR        },
2123     { OOO_STRING_SVTOOLS_HTML_META_changed,       HTML_META_CHANGED       },
2124     { OOO_STRING_SVTOOLS_HTML_META_changedby,     HTML_META_CHANGEDBY     },
2125     { OOO_STRING_SVTOOLS_HTML_META_classification,HTML_META_CLASSIFICATION},
2126     { OOO_STRING_SVTOOLS_HTML_META_content_type,  HTML_META_CONTENT_TYPE  },
2127     { OOO_STRING_SVTOOLS_HTML_META_created,       HTML_META_CREATED       },
2128     { OOO_STRING_SVTOOLS_HTML_META_description,   HTML_META_DESCRIPTION   },
2129     { OOO_STRING_SVTOOLS_HTML_META_keywords,      HTML_META_KEYWORDS      },
2130     { OOO_STRING_SVTOOLS_HTML_META_generator,     HTML_META_GENERATOR     },
2131     { OOO_STRING_SVTOOLS_HTML_META_refresh,       HTML_META_REFRESH       },
2132     { OOO_STRING_SVTOOLS_HTML_META_sdendnote,     HTML_META_SDENDNOTE     },
2133     { OOO_STRING_SVTOOLS_HTML_META_sdfootnote,    HTML_META_SDFOOTNOTE    },
2134     { 0,                                          0                       }
2135 };
2136 
2137 
AddMetaUserDefined(::rtl::OUString const &)2138 void HTMLParser::AddMetaUserDefined( ::rtl::OUString const & )
2139 {
2140 }
2141 
ParseMetaOptionsImpl(const uno::Reference<document::XDocumentProperties> & i_xDocProps,SvKeyValueIterator * i_pHTTPHeader,const HTMLOptions * i_pOptions,rtl_TextEncoding & o_rEnc)2142 bool HTMLParser::ParseMetaOptionsImpl(
2143         const uno::Reference<document::XDocumentProperties> & i_xDocProps,
2144         SvKeyValueIterator *i_pHTTPHeader,
2145         const HTMLOptions *i_pOptions,
2146         rtl_TextEncoding& o_rEnc )
2147 {
2148     String aName, aContent;
2149     sal_uInt16 nAction = HTML_META_NONE;
2150     bool bHTTPEquiv = false, bChanged = false;
2151 
2152     for ( sal_uInt16 i = i_pOptions->Count(); i; )
2153     {
2154         const HTMLOption *pOption = (*i_pOptions)[ --i ];
2155         switch ( pOption->GetToken() )
2156         {
2157             case HTML_O_NAME:
2158                 aName = pOption->GetString();
2159                 if ( HTML_META_NONE==nAction )
2160                 {
2161                     pOption->GetEnum( nAction, aHTMLMetaNameTable );
2162                 }
2163                 break;
2164             case HTML_O_HTTPEQUIV:
2165                 aName = pOption->GetString();
2166                 pOption->GetEnum( nAction, aHTMLMetaNameTable );
2167                 bHTTPEquiv = true;
2168                 break;
2169             case HTML_O_CONTENT:
2170                 aContent = pOption->GetString();
2171                 break;
2172         }
2173     }
2174 
2175     if ( bHTTPEquiv || HTML_META_DESCRIPTION != nAction )
2176     {
2177         // if it is not a Description, remove CRs and LFs from CONTENT
2178         aContent.EraseAllChars( _CR );
2179         aContent.EraseAllChars( _LF );
2180     }
2181     else
2182     {
2183         // convert line endings for Description
2184         aContent.ConvertLineEnd();
2185     }
2186 
2187 
2188     if ( bHTTPEquiv && i_pHTTPHeader )
2189     {
2190         // #57232#: Netscape seems to just ignore a closing ", so we do too
2191         if ( aContent.Len() && '"' == aContent.GetChar( aContent.Len()-1 ) )
2192         {
2193             aContent.Erase( aContent.Len() - 1 );
2194         }
2195         SvKeyValue aKeyValue( aName, aContent );
2196         i_pHTTPHeader->Append( aKeyValue );
2197     }
2198 
2199     switch ( nAction )
2200     {
2201         case HTML_META_AUTHOR:
2202             if (i_xDocProps.is()) {
2203                 i_xDocProps->setAuthor( aContent );
2204                 bChanged = true;
2205             }
2206             break;
2207         case HTML_META_DESCRIPTION:
2208             if (i_xDocProps.is()) {
2209                 i_xDocProps->setDescription( aContent );
2210                 bChanged = true;
2211             }
2212             break;
2213         case HTML_META_KEYWORDS:
2214             if (i_xDocProps.is()) {
2215                 i_xDocProps->setKeywords(
2216                     ::comphelper::string::convertCommaSeparated(aContent));
2217                 bChanged = true;
2218             }
2219             break;
2220         case HTML_META_CLASSIFICATION:
2221             if (i_xDocProps.is()) {
2222                 i_xDocProps->setSubject( aContent );
2223                 bChanged = true;
2224             }
2225             break;
2226 
2227         case HTML_META_CHANGEDBY:
2228             if (i_xDocProps.is()) {
2229                 i_xDocProps->setModifiedBy( aContent );
2230             }
2231             break;
2232 
2233         case HTML_META_CREATED:
2234         case HTML_META_CHANGED:
2235             if ( i_xDocProps.is() && aContent.Len() &&
2236                  aContent.GetTokenCount() == 2 )
2237             {
2238                 Date aDate( (sal_uLong)aContent.GetToken(0).ToInt32() );
2239                 Time aTime( (sal_uLong)aContent.GetToken(1).ToInt32() );
2240                 DateTime aDateTime( aDate, aTime );
2241                 ::util::DateTime uDT(aDateTime.Get100Sec(),
2242                     aDateTime.GetSec(), aDateTime.GetMin(),
2243                     aDateTime.GetHour(), aDateTime.GetDay(),
2244                     aDateTime.GetMonth(), aDateTime.GetYear());
2245                 if ( HTML_META_CREATED==nAction )
2246                     i_xDocProps->setCreationDate( uDT );
2247                 else
2248                     i_xDocProps->setModificationDate( uDT );
2249                 bChanged = true;
2250             }
2251             break;
2252 
2253         case HTML_META_REFRESH:
2254             DBG_ASSERT( !bHTTPEquiv || i_pHTTPHeader,
2255         "Reload-URL aufgrund unterlassener MUSS-Aenderung verlorengegangen" );
2256             break;
2257 
2258         case HTML_META_CONTENT_TYPE:
2259             if ( aContent.Len() )
2260             {
2261                 o_rEnc = GetEncodingByMIME( aContent );
2262             }
2263             break;
2264 
2265         case HTML_META_NONE:
2266             if ( !bHTTPEquiv )
2267             {
2268                 if (i_xDocProps.is())
2269                 {
2270                     uno::Reference<beans::XPropertyContainer> xUDProps
2271                         = i_xDocProps->getUserDefinedProperties();
2272                     try {
2273                         xUDProps->addProperty(aName,
2274                             beans::PropertyAttribute::REMOVEABLE,
2275                             uno::makeAny(::rtl::OUString(aContent)));
2276                         AddMetaUserDefined(aName);
2277                         bChanged = true;
2278                     } catch (uno::Exception &) {
2279                         // ignore
2280                     }
2281                 }
2282             }
2283             break;
2284         default:
2285             break;
2286     }
2287 
2288     return bChanged;
2289 }
2290 
ParseMetaOptions(const uno::Reference<document::XDocumentProperties> & i_xDocProps,SvKeyValueIterator * i_pHeader)2291 bool HTMLParser::ParseMetaOptions(
2292         const uno::Reference<document::XDocumentProperties> & i_xDocProps,
2293         SvKeyValueIterator *i_pHeader )
2294 {
2295     sal_uInt16 nContentOption = HTML_O_CONTENT;
2296     rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
2297 
2298     bool bRet = ParseMetaOptionsImpl( i_xDocProps, i_pHeader,
2299                       GetOptions(&nContentOption),
2300                       eEnc );
2301 
2302     // If the encoding is set by a META tag, it may only overwrite the
2303     // current encoding if both, the current and the new encoding, are 1-sal_uInt8
2304     // encodings. Everything else cannot lead to reasonable results.
2305     if (RTL_TEXTENCODING_DONTKNOW != eEnc &&
2306         rtl_isOctetTextEncoding( eEnc ) &&
2307         rtl_isOctetTextEncoding( GetSrcEncoding() ) )
2308     {
2309         eEnc = GetExtendedCompatibilityTextEncoding( eEnc ); // #89973#
2310         SetSrcEncoding( eEnc );
2311     }
2312 
2313     return bRet;
2314 }
2315 
GetEncodingByMIME(const String & rMime)2316 rtl_TextEncoding HTMLParser::GetEncodingByMIME( const String& rMime )
2317 {
2318     ByteString sType;
2319     ByteString sSubType;
2320     INetContentTypeParameterList aParameters;
2321     ByteString sMime( rMime, RTL_TEXTENCODING_ASCII_US );
2322     if (INetContentTypes::parse(sMime, sType, sSubType, &aParameters))
2323     {
2324         const INetContentTypeParameter * pCharset
2325             = aParameters.find("charset");
2326         if (pCharset != 0)
2327         {
2328             ByteString sValue( pCharset->m_sValue, RTL_TEXTENCODING_ASCII_US );
2329             return GetExtendedCompatibilityTextEncoding(
2330                     rtl_getTextEncodingFromMimeCharset( sValue.GetBuffer() ) );
2331         }
2332     }
2333     return RTL_TEXTENCODING_DONTKNOW;
2334 }
2335 
GetEncodingByHttpHeader(SvKeyValueIterator * pHTTPHeader)2336 rtl_TextEncoding HTMLParser::GetEncodingByHttpHeader( SvKeyValueIterator *pHTTPHeader )
2337 {
2338     rtl_TextEncoding eRet = RTL_TEXTENCODING_DONTKNOW;
2339     if( pHTTPHeader )
2340     {
2341         SvKeyValue aKV;
2342         for( sal_Bool bCont = pHTTPHeader->GetFirst( aKV ); bCont;
2343              bCont = pHTTPHeader->GetNext( aKV ) )
2344         {
2345             if( aKV.GetKey().EqualsIgnoreCaseAscii( OOO_STRING_SVTOOLS_HTML_META_content_type ) )
2346             {
2347                 if( aKV.GetValue().Len() )
2348                 {
2349                     eRet = HTMLParser::GetEncodingByMIME( aKV.GetValue() );
2350                 }
2351             }
2352         }
2353     }
2354     return eRet;
2355 }
2356 
SetEncodingByHTTPHeader(SvKeyValueIterator * pHTTPHeader)2357 sal_Bool HTMLParser::SetEncodingByHTTPHeader(
2358                                 SvKeyValueIterator *pHTTPHeader )
2359 {
2360     sal_Bool bRet = sal_False;
2361     rtl_TextEncoding eEnc = HTMLParser::GetEncodingByHttpHeader( pHTTPHeader );
2362     if(RTL_TEXTENCODING_DONTKNOW != eEnc)
2363     {
2364         SetSrcEncoding( eEnc );
2365         bRet = sal_True;
2366     }
2367     return bRet;
2368 }
2369 
2370 
2371