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