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