xref: /core/sw/source/core/bastyp/calc.cxx (revision 474a9171)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <config_features.h>
21 
22 #include <calc.hxx>
23 #include <cfloat>
24 #include <climits>
25 #include <memory>
26 #include <string_view>
27 #include <comphelper/processfactory.hxx>
28 #include <comphelper/string.hxx>
29 #include <cstdlib>
30 #include <dbmgr.hxx>
31 #include <docfld.hxx>
32 #include <docstat.hxx>
33 #include <doc.hxx>
34 #include <IDocumentFieldsAccess.hxx>
35 #include <IDocumentStatistics.hxx>
36 #include <editeng/langitem.hxx>
37 #include <expfld.hxx>
38 #include <hintids.hxx>
39 #include <o3tl/temporary.hxx>
40 #include <osl/diagnose.h>
41 #include <rtl/math.hxx>
42 #include <shellres.hxx>
43 #include <svl/languageoptions.hxx>
44 #include <svl/zforlist.hxx>
45 #include <swmodule.hxx>
46 #include <swtypes.hxx>
47 #include <unotools/charclass.hxx>
48 #include <unotools/localedatawrapper.hxx>
49 #include <unotools/useroptions.hxx>
50 #include <usrfld.hxx>
51 #include <viewsh.hxx>
52 #include <com/sun/star/i18n/KParseTokens.hpp>
53 #include <com/sun/star/i18n/KParseType.hpp>
54 
55 using namespace ::com::sun::star;
56 
57 const char sCalc_Add[]  =   "add";
58 const char sCalc_Sub[]  =   "sub";
59 const char sCalc_Mul[]  =   "mul";
60 const char sCalc_Div[]  =   "div";
61 const char sCalc_Phd[]  =   "phd";
62 const char sCalc_Sqrt[] =   "sqrt";
63 const char sCalc_Pow[]  =   "pow";
64 const char sCalc_Or[]   =   "or";
65 const char sCalc_Xor[]  =   "xor";
66 const char sCalc_And[]  =   "and";
67 const char sCalc_Not[]  =   "not";
68 const char sCalc_Eq[]   =   "eq";
69 const char sCalc_Neq[]  =   "neq";
70 const char sCalc_Leq[]  =   "leq";
71 const char sCalc_Geq[]  =   "geq";
72 const char sCalc_L[]    =   "l";
73 const char sCalc_G[]    =   "g";
74 const char sCalc_Sum[]  =   "sum";
75 const char sCalc_Mean[] =   "mean";
76 const char sCalc_Min[]  =   "min";
77 const char sCalc_Max[]  =   "max";
78 const char sCalc_Sin[]  =   "sin";
79 const char sCalc_Cos[]  =   "cos";
80 const char sCalc_Tan[]  =   "tan";
81 const char sCalc_Asin[] =   "asin";
82 const char sCalc_Acos[] =   "acos";
83 const char sCalc_Atan[] =   "atan";
84 const char sCalc_Round[]=   "round";
85 const char sCalc_Date[] =   "date";
86 
87 // ATTENTION: sorted list of all operators
88 struct CalcOp
89 {
90     union{
91         const char* pName;
92         const OUString* pUName;
93     };
94     SwCalcOper eOp;
95 };
96 
97 CalcOp const aOpTable[] = {
98 /* ACOS */    {{sCalc_Acos},       CALC_ACOS},  // Arc cosine
99 /* ADD */     {{sCalc_Add},        CALC_PLUS},  // Addition
100 /* AND */     {{sCalc_And},        CALC_AND},   // log. AND
101 /* ASIN */    {{sCalc_Asin},       CALC_ASIN},  // Arc sine
102 /* ATAN */    {{sCalc_Atan},       CALC_ATAN},  // Arc tangent
103 /* COS */     {{sCalc_Cos},        CALC_COS},   // Cosine
104 /* DATE */    {{sCalc_Date},       CALC_DATE},  // Date
105 /* DIV */     {{sCalc_Div},        CALC_DIV},   // Division
106 /* EQ */      {{sCalc_Eq},         CALC_EQ},    // Equality
107 /* G */       {{sCalc_G},          CALC_GRE},   // Greater than
108 /* GEQ */     {{sCalc_Geq},        CALC_GEQ},   // Greater or equal
109 /* L */       {{sCalc_L},          CALC_LES},   // Less than
110 /* LEQ */     {{sCalc_Leq},        CALC_LEQ},   // Less or equal
111 /* MAX */     {{sCalc_Max},        CALC_MAX},   // Maximum value
112 /* MEAN */    {{sCalc_Mean},       CALC_MEAN},  // Mean
113 /* MIN */     {{sCalc_Min},        CALC_MIN},   // Minimum value
114 /* MUL */     {{sCalc_Mul},        CALC_MUL},   // Multiplication
115 /* NEQ */     {{sCalc_Neq},        CALC_NEQ},   // Not equal
116 /* NOT */     {{sCalc_Not},        CALC_NOT},   // log. NOT
117 /* OR */      {{sCalc_Or},         CALC_OR},    // log. OR
118 /* PHD */     {{sCalc_Phd},        CALC_PHD},   // Percentage
119 /* POW */     {{sCalc_Pow},        CALC_POW},   // Exponentiation
120 /* ROUND */   {{sCalc_Round},      CALC_ROUND}, // Rounding
121 /* SIN */     {{sCalc_Sin},        CALC_SIN},   // Sine
122 /* SQRT */    {{sCalc_Sqrt},       CALC_SQRT},  // Square root
123 /* SUB */     {{sCalc_Sub},        CALC_MINUS}, // Subtraction
124 /* SUM */     {{sCalc_Sum},        CALC_SUM},   // Sum
125 /* TAN */     {{sCalc_Tan},        CALC_TAN},   // Tangent
126 /* XOR */     {{sCalc_Xor},        CALC_XOR}    // log. XOR
127 };
128 
129 double const nRoundVal[] = {
130     5.0e+0, 0.5e+0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6,
131     0.5e-7, 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,
132     0.5e-15,0.5e-16
133 };
134 
135 // First character may be any alphabetic or underscore.
136 const sal_Int32 coStartFlags =
137         i18n::KParseTokens::ANY_LETTER_OR_NUMBER |
138         i18n::KParseTokens::ASC_UNDERSCORE |
139         i18n::KParseTokens::IGNORE_LEADING_WS;
140 
141 // Continuing characters may be any alphanumeric, underscore, or dot.
142 const sal_Int32 coContFlags =
143     ( coStartFlags | i18n::KParseTokens::ASC_DOT )
144         & ~i18n::KParseTokens::IGNORE_LEADING_WS;
145 
146 extern "C" {
147 static int OperatorCompare( const void *pFirst, const void *pSecond)
148 {
149     int nRet = 0;
150     if( CALC_NAME == static_cast<const CalcOp*>(pFirst)->eOp )
151     {
152         if( CALC_NAME == static_cast<const CalcOp*>(pSecond)->eOp )
153             nRet = static_cast<const CalcOp*>(pFirst)->pUName->compareTo(
154                    *static_cast<const CalcOp*>(pSecond)->pUName );
155         else
156             nRet = static_cast<const CalcOp*>(pFirst)->pUName->compareToAscii(
157                    static_cast<const CalcOp*>(pSecond)->pName );
158     }
159     else
160     {
161         if( CALC_NAME == static_cast<const CalcOp*>(pSecond)->eOp )
162             nRet = -1 * static_cast<const CalcOp*>(pSecond)->pUName->compareToAscii(
163                         static_cast<const CalcOp*>(pFirst)->pName );
164         else
165             nRet = strcmp( static_cast<const CalcOp*>(pFirst)->pName,
166                            static_cast<const CalcOp*>(pSecond)->pName );
167     }
168     return nRet;
169 }
170 }// extern "C"
171 
172 CalcOp* FindOperator( const OUString& rSrch )
173 {
174     CalcOp aSrch;
175     aSrch.pUName = &rSrch;
176     aSrch.eOp = CALC_NAME;
177 
178     return static_cast<CalcOp*>(bsearch( static_cast<void*>(&aSrch),
179                               static_cast<void const *>(aOpTable),
180                               SAL_N_ELEMENTS( aOpTable ),
181                               sizeof( CalcOp ),
182                               OperatorCompare ));
183 }
184 
185 static LanguageType GetDocAppScriptLang( SwDoc const & rDoc )
186 {
187     return static_cast<const SvxLanguageItem&>(rDoc.GetDefault(
188                GetWhichOfScript( RES_CHRATR_LANGUAGE,
189                                  SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() ))
190             )).GetLanguage();
191 }
192 
193 static double lcl_ConvertToDateValue( SwDoc& rDoc, sal_Int32 nDate )
194 {
195     double nRet = 0;
196     SvNumberFormatter* pFormatter = rDoc.GetNumberFormatter();
197     if( pFormatter )
198     {
199         const Date& rNull = pFormatter->GetNullDate();
200         Date aDate( nDate >> 24, (nDate& 0x00FF0000) >> 16, nDate& 0x0000FFFF );
201         nRet = aDate - rNull;
202     }
203     return nRet;
204 }
205 
206 SwCalc::SwCalc( SwDoc& rD )
207     : m_aVarTable(TBLSZ)
208     , m_aErrExpr( OUString(), SwSbxValue(), nullptr )
209     , m_nCommandPos(0)
210     , m_rDoc( rD )
211     , m_pLocaleDataWrapper( m_aSysLocale.GetLocaleDataPtr() )
212     , m_pCharClass( &GetAppCharClass() )
213     , m_nListPor( 0 )
214     , m_eCurrOper( CALC_NAME )
215     , m_eCurrListOper( CALC_NAME )
216     , m_eError( SwCalcError::NONE )
217 {
218     m_aErrExpr.aStr = "~C_ERR~";
219     LanguageType eLang = GetDocAppScriptLang( m_rDoc );
220 
221     if( eLang != m_pLocaleDataWrapper->getLanguageTag().getLanguageType() ||
222         eLang != m_pCharClass->getLanguageTag().getLanguageType() )
223     {
224         LanguageTag aLanguageTag( eLang );
225         m_pCharClass = new CharClass( ::comphelper::getProcessComponentContext(), aLanguageTag );
226         m_pLocaleDataWrapper = new LocaleDataWrapper( aLanguageTag );
227     }
228 
229     m_sCurrSym = comphelper::string::strip(m_pLocaleDataWrapper->getCurrSymbol(), ' ');
230     m_sCurrSym  = m_pCharClass->lowercase( m_sCurrSym );
231 
232     static char const
233         sNType0[] = "false",
234         sNType1[] = "true",
235         sNType2[] = "pi",
236         sNType3[] = "e",
237         sNType4[] = "tables",
238         sNType5[] = "graf",
239         sNType6[] = "ole",
240         sNType7[] = "page",
241         sNType8[] = "para",
242         sNType9[] = "word",
243         sNType10[]= "char",
244 
245         sNType11[] = "user_firstname" ,
246         sNType12[] = "user_lastname" ,
247         sNType13[] = "user_initials" ,
248         sNType14[] = "user_company" ,
249         sNType15[] = "user_street" ,
250         sNType16[] = "user_country" ,
251         sNType17[] = "user_zipcode" ,
252         sNType18[] = "user_city" ,
253         sNType19[] = "user_title" ,
254         sNType20[] = "user_position" ,
255         sNType21[] = "user_tel_work" ,
256         sNType22[] = "user_tel_home" ,
257         sNType23[] = "user_fax" ,
258         sNType24[] = "user_email" ,
259         sNType25[] = "user_state" ,
260         sNType26[] = "graph"
261         ;
262     static const char* const sNTypeTab[ 27 ] =
263     {
264         sNType0, sNType1, sNType2, sNType3, sNType4, sNType5,
265         sNType6, sNType7, sNType8, sNType9, sNType10, sNType11,
266         sNType12, sNType13, sNType14, sNType15, sNType16, sNType17,
267         sNType18, sNType19, sNType20, sNType21, sNType22, sNType23,
268         sNType24,
269 
270         // those have two HashIds
271         sNType25, sNType26
272     };
273     static sal_uInt16 const aHashValue[ 27 ] =
274     {
275         34, 38, 43,  7, 18, 32, 22, 29, 30, 33,  3,
276         28, 24, 40,  9, 11, 26, 45,  4, 23, 36, 44, 19,  5,  1,
277         // those have two HashIds
278         11, 38
279     };
280     static UserOptToken const aAdrToken[ 12 ] =
281     {
282         UserOptToken::Company, UserOptToken::Street, UserOptToken::Country, UserOptToken::Zip,
283         UserOptToken::City, UserOptToken::Title, UserOptToken::Position, UserOptToken::TelephoneWork,
284         UserOptToken::TelephoneHome, UserOptToken::Fax, UserOptToken::Email, UserOptToken::State
285     };
286 
287     static sal_uInt16 SwDocStat::* const aDocStat1[ 3 ] =
288     {
289         &SwDocStat::nTable, &SwDocStat::nGrf, &SwDocStat::nOLE
290     };
291     static sal_uLong SwDocStat::* const aDocStat2[ 4 ] =
292     {
293         &SwDocStat::nPage, &SwDocStat::nPara,
294         &SwDocStat::nWord, &SwDocStat::nChar
295     };
296 
297 #if TBLSZ != 47
298 #error Did you adjust all hash values?
299 #endif
300 
301     const SwDocStat& rDocStat = m_rDoc.getIDocumentStatistics().GetDocStat();
302 
303     SwSbxValue nVal;
304     OUString sTmpStr;
305     sal_uInt16 n;
306 
307     for( n = 0; n < 25; ++n )
308     {
309         sTmpStr = OUString::createFromAscii(sNTypeTab[n]);
310         m_aVarTable[ aHashValue[ n ] ].reset( new SwCalcExp( sTmpStr, nVal, nullptr ) );
311     }
312 
313     m_aVarTable[ aHashValue[ 0 ] ]->nValue.PutBool( false );
314     m_aVarTable[ aHashValue[ 1 ] ]->nValue.PutBool( true );
315     m_aVarTable[ aHashValue[ 2 ] ]->nValue.PutDouble( F_PI );
316     m_aVarTable[ aHashValue[ 3 ] ]->nValue.PutDouble( 2.7182818284590452354 );
317 
318     for( n = 0; n < 3; ++n )
319         m_aVarTable[ aHashValue[ n + 4 ] ]->nValue.PutLong( rDocStat.*aDocStat1[ n ]  );
320     for( n = 0; n < 4; ++n )
321         m_aVarTable[ aHashValue[ n + 7 ] ]->nValue.PutLong( rDocStat.*aDocStat2[ n ]  );
322 
323     SvtUserOptions& rUserOptions = SW_MOD()->GetUserOptions();
324 
325     m_aVarTable[ aHashValue[ 11 ] ]->nValue.PutString( rUserOptions.GetFirstName() );
326     m_aVarTable[ aHashValue[ 12 ] ]->nValue.PutString( rUserOptions.GetLastName() );
327     m_aVarTable[ aHashValue[ 13 ] ]->nValue.PutString( rUserOptions.GetID() );
328 
329     for( n = 0; n < 11; ++n )
330         m_aVarTable[ aHashValue[ n + 14 ] ]->nValue.PutString(
331                                         rUserOptions.GetToken( aAdrToken[ n ] ));
332 
333     nVal.PutString( rUserOptions.GetToken( aAdrToken[ 11 ] ));
334     sTmpStr = OUString::createFromAscii(sNTypeTab[25]);
335     m_aVarTable[ aHashValue[ 25 ] ]->pNext.reset( new SwCalcExp( sTmpStr, nVal, nullptr ) );
336 
337 } // SwCalc::SwCalc
338 
339 SwCalc::~SwCalc() COVERITY_NOEXCEPT_FALSE
340 {
341     if( m_pLocaleDataWrapper != m_aSysLocale.GetLocaleDataPtr() )
342         delete m_pLocaleDataWrapper;
343     if( m_pCharClass != &GetAppCharClass() )
344         delete m_pCharClass;
345 }
346 
347 SwSbxValue SwCalc::Calculate( const OUString& rStr )
348 {
349     m_eError = SwCalcError::NONE;
350     SwSbxValue nResult;
351 
352     if( rStr.isEmpty() )
353         return nResult;
354 
355     m_nListPor = 0;
356     m_eCurrListOper = CALC_PLUS; // default: sum
357 
358     m_sCommand = rStr;
359     m_nCommandPos = 0;
360 
361     for (;;)
362     {
363         m_eCurrOper = GetToken();
364         if (m_eCurrOper == CALC_ENDCALC || m_eError != SwCalcError::NONE )
365             break;
366         nResult = Expr();
367     }
368 
369     if( m_eError != SwCalcError::NONE)
370         nResult.PutDouble( DBL_MAX );
371 
372     return nResult;
373 }
374 
375 OUString SwCalc::GetStrResult( const SwSbxValue& rVal )
376 {
377     if( !rVal.IsDouble() )
378     {
379         return rVal.GetOUString();
380     }
381     return GetStrResult( rVal.GetDouble() );
382 }
383 
384 OUString SwCalc::GetStrResult( double nValue )
385 {
386     if( nValue >= DBL_MAX )
387         switch( m_eError )
388         {
389         case SwCalcError::Syntax          :   return SwViewShell::GetShellRes()->aCalc_Syntax;
390         case SwCalcError::DivByZero       :   return SwViewShell::GetShellRes()->aCalc_ZeroDiv;
391         case SwCalcError::FaultyBrackets  :   return SwViewShell::GetShellRes()->aCalc_Brack;
392         case SwCalcError::OverflowInPower :   return SwViewShell::GetShellRes()->aCalc_Pow;
393         case SwCalcError::Overflow        :   return SwViewShell::GetShellRes()->aCalc_Overflow;
394         default                           :   return SwViewShell::GetShellRes()->aCalc_Default;
395         }
396 
397     const sal_Int32 nDecPlaces = 15;
398     OUString aRetStr( ::rtl::math::doubleToUString(
399                         nValue,
400                         rtl_math_StringFormat_Automatic,
401                         nDecPlaces,
402                         m_pLocaleDataWrapper->getNumDecimalSep()[0],
403                         true ));
404     return aRetStr;
405 }
406 
407 SwCalcExp* SwCalc::VarInsert( const OUString &rStr )
408 {
409     OUString aStr = m_pCharClass->lowercase( rStr );
410     return VarLook( aStr, true );
411 }
412 
413 SwCalcExp* SwCalc::VarLook( const OUString& rStr, bool bIns )
414 {
415     m_aErrExpr.nValue.SetVoidValue(false);
416 
417     sal_uInt16 ii = 0;
418     OUString aStr = m_pCharClass->lowercase( rStr );
419 
420     SwCalcExp* pFnd = m_aVarTable.Find(aStr, &ii);
421 
422     if( !pFnd )
423     {
424         // then check doc
425         SwHashTable<SwCalcFieldType> const & rDocTable = m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().GetFieldTypeTable();
426         for( SwHash* pEntry = rDocTable[ii].get(); pEntry; pEntry = pEntry->pNext.get() )
427         {
428             if( aStr == pEntry->aStr )
429             {
430                 // then insert here
431                 pFnd = new SwCalcExp( aStr, SwSbxValue(),
432                                     static_cast<SwCalcFieldType*>(pEntry)->pFieldType );
433                 pFnd->pNext = std::move( m_aVarTable[ii] );
434                 m_aVarTable[ii].reset(pFnd);
435                 break;
436             }
437         }
438     }
439 
440     if( pFnd )
441     {
442         if( pFnd->pFieldType && pFnd->pFieldType->Which() == SwFieldIds::User )
443         {
444             SwUserFieldType* pUField = const_cast<SwUserFieldType*>(static_cast<const SwUserFieldType*>(pFnd->pFieldType));
445             if( nsSwGetSetExpType::GSE_STRING & pUField->GetType() )
446             {
447                 pFnd->nValue.PutString( pUField->GetContent() );
448             }
449             else if( !pUField->IsValid() )
450             {
451                 // Save the current values...
452                 sal_uInt16 nListPor = m_nListPor;
453                 SwSbxValue nLastLeft = m_nLastLeft;
454                 SwSbxValue nNumberValue = m_nNumberValue;
455                 sal_Int32 nCommandPos = m_nCommandPos;
456                 SwCalcOper eCurrOper = m_eCurrOper;
457                 SwCalcOper eCurrListOper = m_eCurrListOper;
458                 OUString sCurrCommand = m_sCommand;
459 
460                 pFnd->nValue.PutDouble( pUField->GetValue( *this ) );
461 
462                 // ...and write them back.
463                 m_nListPor = nListPor;
464                 m_nLastLeft = nLastLeft;
465                 m_nNumberValue = nNumberValue;
466                 m_nCommandPos = nCommandPos;
467                 m_eCurrOper = eCurrOper;
468                 m_eCurrListOper = eCurrListOper;
469                 m_sCommand = sCurrCommand;
470             }
471             else
472             {
473                 pFnd->nValue.PutDouble( pUField->GetValue() );
474             }
475         }
476         else if ( !pFnd->pFieldType && pFnd->nValue.IsDBvalue() )
477         {
478             if ( pFnd->nValue.IsString() )
479                 m_aErrExpr.nValue.PutString( pFnd->nValue.GetOUString() );
480             else if ( pFnd->nValue.IsDouble() )
481                 m_aErrExpr.nValue.PutDouble( pFnd->nValue.GetDouble() );
482             pFnd = &m_aErrExpr;
483         }
484         return pFnd;
485     }
486 
487     // At this point the "real" case variable has to be used
488     OUString const sTmpName( ::ReplacePoint(rStr) );
489 
490     if( !bIns )
491     {
492 #if HAVE_FEATURE_DBCONNECTIVITY
493         SwDBManager *pMgr = m_rDoc.GetDBManager();
494 
495         OUString sDBName(GetDBName( sTmpName ));
496         OUString sSourceName(sDBName.getToken(0, DB_DELIM));
497         OUString sTableName(sDBName.getToken(0, ';').getToken(1, DB_DELIM));
498         if( pMgr && !sSourceName.isEmpty() && !sTableName.isEmpty() &&
499             pMgr->OpenDataSource(sSourceName, sTableName))
500         {
501             OUString sColumnName( GetColumnName( sTmpName ));
502             OSL_ENSURE(!sColumnName.isEmpty(), "Missing DB column name");
503 
504             OUString sDBNum( SwFieldType::GetTypeStr(SwFieldTypesEnum::DatabaseSetNumber) );
505             sDBNum = m_pCharClass->lowercase(sDBNum);
506 
507             // Initialize again because this doesn't happen in docfld anymore for
508             // elements != SwFieldIds::Database. E.g. if there is an expression field before
509             // a DB_Field in a document.
510             const sal_uInt32 nTmpRec = pMgr->GetSelectedRecordId(sSourceName, sTableName);
511             VarChange(sDBNum, nTmpRec);
512 
513             if( sDBNum.equalsIgnoreAsciiCase(sColumnName) )
514             {
515                 m_aErrExpr.nValue.PutULong(nTmpRec);
516                 return &m_aErrExpr;
517             }
518 
519             OUString sResult;
520             double nNumber = DBL_MAX;
521 
522             LanguageType nLang = m_pLocaleDataWrapper->getLanguageTag().getLanguageType();
523             if(pMgr->GetColumnCnt( sSourceName, sTableName, sColumnName,
524                                     nTmpRec, nLang, sResult, &nNumber ))
525             {
526                 if (nNumber != DBL_MAX)
527                     m_aErrExpr.nValue.PutDouble( nNumber );
528                 else
529                     m_aErrExpr.nValue.PutString( sResult );
530 
531                 return &m_aErrExpr;
532             }
533         }
534         else
535 #endif
536         {
537             //data source was not available - set return to "NoValue"
538             m_aErrExpr.nValue.SetVoidValue(true);
539         }
540         // NEVER save!
541         return &m_aErrExpr;
542     }
543 
544     SwCalcExp* pNewExp = new SwCalcExp( aStr, SwSbxValue(), nullptr );
545     pNewExp->pNext = std::move( m_aVarTable[ ii ] );
546     m_aVarTable[ ii ].reset( pNewExp );
547 
548     OUString sColumnName( GetColumnName( sTmpName ));
549     OSL_ENSURE( !sColumnName.isEmpty(), "Missing DB column name" );
550     if( sColumnName.equalsIgnoreAsciiCase(
551                             SwFieldType::GetTypeStr( SwFieldTypesEnum::DatabaseSetNumber ) ))
552     {
553 #if HAVE_FEATURE_DBCONNECTIVITY
554         SwDBManager *pMgr = m_rDoc.GetDBManager();
555         OUString sDBName(GetDBName( sTmpName ));
556         OUString sSourceName(sDBName.getToken(0, DB_DELIM));
557         OUString sTableName(sDBName.getToken(0, ';').getToken(1, DB_DELIM));
558         if( pMgr && !sSourceName.isEmpty() && !sTableName.isEmpty() &&
559             pMgr->OpenDataSource(sSourceName, sTableName) &&
560             !pMgr->IsInMerge())
561         {
562             pNewExp->nValue.PutULong( pMgr->GetSelectedRecordId(sSourceName, sTableName));
563         }
564         else
565 #endif
566         {
567             pNewExp->nValue.SetVoidValue(true);
568         }
569     }
570 
571     return pNewExp;
572 }
573 
574 void SwCalc::VarChange( const OUString& rStr, double nValue )
575 {
576     SwSbxValue aVal( nValue );
577     VarChange( rStr, aVal );
578 }
579 
580 void SwCalc::VarChange( const OUString& rStr, const SwSbxValue& rValue )
581 {
582     OUString aStr = m_pCharClass->lowercase( rStr );
583 
584     sal_uInt16 nPos = 0;
585     SwCalcExp* pFnd = m_aVarTable.Find( aStr, &nPos );
586 
587     if( !pFnd )
588     {
589         pFnd = new SwCalcExp( aStr, rValue, nullptr );
590         pFnd->pNext = std::move( m_aVarTable[ nPos ] );
591         m_aVarTable[ nPos ].reset( pFnd );
592     }
593     else
594     {
595         pFnd->nValue = rValue;
596     }
597 }
598 
599 bool SwCalc::Push( const SwUserFieldType* pUserFieldType )
600 {
601     if( m_aRekurStack.end() != std::find(m_aRekurStack.begin(), m_aRekurStack.end(), pUserFieldType ) )
602         return false;
603 
604     m_aRekurStack.push_back( pUserFieldType );
605     return true;
606 }
607 
608 void SwCalc::Pop()
609 {
610     OSL_ENSURE( m_aRekurStack.size(), "SwCalc: Pop on an invalid pointer" );
611 
612     m_aRekurStack.pop_back();
613 }
614 
615 CharClass* SwCalc::GetCharClass()
616 {
617     return m_pCharClass;
618 }
619 
620 SwCalcOper SwCalc::GetToken()
621 {
622     if( m_nCommandPos >= m_sCommand.getLength() )
623     {
624         m_eCurrOper = CALC_ENDCALC;
625         return m_eCurrOper;
626     }
627 
628     using namespace ::com::sun::star::i18n;
629     {
630         // Parse any token.
631         ParseResult aRes = m_pCharClass->parseAnyToken( m_sCommand, m_nCommandPos,
632                                                       coStartFlags, OUString(),
633                                                       coContFlags, OUString());
634 
635         bool bSetError = true;
636         sal_Int32 nRealStt = m_nCommandPos + aRes.LeadingWhiteSpace;
637         if( aRes.TokenType & (KParseType::ASC_NUMBER | KParseType::UNI_NUMBER) )
638         {
639             m_nNumberValue.PutDouble( aRes.Value );
640             m_eCurrOper = CALC_NUMBER;
641             bSetError = false;
642         }
643         else if( aRes.TokenType & KParseType::IDENTNAME )
644         {
645             OUString aName( m_sCommand.copy( nRealStt,
646                             aRes.EndPos - nRealStt ) );
647             //#101436#: The variable may contain a database name. It must not be
648             // converted to lower case! Instead all further comparisons must be
649             // done case-insensitive
650             OUString sLowerCaseName = m_pCharClass->lowercase( aName );
651             // catch currency symbol
652             if( sLowerCaseName == m_sCurrSym )
653             {
654                 m_nCommandPos = aRes.EndPos;
655                 return GetToken(); // call again
656             }
657 
658             // catch operators
659             CalcOp* pFnd = ::FindOperator( sLowerCaseName );
660             if( pFnd )
661             {
662                 m_eCurrOper = pFnd->eOp;
663                 switch( m_eCurrOper )
664                 {
665                 case CALC_SUM:
666                 case CALC_MEAN:
667                     m_eCurrListOper = CALC_PLUS;
668                     break;
669                 case CALC_MIN:
670                     m_eCurrListOper = CALC_MIN_IN;
671                     break;
672                 case CALC_MAX:
673                     m_eCurrListOper = CALC_MAX_IN;
674                     break;
675                 case CALC_DATE:
676                     m_eCurrListOper = CALC_MONTH;
677                     break;
678                 default:
679                     break;
680                 }
681                 m_nCommandPos = aRes.EndPos;
682                 return m_eCurrOper;
683             }
684             m_aVarName = aName;
685             m_eCurrOper = CALC_NAME;
686             bSetError = false;
687         }
688         else if ( aRes.TokenType & KParseType::DOUBLE_QUOTE_STRING )
689         {
690             m_nNumberValue.PutString( aRes.DequotedNameOrString );
691             m_eCurrOper = CALC_NUMBER;
692             bSetError = false;
693         }
694         else if( aRes.TokenType & KParseType::ONE_SINGLE_CHAR )
695         {
696             OUString aName( m_sCommand.copy( nRealStt,
697                               aRes.EndPos - nRealStt ));
698             if( 1 == aName.getLength() )
699             {
700                 bSetError = false;
701                 sal_Unicode ch = aName[0];
702                 switch( ch )
703                 {
704                 case ';':
705                     if( CALC_MONTH == m_eCurrListOper || CALC_DAY == m_eCurrListOper )
706                     {
707                         m_eCurrOper = m_eCurrListOper;
708                         break;
709                     }
710                     [[fallthrough]];
711                 case '\n':
712                     m_eCurrOper = CALC_PRINT;
713                     break;
714 
715                 case '%':
716                 case '^':
717                 case '*':
718                 case '/':
719                 case '+':
720                 case '-':
721                 case '(':
722                 case ')':
723                     m_eCurrOper = SwCalcOper(ch);
724                     break;
725 
726                 case '=':
727                 case '!':
728                     {
729                         SwCalcOper eTmp2;
730                         if( '=' == ch )
731                         {
732                             m_eCurrOper = SwCalcOper('=');
733                             eTmp2 = CALC_EQ;
734                         }
735                         else
736                         {
737                             m_eCurrOper = CALC_NOT;
738                             eTmp2 = CALC_NEQ;
739                         }
740 
741                         if( aRes.EndPos < m_sCommand.getLength() &&
742                             '=' == m_sCommand[aRes.EndPos] )
743                         {
744                             m_eCurrOper = eTmp2;
745                             ++aRes.EndPos;
746                         }
747                     }
748                     break;
749 
750                 case cListDelim:
751                     m_eCurrOper = m_eCurrListOper;
752                     break;
753 
754                 case '[':
755                     if( aRes.EndPos < m_sCommand.getLength() )
756                     {
757                         m_aVarName.setLength(0);
758                         sal_Int32 nFndPos = aRes.EndPos,
759                                   nSttPos = nFndPos;
760 
761                         do {
762                             nFndPos = m_sCommand.indexOf( ']', nFndPos );
763                             if( -1 != nFndPos )
764                             {
765                                 // ignore the ]
766                                 if ('\\' == m_sCommand[nFndPos-1])
767                                 {
768                                     m_aVarName.append(std::u16string_view(m_sCommand).substr(nSttPos,
769                                                     nFndPos - nSttPos - 1) );
770                                     nSttPos = ++nFndPos;
771                                 }
772                                 else
773                                     break;
774                             }
775                         } while( nFndPos != -1 );
776 
777                         if( nFndPos != -1 )
778                         {
779                             if( nSttPos != nFndPos )
780                                 m_aVarName.append(std::u16string_view(m_sCommand).substr(nSttPos,
781                                                     nFndPos - nSttPos) );
782                             aRes.EndPos = nFndPos + 1;
783                             m_eCurrOper = CALC_NAME;
784                         }
785                         else
786                             bSetError = true;
787                     }
788                     else
789                     {
790                         bSetError = true;
791                     }
792                     break;
793 
794                 default:
795                     bSetError = true;
796                     break;
797                 }
798             }
799         }
800         else if( aRes.TokenType & KParseType::BOOLEAN )
801         {
802             OUString aName( m_sCommand.copy( nRealStt,
803                                          aRes.EndPos - nRealStt ));
804             if( !aName.isEmpty() )
805             {
806                 sal_Unicode ch = aName[0];
807 
808                 bSetError = true;
809                 if ('<' == ch || '>' == ch)
810                 {
811                     bSetError = false;
812 
813                     SwCalcOper eTmp2 = ('<' == ch) ? CALC_LEQ : CALC_GEQ;
814                     m_eCurrOper = ('<' == ch) ? CALC_LES : CALC_GRE;
815 
816                     if( 2 == aName.getLength() && '=' == aName[1] )
817                         m_eCurrOper = eTmp2;
818                     else if( 1 != aName.getLength() )
819                         bSetError = true;
820                 }
821             }
822         }
823         else if( nRealStt == m_sCommand.getLength() )
824         {
825             m_eCurrOper = CALC_ENDCALC;
826             bSetError = false;
827         }
828 
829         if( bSetError )
830         {
831             m_eError = SwCalcError::Syntax;
832             m_eCurrOper = CALC_PRINT;
833         }
834         m_nCommandPos = aRes.EndPos;
835     };
836 
837     return m_eCurrOper;
838 }
839 
840 SwSbxValue SwCalc::Term()
841 {
842     SwSbxValue left( Prim() );
843     m_nLastLeft = left;
844     for(;;)
845     {
846         sal_uInt16 nSbxOper = USHRT_MAX;
847 
848         switch( m_eCurrOper )
849         {
850         case CALC_AND:
851             {
852                 GetToken();
853                 bool bB = Prim().GetBool();
854                 left.PutBool( left.GetBool() && bB );
855             }
856             break;
857         case CALC_OR:
858             {
859                 GetToken();
860                 bool bB = Prim().GetBool();
861                 left.PutBool( left.GetBool() || bB );
862             }
863             break;
864         case CALC_XOR:
865             {
866                 GetToken();
867                 bool bR = Prim().GetBool();
868                 bool bL = left.GetBool();
869                 left.PutBool(bL != bR);
870             }
871             break;
872 
873         case CALC_EQ:   nSbxOper = SbxEQ;   break;
874         case CALC_NEQ:  nSbxOper = SbxNE;   break;
875         case CALC_LEQ:  nSbxOper = SbxLE;   break;
876         case CALC_GEQ:  nSbxOper = SbxGE;   break;
877         case CALC_GRE:  nSbxOper = SbxGT;   break;
878         case CALC_LES:  nSbxOper = SbxLT;   break;
879 
880         case CALC_MUL:  nSbxOper = SbxMUL;  break;
881         case CALC_DIV:  nSbxOper = SbxDIV;  break;
882 
883         case CALC_MIN_IN:
884             {
885                 GetToken();
886                 SwSbxValue e = Prim();
887                 left = left.GetDouble() < e.GetDouble() ? left : e;
888             }
889             break;
890         case CALC_MAX_IN:
891             {
892                 GetToken();
893                 SwSbxValue e = Prim();
894                 left = left.GetDouble() > e.GetDouble() ? left : e;
895             }
896             break;
897         case CALC_MONTH:
898             {
899                 GetToken();
900                 SwSbxValue e = Prim();
901                 sal_Int32 nYear = static_cast<sal_Int32>(floor( left.GetDouble() ));
902                 nYear = nYear & 0x0000FFFF;
903                 sal_Int32 nMonth = static_cast<sal_Int32>(floor( e.GetDouble() ));
904                 nMonth = ( nMonth & 0x000000FF ) << 16;
905                 left.PutLong( nMonth + nYear );
906                 m_eCurrOper = CALC_DAY;
907             }
908             break;
909         case CALC_DAY:
910             {
911                 GetToken();
912                 SwSbxValue e = Prim();
913                 sal_Int32 nYearMonth = static_cast<sal_Int32>(floor( left.GetDouble() ));
914                 nYearMonth = nYearMonth & 0x00FFFFFF;
915                 sal_Int32 nDay = static_cast<sal_Int32>(floor( e.GetDouble() ));
916                 nDay = ( nDay & 0x000000FF ) << 24;
917                 left = lcl_ConvertToDateValue( m_rDoc, nDay + nYearMonth );
918             }
919             break;
920         case CALC_ROUND:
921             {
922                 GetToken();
923                 SwSbxValue e = Prim();
924 
925                 double fVal = 0;
926                 double fFac = 1;
927                 sal_Int32 nDec = static_cast<sal_Int32>(floor( e.GetDouble() ));
928                 if( nDec < -20 || nDec > 20 )
929                 {
930                     m_eError = SwCalcError::Overflow;
931                     left.Clear();
932                     return left;
933                 }
934                 fVal = left.GetDouble();
935                 if( nDec >= 0)
936                 {
937                     for (sal_Int32 i = 0; i < nDec; ++i )
938                         fFac *= 10.0;
939                 }
940                 else
941                 {
942                     for (sal_Int32 i = 0; i < -nDec; ++i )
943                         fFac /= 10.0;
944                 }
945 
946                 fVal *= fFac;
947                 bool bSign;
948                 if (fVal < 0.0)
949                 {
950                     fVal *= -1.0;
951                     bSign = true;
952                 }
953                 else
954                 {
955                     bSign = false;
956                 }
957 
958                 // rounding
959                 double fNum = fVal; // find the exponent
960                 int nExp = 0;
961                 if( fNum > 0 )
962                 {
963                     while( fNum < 1.0 )
964                     {
965                         fNum *= 10.0;
966                         --nExp;
967                     }
968                     while( fNum >= 10.0 )
969                     {
970                         fNum /= 10.0;
971                         ++nExp;
972                     }
973                 }
974                 nExp = 15 - nExp;
975                 if( nExp > 15 )
976                     nExp = 15;
977                 else if( nExp <= 1 )
978                     nExp = 0;
979                 fVal = floor( fVal+ 0.5 + nRoundVal[ nExp ] );
980 
981                 if (bSign)
982                     fVal *= -1.0;
983 
984                 fVal /= fFac;
985 
986                 left.PutDouble( fVal );
987             }
988             break;
989 
990 //#77448# (=2*3^2 != 18)
991 
992         default:
993             return left;
994         }
995 
996         if( USHRT_MAX != nSbxOper )
997         {
998             // #i47706: cast to SbxOperator AFTER comparing to USHRT_MAX
999             SbxOperator eSbxOper = static_cast<SbxOperator>(nSbxOper);
1000 
1001             GetToken();
1002             if( SbxEQ <= eSbxOper && eSbxOper <= SbxGE )
1003             {
1004                 left.PutBool( left.Compare( eSbxOper, Prim() ));
1005             }
1006             else
1007             {
1008                 SwSbxValue aRight( Prim() );
1009                 aRight.MakeDouble();
1010                 left.MakeDouble();
1011 
1012                 if( SbxDIV == eSbxOper && !aRight.GetDouble() )
1013                     m_eError = SwCalcError::DivByZero;
1014                 else
1015                     left.Compute( eSbxOper, aRight );
1016             }
1017         }
1018     }
1019 }
1020 
1021 SwSbxValue SwCalc::StdFunc(pfCalc pFnc, bool bChkTrig)
1022 {
1023     SwSbxValue nErg;
1024     GetToken();
1025     double nVal = Prim().GetDouble();
1026     if( !bChkTrig || ( nVal > -1 && nVal < 1 ) )
1027         nErg.PutDouble( (*pFnc)( nVal ) );
1028     else
1029         m_eError = SwCalcError::Overflow;
1030     return nErg;
1031 }
1032 
1033 SwSbxValue SwCalc::PrimFunc(bool &rChkPow)
1034 {
1035     rChkPow = false;
1036 
1037     switch (m_eCurrOper)
1038     {
1039         case CALC_SIN:
1040             SAL_INFO("sw.calc", "sin");
1041             return StdFunc(&sin, false);
1042             break;
1043         case CALC_COS:
1044             SAL_INFO("sw.calc", "cos");
1045             return StdFunc(&cos, false);
1046             break;
1047         case CALC_TAN:
1048             SAL_INFO("sw.calc", "tan");
1049             return StdFunc(&tan, false);
1050             break;
1051         case CALC_ATAN:
1052             SAL_INFO("sw.calc", "atan");
1053             return StdFunc(&atan, false);
1054             break;
1055         case CALC_ASIN:
1056             SAL_INFO("sw.calc", "asin");
1057             return StdFunc(&asin, true);
1058             break;
1059         case CALC_ACOS:
1060             SAL_INFO("sw.calc", "acos");
1061             return StdFunc(&acos, true);
1062             break;
1063         case CALC_NOT:
1064         {
1065             SAL_INFO("sw.calc", "not");
1066             GetToken();
1067             SwSbxValue nErg = Prim();
1068             if( SbxSTRING == nErg.GetType() )
1069             {
1070                 nErg.PutBool( nErg.GetOUString().isEmpty() );
1071             }
1072             else if(SbxBOOL == nErg.GetType() )
1073             {
1074                 nErg.PutBool(!nErg.GetBool());
1075             }
1076             // Evaluate arguments manually so that the binary NOT below does not
1077             // get called. We want a BOOLEAN NOT.
1078             else if (nErg.IsNumeric())
1079             {
1080                 nErg.PutLong( nErg.GetDouble() == 0.0 ? 1 : 0 );
1081             }
1082             else
1083             {
1084                 OSL_FAIL( "unexpected case. computing binary NOT" );
1085                 //!! computes a binary NOT
1086                 nErg.Compute( SbxNOT, nErg );
1087             }
1088             return nErg;
1089             break;
1090         }
1091         case CALC_NUMBER:
1092         {
1093             SAL_INFO("sw.calc", "number: " << m_nNumberValue.GetDouble());
1094             SwSbxValue nErg;
1095             if( GetToken() == CALC_PHD )
1096             {
1097                 double aTmp = m_nNumberValue.GetDouble();
1098                 aTmp *= 0.01;
1099                 nErg.PutDouble( aTmp );
1100                 GetToken();
1101             }
1102             else if( m_eCurrOper == CALC_NAME )
1103             {
1104                 m_eError = SwCalcError::Syntax;
1105             }
1106             else
1107             {
1108                 nErg = m_nNumberValue;
1109                 rChkPow = true;
1110             }
1111             return nErg;
1112             break;
1113         }
1114         case CALC_NAME:
1115         {
1116             SAL_INFO("sw.calc", "name");
1117             SwSbxValue nErg;
1118             switch(SwCalcOper eOper = GetToken())
1119             {
1120                 case CALC_ASSIGN:
1121                 {
1122                     SwCalcExp* n = VarInsert(m_aVarName.toString());
1123                     GetToken();
1124                     nErg = n->nValue = Expr();
1125                     break;
1126                 }
1127                 default:
1128                     nErg = VarLook(m_aVarName.toString())->nValue;
1129                     // Explicitly disallow unknown function names (followed by "("),
1130                     // allow unknown variable names (equal to zero)
1131                     if (nErg.IsVoidValue() && (eOper == CALC_LP))
1132                         m_eError = SwCalcError::Syntax;
1133                     else
1134                         rChkPow = true;
1135                     break;
1136             }
1137             return nErg;
1138             break;
1139         }
1140         case CALC_MINUS:
1141         {
1142             SAL_INFO("sw.calc", "-");
1143             SwSbxValue nErg;
1144             GetToken();
1145             nErg.PutDouble( -(Prim().GetDouble()) );
1146             return nErg;
1147             break;
1148         }
1149         case CALC_LP:
1150         {
1151             SAL_INFO("sw.calc", "(");
1152             GetToken();
1153             SwSbxValue nErg = Expr();
1154             if( m_eCurrOper != CALC_RP )
1155             {
1156                 m_eError = SwCalcError::FaultyBrackets;
1157             }
1158             else
1159             {
1160                 GetToken();
1161                 rChkPow = true; // in order for =(7)^2 to work
1162             }
1163             return nErg;
1164             break;
1165         }
1166         case CALC_RP:
1167             // ignore, see tdf#121962
1168             SAL_INFO("sw.calc", ")");
1169             break;
1170         case CALC_MEAN:
1171         {
1172             SAL_INFO("sw.calc", "mean");
1173             m_nListPor = 1;
1174             GetToken();
1175             SwSbxValue nErg = Expr();
1176             double aTmp = nErg.GetDouble();
1177             aTmp /= m_nListPor;
1178             nErg.PutDouble( aTmp );
1179             return nErg;
1180             break;
1181         }
1182         case CALC_SQRT:
1183         {
1184             SAL_INFO("sw.calc", "sqrt");
1185             GetToken();
1186             SwSbxValue nErg = Prim();
1187             if( nErg.GetDouble() < 0 )
1188                 m_eError = SwCalcError::Overflow;
1189             else
1190                 nErg.PutDouble( sqrt( nErg.GetDouble() ));
1191             return nErg;
1192             break;
1193         }
1194         case CALC_SUM:
1195         case CALC_DATE:
1196         case CALC_MIN:
1197         case CALC_MAX:
1198         {
1199             SAL_INFO("sw.calc", "sum/date/min/max");
1200             GetToken();
1201             SwSbxValue nErg = Expr();
1202             return nErg;
1203             break;
1204         }
1205         case CALC_ENDCALC:
1206         {
1207             SAL_INFO("sw.calc", "endcalc");
1208             SwSbxValue nErg;
1209             nErg.Clear();
1210             return nErg;
1211             break;
1212         }
1213         default:
1214             SAL_INFO("sw.calc", "syntax error");
1215             m_eError = SwCalcError::Syntax;
1216             break;
1217     }
1218 
1219     return SwSbxValue();
1220 }
1221 
1222 SwSbxValue SwCalc::Prim()
1223 {
1224     bool bChkPow;
1225     SwSbxValue nErg = PrimFunc(bChkPow);
1226 
1227     if (bChkPow && m_eCurrOper == CALC_POW)
1228     {
1229         double dleft = nErg.GetDouble();
1230         GetToken();
1231         double right = Prim().GetDouble();
1232 
1233         double fraction;
1234         fraction = modf( right, &o3tl::temporary(double()) );
1235         if( ( dleft < 0.0 && 0.0 != fraction ) ||
1236             ( 0.0 == dleft && right < 0.0 ) )
1237         {
1238             m_eError = SwCalcError::Overflow;
1239             nErg.Clear();
1240         }
1241         else
1242         {
1243             dleft = pow(dleft, right );
1244             if( dleft == HUGE_VAL )
1245             {
1246                 m_eError = SwCalcError::OverflowInPower;
1247                 nErg.Clear();
1248             }
1249             else
1250             {
1251                 nErg.PutDouble( dleft );
1252             }
1253         }
1254     }
1255 
1256     return nErg;
1257 }
1258 
1259 SwSbxValue  SwCalc::Expr()
1260 {
1261     SwSbxValue left = Term();
1262     m_nLastLeft = left;
1263     for(;;)
1264     {
1265         switch(m_eCurrOper)
1266         {
1267             case CALC_PLUS:
1268             {
1269                 GetToken();
1270                 left.MakeDouble();
1271                 SwSbxValue right(Term());
1272                 right.MakeDouble();
1273                 left.Compute(SbxPLUS, right);
1274                 m_nListPor++;
1275                 break;
1276             }
1277             case CALC_MINUS:
1278             {
1279                 GetToken();
1280                 left.MakeDouble();
1281                 SwSbxValue right(Term());
1282                 right.MakeDouble();
1283                 left.Compute(SbxMINUS, right);
1284                 break;
1285             }
1286             default:
1287             {
1288                 return left;
1289             }
1290         }
1291     }
1292 }
1293 
1294 OUString SwCalc::GetColumnName(const OUString& rName)
1295 {
1296     sal_Int32 nPos = rName.indexOf(DB_DELIM);
1297     if( -1 != nPos )
1298     {
1299         nPos = rName.indexOf(DB_DELIM, nPos + 1);
1300 
1301         if( -1 != nPos )
1302             return rName.copy(nPos + 1);
1303     }
1304     return rName;
1305 }
1306 
1307 OUString SwCalc::GetDBName(const OUString& rName)
1308 {
1309     sal_Int32 nPos = rName.indexOf(DB_DELIM);
1310     if( -1 != nPos )
1311     {
1312         nPos = rName.indexOf(DB_DELIM, nPos + 1);
1313 
1314         if( -1 != nPos )
1315             return rName.copy( 0, nPos );
1316     }
1317     SwDBData aData = m_rDoc.GetDBData();
1318     return aData.sDataSource + OUStringChar(DB_DELIM) + aData.sCommand;
1319 }
1320 
1321 namespace
1322 {
1323     bool lcl_Str2Double( const OUString& rCommand, sal_Int32& rCommandPos,
1324                                 double& rVal,
1325                                 const LocaleDataWrapper* const pLclData )
1326     {
1327         assert(pLclData);
1328         const sal_Unicode nCurrCmdPos = rCommandPos;
1329         rtl_math_ConversionStatus eStatus;
1330         const sal_Unicode* pEnd;
1331         rVal = pLclData->stringToDouble( rCommand.getStr() + rCommandPos,
1332                                          rCommand.getStr() + rCommand.getLength(),
1333                                          true,
1334                                          &eStatus,
1335                                          &pEnd );
1336         rCommandPos = static_cast<sal_Int32>(pEnd - rCommand.getStr());
1337 
1338         return rtl_math_ConversionStatus_Ok == eStatus &&
1339                nCurrCmdPos != rCommandPos;
1340     }
1341 }
1342 
1343 bool SwCalc::Str2Double( const OUString& rCommand, sal_Int32& rCommandPos,
1344                          double& rVal )
1345 {
1346     const SvtSysLocale aSysLocale;
1347     return lcl_Str2Double( rCommand, rCommandPos, rVal, aSysLocale.GetLocaleDataPtr() );
1348 }
1349 
1350 bool SwCalc::Str2Double( const OUString& rCommand, sal_Int32& rCommandPos,
1351                          double& rVal, SwDoc const * const pDoc )
1352 {
1353     const SvtSysLocale aSysLocale;
1354     std::unique_ptr<const LocaleDataWrapper> pLclD;
1355     if( pDoc )
1356     {
1357         LanguageType eLang = GetDocAppScriptLang( *pDoc );
1358         if (eLang != aSysLocale.GetLanguageTag().getLanguageType())
1359         {
1360             pLclD.reset( new LocaleDataWrapper( LanguageTag( eLang )) );
1361         }
1362     }
1363 
1364     bool const bRet = lcl_Str2Double(rCommand, rCommandPos, rVal,
1365                                      pLclD ? pLclD.get() : aSysLocale.GetLocaleDataPtr());
1366 
1367     return bRet;
1368 }
1369 
1370 bool SwCalc::IsValidVarName( const OUString& rStr, OUString* pValidName )
1371 {
1372     bool bRet = false;
1373     using namespace ::com::sun::star::i18n;
1374     {
1375         // Parse any token.
1376         ParseResult aRes = GetAppCharClass().parseAnyToken( rStr, 0,
1377                                                     coStartFlags, OUString(),
1378                                                      coContFlags, OUString() );
1379 
1380         if( aRes.TokenType & KParseType::IDENTNAME )
1381         {
1382             bRet = aRes.EndPos == rStr.getLength();
1383             if( pValidName )
1384             {
1385                 *pValidName = rStr.copy( aRes.LeadingWhiteSpace,
1386                                          aRes.EndPos - aRes.LeadingWhiteSpace );
1387             }
1388         }
1389         else if( pValidName )
1390             pValidName->clear();
1391     }
1392     return bRet;
1393 }
1394 
1395 SwHash::SwHash(const OUString& rStr)
1396     : aStr(rStr)
1397 {
1398 }
1399 
1400 SwHash::~SwHash()
1401 {
1402 }
1403 
1404 SwCalcExp::SwCalcExp(const OUString& rStr, const SwSbxValue& rVal,
1405                       const SwFieldType* pType)
1406     : SwHash(rStr)
1407     , nValue(rVal)
1408     , pFieldType(pType)
1409 {
1410 }
1411 
1412 bool SwSbxValue::GetBool() const
1413 {
1414     return SbxSTRING == GetType() ? !GetOUString().isEmpty()
1415                                   : SbxValue::GetBool();
1416 }
1417 
1418 double SwSbxValue::GetDouble() const
1419 {
1420     double nRet;
1421     if( SbxSTRING == GetType() )
1422     {
1423         sal_Int32 nStt = 0;
1424         SwCalc::Str2Double( GetOUString(), nStt, nRet );
1425     }
1426     else if (IsBool())
1427     {
1428         nRet = GetBool() ? 1.0 : 0.0;
1429     }
1430     else
1431     {
1432         nRet = SbxValue::GetDouble();
1433     }
1434     return nRet;
1435 }
1436 
1437 SwSbxValue& SwSbxValue::MakeDouble()
1438 {
1439     if( GetType() == SbxSTRING  || GetType() == SbxBOOL )
1440         PutDouble( GetDouble() );
1441     return *this;
1442 }
1443 
1444 #ifdef STANDALONE_HASHCALC
1445 
1446 // this is example code how to create hash values in the CTOR:
1447 
1448 #include <stdio.h>
1449 void main()
1450 {
1451     static char
1452         sNType0[] = "false",    sNType1[] = "true",     sNType2[] = "pi",
1453         sNType3[] = "e",        sNType4[] = "tables",   sNType5[] = "graf",
1454         sNType6[] = "ole",      sNType7[] = "page",     sNType8[] = "para",
1455         sNType9[] = "word",     sNType10[]= "char",
1456         sNType11[] = "user_company" ,       sNType12[] = "user_firstname" ,
1457         sNType13[] = "user_lastname" ,      sNType14[] = "user_initials",
1458         sNType15[] = "user_street" ,        sNType16[] = "user_country" ,
1459         sNType17[] = "user_zipcode" ,       sNType18[] = "user_city" ,
1460         sNType19[] = "user_title" ,         sNType20[] = "user_position" ,
1461         sNType21[] = "user_tel_home",       sNType22[] = "user_tel_work",
1462         sNType23[] = "user_fax" ,           sNType24[] = "user_email" ,
1463         sNType25[] = "user_state",          sNType26[] = "graph"
1464         ;
1465 
1466     static const char* sNTypeTab[ 27 ] =
1467     {
1468         sNType0, sNType1, sNType2, sNType3, sNType4, sNType5,
1469         sNType6, sNType7, sNType8, sNType9, sNType10, sNType11,
1470         sNType12, sNType13, sNType14, sNType15, sNType16, sNType17,
1471         sNType18, sNType19, sNType20, sNType21, sNType22, sNType23,
1472         sNType24, sNType25, sNType26
1473     };
1474 
1475     const unsigned short nTableSize = 47;
1476     int aArr[ nTableSize ] = { 0 };
1477     char ch;
1478 
1479     for( int n = 0; n < 27; ++n )
1480     {
1481         unsigned int ii = 0;
1482         const char* pp = sNTypeTab[ n ];
1483 
1484         while( *pp )
1485         {
1486             ii = ii << 1 ^ *pp++;
1487         }
1488         ii %= nTableSize;
1489 
1490         ch = aArr[ ii ] ? 'X' : ' ';
1491         aArr[ ii ] = 1;
1492         printf( "%-20s -> %3u [%c]\n", sNTypeTab[ n ], ii, ch );
1493     }
1494 }
1495 
1496 #endif
1497 
1498 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1499