xref: /trunk/main/sw/source/core/bastyp/calc.cxx (revision 143bd248)
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_sw.hxx"
26 
27 
28 #include <cctype>
29 #if defined(MACOSX)
30 #include <stdlib.h>
31 #endif
32 #include <cstdlib>
33 #include <climits>
34 // #include <cmath>
35 #include <cfloat>
36 #include <hintids.hxx>
37 #include <osl/diagnose.hxx>
38 #include <rtl/math.hxx>
39 #include <editeng/langitem.hxx>
40 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
41 #include <comphelper/processfactory.hxx>
42 #include <unotools/localedatawrapper.hxx>
43 #include <unotools/charclass.hxx>
44 #include <editeng/unolingu.hxx>
45 #include <editeng/scripttypeitem.hxx>
46 #include <unotools/useroptions.hxx>
47 #include <tools/datetime.hxx>
48 #include <svl/zforlist.hxx>
49 #include <swmodule.hxx>
50 #include <doc.hxx>
51 #include <viewsh.hxx>
52 #include <docstat.hxx>
53 #include <calc.hxx>
54 #include <shellres.hxx>
55 #include <dbfld.hxx>
56 #include <expfld.hxx>
57 #include <usrfld.hxx>
58 #ifndef _DBMGR_HXX
59 #include <dbmgr.hxx>
60 #endif
61 #include <docfld.hxx>
62 #include <swunodef.hxx>
63 #include <swtypes.hxx>
64 
65 using namespace ::com::sun::star;
66 
67 // tippt sich schneller
68 #define RESOURCE ViewShell::GetShellRes()
69 
70 const sal_Char __FAR_DATA sCalc_Add[] 	= 	"add";
71 const sal_Char __FAR_DATA sCalc_Sub[]	=	"sub";
72 const sal_Char __FAR_DATA sCalc_Mul[]	=	"mul";
73 const sal_Char __FAR_DATA sCalc_Div[]	=	"div";
74 const sal_Char __FAR_DATA sCalc_Phd[]	=	"phd";
75 const sal_Char __FAR_DATA sCalc_Sqrt[]	=	"sqrt";
76 const sal_Char __FAR_DATA sCalc_Pow[]	=	"pow";
77 const sal_Char __FAR_DATA sCalc_Or[]	=	"or";
78 const sal_Char __FAR_DATA sCalc_Xor[]	=	"xor";
79 const sal_Char __FAR_DATA sCalc_And[]	=	"and";
80 const sal_Char __FAR_DATA sCalc_Not[]	=	"not";
81 const sal_Char __FAR_DATA sCalc_Eq[]	=	"eq";
82 const sal_Char __FAR_DATA sCalc_Neq[]	=	"neq";
83 const sal_Char __FAR_DATA sCalc_Leq[]	=	"leq";
84 const sal_Char __FAR_DATA sCalc_Geq[]	=	"geq";
85 const sal_Char __FAR_DATA sCalc_L[]		=	"l";
86 const sal_Char __FAR_DATA sCalc_G[]		=	"g";
87 const sal_Char __FAR_DATA sCalc_Sum[]	=	"sum";
88 const sal_Char __FAR_DATA sCalc_Mean[]	=	"mean";
89 const sal_Char __FAR_DATA sCalc_Min[]	=	"min";
90 const sal_Char __FAR_DATA sCalc_Max[]	=	"max";
91 const sal_Char __FAR_DATA sCalc_Sin[]	=	"sin";
92 const sal_Char __FAR_DATA sCalc_Cos[]	=	"cos";
93 const sal_Char __FAR_DATA sCalc_Tan[]	=	"tan";
94 const sal_Char __FAR_DATA sCalc_Asin[]	=	"asin";
95 const sal_Char __FAR_DATA sCalc_Acos[]	=	"acos";
96 const sal_Char __FAR_DATA sCalc_Atan[]	=	"atan";
97 const sal_Char __FAR_DATA sCalc_Round[]	=	"round";
98 const sal_Char __FAR_DATA sCalc_Date[]	=	"date";
99 
100 
101 
102 //!!!!! ACHTUNG - Sortierte Liste aller Operatoren !!!!!
103 struct _CalcOp
104 {
105 	union{
106 		const sal_Char* pName;
107 		const String* pUName;
108 	};
109 	SwCalcOper eOp;
110 };
111 
112 _CalcOp	__READONLY_DATA aOpTable[] = {
113 /* ACOS */    {{sCalc_Acos},       CALC_ACOS},  // Arcuscosinus
114 /* ADD */     {{sCalc_Add},        CALC_PLUS},  // Addition
115 /* AND */     {{sCalc_And},        CALC_AND},  	// log. und
116 /* ASIN */    {{sCalc_Asin},       CALC_ASIN},  // Arcussinus
117 /* ATAN */    {{sCalc_Atan},       CALC_ATAN},  // Arcustangens
118 /* COS */     {{sCalc_Cos},        CALC_COS},  	// Cosinus
119 /* DATE */    {{sCalc_Date},       CALC_DATE},	// Date
120 /* DIV */     {{sCalc_Div},        CALC_DIV},   // Dividieren
121 /* EQ */      {{sCalc_Eq},         CALC_EQ},   	// gleich
122 /* G */       {{sCalc_G},          CALC_GRE},  	// groesser
123 /* GEQ */     {{sCalc_Geq},        CALC_GEQ},  	// groesser gleich
124 /* L */       {{sCalc_L},          CALC_LES},  	// kleiner
125 /* LEQ */     {{sCalc_Leq},        CALC_LEQ},  	// kleiner gleich
126 /* MAX */     {{sCalc_Max},        CALC_MAX},  	// Maximalwert
127 /* MEAN */    {{sCalc_Mean},       CALC_MEAN},  // Mittelwert
128 /* MIN */     {{sCalc_Min},        CALC_MIN},  	// Minimalwert
129 /* MUL */     {{sCalc_Mul},        CALC_MUL},  	// Multiplizieren
130 /* NEQ */     {{sCalc_Neq},        CALC_NEQ},  	// nicht gleich
131 /* NOT */     {{sCalc_Not},        CALC_NOT},  	// log. nicht
132 /* OR */      {{sCalc_Or},         CALC_OR},   	// log. oder
133 /* PHD */     {{sCalc_Phd},        CALC_PHD},   // Prozent
134 /* POW */     {{sCalc_Pow},        CALC_POW},	// Potenzieren
135 /* ROUND */   {{sCalc_Round},      CALC_ROUND},	// Runden
136 /* SIN */     {{sCalc_Sin},        CALC_SIN},  	// Sinus
137 /* SQRT */    {{sCalc_Sqrt},       CALC_SQRT},	// Wurzel
138 /* SUB */     {{sCalc_Sub},        CALC_MINUS},	// Subtraktion
139 /* SUM */     {{sCalc_Sum},        CALC_SUM},  	// Summe
140 /* TAN */     {{sCalc_Tan},        CALC_TAN},  	// Tangens
141 /* XOR */     {{sCalc_Xor},        CALC_XOR}  	// log. xoder
142 };
143 
144 double __READONLY_DATA nRoundVal[] = {
145 	5.0e+0,	0.5e+0,	0.5e-1,	0.5e-2,	0.5e-3,	0.5e-4,	0.5e-5,	0.5e-6,
146 	0.5e-7,	0.5e-8,	0.5e-9,	0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,
147 	0.5e-15,0.5e-16
148 };
149 
150 double __READONLY_DATA nKorrVal[] = {
151 	9, 9e-1, 9e-2, 9e-3, 9e-4, 9e-5, 9e-6, 9e-7, 9e-8,
152 	9e-9, 9e-10, 9e-11, 9e-12, 9e-13, 9e-14
153 };
154 
155 	// First character may be any alphabetic or underscore.
156 const sal_Int32 coStartFlags =
157         i18n::KParseTokens::ANY_LETTER_OR_NUMBER |
158         i18n::KParseTokens::ASC_UNDERSCORE |
159         i18n::KParseTokens::IGNORE_LEADING_WS;
160 
161 	// Continuing characters may be any alphanumeric or underscore or dot.
162 const sal_Int32 coContFlags =
163     ( coStartFlags | i18n::KParseTokens::ASC_DOT )
164         & ~i18n::KParseTokens::IGNORE_LEADING_WS;
165 
166 
167 extern "C" {
168 static int
169 #if defined( WNT )
170  __cdecl
171 #endif
172 #if defined( ICC )
173  _Optlink
174 #endif
OperatorCompare(const void * pFirst,const void * pSecond)175 	OperatorCompare( const void *pFirst, const void *pSecond)
176 {
177     int nRet = 0;
178     if( CALC_NAME == ((_CalcOp*)pFirst)->eOp )
179     {
180         if( CALC_NAME == ((_CalcOp*)pSecond)->eOp )
181             nRet = ((_CalcOp*)pFirst)->pUName->CompareTo(
182                             *((_CalcOp*)pSecond)->pUName );
183         else
184             nRet = ((_CalcOp*)pFirst)->pUName->CompareToAscii(
185                             ((_CalcOp*)pSecond)->pName );
186     }
187     else
188     {
189         if( CALC_NAME == ((_CalcOp*)pSecond)->eOp )
190             nRet = -1 * ((_CalcOp*)pSecond)->pUName->CompareToAscii(
191                             ((_CalcOp*)pFirst)->pName );
192         else
193             nRet = strcmp( ((_CalcOp*)pFirst)->pName,
194                             ((_CalcOp*)pSecond)->pName );
195     }
196 	return nRet;
197 }
198 
199 }// extern "C"
200 
FindOperator(const String & rSrch)201 _CalcOp* FindOperator( const String& rSrch )
202 {
203 	_CalcOp aSrch;
204 	aSrch.pUName = &rSrch;
205 	aSrch.eOp = CALC_NAME;
206 
207 	return (_CalcOp*)bsearch(   (void*) &aSrch,
208 								(void*) aOpTable,
209 								sizeof( aOpTable ) / sizeof( _CalcOp ),
210 								sizeof( _CalcOp ),
211 								OperatorCompare );
212 }
213 
214 
215 //-----------------------------------------------------------------------------
216 
Find(const String & rStr,SwHash ** ppTable,sal_uInt16 nTblSize,sal_uInt16 * pPos)217 SwHash* Find( const String& rStr, SwHash** ppTable, sal_uInt16 nTblSize,
218 				sal_uInt16* pPos )
219 {
220 	sal_uLong ii = 0;
221 	for( xub_StrLen n = 0; n < rStr.Len(); ++n )
222 		ii = ii << 1 ^ rStr.GetChar( n );
223 	ii %= nTblSize;
224 
225 	if( pPos )
226 		*pPos = (sal_uInt16)ii;
227 
228 	for( SwHash* pEntry = *(ppTable+ii); pEntry; pEntry = pEntry->pNext )
229 		if( rStr == pEntry->aStr )
230 			return pEntry;
231 	return 0;
232 }
233 
GetDocAppScriptLang(SwDoc & rDoc)234 inline LanguageType GetDocAppScriptLang( SwDoc& rDoc )
235 {
236 	return ((SvxLanguageItem&)rDoc.GetDefault(
237 							GetWhichOfScript( RES_CHRATR_LANGUAGE,
238                                 GetI18NScriptTypeOfLanguage( (sal_uInt16)GetAppLanguage() ))
239 			)).GetLanguage();
240 }
241 
lcl_ConvertToDateValue(SwDoc & rDoc,sal_Int32 nDate)242 double lcl_ConvertToDateValue( SwDoc& rDoc, sal_Int32 nDate )
243 {
244     double nRet = 0;
245     SvNumberFormatter* pFormatter = rDoc.GetNumberFormatter();
246     if( pFormatter )
247     {
248         Date* pNull = pFormatter->GetNullDate();
249         Date aDate( nDate >> 24, (nDate & 0x00FF0000) >> 16, nDate & 0x0000FFFF );
250         nRet = aDate - *pNull;
251     }
252     return nRet;
253 }
254 
255 //-----------------------------------------------------------------------------
256 
257 /******************************************************************************
258 |*
259 |*	SwCalc::SwCalc( SwDoc* pD ) :
260 |*
261 |*	Erstellung			OK 12-02-93 11:04am
262 |*	Letzte Aenderung	JP 03.11.95
263 |*
264 |******************************************************************************/
265 
SwCalc(SwDoc & rD)266 SwCalc::SwCalc( SwDoc& rD )
267 	:
268 	aErrExpr( aEmptyStr, SwSbxValue(), 0 ),
269 	rDoc( rD ),
270 	pLclData( m_aSysLocale.GetLocaleDataPtr() ),
271 	pCharClass( &GetAppCharClass() ),
272 	nListPor( 0 ),
273 	eError( CALC_NOERR )
274 {
275 	aErrExpr.aStr.AssignAscii( "~C_ERR~" );
276 	memset( VarTable, 0, sizeof(VarTable) );
277 	LanguageType eLang = GetDocAppScriptLang( rDoc );
278 
279     if( eLang != SvxLocaleToLanguage( pLclData->getLocale() ) ||
280         eLang != SvxLocaleToLanguage( pCharClass->getLocale() ) )
281 	{
282 		STAR_NMSPC::lang::Locale aLocale( SvxCreateLocale( eLang ));
283 		STAR_REFERENCE( lang::XMultiServiceFactory ) xMSF(
284 							::comphelper::getProcessServiceFactory() );
285 		pCharClass = new CharClass( xMSF, aLocale );
286 		pLclData = new LocaleDataWrapper( xMSF, aLocale );
287 	}
288 
289 	sCurrSym = pLclData->getCurrSymbol();
290 	sCurrSym.EraseLeadingChars().EraseTrailingChars();
291 	pCharClass->toLower( sCurrSym );
292 
293 static sal_Char __READONLY_DATA
294 	sNType0[] = "false",
295 	sNType1[] = "true",
296 	sNType2[] = "pi",
297 	sNType3[] = "e",
298 	sNType4[] = "tables",
299 	sNType5[] = "graf",
300 	sNType6[] = "ole",
301 	sNType7[] = "page",
302 	sNType8[] = "para",
303 	sNType9[] = "word",
304 	sNType10[]= "char",
305 
306 	sNType11[] = "user_firstname" ,
307 	sNType12[] = "user_lastname" ,
308 	sNType13[] = "user_initials" ,
309 	sNType14[] = "user_company" ,
310 	sNType15[] = "user_street" ,
311 	sNType16[] = "user_country" ,
312 	sNType17[] = "user_zipcode" ,
313 	sNType18[] = "user_city" ,
314 	sNType19[] = "user_title" ,
315 	sNType20[] = "user_position" ,
316 	sNType21[] = "user_tel_work" ,
317 	sNType22[] = "user_tel_home" ,
318 	sNType23[] = "user_fax" ,
319 	sNType24[] = "user_email" ,
320 	sNType25[] = "user_state" ,
321 	sNType26[] = "graph"
322 	;
323 
324 static const sal_Char* __READONLY_DATA sNTypeTab[ 27 ] =
325 {
326 	sNType0, sNType1, sNType2, sNType3, sNType4, sNType5,
327 	sNType6, sNType7, sNType8, sNType9, sNType10, sNType11,
328 	sNType12, sNType13, sNType14, sNType15, sNType16, sNType17,
329 	sNType18, sNType19, sNType20, sNType21, sNType22, sNType23,
330 	sNType24,
331 
332 	// diese sind mit doppelten HashIds
333 	sNType25, sNType26
334 };
335 static sal_uInt16 __READONLY_DATA aHashValue[ 27 ] =
336 {
337 	34, 38, 43,  7, 18, 32, 22, 29, 30, 33,  3,
338 	28, 24, 40,  9, 11, 26, 45,  4, 23, 36, 44, 19,  5,  1,
339 	// diese sind mit doppelten HashIds
340 	11, 38
341 };
342 static sal_uInt16 __READONLY_DATA aAdrToken[ 12 ] =
343 {
344     USER_OPT_COMPANY, USER_OPT_STREET, USER_OPT_COUNTRY, USER_OPT_ZIP,
345     USER_OPT_CITY, USER_OPT_TITLE, USER_OPT_POSITION, USER_OPT_TELEPHONEWORK,
346     USER_OPT_TELEPHONEHOME, USER_OPT_FAX, USER_OPT_EMAIL, USER_OPT_STATE
347 };
348 
349 static sal_uInt16 SwDocStat::* __READONLY_DATA aDocStat1[ 3 ] =
350 {
351 	&SwDocStat::nTbl, &SwDocStat::nGrf, &SwDocStat::nOLE
352 };
353 static sal_uLong SwDocStat::* __READONLY_DATA aDocStat2[ 4 ] =
354 {
355 	&SwDocStat::nPage, &SwDocStat::nPara,
356 	&SwDocStat::nWord, &SwDocStat::nChar
357 };
358 
359 #if TBLSZ != 47
360 #error Alle Hashwerte angepasst?
361 #endif
362 
363 	const SwDocStat& rDocStat = rDoc.GetDocStat();
364 
365 	SwSbxValue nVal;
366 	String sTmpStr;
367 	sal_uInt16 n;
368 
369 	for( n = 0; n < 25; ++n )
370 	{
371 		sTmpStr.AssignAscii( sNTypeTab[ n ] );
372 		VarTable[ aHashValue[ n ] ] = new SwCalcExp( sTmpStr, nVal, 0 );
373 	}
374 
375 	((SwCalcExp*)VarTable[ aHashValue[ 0 ] ])->nValue.PutBool( sal_False );
376 	((SwCalcExp*)VarTable[ aHashValue[ 1 ] ])->nValue.PutBool( sal_True );
377 	((SwCalcExp*)VarTable[ aHashValue[ 2 ] ])->nValue.PutDouble( F_PI );
378 	((SwCalcExp*)VarTable[ aHashValue[ 3 ] ])->nValue.PutDouble( 2.7182818284590452354 );
379 
380 	for( n = 0; n < 3; ++n )
381 		((SwCalcExp*)VarTable[ aHashValue[ n + 4 ] ])->nValue.PutLong( rDocStat.*aDocStat1[ n ]  );
382 	for( n = 0; n < 4; ++n )
383 		((SwCalcExp*)VarTable[ aHashValue[ n + 7 ] ])->nValue.PutLong( rDocStat.*aDocStat2[ n ]  );
384 
385     SvtUserOptions& rUserOptions = SW_MOD()->GetUserOptions();
386 
387     ((SwCalcExp*)VarTable[ aHashValue[ 11 ] ])->nValue.PutString( (String)rUserOptions.GetFirstName() );
388     ((SwCalcExp*)VarTable[ aHashValue[ 12 ] ])->nValue.PutString( (String)rUserOptions.GetLastName() );
389     ((SwCalcExp*)VarTable[ aHashValue[ 13 ] ])->nValue.PutString( (String)rUserOptions.GetID() );
390 
391 	for( n = 0; n < 11; ++n )
392 		((SwCalcExp*)VarTable[ aHashValue[ n + 14 ] ])->nValue.PutString(
393                                         (String)rUserOptions.GetToken( aAdrToken[ n ] ));
394 
395     nVal.PutString( (String)rUserOptions.GetToken( aAdrToken[ 11 ] ));
396 	sTmpStr.AssignAscii( sNTypeTab[ 25 ] );
397 	VarTable[ aHashValue[ 25 ] ]->pNext = new SwCalcExp( sTmpStr, nVal, 0 );
398 
399 // at time its better not to use "graph", because then the im-/export have
400 // to change in all formulas this name.
401 //	nVal.PutLong( rDocStat.*aDocStat1[ 1 ]  );
402 //	VarTable[ aHashValue[ 26 ] ]->pNext = new SwCalcExp(
403 //												sNTypeTab[ 26 ], nVal, 0 );
404 }
405 
406 /******************************************************************************
407 |*
408 |*	SwCalc::~SwCalc()
409 |*
410 |*	Erstellung			OK 12-02-93 11:04am
411 |*	Letzte Aenderung	OK 12-02-93 11:04am
412 |*
413 |******************************************************************************/
414 
~SwCalc()415 SwCalc::~SwCalc()
416 {
417 	for( sal_uInt16 n = 0; n < TBLSZ; ++n )
418 		delete VarTable[n];
419 	if( pLclData != m_aSysLocale.GetLocaleDataPtr() )
420 		delete pLclData;
421 	if( pCharClass != &GetAppCharClass() )
422 		delete pCharClass;
423 }
424 
425 /******************************************************************************
426 |*
427 |*	SwSbxValue SwCalc::Calculate( const String& rStr )
428 |*
429 |*	Erstellung			OK 12-02-93 11:04am
430 |*	Letzte Aenderung	OK 12-02-93 11:04am
431 |*
432 |******************************************************************************/
433 
Calculate(const String & rStr)434 SwSbxValue SwCalc::Calculate( const String& rStr )
435 {
436 	eError = CALC_NOERR;
437 	SwSbxValue nResult;
438 
439 	if( !rStr.Len() )
440 		return nResult;
441 
442 	nListPor = 0;
443 	eCurrListOper = CALC_PLUS;			// defaulten auf Summe
444 
445 	sCommand = rStr;
446 	nCommandPos = 0;
447 
448 	while( (eCurrOper = GetToken()) != CALC_ENDCALC && eError == CALC_NOERR )
449 		nResult = Expr();
450 
451 	if( eError )
452 		nResult.PutDouble( DBL_MAX );
453 
454 	return nResult;
455 }
456 
457 /******************************************************************************
458 |*
459 |*	String SwCalc::GetStrResult( SwSbxValue nValue, sal_Bool bRound = sal_True )
460 |*	Beschreibung		Der Parameter bRound ist auf sal_True defaultet und darf
461 |*						nur beim errechnen von Tabellenzellen auf sal_False gesetzt
462 |*						werden, damit keine Rundungsfehler beim zusammenstellen
463 |*						der Formel entstehen.
464 |*	Erstellung			OK 12-02-93 11:04am
465 |*	Letzte Aenderung	JP 19.02.98
466 |*
467 |******************************************************************************/
468 
GetStrResult(const SwSbxValue & rVal,sal_Bool bRound)469 String SwCalc::GetStrResult( const SwSbxValue& rVal, sal_Bool bRound )
470 {
471 	if( !rVal.IsDouble() )
472         return rVal.GetString();
473 
474 	return GetStrResult( rVal.GetDouble(), bRound );
475 }
476 
477 
GetStrResult(double nValue,sal_Bool)478 String SwCalc::GetStrResult( double nValue, sal_Bool )
479 {
480 	if( nValue >= DBL_MAX )
481 		switch( eError )
482 		{
483 			case CALC_SYNTAX	:	return RESOURCE->aCalc_Syntax;
484 			case CALC_ZERODIV	:	return RESOURCE->aCalc_ZeroDiv;
485 			case CALC_BRACK		:	return RESOURCE->aCalc_Brack;
486 			case CALC_POWERR	:	return RESOURCE->aCalc_Pow;
487 			case CALC_VARNFND	:	return RESOURCE->aCalc_VarNFnd;
488 			case CALC_OVERFLOW	:	return RESOURCE->aCalc_Overflow;
489 			case CALC_WRONGTIME :	return RESOURCE->aCalc_WrongTime;
490 			default				:	return RESOURCE->aCalc_Default;
491 		}
492 
493 	sal_uInt16	nDec = 15; //pLclData->getNumDigits();
494     String	aRetStr( ::rtl::math::doubleToUString( nValue,
495                 rtl_math_StringFormat_Automatic,
496                 nDec,
497                 pLclData->getNumDecimalSep().GetChar(0),
498                 true ));
499 
500 	return aRetStr;
501 }
502 
503 /******************************************************************************
504 |*
505 |*	SwCalcExp* SwCalc::VarLook( const String& )
506 |*
507 |*	Erstellung			OK 12-02-93 11:04am
508 |*	Letzte Aenderung	JP 15.11.99
509 |*
510 |******************************************************************************/
511 
VarInsert(const String & rStr)512 SwCalcExp* SwCalc::VarInsert( const String &rStr )
513 {
514 	String aStr( rStr );
515 	pCharClass->toLower( aStr );
516 	return VarLook( aStr, 1 );
517 }
518 
519 /******************************************************************************
520 |*
521 |*	SwCalcExp* SwCalc::VarLook( const String& , sal_uInt16 ins )
522 |*
523 |*	Erstellung			OK 12-02-93 11:04am
524 |*	Letzte Aenderung	JP 15.11.99
525 |*
526 |******************************************************************************/
VarLook(const String & rStr,sal_uInt16 ins)527 SwCalcExp* SwCalc::VarLook( const String& rStr, sal_uInt16 ins )
528 {
529     aErrExpr.nValue.SetVoidValue(false);
530 
531     sal_uInt16 ii = 0;
532     String aStr( rStr );
533 	pCharClass->toLower( aStr );
534 
535     SwHash* pFnd = Find( aStr, VarTable, TBLSZ, &ii );
536 
537 	if( !pFnd )
538 	{
539 		// dann sehen wir mal im Doc nach:
540 		SwHash** ppDocTbl = rDoc.GetUpdtFlds().GetFldTypeTable();
541 		for( SwHash* pEntry = *(ppDocTbl+ii); pEntry; pEntry = pEntry->pNext )
542             if( aStr == pEntry->aStr )
543 			{
544 				// dann hier zufuegen
545                 pFnd = new SwCalcExp( aStr, SwSbxValue(),
546 									((SwCalcFldType*)pEntry)->pFldType );
547 				pFnd->pNext = *(VarTable+ii);
548 				*(VarTable+ii) = pFnd;
549 				break;
550 			}
551 	}
552 
553 	if( pFnd )
554 	{
555 		SwCalcExp* pFndExp = (SwCalcExp*)pFnd;
556 
557 		if( pFndExp->pFldType && pFndExp->pFldType->Which() == RES_USERFLD )
558 		{
559 			SwUserFieldType* pUFld = (SwUserFieldType*)pFndExp->pFldType;
560 			if( nsSwGetSetExpType::GSE_STRING & pUFld->GetType() )
561 				pFndExp->nValue.PutString( pUFld->GetContent() );
562 			else if( !pUFld->IsValid() )
563 			{
564 				// Die aktuellen Werte sichern . . .
565 				sal_uInt16			nOld_ListPor		= nListPor;
566 				SwSbxValue		nOld_LastLeft		= nLastLeft;
567 				SwSbxValue		nOld_NumberValue	= nNumberValue;
568 				xub_StrLen		nOld_CommandPos		= nCommandPos;
569 				SwCalcOper		eOld_CurrOper	  	= eCurrOper;
570 				SwCalcOper		eOld_CurrListOper	= eCurrListOper;
571 
572 				pFndExp->nValue.PutDouble( pUFld->GetValue( *this ) );
573 
574 				// . . . und zurueck damit.
575 				nListPor		= nOld_ListPor;
576 				nLastLeft		= nOld_LastLeft;
577 				nNumberValue	= nOld_NumberValue;
578 				nCommandPos		= nOld_CommandPos;
579 				eCurrOper		= eOld_CurrOper;
580 				eCurrListOper	= eOld_CurrListOper;
581 			}
582 			else
583 				pFndExp->nValue.PutDouble( pUFld->GetValue() );
584 		}
585 		return pFndExp;
586 	}
587 
588 	// Name(p)=Adress.PLZ oder Adress.DATENSATZNUMMER
589 	// DBSETNUMBERFLD = DatenSATZ-nummernfeld (NICHT "setze Datensatznummer!!!")
590     // #101436#: At this point the "real" case variable has to be used
591     String sTmpName( rStr );
592 	::ReplacePoint( sTmpName );
593 
594 	if( !ins )
595 	{
596 		SwNewDBMgr *pMgr = rDoc.GetNewDBMgr();
597 
598 		// Name(p)=Adress.PLZ oder Adress.DATENSATZNUMMER
599 		// DBSETNUMBERFLD = DatenSATZ-nummernfeld (NICHT "setze Datensatznummer!!!")
600 		String sDBName(GetDBName( sTmpName ));
601 		String sSourceName(sDBName.GetToken(0, DB_DELIM));
602 		String sTableName(sDBName.GetToken(0).GetToken(1, DB_DELIM));
603 		if( pMgr && sSourceName.Len() && sTableName.Len() &&
604             pMgr->OpenDataSource(sSourceName, sTableName, -1, false))
605 		{
606 			String sColumnName( GetColumnName( sTmpName ));
607 			ASSERT (sColumnName.Len(), "DB-Spaltenname fehlt!");
608 
609 			String sDBNum( SwFieldType::GetTypeStr(TYP_DBSETNUMBERFLD) );
610 			pCharClass->toLower(sDBNum);
611 
612 			// Hier nochmal initialisieren, da das nicht mehr in docfld
613 			// fuer Felder != RES_DBFLD geschieht. Z.B. wenn ein Expressionfield
614 			// vor einem DB_Field in einem Dok vorkommt.
615 			VarChange( sDBNum, pMgr->GetSelectedRecordId(sSourceName, sTableName));
616 
617 			if( sDBNum.EqualsIgnoreCaseAscii(sColumnName) )
618 			{
619 				aErrExpr.nValue.PutLong(long(pMgr->GetSelectedRecordId(sSourceName, sTableName)));
620 				return &aErrExpr;
621 			}
622 
623 			sal_uLong nTmpRec = 0;
624 			if( 0 != ( pFnd = Find( sDBNum, VarTable, TBLSZ ) ) )
625 				nTmpRec = ((SwCalcExp*)pFnd)->nValue.GetULong();
626 
627 			String sResult;
628 			double nNumber = DBL_MAX;
629 
630 			long nLang = SvxLocaleToLanguage( pLclData->getLocale() );
631 			if(pMgr->GetColumnCnt( sSourceName, sTableName, sColumnName,
632 									nTmpRec, nLang, sResult, &nNumber ))
633 			{
634 				if (nNumber != DBL_MAX)
635 					aErrExpr.nValue.PutDouble( nNumber );
636 				else
637 					aErrExpr.nValue.PutString( sResult );
638 
639 				return &aErrExpr;
640 			}
641 		}
642         else
643         {
644             //data source was not available - set return to "NoValue"
645             aErrExpr.nValue.SetVoidValue(true);
646         }
647         // auf keinen fall eintragen!!
648 		return &aErrExpr;
649 	}
650 
651 
652     SwCalcExp* pNewExp = new SwCalcExp( aStr, SwSbxValue(), 0 );
653 	pNewExp->pNext = VarTable[ ii ];
654 	VarTable[ ii ] = pNewExp;
655 
656 	String sColumnName( GetColumnName( sTmpName ));
657 	ASSERT( sColumnName.Len(), "DB-Spaltenname fehlt!" );
658 	if( sColumnName.EqualsIgnoreCaseAscii(
659 							SwFieldType::GetTypeStr( TYP_DBSETNUMBERFLD ) ))
660 	{
661 		SwNewDBMgr *pMgr = rDoc.GetNewDBMgr();
662 		String sDBName(GetDBName( sTmpName ));
663 		String sSourceName(sDBName.GetToken(0, DB_DELIM));
664 		String sTableName(sDBName.GetToken(0).GetToken(1, DB_DELIM));
665 		if( pMgr && sSourceName.Len() && sTableName.Len() &&
666             pMgr->OpenDataSource(sSourceName, sTableName, -1, false) &&
667 			!pMgr->IsInMerge())
668 			pNewExp->nValue.PutULong( pMgr->GetSelectedRecordId(sSourceName, sTableName));
669         else
670             pNewExp->nValue.SetVoidValue(true);
671     }
672 
673 	return pNewExp;
674 }
675 
676 /******************************************************************************
677 |*
678 |*	sal_Bool SwCalc::VarChange( const String& rStr, const SwSbxValue nValue )
679 |*
680 |*	Erstellung			OK 12-02-93 11:04am
681 |*	Letzte Aenderung	OK 12-02-93 11:04am
682 |*
683 |******************************************************************************/
684 
VarChange(const String & rStr,double nValue)685 void SwCalc::VarChange( const String& rStr, double nValue )
686 {
687 	SwSbxValue aVal( nValue );
688 	VarChange( rStr, aVal );
689 }
690 
VarChange(const String & rStr,const SwSbxValue & rValue)691 void SwCalc::VarChange( const String& rStr, const SwSbxValue& rValue )
692 {
693 	String aStr( rStr );
694 	pCharClass->toLower( aStr );
695 
696 	sal_uInt16 nPos = 0;
697 	SwCalcExp* pFnd = (SwCalcExp*)Find( aStr, VarTable, TBLSZ, &nPos );
698 
699 	if( !pFnd )
700 	{
701 		pFnd = new SwCalcExp( aStr, SwSbxValue( rValue ), 0 );
702 		pFnd->pNext = VarTable[ nPos ];
703 		VarTable[ nPos ] = pFnd;
704 	}
705 	else
706 		pFnd->nValue = rValue;
707 }
708 
709 /******************************************************************************
710 |*
711 |*	sal_Bool SwCalc::Push( const void* pPtr )
712 |*
713 |*	Erstellung			OK 12-02-93 11:05am
714 |*	Letzte Aenderung	OK 12-02-93 11:05am
715 |*
716 |******************************************************************************/
717 
Push(const VoidPtr pPtr)718 sal_Bool SwCalc::Push( const VoidPtr pPtr )
719 {
720 	if( USHRT_MAX != aRekurStk.GetPos( pPtr ) )
721 		return sal_False;
722 
723 	aRekurStk.Insert( pPtr, aRekurStk.Count() );
724 	return sal_True;
725 }
726 
727 /******************************************************************************
728 |*
729 |*	void SwCalc::Pop( const void* pPtr )
730 |*
731 |*	Erstellung			OK 12-02-93 11:05am
732 |*	Letzte Aenderung	OK 12-02-93 11:05am
733 |*
734 |******************************************************************************/
735 
Pop(const VoidPtr)736 void SwCalc::Pop( const VoidPtr )
737 {
738     ASSERT( aRekurStk.Count(), "SwCalc: Pop auf ungueltigen Ptr" );
739 
740 	aRekurStk.Remove( aRekurStk.Count() - 1 );
741 }
742 
743 
744 /******************************************************************************
745 |*
746 |*	SwCalcOper SwCalc::GetToken()
747 |*
748 |*	Erstellung			OK 12-02-93 11:05am
749 |*	Letzte Aenderung	JP 03.11.95
750 |*
751 |******************************************************************************/
752 
GetToken()753 SwCalcOper SwCalc::GetToken()
754 {
755 #if OSL_DEBUG_LEVEL > 1
756 //JP 25.01.2001: static for switch back to the "old" implementation of the
757 //				calculator, which don't use the I18N routines.
758 static int nUseOld = 0;
759 if( !nUseOld )
760 {
761 #endif
762 
763 	if( nCommandPos >= sCommand.Len() )
764 		return eCurrOper = CALC_ENDCALC;
765 
766     using namespace ::com::sun::star::i18n;
767 	{
768 		// Parse any token.
769 		ParseResult aRes = pCharClass->parseAnyToken( sCommand, nCommandPos,
770 													coStartFlags, aEmptyStr,
771 													coContFlags, aEmptyStr );
772 
773 		sal_Bool bSetError = sal_True;
774 		xub_StrLen nRealStt = nCommandPos + (xub_StrLen)aRes.LeadingWhiteSpace;
775 		if( aRes.TokenType & (KParseType::ASC_NUMBER | KParseType::UNI_NUMBER) )
776 		{
777 			nNumberValue.PutDouble( aRes.Value );
778 			eCurrOper = CALC_NUMBER;
779 			bSetError = sal_False;
780 		}
781 		else if( aRes.TokenType & KParseType::IDENTNAME )
782 		{
783 			String aName( sCommand.Copy( nRealStt, static_cast<xub_StrLen>(aRes.EndPos) - nRealStt ));
784             //#101436#: the variable may contain a database name it must not be converted to lower case
785             // instead all further comparisons must be done case-insensitive
786             //pCharClass->toLower( aName );
787             String sLowerCaseName(aName);
788             pCharClass->toLower( sLowerCaseName );
789 			// Currency-Symbol abfangen
790             if( sLowerCaseName == sCurrSym )
791 			{
792 				nCommandPos = (xub_StrLen)aRes.EndPos;
793 				return GetToken();	// also nochmal aufrufen
794 			}
795 
796 			// Operations abfangen
797             _CalcOp* pFnd = ::FindOperator( sLowerCaseName );
798 			if( pFnd )
799 			{
800 				switch( ( eCurrOper = ((_CalcOp*)pFnd)->eOp ) )
801 				{
802 					case CALC_SUM:
803 					case CALC_MEAN:
804 						eCurrListOper = CALC_PLUS;
805 						break;
806 					case CALC_MIN:
807 						eCurrListOper = CALC_MIN_IN;
808 						break;
809 					case CALC_MAX:
810 						eCurrListOper = CALC_MAX_IN;
811 						break;
812 					case CALC_DATE:
813 						eCurrListOper = CALC_MONTH;
814 						break;
815 					default:
816 						break;
817 				}
818 				nCommandPos = (xub_StrLen)aRes.EndPos;
819 				return eCurrOper;
820 			}
821 			aVarName = aName;
822 			eCurrOper = CALC_NAME;
823 			bSetError = sal_False;
824 		}
825 		else if ( aRes.TokenType & KParseType::DOUBLE_QUOTE_STRING )
826 		{
827 			nNumberValue.PutString( String( aRes.DequotedNameOrString ));
828 			eCurrOper = CALC_NUMBER;
829 			bSetError = sal_False;
830 		}
831 		else if( aRes.TokenType & KParseType::ONE_SINGLE_CHAR )
832 		{
833 			String aName( sCommand.Copy( nRealStt, static_cast<xub_StrLen>(aRes.EndPos) - nRealStt ));
834 			if( 1 == aName.Len() )
835 			{
836 				bSetError = sal_False;
837 				sal_Unicode ch = aName.GetChar( 0 );
838 				switch( ch )
839 				{
840 				case ';': if( CALC_MONTH == eCurrListOper ||
841                               CALC_DAY == eCurrListOper )
842                           {
843                               eCurrOper = eCurrListOper;
844                               break;
845                           }
846 				case '\n':
847 							eCurrOper = CALC_PRINT;
848 							break;
849 				case '%':
850 				case '^':
851 				case '*':
852 				case '/':
853 				case '+':
854 				case '-':
855 				case '(':
856 				case ')':	eCurrOper = SwCalcOper(ch);
857 							break;
858 
859 				case '=':
860 				case '!':
861 						{
862 							SwCalcOper eTmp2;
863 							if( '=' == ch )
864 								eCurrOper = SwCalcOper('='), eTmp2 = CALC_EQ;
865 							else
866 								eCurrOper = CALC_NOT, eTmp2 = CALC_NEQ;
867 
868 							if( aRes.EndPos < sCommand.Len() &&
869 								'=' == sCommand.GetChar( (xub_StrLen)aRes.EndPos ) )
870 							{
871 								eCurrOper = eTmp2;
872 								++aRes.EndPos;
873 							}
874 						}
875 						break;
876 
877 				case cListDelim :
878 						eCurrOper = eCurrListOper;
879 						break;
880 
881 				case '[':
882 						if( aRes.EndPos < sCommand.Len() )
883 						{
884 							aVarName.Erase();
885 							xub_StrLen nFndPos = (xub_StrLen)aRes.EndPos,
886 										nSttPos = nFndPos;
887 
888 							do{
889 								if( STRING_NOTFOUND != ( nFndPos =
890 									sCommand.Search( ']', nFndPos )) )
891 								{
892 									// ignore the ]
893 									if( '\\' == sCommand.GetChar(nFndPos-1))
894 									{
895 										aVarName += sCommand.Copy( nSttPos,
896 													nFndPos - nSttPos - 1 );
897 										nSttPos = ++nFndPos;
898 									}
899 									else
900 										break;
901 								}
902 							} while( STRING_NOTFOUND != nFndPos );
903 
904 							if( STRING_NOTFOUND != nFndPos )
905 							{
906 								if( nSttPos != nFndPos )
907 									aVarName += sCommand.Copy( nSttPos,
908 													nFndPos - nSttPos );
909 								aRes.EndPos = nFndPos + 1;
910 								eCurrOper = CALC_NAME;
911 							}
912 							else
913 								bSetError = sal_True;
914 						}
915 						else
916 							bSetError = sal_True;
917 						break;
918 
919 				default:
920 					bSetError = sal_True;
921 					break;
922 				}
923 			}
924 		}
925 		else if( aRes.TokenType & KParseType::BOOLEAN )
926 		{
927 			String aName( sCommand.Copy( nRealStt, static_cast<xub_StrLen>(aRes.EndPos) - nRealStt ));
928 			if( aName.Len() )
929 			{
930 				sal_Unicode ch = aName.GetChar(0);
931 
932 				bSetError = sal_True;
933 				if ('<' == ch || '>' == ch)
934 				{
935 					bSetError = sal_False;
936 
937 					SwCalcOper eTmp2 = ('<' == ch) ? CALC_LEQ : CALC_GEQ;
938 					eCurrOper = ('<' == ch) ? CALC_LES : CALC_GRE;
939 
940 					if( 2 == aName.Len() && '=' == aName.GetChar(1) )
941 						eCurrOper = eTmp2;
942 					else if( 1 != aName.Len() )
943 						bSetError = sal_True;
944 				}
945 			}
946 		}
947 		else if( nRealStt == sCommand.Len() )
948 		{
949 			eCurrOper = CALC_ENDCALC;
950 			bSetError = sal_False;
951 		}
952 
953 		if( bSetError )
954 		{
955 			eError = CALC_SYNTAX;
956 			eCurrOper = CALC_PRINT;
957 		}
958 		nCommandPos = (xub_StrLen)aRes.EndPos;
959 	};
960 
961 #if OSL_DEBUG_LEVEL > 1
962 
963 #define NextCh( s, n )	(nCommandPos < sCommand.Len() ? sCommand.GetChar( nCommandPos++ ) : 0)
964 
965 }
966 else
967 {
968 	sal_Unicode ch;
969 	sal_Unicode cTSep = pLclData->getNumThousandSep().GetChar(0),
970 				cDSep = pLclData->getNumDecimalSep().GetChar(0);
971 
972 	do {
973 		if( 0 == ( ch = NextCh( sCommand, nCommandPos ) ) )
974 			return eCurrOper = CALC_ENDCALC;
975 	} while ( ch == '\t' || ch == ' ' || ch == cTSep );
976 
977 	if( ch == cDSep )
978 		ch = '.';
979 
980 	switch( ch )
981 	{
982 		case ';': if( CALC_MONTH == eCurrListOper || CALC_DAY == eCurrListOper )
983                   {
984                       eCurrOper = eCurrListOper;
985                       break;
986                   } // else .. no break
987 		case '\n':
988 					{
989 						sal_Unicode c;
990 						while( nCommandPos < sCommand.Len() && ( ( c =
991 								sCommand.GetChar( nCommandPos ) ) == ' ' ||
992 								c == '\t' || c == '\x0a' || c == '\x0d' ))
993 							++nCommandPos;
994 						eCurrOper = CALC_PRINT;
995 					}
996 					break;
997 		case '%':
998 		case '^':
999 		case '*':
1000 		case '/':
1001 		case '+':
1002 		case '-':
1003 		case '(':
1004 		case ')':	eCurrOper = SwCalcOper(ch);
1005 					break;
1006 
1007 		case '=':   if( '=' == sCommand.GetChar( nCommandPos ) )
1008 					{
1009 						++nCommandPos;
1010 						eCurrOper = CALC_EQ;
1011 					}
1012 					else
1013 						eCurrOper = SwCalcOper(ch);
1014 					break;
1015 
1016 		case '!':   if( '=' == sCommand.GetChar( nCommandPos ) )
1017 					{
1018 						++nCommandPos;
1019 						eCurrOper = CALC_NEQ;
1020 					}
1021 					else
1022 						eCurrOper = CALC_NOT;
1023 					break;
1024 
1025 		case '>':
1026 		case '<':   eCurrOper = '>' == ch  ? CALC_GRE : CALC_LES;
1027 					if( '=' == (ch = sCommand.GetChar( nCommandPos ) ) )
1028 					{
1029 						++nCommandPos;
1030 						eCurrOper = CALC_GRE == eCurrOper ? CALC_GEQ : CALC_LEQ;
1031 					}
1032 					else if( ' ' != ch )
1033 					{
1034 						eError = CALC_SYNTAX;
1035 						eCurrOper = CALC_PRINT;
1036 					}
1037 					break;
1038 
1039 		case cListDelim :
1040 					eCurrOper = eCurrListOper;
1041 					break;
1042 
1043 		case '0':	case '1':	case '2':	case '3':	case '4':
1044 		case '5':	case '6':	case '7':	case '8':	case '9':
1045 		case ',':
1046 		case '.':	{
1047 						double nVal;
1048 						--nCommandPos; 		//  auf das 1. Zeichen zurueck
1049 						if( Str2Double( sCommand, nCommandPos, nVal, pLclData ))
1050 						{
1051 							nNumberValue.PutDouble( nVal );
1052 							eCurrOper = CALC_NUMBER;
1053 						}
1054 						else
1055 						{
1056 							// fehlerhafte Zahl
1057 							eError = CALC_SYNTAX;
1058 							eCurrOper = CALC_PRINT;
1059 						}
1060 					}
1061 					break;
1062 
1063 		case '[':	{
1064 						String aStr;
1065 						sal_Bool bIgnore = sal_False;
1066 						do {
1067 							while( 0 != ( ch = NextCh( sCommand, nCommandPos  ))
1068 									&& ch != ']' )
1069 							{
1070 								if( !bIgnore && '\\' == ch )
1071 									bIgnore = sal_True;
1072 								else if( bIgnore )
1073 									bIgnore = sal_False;
1074 								aStr += ch;
1075 							}
1076 
1077 							if( !bIgnore )
1078 								break;
1079 
1080 							aStr.SetChar( aStr.Len() - 1, ch );
1081 						} while( ch );
1082 
1083 						aVarName = aStr;
1084 						eCurrOper = CALC_NAME;
1085 					}
1086 					break;
1087 
1088 		case '"':	{
1089 						xub_StrLen nStt = nCommandPos;
1090 						while( 0 != ( ch = NextCh( sCommand, nCommandPos ) )
1091 								&& '"' != ch )
1092 							;
1093 
1094 						xub_StrLen nLen = nCommandPos - nStt;
1095 						if( '"' == ch )
1096 							--nLen;
1097 						nNumberValue.PutString( sCommand.Copy( nStt, nLen ));
1098 						eCurrOper = CALC_NUMBER;
1099 					}
1100 					break;
1101 
1102 		default:	if( ch && pCharClass->isLetter( sCommand, nCommandPos - 1)
1103 							|| '_' == ch )
1104 					{
1105 						xub_StrLen nStt = nCommandPos-1;
1106 						while( 0 != (ch = NextCh( sCommand, nCommandPos )) &&
1107 							   (pCharClass->isLetterNumeric(
1108 							   					sCommand, nCommandPos - 1) ||
1109 								ch == '_' || ch == '.' ) )
1110 							;
1111 
1112 						if( ch )
1113 							--nCommandPos;
1114 
1115 						String aStr( sCommand.Copy( nStt, nCommandPos-nStt ));
1116 						pCharClass->toLower( aStr );
1117 
1118 
1119 						// Currency-Symbol abfangen
1120 						if( aStr == sCurrSym )
1121 							return GetToken();	// also nochmal aufrufen
1122 
1123 						// Operations abfangen
1124 						_CalcOp* pFnd = ::FindOperator( aStr );
1125 						if( pFnd )
1126 						{
1127 							switch( ( eCurrOper = ((_CalcOp*)pFnd)->eOp ) )
1128 							{
1129 								case CALC_SUM  :
1130 								case CALC_MEAN : eCurrListOper = CALC_PLUS;
1131 													break;
1132 								case CALC_MIN  : eCurrListOper = CALC_MIN_IN;
1133 													break;
1134 								case CALC_MAX  : eCurrListOper = CALC_MAX_IN;
1135 													break;
1136 								case CALC_DATE  : eCurrListOper = CALC_MONTH;
1137 													break;
1138                                 default :
1139                                     break;
1140 							}
1141 							return eCurrOper;
1142 						}
1143 						aVarName = aStr;
1144 						eCurrOper = CALC_NAME;
1145 					}
1146 					else
1147 					{
1148 						eError = CALC_SYNTAX;
1149 						eCurrOper = CALC_PRINT;
1150 					}
1151 					break;
1152 	}
1153 
1154 }
1155 #endif
1156 	return eCurrOper;
1157 }
1158 
1159 /******************************************************************************
1160 |*
1161 |*	SwSbxValue SwCalc::Term()
1162 |*
1163 |*	Erstellung			OK 12-02-93 11:05am
1164 |*	Letzte Aenderung	JP 16.01.96
1165 |*
1166 |******************************************************************************/
1167 
Term()1168 SwSbxValue SwCalc::Term()
1169 {
1170 	SwSbxValue left( Prim() );
1171 	nLastLeft = left;
1172 	for(;;)
1173 	{
1174 		sal_uInt16 nSbxOper = USHRT_MAX;
1175 
1176 		switch( eCurrOper )
1177 		{
1178 // wir haben kein Bitweises verodern, oder ?
1179 //			case CALC_AND:	eSbxOper = SbxAND;	break;
1180 //			case CALC_OR:	eSbxOper = SbxOR;	break;
1181 //			case CALC_XOR:	eSbxOper = SbxXOR;	break;
1182 			case CALC_AND:	{
1183 								GetToken();
1184 								sal_Bool bB = Prim().GetBool();
1185 								left.PutBool( left.GetBool() && bB );
1186 							}
1187 							break;
1188 			case CALC_OR:	{
1189 								GetToken();
1190 								sal_Bool bB = Prim().GetBool();
1191 								left.PutBool( left.GetBool() || bB );
1192 							}
1193 							break;
1194 			case CALC_XOR:	{
1195 								GetToken();
1196 								sal_Bool bR = Prim().GetBool();
1197 								sal_Bool bL = left.GetBool();
1198 								left.PutBool( (bL && !bR) || (!bL && bR) );
1199 							}
1200 							break;
1201 
1202 			case CALC_EQ:	nSbxOper = SbxEQ;	break;
1203 			case CALC_NEQ:	nSbxOper = SbxNE;	break;
1204 			case CALC_LEQ:	nSbxOper = SbxLE;	break;
1205 			case CALC_GEQ:	nSbxOper = SbxGE;	break;
1206 			case CALC_GRE:	nSbxOper = SbxGT;	break;
1207 			case CALC_LES:	nSbxOper = SbxLT;	break;
1208 
1209 			case CALC_MUL:	nSbxOper = SbxMUL; 	break;
1210 			case CALC_DIV:	nSbxOper = SbxDIV;  break;
1211 
1212 			case CALC_MIN_IN:
1213 							{
1214 								GetToken();
1215 								SwSbxValue e = Prim();
1216 								left = left.GetDouble() < e.GetDouble()
1217 											? left : e;
1218 							}
1219 							break;
1220 			case CALC_MAX_IN:
1221 							{
1222 								GetToken();
1223 								SwSbxValue e = Prim();
1224 								left = left.GetDouble() > e.GetDouble()
1225 											? left : e;
1226 							}
1227 							break;
1228 			case CALC_MONTH:
1229 							{
1230 								GetToken();
1231 								SwSbxValue e = Prim();
1232                                 sal_Int32 nYear = (sal_Int32) floor( left.GetDouble() );
1233                                 nYear = nYear & 0x0000FFFF;
1234                                 sal_Int32 nMonth = (sal_Int32) floor( e.GetDouble() );
1235                                 nMonth = ( nMonth & 0x000000FF ) << 16;
1236 								left.PutLong( nMonth + nYear );
1237                                 eCurrOper = CALC_DAY;
1238 							}
1239 							break;
1240 			case CALC_DAY:
1241 							{
1242 								GetToken();
1243 								SwSbxValue e = Prim();
1244                                 sal_Int32 nYearMonth = (sal_Int32) floor( left.GetDouble() );
1245                                 nYearMonth = nYearMonth & 0x00FFFFFF;
1246                                 sal_Int32 nDay = (sal_Int32) floor( e.GetDouble() );
1247                                 nDay = ( nDay & 0x000000FF ) << 24;
1248                                 left = lcl_ConvertToDateValue( rDoc, nDay + nYearMonth );
1249 							}
1250 							break;
1251 			case CALC_ROUND:
1252 							{
1253 								GetToken();
1254 								SwSbxValue e = Prim();
1255 
1256 								double fVal = 0;
1257 								double fFac = 1;
1258 								sal_Int32 nDec = (sal_Int32) floor( e.GetDouble() );
1259 								if( nDec < -20 || nDec > 20 )
1260 								{
1261 									eError = CALC_OVERFLOW;
1262 									left.Clear();
1263 									return left;
1264 								}
1265 								fVal = left.GetDouble();
1266 								sal_uInt16 i;
1267 								if( nDec >= 0)
1268 									for (i = 0; i < (sal_uInt16) nDec; ++i )
1269 										fFac *= 10.0;
1270 								else
1271 									for (i = 0; i < (sal_uInt16) -nDec; ++i )
1272 										fFac /= 10.0;
1273 
1274 								fVal *= fFac;
1275 
1276 								sal_Bool bSign;
1277 								if (fVal < 0.0)
1278 								{
1279 									fVal *= -1.0;
1280 									bSign = sal_True;
1281 								}
1282 								else
1283 									bSign = sal_False;
1284 
1285 								// runden
1286 								double fNum = fVal;				// find the exponent
1287 								int nExp = 0;
1288 								if( fNum > 0 )
1289 								{
1290 									while( fNum < 1.0 ) fNum *= 10.0, --nExp;
1291 									while( fNum >= 10.0 ) fNum /= 10.0, ++nExp;
1292 								}
1293 								nExp = 15 - nExp;
1294 								if( nExp > 15 )
1295 									nExp = 15;
1296 								else if( nExp <= 1 )
1297 									nExp = 0;
1298 								fVal = floor( fVal+ 0.5 + nRoundVal[ nExp ] );
1299 
1300 								if (bSign)
1301 									fVal *= -1.0;
1302 
1303 								fVal /= fFac;
1304 
1305 								left.PutDouble( fVal );
1306 							}
1307 							break;
1308 
1309 /*
1310 // removed here because of #77448# (=2*3^2 != 18)
1311             case CALC_POW:  {
1312 								GetToken();
1313 								double fraction, integer;
1314 								double right = Prim().GetDouble(),
1315                                        dleft = left.GetDouble();
1316 
1317 								fraction = modf( right, &integer );
1318 								if( ( dleft < 0.0 && 0.0 != fraction ) ||
1319 									( 0.0 == dleft && right < 0.0 ) )
1320 								{
1321 									eError = CALC_OVERFLOW;
1322 									left.Clear();
1323 									return left;
1324 								}
1325 								dleft = pow(dleft, right );
1326 								if( dleft == HUGE_VAL )
1327 								{
1328 									eError = CALC_POWERR;
1329 									left.Clear();
1330 									return left;
1331 								}
1332 								left.PutDouble( dleft );
1333 							}
1334 							break;
1335 */
1336 			default:		return left;
1337 		}
1338 
1339 		if( USHRT_MAX != nSbxOper )
1340 		{
1341             // #i47706: cast to SbxOperator AFTER comparing to USHRT_MAX
1342             SbxOperator eSbxOper = (SbxOperator)nSbxOper;
1343 
1344 			GetToken();
1345 			if( SbxEQ <= eSbxOper && eSbxOper <= SbxGE )
1346 				left.PutBool( left.Compare( eSbxOper, Prim() ));
1347 			else
1348 			{
1349 				SwSbxValue aRight( Prim() );
1350 				aRight.MakeDouble();
1351 				left.MakeDouble();
1352 
1353 				if( SbxDIV == eSbxOper && !aRight.GetDouble() )
1354 					eError = CALC_ZERODIV;
1355 				else
1356 					left.Compute( eSbxOper, aRight );
1357 			}
1358 		}
1359 	}
1360 }
1361 
1362 /******************************************************************************
1363 |*
1364 |*	SwSbxValue SwCalc::Prim()
1365 |*
1366 |*	Erstellung			OK 12-02-93 11:05am
1367 |*	Letzte Aenderung	JP 03.11.95
1368 |*
1369 |******************************************************************************/
1370 
1371 extern "C" typedef double (*pfCalc)( double );
1372 
Prim()1373 SwSbxValue SwCalc::Prim()
1374 {
1375 	SwSbxValue nErg;
1376 
1377     pfCalc pFnc = 0;
1378 
1379 	sal_Bool bChkTrig = sal_False, bChkPow = sal_False;
1380 
1381 	switch( eCurrOper )
1382 	{
1383 		case CALC_SIN:		pFnc = &sin;						break;
1384 		case CALC_COS:		pFnc = &cos;						break;
1385 		case CALC_TAN:		pFnc = &tan;						break;
1386 		case CALC_ATAN:		pFnc = &atan;						break;
1387 		case CALC_ASIN:		pFnc = &asin; 	bChkTrig = sal_True;	break;
1388 		case CALC_ACOS:		pFnc = &acos; 	bChkTrig = sal_True;	break;
1389 
1390 		case CALC_NOT:		{
1391 								GetToken();
1392 								nErg = Prim();
1393 								if( SbxSTRING == nErg.GetType() )
1394                                     nErg.PutBool( 0 == nErg.GetString().Len() );
1395 								else if(SbxBOOL == nErg.GetType() )
1396 									nErg.PutBool(!nErg.GetBool());
1397 								// evaluate arguments manually so that the binary NOT below
1398 								// does not get called.
1399 								// We want a BOOLEAN NOT.
1400 								else if (nErg.IsNumeric())
1401 									nErg.PutLong( nErg.GetDouble() == 0.0 ? 1 : 0 );
1402 								else
1403 								{
1404 									DBG_ERROR( "unexpected case. computing binary NOT" );
1405 									//!! computes a binary NOT
1406 									nErg.Compute( SbxNOT, nErg );
1407 								}
1408 							}
1409 							break;
1410 
1411 		case CALC_NUMBER:	if( GetToken() == CALC_PHD )
1412 							{
1413 								double aTmp = nNumberValue.GetDouble();
1414 								aTmp *= 0.01;
1415 								nErg.PutDouble( aTmp );
1416 								GetToken();
1417 							}
1418 							else if( eCurrOper == CALC_NAME )
1419 								eError = CALC_SYNTAX;
1420 							else
1421 							{
1422 								nErg = nNumberValue;
1423 								bChkPow = sal_True;
1424 							}
1425 							break;
1426 
1427 		case CALC_NAME: 	if( GetToken() == CALC_ASSIGN )
1428 							{
1429 								SwCalcExp* n = VarInsert( aVarName );
1430 								GetToken();
1431 								nErg = n->nValue = Expr();
1432 							}
1433 							else
1434 							{
1435 								nErg = VarLook( aVarName )->nValue;
1436 								bChkPow = sal_True;
1437 							}
1438 							break;
1439 
1440 		case CALC_MINUS:	GetToken();
1441 							nErg.PutDouble( -(Prim().GetDouble()) );
1442 							break;
1443 
1444 		case CALC_LP:		{
1445 								GetToken();
1446 								nErg = Expr();
1447 								if( eCurrOper != CALC_RP )
1448 									eError = CALC_BRACK;
1449 								else
1450                                 {
1451 									GetToken();
1452                                     bChkPow = sal_True; // in order for =(7)^2 to work
1453                                 }
1454 							}
1455 							break;
1456 
1457 		case CALC_MEAN:		{
1458 								nListPor = 1;
1459 								GetToken();
1460 								nErg = Expr();
1461 								double aTmp = nErg.GetDouble();
1462 								aTmp /= nListPor;
1463 								nErg.PutDouble( aTmp );
1464 							}
1465 							break;
1466 
1467 		case CALC_SQRT:		{
1468 								GetToken();
1469 								nErg = Prim();
1470 								if( nErg.GetDouble() < 0 )
1471 									eError = CALC_OVERFLOW;
1472 								else
1473 									nErg.PutDouble( sqrt( nErg.GetDouble() ));
1474 							}
1475 							break;
1476 
1477 		case CALC_SUM:
1478 		case CALC_DATE:
1479 		case CALC_MIN:
1480 		case CALC_MAX:		GetToken();
1481 							nErg = Expr();
1482 							break;
1483 
1484 		case CALC_ENDCALC:	nErg.Clear();
1485 							break;
1486 
1487 		default:			eError = CALC_SYNTAX;
1488 							break;
1489 	}
1490 
1491 	if( pFnc )
1492 	{
1493 		GetToken();
1494 		double nVal = Prim().GetDouble();
1495 		if( !bChkTrig || ( nVal > -1 && nVal < 1 ) )
1496 			nErg.PutDouble( (*pFnc)( nVal ) );
1497 		else
1498 			eError = CALC_OVERFLOW;
1499 	}
1500 
1501     // added here because of #77448# (=2*3^2 should be 18)
1502 	if( bChkPow && eCurrOper == CALC_POW )
1503 	{
1504 		double dleft = nErg.GetDouble();
1505 		GetToken();
1506 		double right = Prim().GetDouble();
1507 
1508 		double fraction, integer;
1509 		fraction = modf( right, &integer );
1510 		if( ( dleft < 0.0 && 0.0 != fraction ) ||
1511 			( 0.0 == dleft && right < 0.0 ) )
1512 		{
1513 			eError = CALC_OVERFLOW;
1514 			nErg.Clear();
1515 		}
1516 		else
1517 		{
1518 			dleft = pow(dleft, right );
1519 			if( dleft == HUGE_VAL )
1520 			{
1521 				eError = CALC_POWERR;
1522 				nErg.Clear();
1523 			}
1524 			else
1525 			{
1526 				nErg.PutDouble( dleft );
1527 //				GetToken();
1528 			}
1529 		}
1530 	}
1531 
1532 	return nErg;
1533 }
1534 
1535 /******************************************************************************
1536 |*
1537 |*	SwSbxValue	SwCalc::Expr()
1538 |*
1539 |*	Erstellung			OK 12-02-93 11:06am
1540 |*	Letzte Aenderung	JP 03.11.95
1541 |*
1542 |******************************************************************************/
1543 
Expr()1544 SwSbxValue	SwCalc::Expr()
1545 {
1546 	SwSbxValue left = Term(), right;
1547 	nLastLeft = left;
1548 	for(;;)
1549 		switch(eCurrOper)
1550 		{
1551 			case CALC_PLUS:		GetToken();
1552 								// erzeuge zum addieren auf jedenfall einen
1553 								// Double-Wert
1554 								left.MakeDouble();
1555 								( right = Term() ).MakeDouble();
1556 								left.Compute( SbxPLUS, right );
1557 								nListPor++;
1558 								break;
1559 
1560 			case CALC_MINUS:	GetToken();
1561 								// erzeuge zum addieren auf jedenfall einen
1562 								// Double-Wert
1563 								left.MakeDouble();
1564 								( right = Term() ).MakeDouble();
1565 								left.Compute( SbxMINUS, right );
1566 								break;
1567 
1568 			default:			return left;
1569 		}
1570 }
1571 
1572 //------------------------------------------------------------------------------
1573 
GetColumnName(const String & rName)1574 String SwCalc::GetColumnName(const String& rName)
1575 {
1576 	xub_StrLen nPos = rName.Search(DB_DELIM);
1577 	if( STRING_NOTFOUND != nPos )
1578 	{
1579 		nPos = rName.Search(DB_DELIM, nPos + 1);
1580 
1581 		if( STRING_NOTFOUND != nPos )
1582 			return rName.Copy(nPos + 1);
1583 	}
1584 	return rName;
1585 }
1586 
1587 //------------------------------------------------------------------------------
1588 
GetDBName(const String & rName)1589 String SwCalc::GetDBName(const String& rName)
1590 {
1591 	xub_StrLen nPos = rName.Search(DB_DELIM);
1592 	if( STRING_NOTFOUND != nPos )
1593 	{
1594 		nPos = rName.Search(DB_DELIM, nPos + 1);
1595 
1596 		if( STRING_NOTFOUND != nPos )
1597 			return rName.Copy( 0, nPos );
1598 	}
1599 	SwDBData aData = rDoc.GetDBData();
1600 	String sRet = aData.sDataSource;
1601 	sRet += DB_DELIM;
1602 	sRet += String(aData.sCommand);
1603 	return sRet;
1604 }
1605 
1606 //------------------------------------------------------------------------------
1607 
1608 namespace
1609 {
1610 
1611 static bool
lcl_Str2Double(const String & rCommand,xub_StrLen & rCommandPos,double & rVal,const LocaleDataWrapper * const pLclData)1612 lcl_Str2Double( const String& rCommand, xub_StrLen& rCommandPos, double& rVal,
1613         const LocaleDataWrapper* const pLclData )
1614 {
1615     OSL_ASSERT(pLclData);
1616 	const xub_Unicode nCurrCmdPos = rCommandPos;
1617     rtl_math_ConversionStatus eStatus;
1618     const sal_Unicode* pEnd;
1619     rVal = rtl_math_uStringToDouble( rCommand.GetBuffer() + rCommandPos,
1620             rCommand.GetBuffer() + rCommand.Len(),
1621             pLclData->getNumDecimalSep().GetChar(0),
1622             pLclData->getNumThousandSep().GetChar(0),
1623             &eStatus, &pEnd );
1624 	rCommandPos = static_cast<xub_StrLen>(pEnd - rCommand.GetBuffer());
1625 
1626 	return rtl_math_ConversionStatus_Ok == eStatus && nCurrCmdPos != rCommandPos;
1627 }
1628 
1629 }
1630 
1631 /******************************************************************************
1632  *	Methode		:	sal_Bool SwCalc::Str2Double( double& )
1633  *	Beschreibung:
1634  *	Erstellt	:	OK 07.06.94 12:56
1635  *	Aenderung	: 	JP 27.10.98
1636  ******************************************************************************/
Str2Double(const String & rCommand,xub_StrLen & rCommandPos,double & rVal,const LocaleDataWrapper * const pLclData)1637 bool SwCalc::Str2Double( const String& rCommand, xub_StrLen& rCommandPos,
1638 							double& rVal, const LocaleDataWrapper* const pLclData )
1639 {
1640     const SvtSysLocale aSysLocale;
1641     return lcl_Str2Double( rCommand, rCommandPos, rVal,
1642             pLclData ? pLclData : aSysLocale.GetLocaleDataPtr() );
1643 }
1644 
Str2Double(const String & rCommand,xub_StrLen & rCommandPos,double & rVal,SwDoc * const pDoc)1645 bool SwCalc::Str2Double( const String& rCommand, xub_StrLen& rCommandPos,
1646 							double& rVal, SwDoc* const pDoc )
1647 {
1648     const SvtSysLocale aSysLocale;
1649     ::std::auto_ptr<const LocaleDataWrapper> pLclD;
1650 	if( pDoc )
1651 	{
1652 		LanguageType eLang = GetDocAppScriptLang( *pDoc );
1653         if (eLang !=
1654                 SvxLocaleToLanguage(aSysLocale.GetLocaleData().getLocale()))
1655         {
1656             pLclD.reset( new LocaleDataWrapper(
1657 							::comphelper::getProcessServiceFactory(),
1658                             SvxCreateLocale( eLang ) ) );
1659         }
1660 	}
1661 
1662     bool const bRet = lcl_Str2Double( rCommand, rCommandPos, rVal,
1663             (pLclD.get()) ? pLclD.get() : aSysLocale.GetLocaleDataPtr() );
1664 
1665 	return bRet;
1666 }
1667 
1668 //------------------------------------------------------------------------------
1669 
IsValidVarName(const String & rStr,String * pValidName)1670 sal_Bool SwCalc::IsValidVarName( const String& rStr,
1671 									String* pValidName )
1672 {
1673 	sal_Bool bRet = sal_False;
1674     using namespace ::com::sun::star::i18n;
1675 	{
1676 		// Parse any token.
1677 		ParseResult aRes = GetAppCharClass().parseAnyToken( rStr, 0,
1678 													coStartFlags, aEmptyStr,
1679 								 					coContFlags, aEmptyStr );
1680 
1681 		if( aRes.TokenType & KParseType::IDENTNAME )
1682 		{
1683 			bRet = aRes.EndPos == rStr.Len();
1684 			if( pValidName )
1685 			{
1686 				xub_StrLen nRealStt = (xub_StrLen)aRes.LeadingWhiteSpace;
1687 				*pValidName = rStr.Copy( nRealStt, static_cast<xub_StrLen>(aRes.EndPos) - nRealStt );
1688 			}
1689 		}
1690 		else if( pValidName )
1691 			pValidName->Erase();
1692 	}
1693 	return bRet;
1694 }
1695 
1696 //------------------------------------------------------------------------------
1697 
1698 /******************************************************************************
1699 |*
1700 |*	CTOR DTOR der SwHash classes
1701 |*
1702 |*	Ersterstellung		OK 25.06.93 12:20
1703 |*	Letzte Aenderung	OK 25.06.93 12:20
1704 |*
1705 ******************************************************************************/
1706 
SwHash(const String & rStr)1707 SwHash::SwHash( const String& rStr ) :
1708 	aStr( rStr ),
1709 	pNext( 0 )
1710 {}
1711 
~SwHash()1712 SwHash::~SwHash()
1713 {
1714 	if( pNext )
1715 		delete pNext;
1716 }
1717 
DeleteHashTable(SwHash ** ppHashTable,sal_uInt16 nCount)1718 void DeleteHashTable( SwHash **ppHashTable, sal_uInt16 nCount )
1719 {
1720 	for ( sal_uInt16 i = 0; i < nCount; ++i )
1721 		delete *(ppHashTable+i);
1722 	delete [] ppHashTable;
1723 }
1724 
SwCalcExp(const String & rStr,const SwSbxValue & rVal,const SwFieldType * pType)1725 SwCalcExp::SwCalcExp( const String& rStr, const SwSbxValue& rVal,
1726 					const SwFieldType* pType )
1727 	: SwHash( rStr ),
1728 	nValue( rVal ),
1729 	pFldType( pType )
1730 {
1731 }
1732 
1733 
~SwSbxValue()1734 SwSbxValue::~SwSbxValue()
1735 {
1736 }
1737 
GetBool() const1738 sal_Bool SwSbxValue::GetBool() const
1739 {
1740     return SbxSTRING == GetType() ? 0 != GetString().Len()
1741 								  : 0 != SbxValue::GetBool();
1742 }
1743 
GetDouble() const1744 double SwSbxValue::GetDouble() const
1745 {
1746 	double nRet;
1747 	if( SbxSTRING == GetType() )
1748 	{
1749 		xub_StrLen nStt = 0;
1750         SwCalc::Str2Double( GetString(), nStt, nRet );
1751 	}
1752     else if (IsBool())
1753     {
1754         nRet = 0 != GetBool() ? 1.0 : 0.0;
1755     }
1756     else
1757 		nRet = SbxValue::GetDouble();
1758 	return nRet;
1759 }
1760 
MakeDouble()1761 SwSbxValue& SwSbxValue::MakeDouble()
1762 {
1763 	if( SbxSTRING == GetType() || SbxBOOL == GetType() )
1764 		PutDouble( GetDouble() );
1765 	return *this;
1766 }
1767 
1768 #ifdef STANDALONE_HASHCALC
1769 
1770 // dies ist der Beispielcode zu erzeugen der HashValues im CTOR:
1771 
1772 #include <stdio.h>
1773 
main()1774 void main()
1775 {
1776 static sal_Char
1777 	sNType0[] = "false", 	sNType1[] = "true",		sNType2[] = "pi",
1778 	sNType3[] = "e",		sNType4[] = "tables",	sNType5[] = "graf",
1779 	sNType6[] = "ole",		sNType7[] = "page",		sNType8[] = "para",
1780 	sNType9[] = "word",		sNType10[]= "char",
1781 	sNType11[] = "user_company" ,		sNType12[] = "user_firstname" ,
1782 	sNType13[] = "user_lastname" ,		sNType14[] = "user_initials",
1783 	sNType15[] = "user_street" ,        sNType16[] = "user_country" ,
1784 	sNType17[] = "user_zipcode" ,       sNType18[] = "user_city" ,
1785 	sNType19[] = "user_title" ,         sNType20[] = "user_position" ,
1786 	sNType21[] = "user_tel_home",	    sNType22[] = "user_tel_work",
1787 	sNType23[] = "user_fax" ,           sNType24[] = "user_email" ,
1788 	sNType25[] = "user_state",			sNType26[] = "graph"
1789 	;
1790 
1791 static const sal_Char* sNTypeTab[ 27 ] =
1792 {
1793 	sNType0, sNType1, sNType2, sNType3, sNType4, sNType5,
1794 	sNType6, sNType7, sNType8, sNType9, sNType10, sNType11,
1795 	sNType12, sNType13, sNType14, sNType15, sNType16, sNType17,
1796 	sNType18, sNType19, sNType20, sNType21, sNType22, sNType23,
1797 	sNType24, sNType25, sNType26
1798 };
1799 
1800 	const unsigned short nTblSize = 47;
1801 	int aArr[ nTblSize ] = { 0 };
1802 	sal_Char ch;
1803 
1804 	for( int n = 0; n < 27; ++n )
1805 	{
1806 		unsigned long ii = 0;
1807 		const sal_Char* pp = sNTypeTab[ n ];
1808 
1809 		while( *pp )
1810 			ii = ii << 1 ^ *pp++;
1811 		ii %= nTblSize;
1812 
1813 		ch = aArr[ ii ] ? 'X' : ' ';
1814 		aArr[ ii ] = 1;
1815 		printf( "%-20s -> %3d [%c]\n", sNTypeTab[ n ], ii, ch );
1816 	}
1817 }
1818 
1819 #endif
1820 
1821 
1822 
1823