/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: |