Coverage Report

Created: 2026-03-12 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libmwaw/src/lib/ClarisWksDatabase.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
/* libmwaw
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 <algorithm>
35
#include <iomanip>
36
#include <iostream>
37
#include <limits>
38
#include <map>
39
#include <sstream>
40
41
#include <librevenge/librevenge.h>
42
43
#include "MWAWCell.hxx"
44
#include "MWAWFont.hxx"
45
#include "MWAWSpreadsheetListener.hxx"
46
#include "MWAWParser.hxx"
47
#include "MWAWTable.hxx"
48
49
#include "ClarisWksDbaseContent.hxx"
50
#include "ClarisWksDocument.hxx"
51
#include "ClarisWksStruct.hxx"
52
#include "ClarisWksStyleManager.hxx"
53
54
#include "ClarisWksDatabase.hxx"
55
56
/** Internal: the structures of a ClarisWksDatabase */
57
namespace ClarisWksDatabaseInternal
58
{
59
struct Field {
60
  // the type
61
  enum Type { F_Unknown, F_Text, F_Number, F_Date, F_Time,
62
              F_Formula, F_FormulaSum,
63
              F_Checkbox, F_PopupMenu, F_RadioButton, F_ValueList,
64
              F_Multimedia
65
            };
66
  Field()
67
1.09M
    : m_type(F_Unknown)
68
1.09M
    , m_defType(-1)
69
1.09M
    , m_resType(0)
70
1.09M
    , m_name("")
71
1.09M
    , m_default("")
72
1.09M
    , m_valuesList()
73
1.09M
    , m_formula()
74
1.09M
  {
75
1.09M
  }
76
77
  //! operator<<
78
  friend std::ostream &operator<<(std::ostream &o, Field const &field)
79
0
  {
80
0
    switch (field.m_type) {
81
0
    case F_Text :
82
0
      o << "text,";
83
0
      break;
84
0
    case F_Number :
85
0
      o << "number,";
86
0
      break;
87
0
    case F_Date :
88
0
      o << "date,";
89
0
      break;
90
0
    case F_Time :
91
0
      o << "time,";
92
0
      break;
93
0
    case F_Formula :
94
0
      o << "formula,";
95
0
      break;
96
0
    case F_FormulaSum :
97
0
      o << "formula(summary),";
98
0
      break;
99
0
    case F_Checkbox :
100
0
      o << "checkbox,";
101
0
      break;
102
0
    case F_PopupMenu :
103
0
      o << "popupMenu,";
104
0
      break;
105
0
    case F_RadioButton :
106
0
      o << "radioButton,";
107
0
      break;
108
0
    case F_ValueList:
109
0
      o << "valueList,";
110
0
      break;
111
0
    case F_Multimedia :
112
0
      o << "multimedia,";
113
0
      break;
114
0
    case F_Unknown :
115
0
#if !defined(__clang__)
116
0
    default:
117
0
#endif
118
0
      o << "type=#unknown,";
119
0
      break;
120
0
    }
121
0
    switch (field.m_resType) {
122
0
    case 0:
123
0
      o << "text[format],";
124
0
      break;
125
0
    case 1:
126
0
      o << "number[format],";
127
0
      break;
128
0
    case 2:
129
0
      o << "date[format],";
130
0
      break;
131
0
    case 3:
132
0
      o << "time[format],";
133
0
      break;
134
0
    default:
135
0
      o << "##res[format]=" << field.m_resType << ",";
136
0
      break;
137
0
    }
138
0
    o << "'" << field.m_name << "',";
139
0
    switch (field.m_defType) {
140
0
    case -1:
141
0
      break;
142
0
    case 0:
143
0
      break;
144
0
    case 3:
145
0
      o << "recordInfo,";
146
0
      break;
147
0
    case 7:
148
0
      o << "serial";
149
0
      break;
150
0
    case 8:
151
0
      o << "hasDef,";
152
0
      break; // text with default
153
0
    case 9:
154
0
      o << "popup/radio/control,";
155
0
      break; // with default value ?
156
0
    default:
157
0
      o << "#defType=" << field.m_defType << ",";
158
0
      break;
159
0
    }
160
0
    if (field.m_default.length())
161
0
      o << "defaultVal='" << field.m_default << "',";
162
0
    return o;
163
0
  }
164
165
  bool isText() const
166
163k
  {
167
163k
    return m_type == F_Text;
168
163k
  }
169
  bool isFormula() const
170
633k
  {
171
633k
    return m_type == F_Formula || m_type == F_FormulaSum;
172
633k
  }
173
174
  int getNumDefault(int version) const
175
633k
  {
176
633k
    switch (m_type) {
177
219k
    case F_Text :
178
219k
      if (version >= 4)
179
63.7k
        return 1;
180
155k
      if (m_defType == 8) return 1;
181
147k
      return 0;
182
41.1k
    case F_Number :
183
74.6k
    case F_Date :
184
110k
    case F_Time :
185
112k
    case F_Multimedia :
186
112k
      return 0;
187
32.7k
    case F_Formula :
188
76.6k
    case F_FormulaSum :
189
76.6k
      return 1;
190
15.6k
    case F_Checkbox :
191
15.6k
      return 1;
192
18.8k
    case F_PopupMenu :
193
76.8k
    case F_RadioButton :
194
76.8k
      return 2;
195
28.6k
    case F_ValueList :
196
28.6k
      return (version >= 3) ? 2 : 1;
197
103k
    case F_Unknown :
198
#if !defined(__clang__)
199
    default:
200
#endif
201
103k
      break;
202
633k
    }
203
103k
    return 0;
204
633k
  }
205
206
  Type m_type;
207
  /** the local definition type */
208
  int m_defType;
209
  /** the result type */
210
  int m_resType;
211
  /** the field name */
212
  std::string m_name;
213
  /** the default value */
214
  std::string m_default;
215
  /** list of different value list */
216
  std::vector<MWAWEntry> m_valuesList;
217
  /** the formula */
218
  std::vector<MWAWCellContent::FormulaInstruction> m_formula;
219
};
220
221
////////////////////////////////////////
222
////////////////////////////////////////
223
224
//! Internal: the database of a ClarisWksDatabase
225
struct Database final : public ClarisWksStruct::DSET {
226
  //! constructor
227
  explicit Database(ClarisWksStruct::DSET const &dset = ClarisWksStruct::DSET())
228
664k
    : ClarisWksStruct::DSET(dset)
229
664k
    , m_fields()
230
664k
    , m_content()
231
664k
  {
232
664k
  }
233
  //! destructor
234
  ~Database() final;
235
  //! operator<<
236
  friend std::ostream &operator<<(std::ostream &o, Database const &doc)
237
0
  {
238
0
    o << static_cast<ClarisWksStruct::DSET const &>(doc);
239
0
    return o;
240
0
  }
241
  //! the list of field
242
  std::vector<Field> m_fields;
243
  //! the data
244
  std::shared_ptr<ClarisWksDbaseContent> m_content;
245
};
246
247
Database::~Database()
248
664k
{
249
664k
}
250
251
////////////////////////////////////////
252
//! Internal: the state of a ClarisWksDatabase
253
struct State {
254
  //! constructor
255
  State()
256
402k
    : m_databaseMap()
257
402k
  {
258
402k
  }
259
260
  std::map<int, std::shared_ptr<Database> > m_databaseMap;
261
};
262
263
}
264
265
////////////////////////////////////////////////////////////
266
// constructor/destructor, ...
267
////////////////////////////////////////////////////////////
268
ClarisWksDatabase::ClarisWksDatabase(ClarisWksDocument &document)
269
402k
  : m_document(document)
