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_sc.hxx" 26 27 28 #include "parclass.hxx" 29 #include "token.hxx" 30 #include "global.hxx" 31 #include "callform.hxx" 32 #include "addincol.hxx" 33 #include "funcdesc.hxx" 34 #include <unotools/charclass.hxx> 35 #include <tools/debug.hxx> 36 #include <string.h> 37 38 #if OSL_DEBUG_LEVEL > 1 39 // the documentation thingy 40 #include <stdio.h> 41 #include <com/sun/star/sheet/FormulaLanguage.hpp> 42 #include "compiler.hxx" 43 #include "sc.hrc" // VAR_ARGS 44 #endif 45 46 47 /* Following assumptions are made: 48 * - OpCodes not specified at all will have at least one and only parameters of 49 * type Value, no check is done on the count of parameters => no Bounds type 50 * is returned. 51 * - For OpCodes with a variable number of parameters the type(s) of the last 52 * repeated parameter(s) specified determine(s) the type(s) of all following 53 * parameters. 54 */ 55 56 const ScParameterClassification::RawData ScParameterClassification::pRawData[] = 57 { 58 // { OpCode, {{ Type, ... }, nRepeatLast }}, 59 60 // IF() and CHOOSE() are somewhat special, since the ScJumpMatrix is 61 // created inside those functions and ConvertMatrixParameters() is not 62 // called for them. 63 { ocIf, {{ Array, Reference, Reference }, 0 }}, 64 { ocChose, {{ Array, Reference }, 1 }}, 65 // Other specials. 66 { ocOpen, {{ Bounds }, 0 }}, 67 { ocClose, {{ Bounds }, 0 }}, 68 { ocSep, {{ Bounds }, 0 }}, 69 { ocNoName, {{ Bounds }, 0 }}, 70 { ocErrCell, {{ Bounds }, 0 }}, 71 { ocStop, {{ Bounds }, 0 }}, 72 { ocUnion, {{ Reference, Reference }, 0 }}, 73 { ocRange, {{ Reference, Reference }, 0 }}, 74 // Functions with Value parameters only but not in resource. 75 { ocBackSolver, {{ Value, Value, Value }, 0 }}, 76 { ocTableOp, {{ Value, Value, Value, Value, Value }, 0 }}, 77 // Operators and functions. 78 { ocAdd, {{ Array, Array }, 0 }}, 79 { ocAmpersand, {{ Array, Array }, 0 }}, 80 { ocAnd, {{ Reference }, 1 }}, 81 { ocAreas, {{ Reference }, 0 }}, 82 { ocAveDev, {{ Reference }, 1 }}, 83 { ocAverage, {{ Reference }, 1 }}, 84 { ocAverageA, {{ Reference }, 1 }}, 85 { ocAverageIf, {{ Reference, Value, Reference }, 0 }}, 86 { ocAverageIfs, {{ Reference, Reference, Value }, 2 }}, 87 { ocCell, {{ Value, Reference }, 0 }}, 88 { ocColumn, {{ Reference }, 0 }}, 89 { ocColumns, {{ Reference }, 1 }}, 90 { ocCorrel, {{ ForceArray, ForceArray }, 0 }}, 91 { ocCount, {{ Reference }, 1 }}, 92 { ocCount2, {{ Reference }, 1 }}, 93 { ocCountEmptyCells, {{ Reference }, 0 }}, 94 { ocCountIf, {{ Reference, Value }, 0 }}, 95 { ocCountIfs, {{ Reference, Value }, 2 }}, 96 { ocCovar, {{ ForceArray, ForceArray }, 0 }}, 97 { ocDBAverage, {{ Reference, Reference, Reference }, 0 }}, 98 { ocDBCount, {{ Reference, Reference, Reference }, 0 }}, 99 { ocDBCount2, {{ Reference, Reference, Reference }, 0 }}, 100 { ocDBGet, {{ Reference, Reference, Reference }, 0 }}, 101 { ocDBMax, {{ Reference, Reference, Reference }, 0 }}, 102 { ocDBMin, {{ Reference, Reference, Reference }, 0 }}, 103 { ocDBProduct, {{ Reference, Reference, Reference }, 0 }}, 104 { ocDBStdDev, {{ Reference, Reference, Reference }, 0 }}, 105 { ocDBStdDevP, {{ Reference, Reference, Reference }, 0 }}, 106 { ocDBSum, {{ Reference, Reference, Reference }, 0 }}, 107 { ocDBVar, {{ Reference, Reference, Reference }, 0 }}, 108 { ocDBVarP, {{ Reference, Reference, Reference }, 0 }}, 109 { ocDevSq, {{ Reference }, 1 }}, 110 { ocDiv, {{ Array, Array }, 0 }}, 111 { ocEqual, {{ Array, Array }, 0 }}, 112 { ocForecast, {{ Value, ForceArray, ForceArray }, 0 }}, 113 { ocFrequency, {{ Reference, Reference }, 0 }}, 114 { ocFTest, {{ ForceArray, ForceArray }, 0 }}, 115 { ocGeoMean, {{ Reference }, 1 }}, 116 { ocGCD, {{ Reference }, 1 }}, 117 { ocGreater, {{ Array, Array }, 0 }}, 118 { ocGreaterEqual, {{ Array, Array }, 0 }}, 119 { ocGrowth, {{ Reference, Reference, Reference, Value }, 0 }}, 120 { ocHarMean, {{ Reference }, 1 }}, 121 { ocHLookup, {{ Value, Reference, Value, Value }, 0 }}, 122 { ocIRR, {{ Reference, Value }, 0 }}, 123 { ocIndex, {{ Reference, Value, Value, Value }, 0 }}, 124 { ocIntercept, {{ ForceArray, ForceArray }, 0 }}, 125 { ocIntersect, {{ Reference, Reference }, 0 }}, 126 { ocIsRef, {{ Reference }, 0 }}, 127 { ocLCM, {{ Reference }, 1 }}, 128 { ocKurt, {{ Reference }, 1 }}, 129 { ocLarge, {{ Reference, Value }, 0 }}, 130 { ocLess, {{ Array, Array }, 0 }}, 131 { ocLessEqual, {{ Array, Array }, 0 }}, 132 { ocLookup, {{ Value, ReferenceOrForceArray, ReferenceOrForceArray }, 0 }}, 133 { ocMatch, {{ Value, Reference, Reference }, 0 }}, 134 { ocMatDet, {{ ForceArray }, 0 }}, 135 { ocMatInv, {{ ForceArray }, 0 }}, 136 { ocMatMult, {{ ForceArray, ForceArray }, 0 }}, 137 { ocMatTrans, {{ Array }, 0 }}, // strange, but Xcl doesn't force MatTrans array 138 { ocMatValue, {{ Reference, Value, Value }, 0 }}, 139 { ocMax, {{ Reference }, 1 }}, 140 { ocMaxA, {{ Reference }, 1 }}, 141 { ocMedian, {{ Reference }, 1 }}, 142 { ocMin, {{ Reference }, 1 }}, 143 { ocMinA, {{ Reference }, 1 }}, 144 { ocMIRR, {{ Reference, Value, Value }, 0 }}, 145 { ocModalValue, {{ ForceArray }, 1 }}, 146 { ocMul, {{ Array, Array }, 0 }}, 147 { ocMultiArea, {{ Reference }, 1 }}, 148 { ocN, {{ Reference }, 0 }}, 149 { ocNPV, {{ Value, Reference }, 1 }}, 150 { ocNeg, {{ Array }, 0 }}, 151 { ocNegSub, {{ Array }, 0 }}, 152 { ocNot, {{ Array }, 0 }}, 153 { ocNotEqual, {{ Array, Array }, 0 }}, 154 { ocOffset, {{ Reference, Value, Value, Value, Value }, 0 }}, 155 { ocOr, {{ Reference }, 1 }}, 156 { ocPearson, {{ ForceArray, ForceArray }, 0 }}, 157 { ocPercentile, {{ Reference, Value }, 0 }}, 158 { ocPercentrank, {{ Reference, Value }, 0 }}, 159 { ocPow, {{ Array, Array }, 0 }}, 160 { ocPower, {{ Array, Array }, 0 }}, 161 { ocProb, {{ ForceArray, ForceArray, Value, Value }, 0 }}, 162 { ocProduct, {{ Reference }, 1 }}, 163 { ocQuartile, {{ Reference, Value }, 0 }}, 164 { ocRank, {{ Value, Reference, Value }, 0 }}, 165 { ocRGP, {{ Reference, Reference, Value, Value }, 0 }}, 166 { ocRKP, {{ Reference, Reference, Value, Value }, 0 }}, 167 { ocRow, {{ Reference }, 0 }}, 168 { ocRows, {{ Reference }, 1 }}, 169 { ocRSQ, {{ ForceArray, ForceArray }, 0 }}, 170 { ocSchiefe, {{ Reference }, 1 }}, 171 { ocSlope, {{ ForceArray, ForceArray }, 0 }}, 172 { ocSmall, {{ Reference, Value }, 0 }}, 173 { ocStDev, {{ Reference }, 1 }}, 174 { ocStDevA, {{ Reference }, 1 }}, 175 { ocStDevP, {{ Reference }, 1 }}, 176 { ocStDevPA, {{ Reference }, 1 }}, 177 { ocSTEYX, {{ ForceArray, ForceArray }, 0 }}, 178 { ocSub, {{ Array, Array }, 0 }}, 179 { ocSubTotal, {{ Value, Reference }, 1 }}, 180 { ocSum, {{ Reference }, 1 }}, 181 { ocSumIf, {{ Reference, Value, Reference }, 0 }}, 182 { ocSumIfs, {{ Reference, Reference, Value }, 2 }}, 183 { ocSumProduct, {{ ForceArray }, 1 }}, 184 { ocSumSQ, {{ Reference }, 1 }}, 185 { ocSumX2MY2, {{ ForceArray, ForceArray }, 0 }}, 186 { ocSumX2DY2, {{ ForceArray, ForceArray }, 0 }}, 187 { ocSumXMY2, {{ ForceArray, ForceArray }, 0 }}, 188 { ocTable, {{ Reference }, 0 }}, 189 { ocTables, {{ Reference }, 1 }}, 190 { ocTrend, {{ Reference, Reference, Reference, Value }, 0 }}, 191 { ocTrimMean, {{ Reference, Value }, 0 }}, 192 { ocTTest, {{ ForceArray, ForceArray, Value, Value }, 0 }}, 193 { ocVar, {{ Reference }, 1 }}, 194 { ocVarA, {{ Reference }, 1 }}, 195 { ocVarP, {{ Reference }, 1 }}, 196 { ocVarPA, {{ Reference }, 1 }}, 197 { ocVLookup, {{ Value, Reference, Value, Value }, 0 }}, 198 { ocXor, {{ Reference }, 1 }}, 199 { ocZTest, {{ Reference, Value, Value }, 0 }}, 200 // Excel doubts: 201 // ocT: Excel says (and handles) Reference, error? This means no position 202 // dependent SingleRef if DoubleRef, and no array calculation, just the 203 // upper left corner. We never did that. 204 { ocT, {{ Value }, 0 }}, 205 // The stopper. 206 { ocNone, {{ Bounds }, 0 } } 207 }; 208 209 ScParameterClassification::RunData * ScParameterClassification::pData = NULL; 210 211 212 void ScParameterClassification::Init() 213 { 214 if ( pData ) 215 return; 216 pData = new RunData[ SC_OPCODE_LAST_OPCODE_ID + 1 ]; 217 memset( pData, 0, sizeof(RunData) * (SC_OPCODE_LAST_OPCODE_ID + 1)); 218 219 // init from specified static data above 220 for ( size_t i=0; i < sizeof(pRawData) / sizeof(RawData); ++i ) 221 { 222 const RawData* pRaw = &pRawData[i]; 223 if ( pRaw->eOp > SC_OPCODE_LAST_OPCODE_ID ) 224 { 225 DBG_ASSERT( pRaw->eOp == ocNone, "RawData OpCode error"); 226 } 227 else 228 { 229 RunData* pRun = &pData[ pRaw->eOp ]; 230 #ifdef DBG_UTIL 231 if ( pRun->aData.nParam[0] != Unknown ) 232 { 233 DBG_ERROR1( "already assigned: %d", pRaw->eOp); 234 } 235 #endif 236 memcpy( &(pRun->aData), &(pRaw->aData), sizeof(CommonData)); 237 // fill 0-initialized fields with real values 238 if ( pRun->aData.nRepeatLast ) 239 { 240 for ( size_t j=0; j < CommonData::nMaxParams; ++j ) 241 { 242 if ( pRun->aData.nParam[j] ) 243 pRun->nMinParams = sal::static_int_cast<sal_uInt8>( j+1 ); 244 else if (j >= pRun->aData.nRepeatLast) 245 pRun->aData.nParam[j] = pRun->aData.nParam[j - pRun->aData.nRepeatLast]; 246 else 247 { 248 DBG_ERROR2( "bad classification: eOp %d, repeated param %d negative offset", pRaw->eOp, j); 249 pRun->aData.nParam[j] = Unknown; 250 } 251 } 252 } 253 else 254 { 255 for ( size_t j=0; j < CommonData::nMaxParams; ++j ) 256 { 257 if ( !pRun->aData.nParam[j] ) 258 { 259 if ( j == 0 || pRun->aData.nParam[j-1] != Bounds ) 260 pRun->nMinParams = sal::static_int_cast<sal_uInt8>( j ); 261 pRun->aData.nParam[j] = Bounds; 262 } 263 } 264 if ( !pRun->nMinParams && 265 pRun->aData.nParam[CommonData::nMaxParams-1] != Bounds) 266 pRun->nMinParams = CommonData::nMaxParams; 267 } 268 for ( size_t j=0; j < CommonData::nMaxParams; ++j ) 269 { 270 if ( pRun->aData.nParam[j] == ForceArray || pRun->aData.nParam[j] == ReferenceOrForceArray ) 271 { 272 pRun->bHasForceArray = true; 273 break; // for 274 } 275 } 276 } 277 } 278 279 #if OSL_DEBUG_LEVEL > 1 280 GenerateDocumentation(); 281 #endif 282 } 283 284 285 void ScParameterClassification::Exit() 286 { 287 delete [] pData; 288 pData = NULL; 289 } 290 291 292 ScParameterClassification::Type ScParameterClassification::GetParameterType( 293 const formula::FormulaToken* pToken, sal_uInt16 nParameter) 294 { 295 OpCode eOp = pToken->GetOpCode(); 296 switch ( eOp ) 297 { 298 case ocExternal: 299 return GetExternalParameterType( pToken, nParameter); 300 //break; 301 case ocMacro: 302 return Reference; 303 //break; 304 default: 305 { 306 // added to avoid warnings 307 } 308 } 309 if ( 0 <= (short)eOp && eOp <= SC_OPCODE_LAST_OPCODE_ID ) 310 { 311 sal_uInt8 nRepeat; 312 Type eType; 313 if ( nParameter < CommonData::nMaxParams ) 314 eType = pData[eOp].aData.nParam[nParameter]; 315 else if ( (nRepeat = pData[eOp].aData.nRepeatLast) > 0 ) 316 { 317 // The usual case is 1 repeated parameter, we don't need to 318 // calculate that on each call. 319 sal_uInt16 nParam = (nRepeat > 1 ? 320 (pData[eOp].nMinParams - 321 ((nParameter - pData[eOp].nMinParams) % nRepeat)) : 322 pData[eOp].nMinParams); 323 return pData[eOp].aData.nParam[nParam]; 324 } 325 else 326 eType = Bounds; 327 return eType == Unknown ? Value : eType; 328 } 329 return Unknown; 330 } 331 332 333 ScParameterClassification::Type 334 ScParameterClassification::GetExternalParameterType( const formula::FormulaToken* pToken, 335 sal_uInt16 nParameter) 336 { 337 Type eRet = Unknown; 338 // similar to ScInterpreter::ScExternal() 339 sal_uInt16 nIndex; 340 String aUnoName; 341 String aFuncName( ScGlobal::pCharClass->upper( pToken->GetExternal())); 342 if ( ScGlobal::GetFuncCollection()->SearchFunc( aFuncName, nIndex) ) 343 { 344 FuncData* pFuncData = (FuncData*)ScGlobal::GetFuncCollection()->At( 345 nIndex); 346 if ( nParameter >= pFuncData->GetParamCount() ) 347 eRet = Bounds; 348 else 349 { 350 switch ( pFuncData->GetParamType( nParameter) ) 351 { 352 case PTR_DOUBLE: 353 case PTR_STRING: 354 eRet = Value; 355 break; 356 default: 357 eRet = Reference; 358 // also array types are created using an area reference 359 } 360 } 361 } 362 else if ( (aUnoName = ScGlobal::GetAddInCollection()->FindFunction( 363 aFuncName, sal_False)).Len() ) 364 { 365 // the relevant parts of ScUnoAddInCall without having to create one 366 const ScUnoAddInFuncData* pFuncData = 367 ScGlobal::GetAddInCollection()->GetFuncData( aUnoName, true ); // need fully initialized data 368 if ( pFuncData ) 369 { 370 long nCount = pFuncData->GetArgumentCount(); 371 if ( nCount <= 0 ) 372 eRet = Bounds; 373 else 374 { 375 const ScAddInArgDesc* pArgs = pFuncData->GetArguments(); 376 if ( nParameter >= nCount && 377 pArgs[nCount-1].eType == SC_ADDINARG_VARARGS ) 378 eRet = Value; 379 // last arg is sequence, optional "any"s, we simply can't 380 // determine the type 381 if ( eRet == Unknown ) 382 { 383 if ( nParameter >= nCount ) 384 eRet = Bounds; 385 else 386 { 387 switch ( pArgs[nParameter].eType ) 388 { 389 case SC_ADDINARG_INTEGER: 390 case SC_ADDINARG_DOUBLE: 391 case SC_ADDINARG_STRING: 392 eRet = Value; 393 break; 394 default: 395 eRet = Reference; 396 } 397 } 398 } 399 } 400 } 401 } 402 return eRet; 403 } 404 405 //----------------------------------------------------------------------------- 406 407 #if OSL_DEBUG_LEVEL > 1 408 409 // add remaining functions, all Value parameters 410 void ScParameterClassification::MergeArgumentsFromFunctionResource() 411 { 412 ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); 413 for ( const ScFuncDesc* pDesc = pFuncList->First(); pDesc; 414 pDesc = pFuncList->Next() ) 415 { 416 if ( pDesc->nFIndex > SC_OPCODE_LAST_OPCODE_ID || 417 pData[pDesc->nFIndex].aData.nParam[0] != Unknown ) 418 continue; // not an internal opcode or already done 419 420 RunData* pRun = &pData[ pDesc->nFIndex ]; 421 sal_uInt16 nArgs = pDesc->GetSuppressedArgCount(); 422 if ( nArgs >= PAIRED_VAR_ARGS ) 423 { 424 nArgs -= PAIRED_VAR_ARGS - 2; 425 pRun->aData.nRepeatLast = 2; 426 } 427 else if ( nArgs >= VAR_ARGS ) 428 { 429 nArgs -= VAR_ARGS - 1; 430 pRun->aData.nRepeatLast = 1; 431 } 432 if ( nArgs > CommonData::nMaxParams ) 433 { 434 DBG_ERROR2( "ScParameterClassification::Init: too many arguments in listed function: %s: %d", 435 ByteString( *(pDesc->pFuncName), 436 RTL_TEXTENCODING_UTF8).GetBuffer(), nArgs); 437 nArgs = CommonData::nMaxParams - 1; 438 pRun->aData.nRepeatLast = 1; 439 } 440 pRun->nMinParams = static_cast< sal_uInt8 >( nArgs ); 441 for ( size_t j=0; j < nArgs; ++j ) 442 { 443 pRun->aData.nParam[j] = Value; 444 } 445 if ( pRun->aData.nRepeatLast ) 446 { 447 for ( size_t j = nArgs; j < CommonData::nMaxParams; ++j ) 448 { 449 pRun->aData.nParam[j] = Value; 450 } 451 } 452 else 453 { 454 for ( size_t j = nArgs; j < CommonData::nMaxParams; ++j ) 455 { 456 pRun->aData.nParam[j] = Bounds; 457 } 458 } 459 } 460 } 461 462 463 void ScParameterClassification::GenerateDocumentation() 464 { 465 static const sal_Char aEnvVarName[] = "OOO_CALC_GENPARCLASSDOC"; 466 if ( !getenv( aEnvVarName) ) 467 return; 468 MergeArgumentsFromFunctionResource(); 469 ScAddress aAddress; 470 ScCompiler aComp(NULL,aAddress); 471 ScCompiler::OpCodeMapPtr xMap( aComp.GetOpCodeMap(::com::sun::star::sheet::FormulaLanguage::ENGLISH)); 472 if (!xMap) 473 return; 474 fflush( stderr); 475 size_t nCount = xMap->getSymbolCount(); 476 for ( size_t i=0; i<nCount; ++i ) 477 { 478 OpCode eOp = OpCode(i); 479 if ( xMap->getSymbol(eOp).Len() ) 480 { 481 fprintf( stdout, "%s: ", aEnvVarName); 482 ByteString aStr( xMap->getSymbol(eOp), RTL_TEXTENCODING_UTF8); 483 aStr += "("; 484 formula::FormulaByteToken aToken( eOp); 485 sal_uInt8 nParams = GetMinimumParameters( eOp); 486 // preset parameter count according to opcode value, with some 487 // special handling 488 if ( eOp < SC_OPCODE_STOP_DIV ) 489 { 490 switch ( eOp ) 491 { 492 case ocIf: 493 aToken.SetByte(3); 494 break; 495 case ocChose: 496 aToken.SetByte(2); 497 break; 498 case ocPercentSign: 499 aToken.SetByte(1); 500 break; 501 default:; 502 } 503 } 504 else if ( eOp < SC_OPCODE_STOP_ERRORS ) 505 aToken.SetByte(0); 506 else if ( eOp < SC_OPCODE_STOP_BIN_OP ) 507 { 508 switch ( eOp ) 509 { 510 case ocAnd: 511 case ocOr: 512 aToken.SetByte(1); // (r1)AND(r2) --> AND( r1, ...) 513 break; 514 default: 515 aToken.SetByte(2); 516 } 517 } 518 else if ( eOp < SC_OPCODE_STOP_UN_OP ) 519 aToken.SetByte(1); 520 else if ( eOp < SC_OPCODE_STOP_NO_PAR ) 521 aToken.SetByte(0); 522 else if ( eOp < SC_OPCODE_STOP_1_PAR ) 523 aToken.SetByte(1); 524 else 525 aToken.SetByte( nParams); 526 // compare (this is a mere test for opcode order Div, BinOp, UnOp, 527 // NoPar, 1Par, ...) and override parameter count with 528 // classification 529 if ( nParams != aToken.GetByte() ) 530 fprintf( stdout, "(parameter count differs, token Byte: %d classification: %d) ", 531 aToken.GetByte(), nParams); 532 aToken.SetByte( nParams); 533 if ( nParams != aToken.GetParamCount() ) 534 fprintf( stdout, "(parameter count differs, token ParamCount: %d classification: %d) ", 535 aToken.GetParamCount(), nParams); 536 for ( sal_uInt16 j=0; j < nParams; ++j ) 537 { 538 if ( j > 0 ) 539 aStr += ","; 540 Type eType = GetParameterType( &aToken, j); 541 switch ( eType ) 542 { 543 case Value : 544 aStr += " Value"; 545 break; 546 case Reference : 547 aStr += " Reference"; 548 break; 549 case Array : 550 aStr += " Array"; 551 break; 552 case ForceArray : 553 aStr += " ForceArray"; 554 break; 555 case ReferenceOrForceArray : 556 aStr += " ReferenceOrForceArray"; 557 break; 558 case Bounds : 559 aStr += " (Bounds, classification error?)"; 560 break; 561 default: 562 aStr += " (???, classification error?)"; 563 } 564 } 565 if ( HasRepeatParameters( eOp) ) 566 aStr += ", ..."; 567 if ( nParams ) 568 aStr += " "; 569 aStr += ")"; 570 switch ( eOp ) 571 { 572 case ocZGZ: 573 aStr += " // RRI in English resource, but ZGZ in English-only section"; 574 break; 575 case ocMultiArea: 576 aStr += " // e.g. combined first parameter of INDEX() function, not a real function"; 577 break; 578 case ocBackSolver: 579 aStr += " // goal seek via menu, not a real function"; 580 break; 581 case ocTableOp: 582 aStr += " // MULTIPLE.OPERATIONS in English resource, but TABLE in English-only section"; 583 break; 584 case ocNoName: 585 aStr += " // error function, not a real function"; 586 break; 587 default:; 588 } 589 fprintf( stdout, "%s\n", aStr.GetBuffer()); 590 } 591 } 592 fflush( stdout); 593 } 594 595 #endif // OSL_DEBUG_LEVEL 596 597