xref: /trunk/main/svtools/source/svhtml/parhtml.cxx (revision 5900e8ec)
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