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