xref: /AOO41X/main/basic/source/comp/scanner.cxx (revision e1f63238eb022c8a12b30d46a012444ff20e0951)
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