xref: /trunk/main/basic/source/comp/scanner.cxx (revision cdf0e10c)
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_basic.hxx"
30 
31 #include "sbcomp.hxx"
32 #include <stdio.h>
33 #include <string.h>
34 #include <ctype.h>
35 #if defined UNX
36 #include <stdlib.h>
37 #else
38 #include <math.h>   // atof()
39 #endif
40 #include <rtl/math.hxx>
41 #include <vcl/svapp.hxx>
42 #include <unotools/charclass.hxx>
43 
44 #include <runtime.hxx>
45 
46 SbiScanner::SbiScanner( const ::rtl::OUString& rBuf, StarBASIC* p ) : aBuf( rBuf )
47 {
48 	pBasic   = p;
49 	pLine    = NULL;
50 	nVal	 = 0;
51 	eScanType = SbxVARIANT;
52 	nErrors  = 0;
53 	nBufPos  = 0;
54 	nCurCol1 = 0;
55 	nSavedCol1 = 0;
56 	nColLock = 0;
57 	nLine	 = 0;
58 	nCol1	 = 0;
59 	nCol2	 = 0;
60 	nCol     = 0;
61 	bError	 =
62 	bAbort   =
63 	bSpaces  =
64 	bNumber  =
65 	bSymbol  =
66 	bUsedForHilite =
67 	bCompatible =
68 	bVBASupportOn =
69 	bPrevLineExtentsComment = sal_False;
70 	bHash    =
71 	bErrors  = sal_True;
72 }
73 
74 SbiScanner::~SbiScanner()
75 {}
76 
77 void SbiScanner::LockColumn()
78 {
79 	if( !nColLock++ )
80 		nSavedCol1 = nCol1;
81 }
82 
83 void SbiScanner::UnlockColumn()
84 {
85 	if( nColLock )
86 		nColLock--;
87 }
88 
89 void SbiScanner::GenError( SbError code )
90 {
91     if( GetSbData()->bBlockCompilerError )
92     {
93         bAbort = sal_True;
94         return;
95     }
96 	if( !bError && bErrors )
97 	{
98 		sal_Bool bRes = sal_True;
99 		// Nur einen Fehler pro Statement reporten
100 		bError = sal_True;
101 		if( pBasic )
102 		{
103 			// Falls EXPECTED oder UNEXPECTED kommen sollte, bezieht es sich
104 			// immer auf das letzte Token, also die Col1 uebernehmen
105 			sal_uInt16 nc = nColLock ? nSavedCol1 : nCol1;
106 			switch( code )
107 			{
108 				case SbERR_EXPECTED:
109 				case SbERR_UNEXPECTED:
110 				case SbERR_SYMBOL_EXPECTED:
111 				case SbERR_LABEL_EXPECTED:
112 					nc = nCol1;
113 					if( nc > nCol2 ) nCol2 = nc;
114 					break;
115 			}
116 			bRes = pBasic->CError( code, aError, nLine, nc, nCol2 );
117 		}
118 		bAbort |= !bRes |
119 			 ( code == SbERR_NO_MEMORY || code == SbERR_PROG_TOO_LARGE );
120 	}
121 	if( bErrors )
122 		nErrors++;
123 }
124 
125 // Falls sofort ein Doppelpunkt folgt, wird sal_True zurueckgeliefert.
126 // Wird von SbiTokenizer::MayBeLabel() verwendet, um einen Label zu erkennen
127 
128 sal_Bool SbiScanner::DoesColonFollow()
129 {
130 	if( pLine && *pLine == ':' )
131 	{
132 		pLine++; nCol++; return sal_True;
133 	}
134 	else return sal_False;
135 }
136 
137 // Testen auf ein legales Suffix
138 
139 static SbxDataType GetSuffixType( sal_Unicode c )
140 {
141 	static String aSuffixesStr = String::CreateFromAscii( "%&!#@ $" );
142 	if( c )
143 	{
144 		sal_uInt32 n = aSuffixesStr.Search( c );
145 		if( STRING_NOTFOUND != n && c != ' ' )
146 			return SbxDataType( (sal_uInt16) n + SbxINTEGER );
147 	}
148 	return SbxVARIANT;
149 }
150 
151 // Einlesen des naechsten Symbols in die Variablen aSym, nVal und eType
152 // Returnwert ist sal_False bei EOF oder Fehlern
153 #define BUF_SIZE 80
154 
155 namespace {
156 
157 /** Returns true, if the passed character is a white space character. */
158 inline bool lclIsWhitespace( sal_Unicode cChar )
159 {
160     return (cChar == ' ') || (cChar == '\t') || (cChar == '\f');
161 }
162 
163 } // namespace
164 
165 sal_Bool SbiScanner::NextSym()
166 {
167 	// Fuer den EOLN-Fall merken
168 	sal_uInt16 nOldLine = nLine;
169 	sal_uInt16 nOldCol1 = nCol1;
170 	sal_uInt16 nOldCol2 = nCol2;
171 	sal_Unicode buf[ BUF_SIZE ], *p = buf;
172 	bHash = sal_False;
173 
174 	eScanType = SbxVARIANT;
175 	aSym.Erase();
176 	bSymbol =
177 	bNumber = bSpaces = sal_False;
178 
179 	// Zeile einlesen?
180 	if( !pLine )
181 	{
182 		sal_Int32 n = nBufPos;
183 		sal_Int32 nLen = aBuf.getLength();
184 		if( nBufPos >= nLen )
185 			return sal_False;
186 		const sal_Unicode* p2 = aBuf.getStr();
187 		p2 += n;
188 		while( ( n < nLen ) && ( *p2 != '\n' ) && ( *p2 != '\r' ) )
189 			p2++, n++;
190         // #163944# ignore trailing whitespace
191         sal_Int32 nCopyEndPos = n;
192         while( (nBufPos < nCopyEndPos) && lclIsWhitespace( aBuf[ nCopyEndPos - 1 ] ) )
193             --nCopyEndPos;
194 		aLine = aBuf.copy( nBufPos, nCopyEndPos - nBufPos );
195 		if( n < nLen )
196         {
197 		    if( *p2 == '\r' && *( p2+1 ) == '\n' )
198 			    n += 2;
199 		    else
200 			    n++;
201         }
202 		nBufPos = n;
203         pLine = aLine.getStr();
204 		nOldLine = ++nLine;
205 		nCol = nCol1 = nCol2 = nOldCol1 = nOldCol2 = 0;
206 		nColLock = 0;
207 	}
208 
209 	// Leerstellen weg:
210 	while( lclIsWhitespace( *pLine ) )
211 		pLine++, nCol++, bSpaces = sal_True;
212 
213 	nCol1 = nCol;
214 
215 	// nur Leerzeile?
216 	if( !*pLine )
217 		goto eoln;
218 
219 	if( bPrevLineExtentsComment )
220 		goto PrevLineCommentLbl;
221 
222 	if( *pLine == '#' )
223 	{
224 		pLine++;
225 		nCol++;
226 		bHash = sal_True;
227 	}
228 
229 	// Symbol? Dann Zeichen kopieren.
230     if( BasicSimpleCharClass::isAlpha( *pLine, bCompatible ) || *pLine == '_' )
231 	{
232 		// Wenn nach '_' nichts kommt, ist es ein Zeilenabschluss!
233 		if(	*pLine == '_' && !*(pLine+1) )
234 		{	pLine++;
235 			goto eoln;	}
236 		bSymbol = sal_True;
237 		short n = nCol;
238 		for ( ; (BasicSimpleCharClass::isAlphaNumeric( *pLine, bCompatible ) || ( *pLine == '_' ) ); pLine++ )
239 			nCol++;
240 		aSym = aLine.copy( n, nCol - n );
241 
242 		// Special handling for "go to"
243 		if( bCompatible && *pLine && aSym.EqualsIgnoreCaseAscii( "go" ) )
244 		{
245 			const sal_Unicode* pTestLine = pLine;
246 			short nTestCol = nCol;
247 			while( lclIsWhitespace( *pTestLine ) )
248 			{
249 				pTestLine++;
250 				nTestCol++;
251 			}
252 
253 			if( *pTestLine && *(pTestLine + 1) )
254 			{
255 				String aTestSym = aLine.copy( nTestCol, 2 );
256 				if( aTestSym.EqualsIgnoreCaseAscii( "to" ) )
257 				{
258 					aSym = String::CreateFromAscii( "goto" );
259 					pLine = pTestLine + 2;
260 					nCol = nTestCol + 2;
261 				}
262 			}
263 		}
264 
265 		// Abschliessendes '_' durch Space ersetzen, wenn Zeilenende folgt
266 		// (sonst falsche Zeilenfortsetzung)
267 		if(	!bUsedForHilite && !*pLine && *(pLine-1) == '_' )
268 		{
269 			aSym.GetBufferAccess();		// #109693 force copy if necessary
270 			*((sal_Unicode*)(pLine-1)) = ' ';		// cast wegen const
271 		}
272 		// Typkennung?
273 		// Das Ausrufezeichen bitte nicht testen, wenn
274 		// danach noch ein Symbol anschliesst
275 		else if( *pLine != '!' || !BasicSimpleCharClass::isAlpha( pLine[ 1 ], bCompatible ) )
276 		{
277 			SbxDataType t = GetSuffixType( *pLine );
278 			if( t != SbxVARIANT )
279 			{
280 				eScanType = t;
281 				pLine++;
282 				nCol++;
283 			}
284 		}
285 	}
286 
287 	// Zahl? Dann einlesen und konvertieren.
288 	else if( BasicSimpleCharClass::isDigit( *pLine & 0xFF )
289 		|| ( *pLine == '.' && BasicSimpleCharClass::isDigit( *(pLine+1) & 0xFF ) ) )
290 	{
291 		short exp = 0;
292 		short comma = 0;
293 		short ndig = 0;
294 		short ncdig = 0;
295 		eScanType = SbxDOUBLE;
296 		sal_Bool bBufOverflow = sal_False;
297 		while( strchr( "0123456789.DEde", *pLine ) && *pLine )
298 		{
299 			// AB 4.1.1996: Buffer voll? -> leer weiter scannen
300 			if( (p-buf) == (BUF_SIZE-1) )
301 			{
302 				bBufOverflow = sal_True;
303 				pLine++, nCol++;
304 				continue;
305 			}
306 			// Komma oder Exponent?
307 			if( *pLine == '.' )
308 			{
309 				if( ++comma > 1 )
310 				{
311 					pLine++; nCol++; continue;
312 				}
313 				else *p++ = *pLine++, nCol++;
314 			}
315 			else if( strchr( "DdEe", *pLine ) )
316 			{
317 				if (++exp > 1)
318 				{
319 					pLine++; nCol++; continue;
320 				}
321 //              if( toupper( *pLine ) == 'D' )
322 //                  eScanType = SbxDOUBLE;
323 				*p++ = 'E'; pLine++; nCol++;
324 				// Vorzeichen hinter Exponent?
325 				if( *pLine == '+' )
326 					pLine++, nCol++;
327 				else
328 				if( *pLine == '-' )
329 					*p++ = *pLine++, nCol++;
330 			}
331 			else
332 			{
333 				*p++ = *pLine++, nCol++;
334 				if( comma && !exp ) ncdig++;
335 			}
336 			if (!exp) ndig++;
337 		}
338 		*p = 0;
339 		aSym = p; bNumber = sal_True;
340 		// Komma, Exponent mehrfach vorhanden?
341 		if( comma > 1 || exp > 1 )
342 		{	aError = '.';
343 			GenError( SbERR_BAD_CHAR_IN_NUMBER );	}
344 
345 		// #57844 Lokalisierte Funktion benutzen
346 	    nVal = rtl_math_uStringToDouble( buf, buf+(p-buf), '.', ',', NULL, NULL );
347 		// ALT: nVal = atof( buf );
348 
349 		ndig = ndig - comma;
350 		if( !comma && !exp )
351 		{
352 			if( nVal >= SbxMININT && nVal <= SbxMAXINT )
353 				eScanType = SbxINTEGER;
354 			else
355 			if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
356 				eScanType = SbxLONG;
357 		}
358 		if( bBufOverflow )
359 			GenError( SbERR_MATH_OVERFLOW );
360 		// zu viele Zahlen fuer SINGLE?
361 //      if (ndig > 15 || ncdig > 6)
362 //          eScanType = SbxDOUBLE;
363 //      else
364 //      if( nVal > SbxMAXSNG || nVal < SbxMINSNG )
365 //          eScanType = SbxDOUBLE;
366 
367 		// Typkennung?
368 		SbxDataType t = GetSuffixType( *pLine );
369 		if( t != SbxVARIANT )
370 		{
371 			eScanType = t;
372 			pLine++;
373 			nCol++;
374 		}
375 	}
376 
377 	// Hex/Oktalzahl? Einlesen und konvertieren:
378 	else if( *pLine == '&' )
379 	{
380 		pLine++; nCol++;
381 		sal_Unicode cmp1[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F', 0 };
382 		sal_Unicode cmp2[] = { '0', '1', '2', '3', '4', '5', '6', '7', 0 };
383 		sal_Unicode *cmp = cmp1;
384 		//char *cmp = "0123456789ABCDEF";
385 		sal_Unicode base = 16;
386 		sal_Unicode ndig = 8;
387 		sal_Unicode xch  = *pLine++ & 0xFF; nCol++;
388 		switch( toupper( xch ) )
389 		{
390 			case 'O':
391 				cmp = cmp2; base = 8; ndig = 11; break;
392 				//cmp = "01234567"; base = 8; ndig = 11; break;
393 			case 'H':
394 				break;
395 			default :
396 				// Wird als Operator angesehen
397 				pLine--; nCol--; nCol1 = nCol-1; aSym = '&'; return SYMBOL;
398 		}
399 		bNumber = sal_True;
400 		long l = 0;
401 		int i;
402 		sal_Bool bBufOverflow = sal_False;
403 		while( BasicSimpleCharClass::isAlphaNumeric( *pLine & 0xFF, bCompatible ) )
404 		{
405 			sal_Unicode ch = sal::static_int_cast< sal_Unicode >(
406                 toupper( *pLine & 0xFF ) );
407 			pLine++; nCol++;
408 			// AB 4.1.1996: Buffer voll, leer weiter scannen
409 			if( (p-buf) == (BUF_SIZE-1) )
410 				bBufOverflow = sal_True;
411 			else if( String( cmp ).Search( ch ) != STRING_NOTFOUND )
412 			//else if( strchr( cmp, ch ) )
413 				*p++ = ch;
414 			else
415 			{
416 				aError = ch;
417 				GenError( SbERR_BAD_CHAR_IN_NUMBER );
418 			}
419 		}
420 		*p = 0;
421 		for( p = buf; *p; p++ )
422 		{
423 			i = (*p & 0xFF) - '0';
424 			if( i > 9 ) i -= 7;
425 			l = ( l * base ) + i;
426 			if( !ndig-- )
427 			{
428 				GenError( SbERR_MATH_OVERFLOW ); break;
429 			}
430 		}
431 		if( *pLine == '&' ) pLine++, nCol++;
432 		nVal = (double) l;
433 		eScanType = ( l >= SbxMININT && l <= SbxMAXINT ) ? SbxINTEGER : SbxLONG;
434 		if( bBufOverflow )
435 			GenError( SbERR_MATH_OVERFLOW );
436 	}
437 
438 	// Strings:
439 	else if( *pLine == '"' || *pLine == '[' )
440 	{
441 		sal_Unicode cSep = *pLine;
442 		if( cSep == '[' )
443 			bSymbol = sal_True, cSep = ']';
444 		short n = nCol+1;
445 		while( *pLine )
446 		{
447 			do pLine++, nCol++;
448 			while( *pLine && ( *pLine != cSep ) );
449 			if( *pLine == cSep )
450 			{
451 				pLine++; nCol++;
452 				if( *pLine != cSep || cSep == ']' ) break;
453 			} else aError = cSep, GenError( SbERR_EXPECTED );
454 		}
455 		// If VBA Interop then doen't eat the [] chars
456 		if ( cSep == ']' && bVBASupportOn )
457 			aSym = aLine.copy( n - 1, nCol - n  + 1);
458 		else
459 			aSym = aLine.copy( n, nCol - n - 1 );
460 		// Doppelte Stringbegrenzer raus
461 		String s( cSep );
462 		s += cSep;
463 		sal_uInt16 nIdx = 0;
464 		do
465 		{
466 			nIdx = aSym.Search( s, nIdx );
467 			if( nIdx == STRING_NOTFOUND )
468 				break;
469 			aSym.Erase( nIdx, 1 );
470 			nIdx++;
471 		}
472 		while( true );
473 		if( cSep != ']' )
474 			eScanType = ( cSep == '#' ) ? SbxDATE : SbxSTRING;
475 	}
476 	// ungueltige Zeichen:
477 	else if( ( *pLine & 0xFF ) >= 0x7F )
478 	{
479 		GenError( SbERR_SYNTAX ); pLine++; nCol++;
480 	}
481 	// andere Gruppen:
482 	else
483 	{
484 		short n = 1;
485 		switch( *pLine++ )
486 		{
487 			case '<': if( *pLine == '>' || *pLine == '=' ) n = 2; break;
488 			case '>': if( *pLine == '=' ) n = 2; break;
489 			case ':': if( *pLine == '=' ) n = 2; break;
490 		}
491 		aSym = aLine.copy( nCol, n );
492 		pLine += n-1; nCol = nCol + n;
493 	}
494 
495 	nCol2 = nCol-1;
496 
497 PrevLineCommentLbl:
498 	// Kommentar?
499 	if( bPrevLineExtentsComment || (eScanType != SbxSTRING &&
500 		( aSym.GetBuffer()[0] == '\'' || aSym.EqualsIgnoreCaseAscii( "REM" ) ) ) )
501 	{
502 		bPrevLineExtentsComment = sal_False;
503 		aSym = String::CreateFromAscii( "REM" );
504 		sal_uInt16 nLen = String( pLine ).Len();
505 		if( bCompatible && pLine[ nLen - 1 ] == '_' && pLine[ nLen - 2 ] == ' ' )
506 			bPrevLineExtentsComment = sal_True;
507 		nCol2 = nCol2 + nLen;
508 		pLine = NULL;
509 	}
510 	return sal_True;
511 
512 	// Sonst Zeilen-Ende: aber bitte auf '_' testen, ob die
513 	// Zeile nicht weitergeht!
514 eoln:
515 	if( nCol && *--pLine == '_' )
516 	{
517 		pLine = NULL;
518 		bool bRes = NextSym();
519 		if( bVBASupportOn && aSym.GetBuffer()[0] == '.' )
520 		{
521 			// object _
522 			//    .Method
523 			// ^^^  <- spaces is legal in MSO VBA
524 			OSL_TRACE("*** resetting bSpaces***");
525 			bSpaces = sal_False;
526 		}
527 		return bRes;
528 	}
529 	else
530 	{
531 		pLine = NULL;
532 		nLine = nOldLine;
533 		nCol1 = nOldCol1;
534 		nCol2 = nOldCol2;
535 		aSym = '\n';
536 		nColLock = 0;
537 		return sal_True;
538 	}
539 }
540 
541 LetterTable BasicSimpleCharClass::aLetterTable;
542 
543 LetterTable::LetterTable( void )
544 {
545 	for( int i = 0 ; i < 256 ; ++i )
546 		IsLetterTab[i] = false;
547 
548 	IsLetterTab[0xC0] = true;	// � , CAPITAL LETTER A WITH GRAVE ACCENT
549 	IsLetterTab[0xC1] = true;	// � , CAPITAL LETTER A WITH ACUTE ACCENT
550 	IsLetterTab[0xC2] = true;	// � , CAPITAL LETTER A WITH CIRCUMFLEX ACCENT
551 	IsLetterTab[0xC3] = true;	// � , CAPITAL LETTER A WITH TILDE
552 	IsLetterTab[0xC4] = true;	// � , CAPITAL LETTER A WITH DIAERESIS
553 	IsLetterTab[0xC5] = true;	// � , CAPITAL LETTER A WITH RING ABOVE
554 	IsLetterTab[0xC6] = true;	// � , CAPITAL LIGATURE AE
555 	IsLetterTab[0xC7] = true;	// � , CAPITAL LETTER C WITH CEDILLA
556 	IsLetterTab[0xC8] = true;	// � , CAPITAL LETTER E WITH GRAVE ACCENT
557 	IsLetterTab[0xC9] = true;	// � , CAPITAL LETTER E WITH ACUTE ACCENT
558 	IsLetterTab[0xCA] = true;	// � , CAPITAL LETTER E WITH CIRCUMFLEX ACCENT
559 	IsLetterTab[0xCB] = true;	// � , CAPITAL LETTER E WITH DIAERESIS
560 	IsLetterTab[0xCC] = true;	// � , CAPITAL LETTER I WITH GRAVE ACCENT
561 	IsLetterTab[0xCD] = true;	// � , CAPITAL LETTER I WITH ACUTE ACCENT
562 	IsLetterTab[0xCE] = true;	// � , CAPITAL LETTER I WITH CIRCUMFLEX ACCENT
563 	IsLetterTab[0xCF] = true;	// � , CAPITAL LETTER I WITH DIAERESIS
564 	IsLetterTab[0xD0] = true;	// � , CAPITAL LETTER ETH
565 	IsLetterTab[0xD1] = true;	// � , CAPITAL LETTER N WITH TILDE
566 	IsLetterTab[0xD2] = true;	// � , CAPITAL LETTER O WITH GRAVE ACCENT
567 	IsLetterTab[0xD3] = true;	// � , CAPITAL LETTER O WITH ACUTE ACCENT
568 	IsLetterTab[0xD4] = true;	// � , CAPITAL LETTER O WITH CIRCUMFLEX ACCENT
569 	IsLetterTab[0xD5] = true;	// � , CAPITAL LETTER O WITH TILDE
570 	IsLetterTab[0xD6] = true;	// � , CAPITAL LETTER O WITH DIAERESIS
571 	IsLetterTab[0xD8] = true;	// � , CAPITAL LETTER O WITH STROKE
572 	IsLetterTab[0xD9] = true;	// � , CAPITAL LETTER U WITH GRAVE ACCENT
573 	IsLetterTab[0xDA] = true;	// � , CAPITAL LETTER U WITH ACUTE ACCENT
574 	IsLetterTab[0xDB] = true;	// � , CAPITAL LETTER U WITH CIRCUMFLEX ACCENT
575 	IsLetterTab[0xDC] = true;	// � , CAPITAL LETTER U WITH DIAERESIS
576 	IsLetterTab[0xDD] = true;	// � , CAPITAL LETTER Y WITH ACUTE ACCENT
577 	IsLetterTab[0xDE] = true;	// � , CAPITAL LETTER THORN
578 	IsLetterTab[0xDF] = true;	// � , SMALL LETTER SHARP S
579 	IsLetterTab[0xE0] = true;	// � , SMALL LETTER A WITH GRAVE ACCENT
580 	IsLetterTab[0xE1] = true;	// � , SMALL LETTER A WITH ACUTE ACCENT
581 	IsLetterTab[0xE2] = true;	// � , SMALL LETTER A WITH CIRCUMFLEX ACCENT
582 	IsLetterTab[0xE3] = true;	// � , SMALL LETTER A WITH TILDE
583 	IsLetterTab[0xE4] = true;	// � , SMALL LETTER A WITH DIAERESIS
584 	IsLetterTab[0xE5] = true;	// � , SMALL LETTER A WITH RING ABOVE
585 	IsLetterTab[0xE6] = true;	// � , SMALL LIGATURE AE
586 	IsLetterTab[0xE7] = true;	// � , SMALL LETTER C WITH CEDILLA
587 	IsLetterTab[0xE8] = true;	// � , SMALL LETTER E WITH GRAVE ACCENT
588 	IsLetterTab[0xE9] = true;	// � , SMALL LETTER E WITH ACUTE ACCENT
589 	IsLetterTab[0xEA] = true;	// � , SMALL LETTER E WITH CIRCUMFLEX ACCENT
590 	IsLetterTab[0xEB] = true;	// � , SMALL LETTER E WITH DIAERESIS
591 	IsLetterTab[0xEC] = true;	// � , SMALL LETTER I WITH GRAVE ACCENT
592 	IsLetterTab[0xED] = true;	// � , SMALL LETTER I WITH ACUTE ACCENT
593 	IsLetterTab[0xEE] = true;	// � , SMALL LETTER I WITH CIRCUMFLEX ACCENT
594 	IsLetterTab[0xEF] = true;	// � , SMALL LETTER I WITH DIAERESIS
595 	IsLetterTab[0xF0] = true;	// � , SMALL LETTER ETH
596 	IsLetterTab[0xF1] = true;	// � , SMALL LETTER N WITH TILDE
597 	IsLetterTab[0xF2] = true;	// � , SMALL LETTER O WITH GRAVE ACCENT
598 	IsLetterTab[0xF3] = true;	// � , SMALL LETTER O WITH ACUTE ACCENT
599 	IsLetterTab[0xF4] = true;	// � , SMALL LETTER O WITH CIRCUMFLEX ACCENT
600 	IsLetterTab[0xF5] = true;	// � , SMALL LETTER O WITH TILDE
601 	IsLetterTab[0xF6] = true;	// � , SMALL LETTER O WITH DIAERESIS
602 	IsLetterTab[0xF8] = true;	// � , SMALL LETTER O WITH OBLIQUE BAR
603 	IsLetterTab[0xF9] = true;	// � , SMALL LETTER U WITH GRAVE ACCENT
604 	IsLetterTab[0xFA] = true;	// � , SMALL LETTER U WITH ACUTE ACCENT
605 	IsLetterTab[0xFB] = true;	// � , SMALL LETTER U WITH CIRCUMFLEX ACCENT
606 	IsLetterTab[0xFC] = true;	// � , SMALL LETTER U WITH DIAERESIS
607 	IsLetterTab[0xFD] = true;	// � , SMALL LETTER Y WITH ACUTE ACCENT
608 	IsLetterTab[0xFE] = true;	// � , SMALL LETTER THORN
609 	IsLetterTab[0xFF] = true;	// � , SMALL LETTER Y WITH DIAERESIS
610 }
611 
612 bool LetterTable::isLetterUnicode( sal_Unicode c )
613 {
614 	static CharClass* pCharClass = NULL;
615 	if( pCharClass == NULL )
616 		pCharClass = new CharClass( Application::GetSettings().GetLocale() );
617 	String aStr( c );
618 	bool bRet = pCharClass->isLetter( aStr, 0 );
619 	return bRet;
620 }
621