270
402k
  , m_parserState(document.m_parserState)
271
402k
  , m_state(new ClarisWksDatabaseInternal::State)
272
402k
  , m_mainParser(&document.getMainParser())
273
402k
{
274
402k
}
275
276
ClarisWksDatabase::~ClarisWksDatabase()
277
402k
{ }
278
279
int ClarisWksDatabase::version() const
280
3.02M
{
281
3.02M
  return m_parserState->m_version;
282
3.02M
}
283
284
// fixme
285
int ClarisWksDatabase::numPages() const
286
135k
{
287
135k
  return 1;
288
135k
}
289
////////////////////////////////////////////////////////////
290
// Intermediate level
291
////////////////////////////////////////////////////////////
292
293
////////////////////////////////////////////////////////////
294
// a document part
295
////////////////////////////////////////////////////////////
296
std::shared_ptr<ClarisWksStruct::DSET> ClarisWksDatabase::readDatabaseZone
297
(ClarisWksStruct::DSET const &zone, MWAWEntry const &entry, bool &complete)
298
670k
{
299
670k
  complete = false;
300
670k
  if (!entry.valid() || zone.m_fileType != 3 || entry.length() < 32)
301
6.09k
    return std::shared_ptr<ClarisWksStruct::DSET>();
302
664k
  long pos = entry.begin();
303
664k
  MWAWInputStreamPtr &input= m_parserState->m_input;
304
664k
  input->seek(pos+8+16, librevenge::RVNG_SEEK_SET); // avoid header+8 generic number
305
664k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
306
664k
  libmwaw::DebugStream f;
307
664k
  std::shared_ptr<ClarisWksDatabaseInternal::Database>
308
664k
  databaseZone(new ClarisWksDatabaseInternal::Database(zone));
309
310
664k
  f << "Entries(DatabaseDef):" << *databaseZone << ",";
311
664k
  ascFile.addDelimiter(input->tell(), '|');
312
664k
  ascFile.addPos(pos);
313
664k
  ascFile.addNote(f.str().c_str());
314
315
  // read the last part
316
664k
  long data0Length = zone.m_dataSz;
317
664k
  long N = zone.m_numData;
318
664k
  if (entry.length() -8-12 != data0Length*N + zone.m_headerSz) {
319
474k
    if (data0Length == 0 && N) {
320
14.5k
      MWAW_DEBUG_MSG(("ClarisWksDatabase::readDatabaseZone: can not find definition size\n"));
321
14.5k
      input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
322
14.5k
      return std::shared_ptr<ClarisWksStruct::DSET>();
323
14.5k
    }
324
325
459k
    MWAW_DEBUG_MSG(("ClarisWksDatabase::readDatabaseZone: unexpected size for zone definition, try to continue\n"));
326
459k
  }
327
328
649k
  long dataEnd = entry.end()-N*data0Length;
329
649k
  int numLast = -1;
330
649k
  int const vers=version();
331
649k
  switch (vers) {
332
204k
  case 1:
333
344k
  case 2:
334
394k
  case 3:
335
437k
  case 4:
336
437k
    numLast = 0;
337
437k
    break;
338
161k
  case 5:
339
161k
    numLast = 4;
340
161k
    break;
341
50.4k
  case 6:
342
50.4k
    numLast = 8;
343
50.4k
    break;
344
0
  default:
345
0
    MWAW_DEBUG_MSG(("ClarisWksDatabase::readDatabaseZone: unexpected version\n"));
346
0
    break;
347
649k
  }
348
649k
  if (numLast >= 0 && long(input->tell()) + data0Length + numLast <= dataEnd) {
349
505k
    ascFile.addPos(dataEnd-data0Length-numLast);
350
505k
    ascFile.addNote("DatabaseDef-_");
351
505k
    if (numLast) {
352
163k
      ascFile.addPos(dataEnd-numLast);
353
163k
      ascFile.addNote("DatabaseDef-extra");
354
163k
    }
355
505k
  }
356
649k
  input->seek(dataEnd, librevenge::RVNG_SEEK_SET);
357
358
1.31M
  for (long i = 0; i < N; i++) {
359
668k
    pos = input->tell();
360
361
668k
    f.str("");
362
668k
    f << "DatabaseDef-" << i;
363
668k
    ascFile.addPos(pos);
364
668k
    ascFile.addNote(f.str().c_str());
365
668k
    input->seek(pos+data0Length, librevenge::RVNG_SEEK_SET);
366
668k
  }
367
368
649k
  input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
369
370
649k
  if (m_state->m_databaseMap.find(databaseZone->m_id) != m_state->m_databaseMap.end()) {
371
585k
    MWAW_DEBUG_MSG(("ClarisWksDatabase::readDatabaseZone: zone %d already exists!!!\n", databaseZone->m_id));
372
    /* can only happen if we did not read completly the header, and so
373
       we have previously read some old saved part of the database,
374
       which has remained in the junk zones. */
375
585k
    if (databaseZone->m_id==1)
376
152k
      m_state->m_databaseMap[databaseZone->m_id] = databaseZone;
377
585k
  }
378
63.6k
  else
379
63.6k
    m_state->m_databaseMap[databaseZone->m_id] = databaseZone;
380
381
649k
  databaseZone->m_otherChilds.push_back(databaseZone->m_id+1);
382
383
649k
  pos = input->tell();
384
649k
  bool ok = readFields(*databaseZone);
385
386
649k
  if (ok) {
387
481k
    ok = readDefaults(*databaseZone);
388
481k
    pos = input->tell();
389
481k
  }
390
649k
  if (ok) {
391
402k
    pos = input->tell();
392
402k
    ok = ClarisWksStruct::readStructZone(*m_parserState, "DatabaseListUnkn0", false);
393
402k
  }
394
649k
  if (ok) {
395
314k
    pos = input->tell();
396
    // probably: field number followed by 1 : increasing, 2 : decreasing
397
314k
    ok = ClarisWksStruct::readStructZone(*m_parserState, "DatabaseSortFunction", false);
398
314k
  }
399
649k
  if (ok) {
400
275k
    pos = input->tell();
401
275k
    std::shared_ptr<ClarisWksDbaseContent> content(new ClarisWksDbaseContent(m_document, false));
402
275k
    ok = content->readContent();
403
275k
    if (ok) databaseZone->m_content=content;
404
275k
  }
405
649k
  std::vector<int> listLayout;
406
649k
  if (ok) {
407
137k
    pos = input->tell();
408
137k
    ok = ClarisWksStruct::readIntZone(*m_parserState, "DatabaseLayout", false, 4, listLayout);
409
137k
  }
410
649k
  if (ok) {
411
144k
    for (size_t i=0; i<listLayout.size(); ++i) {
412
46.9k
      pos = input->tell();
413
46.9k
      if (!readLayout(*databaseZone)) {
414
33.9k
        MWAW_DEBUG_MSG(("ClarisWksDatabase::readDatabaseZone: can not read some ListLayout data file\n"));
415
33.9k
        ok=false;
416
33.9k
        input->seek(pos, librevenge::RVNG_SEEK_SET);
417
33.9k
        ascFile.addPos(pos);
418
33.9k
        ascFile.addNote("DatabaseLayout:###");
419
33.9k
        break;
420
33.9k
      }
421
46.9k
    }
422
131k
  }
423
649k
  if (ok) {
424
97.8k
    pos = input->tell();
425
    // in v1-v4 list of id block?, in v5-v6 list of block id+?
426
97.8k
    ok = ClarisWksStruct::readStructZone(*m_parserState, "DatabaseListUnkn3", false);
427
97.8k
  }
428
429
649k
  if (ok) { // never seems,
430
92.1k
    pos=input->tell();
431
92.1k
    auto sz=long(input->readULong(4));
432
92.1k
    if (input->checkPosition(pos+4+sz)) {
433
53.7k
      input->seek(pos+4+sz, librevenge::RVNG_SEEK_SET);
434
53.7k
      ascFile.addPos(pos);
435
53.7k
      if (sz) {
436
26.9k
        MWAW_DEBUG_MSG(("ClarisWksDatabase::readDatabaseZone: find a Unkn4 block\n"));
437
26.9k
        ascFile.addNote("Entries(DatabaseListUnkn4):");
438
26.9k
      }
439
26.7k
      else
440
26.7k
        ascFile.addNote("_");
441
53.7k
    }
442
38.4k
    else {
443
38.4k
      ok=false;
444
38.4k
      MWAW_DEBUG_MSG(("ClarisWksDatabase::readDatabaseZone: find a Unkn4 block does not know how to read it\n"));
445
38.4k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
446
38.4k
    }
447
92.1k
  }
448
649k
  if (ok && vers>1) {
449
40.2k
    pos=input->tell();
450
40.2k
    std::vector<std::string> listString;
451
40.2k
    ok=m_document.readStringList("DatabaseListString", false, listString);
452
40.2k
  }
453
649k
  if (ok) {
454
28.6k
    pos = input->tell();
455
28.6k
    ok = ClarisWksStruct::readStructZone(*m_parserState, "DatabaseUnkn5", false);
456
28.6k
  }
457
649k
  if (ok && vers>=4) {
458
    // version 4 can contains more block: list of int+flag?
459
4.65k
    pos=input->tell();
460
4.65k
    ok = ClarisWksStruct::readStructZone(*m_parserState, "DatabaseUnkn6", false);
461
4.65k
  }
462
  // now the following seems to be different
463
649k
  if (!ok)
464
636k
    input->seek(pos, librevenge::RVNG_SEEK_SET);
465
466
649k
  return databaseZone;
467
649k
}
468
469
////////////////////////////////////////////////////////////
470
//
471
// Intermediate level
472
//
473
////////////////////////////////////////////////////////////
474
bool ClarisWksDatabase::readFields(ClarisWksDatabaseInternal::Database &dBase)
475
649k
{
476
649k
  MWAWInputStreamPtr &input= m_parserState->m_input;
477
649k
  long pos = input->tell();
478
649k
  ClarisWksStruct::Struct header;
479
649k
  if (!header.readHeader(input,true) || (header.m_size && header.m_dataSize<28)) {
480
128k
    MWAW_DEBUG_MSG(("ClarisWksDatabase::readFields: can not read the header\n"));
481
128k
    return false;
482
128k
  }
483
521k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
484
521k
  libmwaw::DebugStream f;
485
521k
  if (header.m_size==0) {
486
109k
    ascFile.addPos(pos);
487
109k
    ascFile.addNote("Nop");
488
109k
    return true;
489
109k
  }
490
411k
  long endPos = pos+4+header.m_size;
491
411k
  f << "Entries(DatabaseField):" << header;
492
411k
  if (header.m_headerSize) {
493
2.42k
    ascFile.addDelimiter(input->tell(),'|');
494
2.42k
    input->seek(header.m_headerSize, librevenge::RVNG_SEEK_CUR);
495
2.42k
  }
496
411k
  ascFile.addPos(pos);
497
411k
  ascFile.addNote(f.str().c_str());
498
499
411k
  dBase.m_fields.resize(size_t(header.m_numData));
500
411k
  int n=0;
501
1.05M
  for (auto &field : dBase.m_fields) {
502
1.05M
    pos = input->tell();
503
1.05M
    f.str("");
504
1.05M
    f << "DatabaseField-" << n++ << ":";
505
506
1.05M
    int const fNameMaxSz = 64;
507
1.05M
    std::string name("");
508
1.05M
    auto sz = long(input->readULong(1));
509
1.05M
    if (sz > fNameMaxSz-1 || sz > header.m_dataSize-1) {
510
40.0k
      input->seek(pos, librevenge::RVNG_SEEK_SET);
511
40.0k
      MWAW_DEBUG_MSG(("ClarisWksDatabase::readFields: find odd field name\n"));
512
40.0k
      return false;
513
40.0k
    }
514
5.75M
    for (long j = 0; j < sz; j++)
515
4.74M
      name += char(input->readULong(1));
516
1.01M
    field.m_name = name;
517
518
1.01M
    input->seek(pos+fNameMaxSz, librevenge::RVNG_SEEK_SET);
519
1.01M
    auto type = static_cast<int>(input->readULong(1));
520
1.01M
    bool ok = true;
521
1.01M
    switch (type) {
522
    // or name
523
389k
    case 0:
524
389k
      field.m_type = ClarisWksDatabaseInternal::Field::F_Text;
525
389k
      break;
526
52.8k
    case 1:
527
52.8k
      field.m_type = ClarisWksDatabaseInternal::Field::F_Number;
528
52.8k
      break;
529
77.9k
    case 2:
530
77.9k
      field.m_type = ClarisWksDatabaseInternal::Field::F_Date;
531
77.9k
      break;
532
79.1k
    case 3:
533
79.1k
      field.m_type = ClarisWksDatabaseInternal::Field::F_Time;
534
79.1k
      break;
535
51.7k
    case 4:
536
51.7k
      if (version() <= 2)
537
24.3k
        field.m_type = ClarisWksDatabaseInternal::Field::F_Formula;
538
27.4k
      else
539
27.4k
        field.m_type = ClarisWksDatabaseInternal::Field::F_PopupMenu;
540
51.7k
      break;
541
65.4k
    case 5:
542
65.4k
      if (version() <= 2)
543
42.2k
        field.m_type = ClarisWksDatabaseInternal::Field::F_FormulaSum;
544
23.1k
      else
545
23.1k
        field.m_type = ClarisWksDatabaseInternal::Field::F_Checkbox;
546
65.4k
      break;
547
64.2k
    case 6:
548
64.2k
      field.m_type = ClarisWksDatabaseInternal::Field::F_RadioButton;
549
64.2k
      break;
550
6.24k
    case 7:
551
6.24k
      if (version() == 4)
552
2.86k
        field.m_type = ClarisWksDatabaseInternal::Field::F_Formula;
553
3.37k
      else
554
3.37k
        field.m_type = ClarisWksDatabaseInternal::Field::F_Multimedia;
555
6.24k
      break;
556
25.7k
    case 8:
557
25.7k
      if (version() == 4)
558
5.43k
        field.m_type = ClarisWksDatabaseInternal::Field::F_FormulaSum;
559
20.3k
      else
560
20.3k
        ok = false;
561
25.7k
      break;
562
21.8k
    case 10:
563
21.8k
      field.m_type = ClarisWksDatabaseInternal::Field::F_Formula;
564
21.8k
      break;
565
10.2k
    case 11:
566
10.2k
      field.m_type = ClarisWksDatabaseInternal::Field::F_FormulaSum;
567
10.2k
      break;
568
165k
    default:
569
165k
      ok = false;
570
165k
      break;
571
1.01M
    }
572
1.01M
    if (!ok)
573
186k
      f << "#type=" << type << ",";
574
1.01M
    auto val = static_cast<int>(input->readULong(1));
575
1.01M
    if (val)
576
358k
      f << "#unkn=" << val << ",";
577
1.01M
    unsigned long ptr = input->readULong(4);
578
1.01M
    if (ptr) // set for formula
579
502k
      f << "ptr=" << std::hex << ptr << std::dec << ",";
580
1.01M
    field.m_resType=static_cast<int>(input->readLong(1));
581
1.01M
    f << "fl?=[" << std::hex;
582
1.01M
    f << input->readULong(1) << ",";
583
1.01M
    f << input->readULong(1) << ",";
584
7.07M
    for (int j = 0; j < 6; j++) {
585
      // some int which seems constant on the database...
586
6.06M
      val = static_cast<int>(input->readULong(2));
587
6.06M
      f <<  val << ",";
588
6.06M
    }
589
1.01M
    f << std::dec << "],";
590
591
1.01M
    if (version() > 1) {
592
12.4M
      for (int j = 0; j < 16; j++) {
593
        /** find f1=600 for a number
594
            f16 = 0[checkbox, ... ], 2[number or text],3 [name field], 82[value list],
595
            f16 & 8: can not be empty
596
        */
597
11.7M
        val = static_cast<int>(input->readLong(2));
598
11.7M
        if (val) f << "f" << j << "=" << std::hex << val << std::dec << ",";
599
11.7M
      }
600
733k
      auto subType = static_cast<int>(input->readULong(2));
601
733k
      if (version() == 2) {
602
234k
        if ((subType & 0x80) && field.m_type == ClarisWksDatabaseInternal::Field::F_Text) {
603
21.7k
          field.m_type = ClarisWksDatabaseInternal::Field::F_ValueList;
604
21.7k
          subType &= 0xFF7F;
605
21.7k
        }
606
234k
        if (subType) f << "f17=" << std::hex << subType << std::dec << ",";
607
234k
      }
608
499k
      else {
609
499k
        if ((subType & 0x80) && field.m_type == ClarisWksDatabaseInternal::Field::F_Text) {
610
31.6k
          field.m_type = ClarisWksDatabaseInternal::Field::F_ValueList;
611
31.6k
          subType &= 0xFF7F;
612
31.6k
        }
613
499k
        ok = true;
614
499k
        switch (subType) {
615
138k
        case 0:
616
138k
          ok = field.m_type == ClarisWksDatabaseInternal::Field::F_Checkbox ||
617
131k
               field.m_type == ClarisWksDatabaseInternal::Field::F_PopupMenu ||
618
116k
               field.m_type == ClarisWksDatabaseInternal::Field::F_RadioButton ||
619
113k
               field.m_type == ClarisWksDatabaseInternal::Field::F_Multimedia;
620
138k
          break;
621
156k
        case 2: // basic
622
156k
          break;
623
1.36k
        case 3:
624
1.36k
          ok = field.m_type == ClarisWksDatabaseInternal::Field::F_Text;
625
1.36k
          if (ok) f << "name[field],";
626
1.36k
          break;
627
4.52k
        case 6:
628
4.52k
          ok = version() == 4 && field.m_type == ClarisWksDatabaseInternal::Field::F_ValueList;;
629
4.52k
          break;
630
199k
        default:
631
199k
          ok = false;
632
499k
        }
633
499k
        if (!ok) f << "#unkSubType=" << std::hex << subType << std::dec << ",";
634
499k
      }
635
733k
      val = static_cast<int>(input->readULong(2));
636
733k
      if (val==0x8000)
637
4.15k
        f << "recordInfo";
638
729k
      else if (val)
639
347k
        f << "#unk1=" << std::hex << val << std::dec << ",";
640
733k
      field.m_defType = static_cast<int>(input->readULong(1));
641
      // default, followed by a number/ptr/... : 7fff ( mean none)
642
733k
    }
643
1.01M
    f << field << ",";
644
1.01M
    long actPos = input->tell();
645
1.01M
    if (actPos != pos && actPos != pos+header.m_dataSize)
646
1.01M
      ascFile.addDelimiter(actPos, '|');
647
1.01M
    ascFile.addPos(pos);
648
1.01M
    ascFile.addNote(f.str().c_str());
649
1.01M
    input->seek(pos+header.m_dataSize, librevenge::RVNG_SEEK_SET);
650
1.01M
  }
651
652
371k
  input->seek(endPos, librevenge::RVNG_SEEK_SET);
653
371k
  return true;
654
411k
}
655
656
bool ClarisWksDatabase::readDefaults(ClarisWksDatabaseInternal::Database &dBase)
657
481k
{
658
481k
  int vers = version();
659
481k
  MWAWInputStreamPtr &input= m_parserState->m_input;
660
481k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
661
481k
  libmwaw::DebugStream f;
662
663
481k
  int n=0;
664
633k
  for (auto &field : dBase.m_fields) {
665
633k
    int numExpected = field.getNumDefault(vers);
666
667
633k
    bool formField = field.isFormula();
668
633k
    bool valueList = field.m_type == ClarisWksDatabaseInternal::Field::F_ValueList;
669
804k
    for (int fi = 0; fi < numExpected; fi++) {
670
      // actually we guess which one are ok
671
319k
      long pos = input->tell();
672
319k
      auto sz = long(input->readULong(4));
673
674
319k
      long endPos = pos+4+sz;
675
319k
      if (!input->checkPosition(endPos)) {
676
47.4k
        MWAW_DEBUG_MSG(("ClarisWksDatabase::readDefaults: can not find value for field: %d\n", fi));
677
47.4k
        input->seek(pos, librevenge::RVNG_SEEK_SET);
678
47.4k
        return false;
679
47.4k
      }
680
272k
      long length = (vers <= 2 && field.isText()) ? sz : static_cast<int>(input->readULong(1));
681
272k
      f.str("");
682
272k
      f << "Entries(DatabaseDft)[" << n++ << "]:";
683
272k
      if (formField) {
684
60.0k
        if (length != sz-1) {
685
9.04k
          MWAW_DEBUG_MSG(("ClarisWksDatabase::readDefaults: can not find formula for field: %ld\n", long(n)));
686
9.04k
          input->seek(pos, librevenge::RVNG_SEEK_SET);
687
9.04k
          return false;
688
9.04k
        }
689
50.9k
        f << "formula,";
690
50.9k
        std::vector<MWAWCellContent::FormulaInstruction> formula;
691
50.9k
        std::string error;
692
50.9k
        if (!dBase.m_content)
693
50.7k
          dBase.m_content.reset(new ClarisWksDbaseContent(m_document, false));
694
50.9k
        if (!dBase.m_content->readFormula(MWAWVec2i(fi,0), endPos, formula, error)) {
695
47.4k
          MWAW_DEBUG_MSG(("ClarisWksDatabase::readDefaults: can not find formula for field: %ld\n", long(n)));
696
47.4k
        }
697
3.47k
        else
698
3.47k
          field.m_formula=formula;
699
1.64M
        for (auto const &fo : formula) f << fo;
700
50.9k
        f << error;
701
50.9k
      }
702
212k
      else {
703
212k
        bool listField = (valueList && fi == 1) || (!valueList && fi==0 && numExpected==2);
704
212k
        if (listField)
705
74.1k
          f << "listString,";
706
138k
        else
707
138k
          f << "string,";
708
212k
        if (vers > 2 && !listField && length != sz-1) {
709
22.3k
          MWAW_DEBUG_MSG(("ClarisWksDatabase::readDefaults: can not find strings for field: %ld\n", long(n)));
710
22.3k
          input->seek(pos, librevenge::RVNG_SEEK_SET);
711
22.3k
          return false;
712
22.3k
        }
713
2.21M
        while (1) {
714
2.21M
          long actPos = input->tell();
715
2.21M
          if (actPos+length > endPos) {
716
69.1k
            MWAW_DEBUG_MSG(("ClarisWksDatabase::readDefaults: can not find strings for field: %ld\n", long(n)));
717
69.1k
            ascFile.addPos(pos);
718
69.1k
            ascFile.addNote("DatabaseDft:###");
719
720
69.1k
            input->seek(pos, librevenge::RVNG_SEEK_SET);
721
69.1k
            return true;
722
69.1k
          }
723
2.14M
          if (listField) {
724
1.89M
            MWAWEntry entry;
725
1.89M
            entry.setBegin(actPos);
726
1.89M
            entry.setLength(length);
727
1.89M
            field.m_valuesList.push_back(entry);
728
1.89M
          }
729
2.14M
          std::string name("");
730
11.2M
          for (long c = 0; c < length; c++)
731
9.10M
            name += char(input->readULong(1));
732
2.14M
          f << "'" << name << "',";
733
2.14M
          if (long(input->tell()) == endPos)
734
120k
            break;
735
2.02M
          length = long(input->readULong(1));
736
2.02M
        }
737
189k
      }
738
171k
      ascFile.addPos(pos);
739
171k
      ascFile.addNote(f.str().c_str());
740
171k
      input->seek(endPos, librevenge::RVNG_SEEK_SET);
741
171k
    }
742
633k
  }
743
333k
  return true;
744
481k
}
745
746
747
bool ClarisWksDatabase::readLayout(ClarisWksDatabaseInternal::Database &dBase)
748
46.9k
{
749
46.9k
  MWAWInputStreamPtr &input= m_parserState->m_input;
750
46.9k
  long pos = input->tell();
751
46.9k
  ClarisWksStruct::Struct header;
752
46.9k
  if (!header.readHeader(input,true) || header.m_headerSize<52 || header.m_dataSize<6) {
753
21.9k
    MWAW_DEBUG_MSG(("ClarisWksDatabase::readLayout: can not read the header\n"));
754
21.9k
    return false;
755
21.9k
  }
756
25.0k
  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
757
25.0k
  libmwaw::DebugStream f;
758
25.0k
  if (header.m_size==0) {
759
0
    ascFile.addPos(pos);
760
0
    ascFile.addNote("Nop");
761
0
    return true;
762
0
  }
763
25.0k
  f << "DatabaseLayout-Part:" << header;
764
25.0k
  auto val = static_cast<int>(input->readLong(2));
765
25.0k
  if (val) f << "f3=" << val << ",";
766
25.0k
  auto childId=int(input->readULong(2));
767
25.0k
  f << "childId=" << childId << ",";
768
25.0k
  dBase.m_otherChilds.push_back(childId);
769
75.2k
  for (int i = 0; i < 2; ++i) { // f4=1-3, f5=0|c6|12a
770
50.1k
    val = static_cast<int>(input->readLong(2));
771
50.1k
    if (val) f << "f" << i+4 << "=" << val << ",";
772
50.1k
  }
773
125k
  for (int i = 0; i<4; ++i) { // always 0|1
774
100k
    val = static_cast<int>(input->readLong(1));
775
100k
    if (val==1) f << "fl" << i << ",";
776
81.3k
    else if (val) f << "#fl" << i << "=" << val << ",";
777
100k
  }
778
25.0k
  auto sSz=static_cast<int>(input->readULong(1));
779
25.0k
  if (sSz>31) {
780
4.44k
    MWAW_DEBUG_MSG(("ClarisWksDatabase::readLayout: find odd string size\n"));
781
4.44k
    f << "#sSz=" << sSz << ",";
782
4.44k
  }
783
20.6k
  else {
784
20.6k
    std::string name("");
785
127k
    for (int i=0; i<sSz; ++i) name+=char(input->readULong(1));
786
20.6k
    f << "\"" << name << "\",";
787
20.6k
  }
788
25.0k
  input->seek(pos+60, librevenge::RVNG_SEEK_SET);
789
25.0k
  val = static_cast<int>(input->readLong(2)); // always 0
790
25.0k
  if (val) f << "g0=" << val << ",";
791
25.0k
  childId=static_cast<int>(input->readULong(2));
792
25.0k
  f << "childId2=" << childId << ",";
793
25.0k
  dBase.m_otherChilds.push_back(childId);
794
795
25.0k
  ascFile.addDelimiter(input->tell(),'|');
796
25.0k
  ascFile.addPos(pos);
797
25.0k
  ascFile.addNote(f.str().c_str());
798
799
25.0k
  input->seek(pos+4+12+header.m_headerSize, librevenge::RVNG_SEEK_SET);
800
116k
  for (long i = 0; i < header.m_numData; i++) {
801
91.8k
    pos=input->tell();
802
91.8k
    f.str("");
803
91.8k
    f << "DatabaseLayout-Part" << i << ":";
804
805
91.8k
    ascFile.addPos(pos);
806
91.8k
    ascFile.addNote(f.str().c_str());
807
91.8k
    input->seek(pos+header.m_dataSize, librevenge::RVNG_SEEK_SET);
808
91.8k
  }
809
810
25.0k
  pos=input->tell();
811
25.0k
  if (!ClarisWksStruct::readStructZone(*m_parserState, "DatabaseLayout", false)) {
812
12.0k
    MWAW_DEBUG_MSG(("ClarisWksDatabase::readLayout: can not read the layout second part\n"));
813
12.0k
    ascFile.addPos(pos);
814
12.0k
    ascFile.addNote("DatabaseLayout-B:###");
815
12.0k
    return false;
816
12.0k
  }
817
13.0k
  return true;
818
25.0k
}
819
820
////////////////////////////////////////////////////////////
821
//
822
// send data
823
//
824
////////////////////////////////////////////////////////////
825
bool ClarisWksDatabase::sendDatabase(int zId, MWAWListenerPtr listener)
826
35.4k
{
827
35.4k
  if (!listener)
828
11.6k
    listener=m_parserState->m_spreadsheetListener;
829
35.4k
  if (!listener) {
830
8.98k
    MWAW_DEBUG_MSG(("ClarisWksDatabase::sendDatabase: called without any listener\n"));
831
8.98k
    return false;
832
8.98k
  }
833
26.5k
  if (listener->getType()!=MWAWListener::Spreadsheet ||
834
24.1k
      (m_parserState->m_kind==MWAWDocument::MWAW_K_DATABASE && zId!=1)) {
835
24.1k
    MWAW_DEBUG_MSG(("ClarisWksDatabase::sendDatabase: sending a database is not implemented\n"));
836
24.1k
    return false;
837
24.1k
  }
838
839
2.35k
  auto *sheetListener=static_cast<MWAWSpreadsheetListener *>(listener.get());
840
2.35k
  auto it=m_state->m_databaseMap.find(zId);
841
2.35k
  if (it == m_state->m_databaseMap.end() || !it->second) {
842
0
    MWAW_DEBUG_MSG(("ClarisWksDatabase::sendDatabase: can not find zone %d!!!\n", zId));
843
0
    return false;
844
0
  }
845
2.35k
  auto &dbase=*it->second;
846
2.35k
  MWAWVec2i minData, maxData;
847
2.35k
  std::vector<int> recordsPos;
848
2.35k
  if (!dbase.m_content || !dbase.m_content->getExtrema(minData,maxData) ||
849
1.67k
      !dbase.m_content->getRecordList(recordsPos)) {
850
676
    MWAW_DEBUG_MSG(("ClarisWksDatabase::sendDatabase: can not find any content\n"));
851
676
    return false;
852
676
  }
853
1.67k
  size_t numDataFields=dbase.m_fields.size();
854
1.67k
  int numFields = std::max(maxData[0]+1,int(numDataFields));
855
1.67k
  std::vector<ClarisWksStyleManager::CellFormat> formats;
856
1.67k
  formats.resize(size_t(numFields), ClarisWksStyleManager::CellFormat());
857
1.67k
  bool hasMultimedia=false;
858
8.37k
  for (size_t f=0; f < dbase.m_fields.size(); ++f) {
859
6.70k
    switch (dbase.m_fields[f].m_type) {
860
845
    case ClarisWksDatabaseInternal::Field::F_Number:
861
845
      formats[f].m_format=MWAWCell::F_NUMBER;
862
845
      formats[f].m_numberFormat=MWAWCell::F_NUMBER_GENERIC;
863
845
      break;
864
1.55k
    case ClarisWksDatabaseInternal::Field::F_Date:
865
1.55k
      formats[f].m_format=MWAWCell::F_DATE;
866
1.55k
      break;
867
842
    case ClarisWksDatabaseInternal::Field::F_Time:
868
842
      formats[f].m_format=MWAWCell::F_TIME;
869
842
      break;
870
0
    case ClarisWksDatabaseInternal::Field::F_Checkbox:
871
0
      formats[f].m_format=MWAWCell::F_BOOLEAN;
872
0
      break;
873
1
    case ClarisWksDatabaseInternal::Field::F_Multimedia:
874
1
      formats[f].m_format=MWAWCell::F_TEXT;
875
1
      hasMultimedia=true;
876
1
      break;
877
42
    case ClarisWksDatabaseInternal::Field::F_Unknown:
878
2.61k
    case ClarisWksDatabaseInternal::Field::F_Text:
879
2.69k
    case ClarisWksDatabaseInternal::Field::F_Formula:
880
2.69k
    case ClarisWksDatabaseInternal::Field::F_FormulaSum:
881
2.69k
    case ClarisWksDatabaseInternal::Field::F_PopupMenu:
882
2.69k
    case ClarisWksDatabaseInternal::Field::F_RadioButton:
883
3.45k
    case ClarisWksDatabaseInternal::Field::F_ValueList: // ok, the text is stored
884
#if !defined(__clang__)
885
    default:
886
#endif
887
3.45k
      switch (dbase.m_fields[f].m_resType) {
888
1.58k
      case 1:
889
1.58k
        formats[f].m_format=MWAWCell::F_NUMBER;
890
1.58k
        formats[f].m_numberFormat=MWAWCell::F_NUMBER_GENERIC;
891
1.58k
        break;
892
2
      case 2:
893
2
        formats[f].m_format=MWAWCell::F_DATE;
894
2
        break;
895
0
      case 3:
896
0
        formats[f].m_format=MWAWCell::F_TIME;
897
0
        break;
898
1.86k
      default:
899
1.86k
        break;
900
3.45k
      }
901
3.45k
      break;
902
6.70k
    }
903
6.70k
  }
904
1.67k
  dbase.m_content->setDatabaseFormats(formats);
905
906
1.67k
  std::vector<float> colSize(size_t(numFields),72);
907
1.67k
  sheetListener->openSheet(colSize, librevenge::RVNG_POINT);
908
1.67k
  MWAWInputStreamPtr &input= m_parserState->m_input;
909
1.67k
  MWAWFont const defFont;
910
  // increase the row height, if we can have some picture
911
1.67k
  float const rowHeight=!hasMultimedia ? 14.f : 72.f;
912
32.2k
  for (size_t r=0; r < recordsPos.size(); ++r) {
913
30.5k
    sheetListener->openSheetRow(rowHeight, librevenge::RVNG_POINT);
914
152k
    for (int c=0; c < numFields; ++c) {
915
122k
      ClarisWksDbaseContent::Record rec;
916
122k
      if (!dbase.m_content->get(MWAWVec2i(c,recordsPos[r]),rec)) continue;
917
40.2k
      sheetListener->setFont(defFont);
918
40.2k
      MWAWCell cell;
919
40.2k
      cell.setPosition(MWAWVec2i(c,int(r)));
920
40.2k
      cell.setFormat(rec.m_format);
921
40.2k
      cell.setHAlignment(rec.m_hAlign);
922
40.2k
      bool isMultimedia=false;
923
40.2k
      if (c<int(numDataFields)) {
924
40.2k
        auto const &field=dbase.m_fields[size_t(c)];
925
40.2k
        if (field.m_type==ClarisWksDatabaseInternal::Field::F_Multimedia)
926
4
          isMultimedia=true;
927
40.2k
        else if (field.m_type==ClarisWksDatabaseInternal::Field::F_Formula && !field.m_formula.empty()) {
928
0
          rec.m_content.m_formula=field.m_formula;
929
0
          for (auto &d : rec.m_content.m_formula) {
930
0
            if (d.m_type == MWAWCellContent::FormulaInstruction::F_Cell)
931
0
              d.m_position[0][1]=int(r);
932
0
          }
933
0
        }
934
40.2k
        else if (field.m_type==ClarisWksDatabaseInternal::Field::F_PopupMenu ||
935
40.2k
                 field.m_type==ClarisWksDatabaseInternal::Field::F_RadioButton) {
936
5
          if (rec.m_content.isValueSet()) {
937
3
            auto enumId = int(rec.m_content.m_value+0.5);
938
            // checkme: if the enum list is a list of float, the enum value can be stored as value:-~
939
3
            if (enumId > 0 && enumId <= int(field.m_valuesList.size()) &&
940
0
                double(enumId)-0.01 < rec.m_content.m_value &&
941
0
                double(enumId)+0.01 > rec.m_content.m_value) {
942
0
              rec.m_format.m_format=MWAWCell::F_TEXT;
943
0
              rec.m_content.m_textEntry=field.m_valuesList[size_t(enumId-1)];
944
0
              rec.m_content.m_valueSet=false;
945
0
              cell.setFormat(rec.m_format);
946
0
            }
947
3
          }
948
5
        }
949
40.2k
      }
950
      // change the reference date from 1/1/1904 to 1/1/1900
951
40.2k
      if (rec.m_format.m_format==MWAWCell::F_DATE && rec.m_content.isValueSet())
952
2.39k
        rec.m_content.setValue(rec.m_content.m_value+1460);
953
37.8k
      else if (isMultimedia) // do not export the picture id
954
4
        rec.m_content.m_valueSet=false;
955
40.2k
      sheetListener->openSheetCell(cell, rec.m_content);
956
40.2k
      if (isMultimedia) {
957
4
        auto pictId = int(rec.m_content.m_value+0.5); // pictId is saved as float, converts it back to an int
958
4
        if (pictId>0) {
959
2
          MWAWPosition pos(MWAWVec2f(0,0), MWAWVec2f(72,72), librevenge::RVNG_POINT);
960
2
          pos.m_anchorTo=MWAWPosition::Cell;
961
          // we have only one sheet, so compute the cell name by hand
962
2
          std::string endCellName=std::string("Sheet0.")+MWAWCell::getBasicCellName(MWAWVec2i(int(c+1),int(r+1)));
963
2
          pos.m_anchorCellName=endCellName.c_str();
964
2
          m_document.sendDatabasePictZone(pictId, listener, pos);
965
2
        }
966
4
      }
967
40.2k
      else if (rec.m_content.m_textEntry.valid()) {
968
18.6k
        long fPos = input->tell();
969
18.6k
        input->seek(rec.m_content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET);
970
18.6k
        long endPos = rec.m_content.m_textEntry.end();
971
18.6k
        int cPos=0;
972
4.67M
        while (!input->isEnd() && input->tell() < endPos) {
973
4.65M
          if (rec.m_posToFontMap.find(cPos) != rec.m_posToFontMap.end())
974
9.50k
            sheetListener->setFont(rec.m_posToFontMap.find(cPos)->second);
975
4.65M
          auto ch=static_cast<unsigned char>(input->readULong(1));
976
4.65M
          if (ch==9)
977
8.70k
            sheetListener->insertTab();
978
4.64M
          else if (ch==0xa || ch==0xd)
979
58.3k
            sheetListener->insertEOL();
980
4.58M
          else
981
4.58M
            sheetListener->insertCharacter(ch, input, endPos);
982
4.65M
          ++cPos;
983
4.65M
        }
984
18.6k
        input->seek(fPos,librevenge::RVNG_SEEK_SET);
985
18.6k
      }
986
40.2k
      sheetListener->closeSheetCell();
987
40.2k
    }
988
30.5k
    sheetListener->closeSheetRow();
989
30.5k
  }
990
1.67k
  sheetListener->closeSheet();
991
1.67k
  return true;
992
1.67k
}
993
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: