Coverage Report

Created: 2026-06-13 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libstaroffice/src/lib/StarCellFormula.cxx
Line
Count
Source
1
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3
/* libstaroffice
4
* Version: MPL 2.0 / LGPLv2+
5
*
6
* The contents of this file are subject to the Mozilla Public License Version
7
* 2.0 (the "License"); you may not use this file except in compliance with
8
* the License or as specified alternatively below. You may obtain a copy of
9
* the License at http://www.mozilla.org/MPL/
10
*
11
* Software distributed under the License is distributed on an "AS IS" basis,
12
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
* for the specific language governing rights and limitations under the
14
* License.
15
*
16
* Major Contributor(s):
17
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20
* Copyright (C) 2006, 2007 Andrew Ziem
21
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22
*
23
*
24
* All Rights Reserved.
25
*
26
* For minor contributions see the git repository.
27
*
28
* Alternatively, the contents of this file may be used under the terms of
29
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30
* in which case the provisions of the LGPLv2+ are applicable
31
* instead of those above.
32
*/
33
34
#include <cstring>
35
#include <iomanip>
36
#include <iostream>
37
#include <limits>
38
#include <sstream>
39
40
#include <librevenge/librevenge.h>
41
42
#include "STOFFOLEParser.hxx"
43
44
#include "StarEncoding.hxx"
45
#include "StarZone.hxx"
46
47
#include "StarCellFormula.hxx"
48
49
//! namespace used to define StarCellFormula structures
50
namespace StarCellFormulaInternal
51
{
52
//! a structure used to store a token
53
struct Token {
54
  //! the different type
55
  enum Type { Function, Long, Double, String, String2, External, Cell, CellList, Index, Jump, Empty, Missing, Error, Unknown };
56
  //! the content type
57
  enum Content { C_Data, C_FunctionOperator };
58
  //! constructor
59
  Token()
60
259k
    : m_type(Unknown)
61
259k
    , m_content(C_Data)
62
259k
    , m_operation(0)
63
259k
    , m_longValue(0)
64
259k
    , m_doubleValue(0)
65
259k
    , m_textValue("")
66
259k
    , m_index(0)
67
259k
    , m_jumpPositions()
68
259k
    , m_instruction()
69
259k
    , m_extra("")
70
259k
  {
71
259k
  }
72
  //! operator<<
73
  friend std::ostream &operator<<(std::ostream &o, Token const &tok)
74
0
  {
75
0
    switch (tok.m_type) {
76
0
    case Long:
77
0
      o << tok.m_longValue << ",";
78
0
      break;
79
0
    case Double:
80
0
      o << tok.m_doubleValue << ",";
81
0
      break;
82
0
    case String:
83
0
    case String2:
84
0
    case External:
85
0
      if (tok.m_type==String2) o << "string2,";
86
0
      else if (tok.m_type==External) o << "external,";
87
0
      o << tok.m_textValue.cstr() << ",";
88
0
      break;
89
0
    case Cell:
90
0
      o << "cells=" << tok.m_positions[0] << "[" << tok.m_relPositions[0] << "],";
91
0
      break;
92
0
    case CellList:
93
0
      o << "cells=" << tok.m_positions[0] << "[" << tok.m_relPositions[0] << "]"
94
0
        << "<->" << tok.m_positions[1] << "[" << tok.m_relPositions[1] << "],";
95
0
      break;
96
0
    case Index:
97
0
      o << "index=" << tok.m_longValue << ",";
98
0
      break;
99
0
    case Function:
100
0
      o << tok.m_instruction << ",";
101
0
      break;
102
0
    case Jump: {
103
0
      o << tok.m_instruction << ",";
104
0
      o << "jump=[";
105
0
      for (int jumpPosition : tok.m_jumpPositions)
106
0
        o << jumpPosition << ",";
107
0
      o << "],";
108
0
      break;
109
0
    }
110
0
    case Missing:
111
0
      o << "#missing,";
112
0
      break;
113
0
    case Error:
114
0
      o << "#error,";
115
0
      break;
116
0
    case Empty:
117
0
      break;
118
0
    case Unknown:
119
0
    default:
120
0
      o << "###unknown,";
121
0
      break;
122
0
    }
123
0
    o << tok.m_extra;
124
0
    return o;
125
0
  }
126
  //! return a instruction corresponding to a token
127
  bool get(STOFFCellContent::FormulaInstruction &instr, bool &ignore);
128
  //! try to update the function/operator
129
  bool updateFunction();
130
  //! a static function to recompile a formula from Polish notation
131
  static bool addToken(std::vector<std::vector<Token> > &stack, Token const &token);
132
  //! the type
133
  Type m_type;
134
  //! the content type
135
  Content m_content;
136
  //! the operation
137
  unsigned m_operation;
138
  //! the long value
139
  long m_longValue;
140
  //! the double value
141
  double m_doubleValue;
142
  //! the string value
143
  librevenge::RVNGString m_textValue;
144
  //! the cells positions: col, row, tab
145
  STOFFVec3i m_positions[2];
146
  //! the cells relative positions
147
  STOFFVec3b m_relPositions[2];
148
  //! the index
149
  int m_index;
150
  //! the jump position(for if, choose, ...)
151
  std::vector<int> m_jumpPositions;
152
  //! the final instruction
153
  STOFFCellContent::FormulaInstruction m_instruction;
154
  //! extra data
155
  std::string m_extra;
156
};
157
158
bool Token::get(STOFFCellContent::FormulaInstruction &instr, bool &ignore)
159
236k
{
160
236k
  ignore=false;
161
236k
  switch (m_type) {
162
4
  case Jump:
163
148k
  case Function:
164
148k
    instr=m_instruction;
165
148k
    ignore=instr.m_content.empty();
166
148k
    return true;
167
372
  case Long:
168
372
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Long;
169
372
    instr.m_longValue=m_longValue;
170
372
    return true;
171
1.81k
  case Double:
172
1.81k
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Double;
173
1.81k
    instr.m_doubleValue=m_doubleValue;
174
1.81k
    return true;
175
15
  case String:
176
239
  case String2:
177
243
  case External:
178
243
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Text;
179
243
    instr.m_content=m_textValue;
180
243
    return true;
181
72.7k
  case Cell:
182
85.3k
  case CellList:
183
85.3k
    instr.m_type=m_type==Cell ? STOFFCellContent::FormulaInstruction::F_Cell :
184
85.3k
                 STOFFCellContent::FormulaInstruction::F_CellList;
185
85.3k
    instr.m_position[0]=STOFFVec2i(m_positions[0][0],m_positions[0][1]);
186
85.3k
    instr.m_positionRelative[0]=STOFFVec2b(m_relPositions[0][0],m_relPositions[0][1]);
187
85.3k
    instr.m_sheetId=m_positions[0][2];
188
85.3k
    if (m_type==Cell)
189
72.7k
      return true;
190
12.6k
    instr.m_position[1]=STOFFVec2i(m_positions[1][0],m_positions[1][1]);
191
12.6k
    instr.m_positionRelative[1]=STOFFVec2b(m_relPositions[1][0],m_relPositions[1][1]);
192
12.6k
    return true;
193
136
  case Empty:
194
137
  case Missing:
195
138
  case Error:
196
138
    ignore=true;
197
138
    return true;
198
0
  case Unknown:
199
87
  case Index:
200
87
  default:
201
87
    break;
202
236k
  }
203
87
  return false;
204
236k
}
205
206
bool Token::updateFunction()
207
69.3k
{
208
69.3k
  unsigned const &nOp=m_operation;
209
69.3k
  auto &instr=m_instruction;
210
211
  // binary op
212
69.3k
  if (nOp==33 || nOp==34) { // change, ie reconstructor a&&b in AND(a,b), ...
213
191
    m_content=StarCellFormulaInternal::Token::C_FunctionOperator;
214
191
    m_longValue=2;
215
191
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Function;
216
191
    instr.m_content=nOp==33 ? "and" : "or";
217
191
  }
218
69.1k
  else if (nOp>=21 && nOp<=37) {
219
16.7k
    static char const* const wh[]=
220
16.7k
    {"+", "-", "*", "/", "&", "^", "=", "<>", "<", ">", "<=", ">=", "OR", "AND", "!", "~", ":"};
221
16.7k
    m_content=StarCellFormulaInternal::Token::C_FunctionOperator;
222
16.7k
    m_longValue=2;
223
16.7k
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Operator;
224
16.7k
    instr.m_content=wh[nOp-21];
225
16.7k
  }
226
  // unary op
227
52.4k
  else if (nOp==41) { // changeme ie reconstruct ~a in NOT(a)
228
85
    m_content=StarCellFormulaInternal::Token::C_FunctionOperator;
229
85
    m_longValue=1;
230
85
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Function;
231
85
    instr.m_content="Not";
232
85
  }
233
52.3k
  else if (nOp==42 || nOp==43) { // neg and neg_sub
234
152
    m_content=StarCellFormulaInternal::Token::C_FunctionOperator;
235
152
    m_longValue=1;
236
152
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Operator;
237
152
    instr.m_content="-";
238
152
  }
239
  // function no parameter
240
52.1k
  else if (nOp>=46 && nOp<=53) {  // 60 endNoPar
241
406
    static char const* const wh[]= {
242
406
      "Pi", "Random", "True", "False", "Today"/*getActDate*/, "Now"/*getActTime*/,
243
406
      "NA", "Current"
244
406
    };
245
406
    m_content=StarCellFormulaInternal::Token::C_FunctionOperator;
246
406
    m_longValue=0;
247
406
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Function;
248
406
    instr.m_content=wh[nOp-46];
249
406
  }
250
  // one parameter
251
51.7k
  else if (nOp==89) { // CHANGEME: pm operator
252
77
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Text;
253
77
    libstoff::appendUnicode(0xb1, instr.m_content);
254
77
  }
255
51.7k
  else if (nOp>=61 && nOp<=131) { // 200 endOnePar
256
556
    m_content=StarCellFormulaInternal::Token::C_FunctionOperator;
257
556
    m_longValue=1;
258
556
    static char const* const wh[]= {
259
556
      "Degrees", "Radians", "Sin", "Cos", "Tan", "Cot", "Asin", "Acos", "Atan", "ACot", // 70
260
556
      "SinH", "CosH", "TanH", "CotH", "AsinH", "ACosH", "ATanH", "ACosH", // 78
261
556
      "Exp", "Ln", "Sqrt", "Fact", // 82
262
556
      "Year", "Month", "Day", "Hour", "Minute", "Second", "PlusMinus" /* checkme*/, // 89
263
556
      "Abs", "Int", "Phi", "Gauss", "IsBlank", "IsText", "IsNonText", "IsLogical", // 97
264
556
      "Type", "IsRef", "IsNumber",  "IsFormula", "IsNA", "IsErr", "IsError", "IsEven", // 105
265
556
      "IsOdd", "N", // 107
266
556
      "DateValue", "TimeValue", "Code", "Trim", "Upper", "Proper", "Lower", "Len", "T", // 116
267
556
      "Value", "Clean", "Char", "Log10", "Even", "Odd", "NormDist", "Fisher", "FisherInv", // 125
268
556
      "NormSInv", "GammaLn", "ErrorType", "IsErr" /* errCell*/, "Formula", "Arabic"
269
556
    };
270
556
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Function;
271
556
    instr.m_content=wh[nOp-61];
272
556
  }
273
  // multiple
274
51.1k
  else if (nOp>=201 && nOp<=386) {
275
39.3k
    m_content=StarCellFormulaInternal::Token::C_FunctionOperator;
276
39.3k
    static char const* const wh[]= {
277
39.3k
      "Atan2", "Ceil", "Floor", "Round", "RoundUp", "RoundDown", "Trunc", "Log", // 208
278
39.3k
      "Power", "GCD", "LCM", "Mod", "SumProduct", "SumSQ", "SumX2MY2", "SumX2PY2", "SumXMY2", // 217
279
39.3k
      "Date", "Time", "Days", "Days360", "Min", "Max", "Sum", "Product", "Average", "Count", // 227
280
39.3k
      "CountA", "NPV", "IRR", "Var", "VarP", "StDev", "StDevP", "B", "NormDist", "ExpDist", // 237
281
39.3k
      "BinomDist", "Poisson", "Combin", "CombinA", "Permut",  "PermutationA", "PV", "SYD", "DDB", "DB",
282
39.3k
      "VDB", "Duration", "SLN", "PMT", "Columns", "Rows", "Column", "Row", "RRI", "FV", // 257
283
39.3k
      "NPER", "Rate", "IPMT", "PPMT", "CUMIPMT", "CUMPRINC", "Effective", "Nominal", "SubTotal", "DSum", // 267
284
39.3k
      "DCount", "DCountA", "DAverage", "DGet", "DMax", "DMin", "DProduct", "DStDev", "DStDevP", "DVar",
285
39.3k
      "DVarP", "Indirect", "Address", "Match", "CountBlank", "CountIf", "SumIf", "LookUp", "VLookUp", "HLookUp", // 287
286
39.3k
      "MultiRange", "Offset", "Index", "Areas", "Dollar", "Replace", "Fixed", "Find", "Exact", "Left", // 297
287
39.3k
      "Right", "Search", "Mid", "Text", "Substitute", "Rept", "Concatenate", "MValue", "MDeterm", "MInverse",
288
39.3k
      "MMult", "Transpose", "MUnit", "GoalSeek", "HypGeomDist", "HYPGEOM.DIST", "LogNormDist", "TDist", "FDist", "ChiDist", "WeiBull",
289
39.3k
      "NegBinomDist", "CritBinom", "Kurt", "HarMean", "GeoMean", "Standardize", "AveDev", "Skew", "DevSQ", "Median", // 327
290
39.3k
      "Mode", "ZTest", "TTest", "Rank", "Percentile", "PercentRank", "Large", "Small", "Frequency", "Quartile",
291
39.3k
      "NormInv", "Confidence", "FTest", "TrimMean", "Prob", "CorRel", "CoVar", "Pearson", "RSQ", "STEYX", // 347
292
39.3k
      "Slope", "Intercept", "Trend", "Growth", "Linest", "Logest", "Forecast", "ChiInv", "GammaDist", "GammaInv", // 357
293
39.3k
      "TInv", "FInv", "ChiTest", "LogInv", "Multiple.Operations", "BetaDist", "BetaInv", "WeekNum", "WeekDay", "#Name!", // 367
294
39.3k
      "Style", "DDE", "Base", "Sheet", "Sheets", "MinA", "MaxA", "AverageA", "StDevA", "StDevPA", // 377
295
39.3k
      "VarA", "VarPA", "EasterSunday", "Decimal", "Convert", "Roman", "MIRR", "Cell", "IsPMT"
296
39.3k
    };
297
39.3k
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Function;
298
39.3k
    instr.m_content=wh[nOp-201];
299
39.3k
  }
300
11.8k
  else
301
11.8k
    return false;
302
57.5k
  return true;
303
69.3k
}
304
305
bool Token::addToken(std::vector<std::vector<Token> > &stack, Token const &token)
306
6
{
307
6
  std::vector<Token> child;
308
6
  if (token.m_content==C_Data) {
309
6
    child.push_back(token);
310
6
    stack.push_back(child);
311
6
    return true;
312
6
  }
313
0
  bool isOperator=token.m_instruction.m_type==STOFFCellContent::FormulaInstruction::F_Operator;
314
0
  auto nChild=int(token.m_longValue);
315
0
  auto numElt=int(stack.size());
316
0
  int special=0;
317
0
  if (nChild<0) {
318
0
    special=-nChild;
319
0
    nChild=special==2 ? 1 : special;
320
0
  }
321
0
  if (nChild<0 || nChild>numElt || (special>3) || (isOperator && !special && nChild!=1 && nChild!=2)) {
322
0
    STOFF_DEBUG_MSG(("StarCellFormulaInternal::addToken: find unexpected number of child for token %s\n", token.m_instruction.m_content.cstr()));
323
0
    return false;
324
0
  }
325
0
  if (special==2 || (!special && (!isOperator || nChild==1)))
326
0
    child.push_back(token);
327
0
  Token sep;
328
0
  sep.m_type=StarCellFormulaInternal::Token::Function;
329
0
  sep.m_instruction.m_type=STOFFCellContent::FormulaInstruction::F_Operator;
330
0
  sep.m_instruction.m_content="(";
331
  // todo: check if we really need to insert a ( when type is operator, ...
332
0
  if (!special || special==2)
333
0
    child.push_back(sep);
334
0
  for (int c=0; c<nChild; ++c) {
335
0
    if (special)
336
0
      ;
337
0
    else if (c && !isOperator) {
338
0
      sep.m_instruction.m_content=";";
339
0
      child.push_back(sep);
340
0
    }
341
0
    else if (c)
342
0
      child.push_back(token);
343
0
    auto const &node=stack[size_t(numElt-nChild+c)];
344
0
    child.insert(child.end(), node.begin(), node.end());
345
0
  }
346
0
  sep.m_instruction.m_content=")";
347
0
  if (!special)
348
0
    child.push_back(sep);
349
0
  else if (special==2) {
350
0
    sep.m_instruction.m_content=";";
351
0
    child.push_back(sep);
352
0
  }
353
0
  else
354
0
    child.push_back(token);
355
0
  stack.resize(size_t(numElt-nChild+1));
356
0
  stack[size_t(numElt-nChild)] = child;
357
358
0
  return true;
359
0
}
360
361
}
362
////////////////////////////////////////////////////////////
363
// main zone
364
////////////////////////////////////////////////////////////
365
void StarCellFormula::updateFormula(STOFFCellContent &content, std::vector<librevenge::RVNGString> const &sheetNames, int sheetId)
366
54.1k
{
367
54.1k
  auto numNames=int(sheetNames.size());
368
235k
  for (auto &form : content.m_formula) {
369
235k
    if ((form.m_type!=STOFFCellContent::FormulaInstruction::F_Cell &&
370
162k
         form.m_type!=STOFFCellContent::FormulaInstruction::F_CellList) ||
371
85.2k
        form.m_sheetId<0 || form.m_sheetId==sheetId)
372
220k
      continue;
373
14.9k
    if (form.m_sheetId>=numNames) {
374
2.91k
      static bool first=true;
375
2.91k
      if (first) {
376
1
        STOFF_DEBUG_MSG(("StarCellFormula::updateFormula: some sheetId are bad\n"));
377
1
        first=false;
378
1
      }
379
2.91k
      continue;
380
2.91k
    }
381
12.0k
    form.m_sheet=sheetNames[size_t(form.m_sheetId)];
382
12.0k
  }
383
54.1k
}
384
385
bool StarCellFormula::readSCFormula(StarZone &zone, STOFFCellContent &content, int version, long lastPos)
386
59.5k
{
387
59.5k
  STOFFInputStreamPtr input=zone.input();
388
59.5k
  long pos=input->tell();
389
390
59.5k
  libstoff::DebugFile &ascFile=zone.ascii();
391
59.5k
  libstoff::DebugStream f;
392
59.5k
  f << "Entries(SCFormula)[" << zone.getRecordLevel() << "]:";
393
  // sc_token.cxx ScTokenArray::Load
394
59.5k
  uint8_t fFlags;
395
59.5k
  *input>>fFlags;
396
59.5k
  if (fFlags&0xf) input->seek((fFlags&0xf), librevenge::RVNG_SEEK_CUR);
397
59.5k
  f << "cMode=" << input->readULong(1) << ","; // if (version<0x201) old mode
398
59.5k
  if (fFlags&0x10) f << "nRefs=" << input->readLong(2) << ",";
399
59.5k
  if (fFlags&0x20) f << "nErrors=" << input->readULong(2) << ",";
400
59.5k
  bool ok=true;
401
59.5k
  std::vector<StarCellFormulaInternal::Token> tokenList, rpnList;
402
59.5k
  if (fFlags&0x40) { // token
403
58.6k
    uint16_t nLen;
404
58.6k
    *input>>nLen;
405
58.6k
    f << "formula=";
406
299k
    for (int tok=0; tok<nLen; ++tok) {
407
244k
      StarCellFormulaInternal::Token token;
408
244k
      if (!readSCToken(zone, token, version, lastPos) || input->tell()>lastPos) {
409
4.18k
        f << "###";
410
4.18k
        ok=false;
411
4.18k
        break;
412
4.18k
      }
413
240k
      f << token;
414
240k
      tokenList.push_back(token);
415
240k
    }
416
58.6k
    f << ",";
417
58.6k
  }
418
59.5k
  if (!ok) {
419
4.18k
    ascFile.addPos(pos);
420
4.18k
    ascFile.addNote(f.str().c_str());
421
4.18k
    return false;
422
4.18k
  }
423
424
55.3k
  bool hasIndex=false, formulaSet=false;
425
236k
  for (auto &token : tokenList) {
426
236k
    STOFFCellContent::FormulaInstruction finalInstr;
427
236k
    bool ignore;
428
236k
    if (token.get(finalInstr,ignore) && !ignore)
429
235k
      content.m_formula.push_back(finalInstr);
430
227
    else if (token.m_type==StarCellFormulaInternal::Token::Index)
431
85
      hasIndex=true;
432
236k
  }
433
55.3k
  if (hasIndex)
434
85
    content.m_formula.clear();
435
55.2k
  else {
436
55.2k
    content.m_contentType=STOFFCellContent::C_FORMULA;
437
55.2k
    formulaSet=true;
438
55.2k
  }
439
55.3k
  if (fFlags&0x80) {
440
54.7k
    uint16_t nRPN;
441
54.7k
    *input >> nRPN;
442
54.7k
    f << "rpn=[";
443
54.7k
    STOFFCellContent rContent;
444
200k
    for (int rpn=0; ok && rpn<int(nRPN); ++rpn) {
445
146k
      uint8_t b1;
446
146k
      *input >> b1;
447
146k
      if (b1==0xff) {
448
217
        rContent.m_formula.clear();
449
217
        StarCellFormulaInternal::Token token;
450
217
        if (!readSCToken(zone, token, version, lastPos)) {
451
216
          ok=false;
452
216
          f << "###";
453
216
        }
454
217
        f << token;
455
217
        rpnList.push_back(token);
456
217
      }
457
145k
      else {
458
145k
        int idx;
459
145k
        if (b1&0x40)
460
393
          idx=int((b1&0x3f) | (input->readULong(1)<<6));
461
145k
        else
462
145k
          idx=int(b1);
463
145k
        if (idx<0 || idx>=int(tokenList.size())) {
464
3.38k
          STOFF_DEBUG_MSG(("StarCellFormula::readSCFormula: can not find the original token\n"));
465
3.38k
          f << "[###Index" << idx << "]";
466
3.38k
        }
467
142k
        else {
468
142k
          rpnList.push_back(tokenList[size_t(idx)]);
469
142k
          f << rpnList.back() << "[Id" << idx << "]";
470
142k
        }
471
145k
      }
472
146k
      if (input->tell()>lastPos) {
473
1.19k
        f << "###";
474
1.19k
        ok=false;
475
1.19k
      }
476
146k
    }
477
54.7k
    f << "],";
478
54.7k
  }
479
55.3k
  if (ok && !formulaSet && rpnList.size()) {
480
3
    std::vector<std::vector<StarCellFormulaInternal::Token> > stack;
481
6
    for (auto const &rpn : rpnList) {
482
6
      if (!StarCellFormulaInternal::Token::addToken(stack, rpn)) {
483
0
        ok=false;
484
0
        break;
485
0
      }
486
6
    }
487
3
    if (ok && stack.size()==1) {
488
1
      hasIndex=false;
489
1
      std::vector<StarCellFormulaInternal::Token> &code=stack[0];
490
1
      for (auto &codeData : code) {
491
1
        STOFFCellContent::FormulaInstruction finalInstr;
492
1
        bool ignore;
493
1
        if (codeData.get(finalInstr,ignore) && !ignore)
494
0
          content.m_formula.push_back(finalInstr);
495
1
        else if (codeData.m_type==StarCellFormulaInternal::Token::Index)
496
1
          hasIndex=true;
497
1
      }
498
1
      if (hasIndex)
499
1
        content.m_formula.clear();
500
0
      else {
501
0
        content.m_contentType=STOFFCellContent::C_FORMULA;
502
0
        formulaSet=true;
503
0
      }
504
1
    }
505
#if 0
506
    else {
507
      std::cerr << "Bad=[\n";
508
      for (auto const &codeList : stack) {
509
        std::cerr << "\t";
510
        for (auto const &code : codeList)
511
          std::cerr << code;
512
        std::cerr << "\n";
513
      }
514
      std::cerr << "]\n";
515
    }
516
#endif
517
3
  }
518
55.3k
  if (!formulaSet) {
519
85
    static bool first=true;
520
85
    if (first) {
521
1
      STOFF_DEBUG_MSG(("StarCellFormula::readSCFormula: can not reconstruct some formula\n"));
522
1
      first=false;
523
1
    }
524
85
    f << "###";
525
85
  }
526
55.3k
  ascFile.addPos(pos);
527
55.3k
  ascFile.addNote(f.str().c_str());
528
55.3k
  return ok;
529
59.5k
}
530
531
bool StarCellFormula::readSCFormula3(StarZone &zone, STOFFCellContent &content, int /*version*/, long lastPos)
532
14.4k
{
533
14.4k
  STOFFInputStreamPtr input=zone.input();
534
14.4k
  long pos=input->tell();
535
536
14.4k
  libstoff::DebugFile &ascFile=zone.ascii();
537
14.4k
  libstoff::DebugStream f;
538
14.4k
  f << "Entries(SCFormula)[" << zone.getRecordLevel() << "]:";
539
14.4k
  bool ok=true;
540
14.4k
  bool hasIndex=false;
541
14.5k
  for (int tok=0; tok<512; ++tok) {
542
14.5k
    bool endData;
543
14.5k
    StarCellFormulaInternal::Token token;
544
14.5k
    if (!readSCToken3(zone, token, endData, lastPos) || input->tell()>lastPos) {
545
14.4k
      f << "###";
546
14.4k
      ok=false;
547
14.4k
      break;
548
14.4k
    }
549
66
    if (endData) break;
550
63
    f << token;
551
63
    STOFFCellContent::FormulaInstruction finalInstr;
552
63
    bool ignore;
553
63
    if (token.get(finalInstr,ignore) && !ignore)
554
62
      content.m_formula.push_back(finalInstr);
555
1
    else if (token.m_type==StarCellFormulaInternal::Token::Index)
556
1
      hasIndex=true;
557
63
  }
558
559
14.4k
  if (ok && hasIndex) {
560
0
    STOFF_DEBUG_MSG(("StarCellFormula::readSCFormula3: formula with index are not implemented\n"));
561
0
    f << "##index,";
562
0
  }
563
14.4k
  else if (ok)
564
3
    content.m_contentType=STOFFCellContent::C_FORMULA;
565
14.4k
  ascFile.addPos(pos);
566
14.4k
  ascFile.addNote(f.str().c_str());
567
14.4k
  return true;
568
14.4k
}
569
570
bool StarCellFormula::readSCToken(StarZone &zone, StarCellFormulaInternal::Token &token, int version, long lastPos)
571
245k
{
572
245k
  STOFFInputStreamPtr input=zone.input();
573
245k
  libstoff::DebugStream f;
574
  // sc_token.cxx ScRawToken::Load
575
245k
  uint16_t nOp;
576
245k
  uint8_t type;
577
245k
  *input >> nOp >> type;
578
245k
  bool ok=true;
579
245k
  token.m_operation=nOp;
580
  // first read the data
581
245k
  switch (type) {
582
153k
  case 0: {
583
153k
    token.m_type=StarCellFormulaInternal::Token::Long;
584
153k
    token.m_longValue=long(input->readLong(1));
585
153k
    break;
586
0
  }
587
2.02k
  case 1: {
588
2.02k
    double val;
589
2.02k
    *input>>val;
590
2.02k
    token.m_type=StarCellFormulaInternal::Token::Double;
591
2.02k
    token.m_doubleValue=val;
592
2.02k
    break;
593
0
  }
594
391
  case 2: // string
595
550
  case 8: // external
596
1.74k
  default: { // ?
597
1.74k
    if (type==8) {
598
159
      auto cByte=int(input->readULong(1));
599
159
      if (cByte)
600
101
        f << "cByte=" << cByte << ",";
601
159
      f << "external,";
602
159
    }
603
1.58k
    else if (type!=2)
604
1.19k
      f << "f" << type << ",";
605
1.74k
    uint8_t nBytes;
606
1.74k
    *input >> nBytes;
607
1.74k
    if (input->tell()+int(nBytes)>lastPos) {
608
797
      STOFF_DEBUG_MSG(("StarCellFormula::readSCToken: can not read text zone\n"));
609
797
      f << "###text";
610
797
      ok=false;
611
797
      break;
612
797
    }
613
944
    token.m_type=type==2 ? StarCellFormulaInternal::Token::String :
614
944
                 type==8 ? StarCellFormulaInternal::Token::External : StarCellFormulaInternal::Token::String2;
615
944
    std::vector<uint8_t> text;
616
4.80k
    for (int i=0; i<int(nBytes); ++i) text.push_back(static_cast<uint8_t>(input->readULong(1)));
617
944
    std::vector<uint32_t> string;
618
944
    std::vector<size_t> srcPositions;
619
944
    StarEncoding::convert(text, zone.getEncoding(), string, srcPositions);
620
944
    token.m_textValue=libstoff::getString(string);
621
944
    break;
622
1.74k
  }
623
74.3k
  case 3:
624
87.1k
  case 4: {
625
87.1k
    int16_t nCol, nRow, nTab;
626
87.1k
    uint8_t nByte;
627
87.1k
    *input >> nCol >> nRow >> nTab >> nByte;
628
87.1k
    token.m_type=type==3 ? StarCellFormulaInternal::Token::Cell : StarCellFormulaInternal::Token::CellList;
629
87.1k
    token.m_positions[0]=STOFFVec3i(nCol, nRow, nTab);
630
87.1k
    if (version<0x10) {
631
706
      token.m_relPositions[0]=STOFFVec3b((nByte&3)!=0, ((nByte>>2)&3)!=0, ((nByte>>4)&3)!=0);
632
706
      if (nByte>>6) f << "fl=" << int(nByte>>6) << ",";
633
706
    }
634
86.4k
    else {
635
86.4k
      token.m_relPositions[0]=STOFFVec3b((nByte&1)!=0, ((nByte>>2)&1)!=0, ((nByte>>4)&1)!=0);
636
86.4k
      if (nByte&0xEA) f << "fl=" << std::hex << int(nByte&0xEF) << std::dec << ",";
637
86.4k
    }
638
87.1k
    if (type==4) {
639
12.7k
      *input >> nCol >> nRow >> nTab >> nByte;
640
12.7k
      token.m_positions[1]=STOFFVec3i(nCol, nRow, nTab);
641
12.7k
      if (nTab!=token.m_positions[0][2]) {
642
115
        STOFF_DEBUG_MSG(("StarCellFormula::readSCToken: referencing different sheet is not implemented\n"));
643
115
        f << "#sheet2=" << nTab << ",";
644
115
      }
645
12.7k
      if (version<0x10) {
646
21
        token.m_relPositions[1]=STOFFVec3b((nByte&3)!=0, ((nByte>>2)&3)!=0, ((nByte>>4)&3)!=0);
647
21
        if (nByte>>6) f << "fl2=" << int(nByte>>6) << ",";
648
21
      }
649
12.7k
      else {
650
12.7k
        token.m_relPositions[1]=STOFFVec3b((nByte&1)!=0, ((nByte>>2)&1)!=0, ((nByte>>4)&1)!=0);
651
12.7k
        if (nByte&0xEA) f << "fl=" << std::hex << int(nByte&0xEA) << std::dec << ",";
652
12.7k
      }
653
12.7k
    }
654
87.1k
    break;
655
74.3k
  }
656
236
  case 6:
657
236
    token.m_type=StarCellFormulaInternal::Token::Index;
658
236
    token.m_longValue=long(input->readULong(2));
659
236
    break;
660
265
  case 7: {
661
265
    uint8_t nByte;
662
265
    *input >> nByte;
663
265
    if (input->tell()+2*int(nByte)>lastPos) {
664
201
      STOFF_DEBUG_MSG(("StarCellFormula::readSCToken: can not read the jump\n"));
665
201
      f << "###jump";
666
201
      ok=false;
667
201
      break;
668
201
    }
669
64
    token.m_type=StarCellFormulaInternal::Token::Jump;
670
64
    f << "J=[";
671
181
    for (int i=0; i<int(nByte); ++i) {
672
117
      token.m_jumpPositions.push_back(int(input->readLong(2)));
673
117
      f << token.m_jumpPositions.back() << ",";
674
117
    }
675
64
    f << "],";
676
64
    break;
677
265
  }
678
78
  case 0x70:
679
78
    token.m_type=StarCellFormulaInternal::Token::Missing;
680
78
    f << "#missing";
681
78
    break;
682
83
  case 0x71:
683
83
    token.m_type=StarCellFormulaInternal::Token::Error;
684
83
    f << "#error";
685
83
    break;
686
245k
  }
687
245k
  STOFFCellContent::FormulaInstruction &instr=token.m_instruction;
688
245k
  switch (nOp) {
689
89.4k
  case 0:
690
89.4k
    break;
691
119
  case 2: // endData
692
119
    token.m_type=StarCellFormulaInternal::Token::Empty;
693
119
    break;
694
114
  case 3:
695
114
    if (type!=8) f << "##type=" << type << ",";
696
114
    break;
697
187
  case 4: // index
698
187
    if (type!=6) f << "##type=" << type << ",";
699
187
    break;
700
95
  case 5:
701
95
    if (type!=7) f << "##type=" << type << ",";
702
95
    token.m_type=StarCellFormulaInternal::Token::Function;
703
95
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Function;
704
95
    instr.m_content="If";
705
    // normally associated with a cond If
706
95
    token.m_content=StarCellFormulaInternal::Token::C_FunctionOperator;
707
95
    token.m_longValue=-2;
708
95
    break;
709
39.3k
  case 7:
710
39.3k
    if (type) f << "##type=" << type << ",";
711
39.3k
    token.m_type=StarCellFormulaInternal::Token::Function;
712
39.3k
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Operator;
713
39.3k
    instr.m_content="(";
714
39.3k
    break;
715
38.2k
  case 8:
716
38.2k
    if (type) f << "##type=" << type << ",";
717
38.2k
    token.m_type=StarCellFormulaInternal::Token::Function;
718
38.2k
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Operator;
719
    // normally in If with [cond id] form0 form1
720
38.2k
    token.m_content=StarCellFormulaInternal::Token::C_FunctionOperator;
721
38.2k
    token.m_longValue=-3;
722
38.2k
    instr.m_content=")";
723
38.2k
    break;
724
17.8k
  case 9:
725
17.8k
    if (type) f << "##type=" << type << ",";
726
17.8k
    token.m_type=StarCellFormulaInternal::Token::Function;
727
17.8k
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Operator;
728
17.8k
    instr.m_content=";";
729
    // normally in If to separe form0 and form1
730
17.8k
    token.m_content=StarCellFormulaInternal::Token::C_FunctionOperator;
731
17.8k
    token.m_longValue=-1;
732
17.8k
    break;
733
301
  case 11:
734
301
    STOFF_DEBUG_MSG(("StarCellFormula::readSCToken: find bad opcode\n"));
735
301
    f << "#bad[opcode],";
736
301
    ok=false;
737
301
    break;
738
211
  case 12:
739
    // extra space
740
211
    if (type) f << "##type=" << type << ",";
741
211
    token.m_type=StarCellFormulaInternal::Token::Empty;
742
211
    break;
743
90
  case 17:
744
90
    if (type) f << "##type=" << type << ",";
745
90
    token.m_type=StarCellFormulaInternal::Token::Function;
746
90
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Operator;
747
90
    instr.m_content="%";
748
90
    break;
749
59.1k
  default:
750
59.1k
    if (type!=0) {
751
1.94k
      STOFF_DEBUG_MSG(("StarCellFormula::readSCToken: can not read a formula\n"));
752
1.94k
      f << "##f" << nOp << "[" << type << "],";
753
1.94k
      ok=false;
754
1.94k
    }
755
57.1k
    else if (token.updateFunction())
756
56.1k
      token.m_type=StarCellFormulaInternal::Token::Function;
757
987
    else {
758
987
      STOFF_DEBUG_MSG(("StarCellFormula::readSCToken: can not read a formula\n"));
759
987
      f << "##f" << nOp << ",";
760
987
      ok=false;
761
987
    }
762
59.1k
    break;
763
245k
  }
764
245k
  token.m_extra=f.str();
765
766
245k
  return ok && input->tell()<=lastPos;
767
245k
}
768
769
bool StarCellFormula::readSCToken3(StarZone &zone, StarCellFormulaInternal::Token &token, bool &endData, long lastPos)
770
14.5k
{
771
14.5k
  endData=false;
772
14.5k
  STOFFInputStreamPtr input=zone.input();
773
  // sc_token.cxx ScRawToken::Load30
774
14.5k
  uint16_t nOp;
775
14.5k
  *input >> nOp;
776
14.5k
  token.m_operation=nOp;
777
14.5k
  bool ok=true;
778
14.5k
  libstoff::DebugStream f;
779
14.5k
  auto &instr=token.m_instruction;
780
14.5k
  switch (nOp) {
781
1.17k
  case 0: {
782
1.17k
    uint8_t type;
783
1.17k
    *input >> type;
784
1.17k
    switch (type) {
785
144
    case 0:
786
144
      token.m_type=StarCellFormulaInternal::Token::Long;
787
144
      token.m_longValue=long(input->readLong(1));
788
144
      break;
789
356
    case 1: {
790
356
      double val;
791
356
      *input>>val;
792
356
      token.m_type=StarCellFormulaInternal::Token::Double;
793
356
      token.m_doubleValue=val;
794
356
      break;
795
0
    }
796
140
    case 2: {
797
140
      std::vector<uint32_t> text;
798
140
      if (!zone.readString(text)) {
799
83
        STOFF_DEBUG_MSG(("StarCellFormula::readSCToken3: can not read text zone\n"));
800
83
        f << "###text";
801
83
        ok=false;
802
83
        break;
803
83
      }
804
57
      token.m_type=StarCellFormulaInternal::Token::String;
805
57
      token.m_textValue=libstoff::getString(text);
806
57
      break;
807
140
    }
808
86
    case 3: {
809
86
      int16_t nPos[3];
810
86
      uint8_t relPos[3], oldFlag;
811
86
      *input >> nPos[0] >> nPos[1] >> nPos[2] >> relPos[0] >> relPos[1] >> relPos[2] >> oldFlag;
812
86
      token.m_type=StarCellFormulaInternal::Token::Cell;
813
86
      token.m_positions[0]=STOFFVec3i(nPos[0], nPos[1], nPos[2]);
814
86
      token.m_relPositions[0]=STOFFVec3b(relPos[0],relPos[1],relPos[2]);
815
86
      if (oldFlag) f << "fl=" << int(oldFlag) << ",";
816
86
      break;
817
140
    }
818
70
    case 4: {
819
70
      int16_t nPos[2][3];
820
70
      uint8_t relPos[2][3], oldFlag[2];
821
70
      *input >> nPos[0][0] >> nPos[0][1] >> nPos[0][2] >> nPos[1][0] >> nPos[1][1] >> nPos[1][2]
822
70
             >> relPos[0][0] >> relPos[0][1] >> relPos[0][2] >> relPos[1][0] >> relPos[1][1] >> relPos[1][2]
823
70
             >> oldFlag[0] >> oldFlag[1];
824
70
      instr.m_type=STOFFCellContent::FormulaInstruction::F_CellList;
825
210
      for (int c=0; c<2; ++c) {
826
140
        token.m_type=StarCellFormulaInternal::Token::CellList;
827
140
        token.m_positions[c]=STOFFVec3i(nPos[c][0], nPos[c][1], nPos[c][2]);
828
140
        token.m_relPositions[c]=STOFFVec3b(relPos[c][0],relPos[c][1],relPos[c][2]);
829
140
      }
830
70
      if (nPos[0][2]!=nPos[1][2] || relPos[0][2]!=relPos[1][2]) {
831
70
        STOFF_DEBUG_MSG(("StarCellFormula::readSCToken3: referencing different sheet is not implemented\n"));
832
70
        f << "#sheet2,";
833
70
      }
834
70
      break;
835
140
    }
836
381
    default:
837
381
      f << "##type=" << int(type) << ",";
838
381
      ok=false;
839
381
      break;
840
1.17k
    }
841
1.17k
    break;
842
1.17k
  }
843
1.17k
  case 2: // stop
844
86
    token.m_type=StarCellFormulaInternal::Token::Empty;
845
86
    endData=true;
846
86
    break;
847
271
  case 3: { // external
848
271
    std::vector<uint32_t> text;
849
271
    if (!zone.readString(text)) {
850
77
      STOFF_DEBUG_MSG(("StarCellFormula::readSCToken3: can not read external zone\n"));
851
77
      f << "###external";
852
77
      ok=false;
853
77
      break;
854
77
    }
855
194
    f << "#external,";
856
194
    token.m_type=StarCellFormulaInternal::Token::External;
857
194
    token.m_textValue=libstoff::getString(text);
858
194
    break;
859
271
  }
860
90
  case 4: // name
861
90
    token.m_type=StarCellFormulaInternal::Token::Index;
862
90
    token.m_longValue=long(input->readULong(2));
863
90
    break;
864
284
  case 5: // jump 3
865
284
    token.m_type=StarCellFormulaInternal::Token::Function;
866
284
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Function;
867
284
    instr.m_content="If";
868
284
    break;
869
74
  case 6: // jump=maxjumpcount
870
74
    token.m_type=StarCellFormulaInternal::Token::Function;
871
74
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Function;
872
74
    instr.m_content="Choose";
873
74
    break;
874
71
  case 7:
875
71
    token.m_type=StarCellFormulaInternal::Token::Function;
876
71
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Operator;
877
71
    instr.m_content="(";
878
71
    break;
879
94
  case 8:
880
94
    token.m_type=StarCellFormulaInternal::Token::Function;
881
94
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Operator;
882
94
    instr.m_content=")";
883
94
    break;
884
84
  case 9:
885
84
    token.m_type=StarCellFormulaInternal::Token::Function;
886
84
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Operator;
887
84
    instr.m_content=";";
888
84
    break;
889
91
  case 17:
890
91
    token.m_type=StarCellFormulaInternal::Token::Function;
891
91
    instr.m_type=STOFFCellContent::FormulaInstruction::F_Operator;
892
91
    instr.m_content="%";
893
91
    break;
894
12.1k
  default:
895
12.1k
    if (token.updateFunction())
896
1.36k
      token.m_type=StarCellFormulaInternal::Token::Function;
897
10.8k
    else {
898
10.8k
      ok=false;
899
10.8k
      f << "###f" << nOp << ",";
900
10.8k
    }
901
12.1k
    break;
902
14.5k
  }
903
14.5k
  token.m_extra=f.str();
904
14.5k
  return ok && input->tell()<=lastPos;
905
14.5k
}
906
907
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: