/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
/* libmwaw
* Version: MPL 2.0 / LGPLv2+
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License or as specified alternatively below. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Major Contributor(s):
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
* Copyright (C) 2006, 2007 Andrew Ziem
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
*
*
* All Rights Reserved.
*
* For minor contributions see the git repository.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
* in which case the provisions of the LGPLv2+ are applicable
* instead of those above.
*/
#include <time.h>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <limits>
#include <librevenge/librevenge.h>
#include "MWAWDebug.hxx"
#include "MWAWHeader.hxx"
#include "MWAWInputStream.hxx"
#include "MWAWListener.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWParser.hxx"
#include "ClarisWksDocument.hxx"
#include "ClarisWksStyleManager.hxx"
#include "ClarisWksDbaseContent.hxx"
ClarisWksDbaseContent::ClarisWksDbaseContent(ClarisWksDocument &document, bool spreadsheet)
: m_version(0)
, m_isSpreadsheet(spreadsheet)
, m_document(document)
, m_parserState(document.m_parserState)
, m_idColumnMap()
, m_positionSet()
, m_dbFormatList()
{
if (!m_parserState || !m_parserState->m_header) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::ClarisWksDbaseContent: can not find the file header\n"));
return;
}
m_version = m_parserState->m_header->getMajorVersion();
}
ClarisWksDbaseContent::~ClarisWksDbaseContent()
{
}
void ClarisWksDbaseContent::setDatabaseFormats(std::vector<ClarisWksStyleManager::CellFormat> const &format)
{
if (m_isSpreadsheet) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::setDatabaseFormats: called with spreadsheet\n"));
return;
}
m_dbFormatList=format;
}
bool ClarisWksDbaseContent::getExtrema(MWAWVec2i &min, MWAWVec2i &max) const
{
if (m_idColumnMap.empty())
return false;
bool first=true;
for (auto it : m_idColumnMap) {
int col=it.first;
Column const &column=it.second;
if (column.m_idRecordMap.empty())
continue;
max[0]=col;
for (auto rIt : column.m_idRecordMap) {
int row=rIt.first;
if (first) {
min[0]=col;
min[1]=max[1]=row;
first=false;
}
else if (row < min[1])
min[1]=row;
else if (row > max[1])
max[1]=row;
}
}
return !first;
}
void ClarisWksDbaseContent::updateCellPositionsSet() const
{
if (!m_positionSet.empty() || m_idColumnMap.empty())
return;
for (auto it : m_idColumnMap) {
int col=it.first;
Column const &column=it.second;
for (auto rIt : column.m_idRecordMap) {
int row=rIt.first;
m_positionSet.insert(MWAWVec2i(col,row));
}
}
}
bool ClarisWksDbaseContent::getRecordList(std::vector<int> &list) const
{
list.resize(0);
if (m_idColumnMap.empty())
return false;
std::set<int> set;
for (auto it : m_idColumnMap) {
Column const &column=it.second;
for (auto rIt : column.m_idRecordMap) {
int row=rIt.first;
if (set.find(row)==set.end())
set.insert(row);
}
}
if (set.empty())
return false;
list = std::vector<int>(set.begin(), set.end());
return true;
}
bool ClarisWksDbaseContent::getColumnList(int row, std::vector<int> &list) const
{
list.resize(0);
if (m_idColumnMap.empty())
return false;
updateCellPositionsSet();
auto rIt=m_positionSet.lower_bound(MWAWVec2i(-1,row));
while (rIt!=m_positionSet.end()) {
MWAWVec2i const &pos=*(rIt++);
if (pos[1]!=row)
break;
list.push_back(pos[0]);
}
return !list.empty();
}
bool ClarisWksDbaseContent::readContent()
{
if (!m_parserState) return false;
MWAWInputStreamPtr &input= m_parserState->m_input;
long pos = input->tell();
auto sz = long(input->readULong(4));
/** ARGHH: this zone is almost the only zone which count the header in sz ... */
long endPos = pos+sz;
std::string zoneName(m_isSpreadsheet ? "spread" : "dbase");
input->seek(endPos, librevenge::RVNG_SEEK_SET);
if (long(input->tell()) != endPos || sz < 6) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readContent: file is too short\n"));
return false;
}
input->seek(pos+4, librevenge::RVNG_SEEK_SET);
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
f << "Entries(DBHeader)[" << zoneName << "]:";
auto N = static_cast<int>(input->readULong(2));
f << "N=" << N << ",";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->pushLimit(endPos);
readColumnList();
if (input->tell() == endPos) {
input->popLimit();
return true;
}
/* can we have more than one sheet ? If so, going into the while
loop may be ok, we will not read the data...*/
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readContent: find extra data\n"));
bool ok=true;
while (input->tell() < endPos) {
pos = input->tell();
sz = long(input->readULong(4));
long zoneEnd=pos+4+sz;
if (zoneEnd > endPos || (sz && sz < 12)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readContent: find a odd content field\n"));
ok=false;
break;
}
if (!sz) {
ascFile.addPos(pos);
ascFile.addNote("Nop");
continue;
}
std::string name("");
for (int i = 0; i < 4; i++)
name+=char(input->readULong(1));
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readContent: find unexpected content field\n"));
f << "DBHeader[" << zoneName << "]:###" << name;
ascFile.addDelimiter(input->tell(),'|');
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(zoneEnd, librevenge::RVNG_SEEK_SET);
}
input->popLimit();
return ok;
}
bool ClarisWksDbaseContent::readColumnList()
{
if (!m_parserState) return false;
MWAWInputStreamPtr &input= m_parserState->m_input;
long pos=input->tell();
long sz=input->readLong(4);
std::string hName("");
for (int i=0; i < 4; ++i) hName+=char(input->readULong(1));
if (sz!=0x408 || hName!="CTAB" || !input->checkPosition(pos+4+sz)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readCOLM: the entry seems bad\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
if (m_isSpreadsheet)
f << "Entries(DBCTAB)[spread]:";
else
f << "Entries(DBCTAB)[dbase]:";
auto N=static_cast<int>(input->readLong(2));
if (N) f << "Ncols=" << N << ",";
auto val=static_cast<int>(input->readLong(2));
if (val) f << "Nrows=" << val << ",";
if (N<0 || N>255) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readColumnList: the entries number of elements seems bad\n"));
f << "####";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
return false;
}
f << "ptr=[";
long ptr;
std::vector<long> listIds;
for (int i=0; i <= N; i++) {
ptr=long(input->readULong(4));
listIds.push_back(ptr);
if (ptr)
f << std::hex << ptr << std::dec << ",";
else
f << "_,";
}
f << "],";
for (int i=N+1; i<256; i++) { // always 0
ptr=long(input->readULong(4));
if (!ptr) continue;
static bool first=true;
if (first) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readColumnList: find some extra values\n"));
first=false;
}
f << "#g" << i << "=" << ptr << ",";
}
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
for (size_t c=0; c<listIds.size(); c++) {
if (!listIds[c]) continue;
pos=input->tell();
if (readColumn(int(c)))
continue;
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
return true;
}
bool ClarisWksDbaseContent::readColumn(int c)
{
if (!m_parserState) return false;
MWAWInputStreamPtr &input= m_parserState->m_input;
long pos=input->tell();
long sz=input->readLong(4);
std::string hName("");
for (int i=0; i < 4; ++i) hName+=char(input->readULong(1));
int cPos[2];
for (int &i : cPos)
i=static_cast<int>(input->readLong(2));
if (sz!=8+4*(cPos[1]-cPos[0]+1) || hName!="COLM" || !input->checkPosition(pos+4+sz)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readCOLM: the entry seems bad\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
if (m_isSpreadsheet)
f << "Entries(DBCOLM)[spread]:";
else
f << "Entries(DBCOLM)[dbase]:";
f << "ptr[" << cPos[0] << "<=>" << cPos[1] << "]=[";
std::vector<long> listIds;
listIds.resize(size_t(cPos[0]),0);
for (int i=cPos[0]; i <= cPos[1]; i++) {
auto ptr=long(input->readULong(4));
listIds.push_back(ptr);
if (ptr)
f << std::hex << ptr << std::dec << ",";
else
f << "_,";
}
f << "],";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
// now read the chnk
ClarisWksDbaseContent::Column col;
bool ok=true;
for (size_t i=0; i < listIds.size(); ++i) {
pos=input->tell();
if (!listIds[i] || readRecordList(MWAWVec2i(c,64*int(i)), col))
continue;
input->seek(pos, librevenge::RVNG_SEEK_SET);
ok=false;
break;
}
if (!col.m_idRecordMap.empty())
m_idColumnMap[c]=col;
return ok;
}
bool ClarisWksDbaseContent::readRecordList(MWAWVec2i const &where, Column &col)
{
if (!m_parserState) return false;
MWAWInputStreamPtr &input= m_parserState->m_input;
long pos=input->tell();
long sz=input->readLong(4);
long endPos=pos+4+sz;
std::string hName("");
for (int i=0; i < 4; ++i) hName+=char(input->readULong(1));
auto N=static_cast<int>(input->readULong(2));
if (sz<6+134 || hName!="CHNK" || !input->checkPosition(pos+4+sz) || N>0x40) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordList: the entry seems bad\n"));
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
std::string zoneName(m_isSpreadsheet ? "spread" : "dbase");
f << "Entries(DBCHNK)[" << zoneName << "]:N=" << N << ",";
auto type=static_cast<int>(input->readULong(2)); // often 400, 800...
f << "type=" << std::hex << type << std::dec << ",";
int dim[2]; // checkme
for (auto &d : dim) d=static_cast<int>(input->readLong(2));
f << "dim=" << dim[0] << "x" << dim[1] << ",";
f << "depl=[";
std::vector<long> ptrLists(64,0);
int find=0;
for (size_t i=0; i < 64; ++i) {
auto depl=long(input->readLong(2));
if (depl==0) {
f << "_,";
continue;
}
find++;
long fPos=pos+4+depl;
if (fPos > endPos) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordList: the %d ptr seems bad\n", static_cast<int>(i)));
f << "###";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
return false;
}
f << std::hex << depl << std::dec << ",";
ptrLists[i]=fPos;
}
f << "],";
if (find!=N) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordList: the number of find data seems bad\n"));
f << "###find=" << find << "!=" << N << ",";
}
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
for (size_t i=0; i<64; ++i) {
if (!ptrLists[i]) continue;
Record record;
MWAWVec2i wh(where[0],where[1]+static_cast<int>(i));
if ((m_isSpreadsheet && readRecordSS(wh, ptrLists[i], record)) ||
(!m_isSpreadsheet && readRecordDB(wh, ptrLists[i], record))) {
col.m_idRecordMap[wh[1]]=record;
continue;
}
f.str("");
f << "DBCHNK[" << zoneName << wh << "]:#";
input->seek(ptrLists[i], librevenge::RVNG_SEEK_SET);
auto fType=static_cast<int>(input->readULong(1));
f << "type=" << std::hex << fType << std::dec << ",";
ascFile.addPos(ptrLists[i]);
ascFile.addNote(f.str().c_str());
col.m_idRecordMap[wh[1]]=record;
}
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
bool ClarisWksDbaseContent::readRecordSSV1(MWAWVec2i const &id, long pos, ClarisWksDbaseContent::Record &record)
{
record=ClarisWksDbaseContent::Record();
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
f << "DBCHNK[spread" << id << "]:";
MWAWInputStreamPtr &input= m_parserState->m_input;
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto val=static_cast<int>(input->readULong(1));
int type=(val>>4);
int fileFormat=(val&0xF);
auto &format=record.m_format;
switch (fileFormat) {
case 0: // general
break;
case 1:
format.m_format=MWAWCell::F_NUMBER;
format.m_numberFormat=MWAWCell::F_NUMBER_CURRENCY;
break;
case 2:
format.m_format=MWAWCell::F_NUMBER;
format.m_numberFormat=MWAWCell::F_NUMBER_PERCENT;
break;
case 3:
format.m_format=MWAWCell::F_NUMBER;
format.m_numberFormat=MWAWCell::F_NUMBER_SCIENTIFIC;
break;
case 4:
format.m_format=MWAWCell::F_NUMBER;
format.m_numberFormat=MWAWCell::F_NUMBER_DECIMAL;
break;
case 5:
case 6:
case 7:
case 8:
case 9: {
static char const *wh[]= {"%m/%d/%y", "%B %d, %y", "%B %d, %Y", "%a, %b %d %y", "%A, %B %d %Y" };
format.m_format=MWAWCell::F_DATE;
format.m_DTFormat=wh[fileFormat-5];
break;
}
case 10:
case 11:
case 12:
case 13: {
static char const *wh[]= {"%I:%M %p", "%I:%M:%S %p", "%H:%M", "%H:%M:%S" };
format.m_format=MWAWCell::F_TIME;
format.m_DTFormat=wh[fileFormat-10];
break;
}
default: // unknown
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: find unknown format\n"));
f << "format=##" << fileFormat << ",";
break;
}
bool ok=true;
auto ord=static_cast<int>(input->readULong(1));
if (ord&8) {
f << "commas[thousand],";
ord &= 0xF7;
}
if (ord&4) {
f << "parenthese[negative],";
ord &= 0xFB;
}
if (ord&2) {
f << "lock,";
ord &= 0xFD;
}
if (ord&1) {
if (!input->checkPosition(pos+8)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: can not read format\n"));
f << "###";
ok = false;
}
else {
MWAWFont &font = record.m_font;
font = MWAWFont();
auto fId= static_cast<int>(input->readULong(2));
if (fId!=0xFFFF)
font.setId(m_document.getStyleManager()->getFontId(static_cast<int>(fId)));
font.setSize(float(input->readULong(1)));
auto flag =static_cast<int>(input->readULong(1));
uint32_t flags=0;
if (flag&0x1) flags |= MWAWFont::boldBit;
if (flag&0x2) flags |= MWAWFont::italicBit;
if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
if (flag&0x8) flags |= MWAWFont::embossBit;
if (flag&0x10) flags |= MWAWFont::shadowBit;
if (flag&0x20) font.setDeltaLetterSpacing(-1);
if (flag&0x40) font.setDeltaLetterSpacing(1);
if (flag&0x80) font.setStrikeOutStyle(MWAWFont::Line::Simple);
font.setFlags(flags);
auto colId = static_cast<int>(input->readULong(1));
if (colId!=1) {
MWAWColor col;
if (m_document.getStyleManager()->getColor(colId, col))
font.setColor(col);
else {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: unknown color %d\n", colId));
}
}
f << "font=[" << font.getDebugString(m_parserState->m_fontConverter) << "],";
val=static_cast<int>(input->readULong(1));
record.m_borders = (val>>4);
if (record.m_borders) {
f << "border=";
if (record.m_borders&1) f << "L";
if (record.m_borders&2) f << "T";
if (record.m_borders&4) f << "R";
if (record.m_borders&8) f << "B";
f << ",";
}
val &=0xF;
switch ((val>>2)) {
case 1:
record.m_hAlign=MWAWCell::HALIGN_LEFT;
f << "left,";
break;
case 2:
record.m_hAlign=MWAWCell::HALIGN_CENTER;
f << "center,";
break;
case 3:
record.m_hAlign=MWAWCell::HALIGN_RIGHT;
f << "right,";
break;
default:
break;
}
val &=0x3;
if (val) f << "#unk=" << val << ",";
ascFile.addDelimiter(pos+2,'|');
input->seek(pos+8, librevenge::RVNG_SEEK_SET);
ascFile.addDelimiter(pos+8,'|');
ord &= 0xFE;
}
}
format.m_digits=(ord>>4);
ord &= 0xF;
if (ok && ord) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: find unexpected order\n"));
f << "###ord=" << std::hex << ord << std::dec;
ok = false;
}
auto &content=record.m_content;
if (ok && type==4) {
f << "formula,";
long actPos=input->tell();
auto formSz=static_cast<int>(input->readULong(1));
if (!input->checkPosition(actPos+2+formSz)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: the formula seems bad\n"));
f << "###";
ok = false;
}
else {
ascFile.addDelimiter(input->tell(),'|');
std::vector<MWAWCellContent::FormulaInstruction> formula;
std::string error;
if (readFormula(id, actPos+1+formSz, formula, error)) {
content.m_contentType=MWAWCellContent::C_FORMULA;
content.m_formula=formula;
}
else {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: can not read a formula\n"));
f << "###";
}
f << "form=";
for (auto const &fo : formula)
f << fo;
f << error << ",";
if ((formSz%2)==0) ++formSz;
input->seek(actPos+1+formSz, librevenge::RVNG_SEEK_SET);
ascFile.addDelimiter(input->tell(),'|');
val=static_cast<int>(input->readULong(1));
type=(val>>4);
if (val&0xF) f << "unkn0=" << (val&0xF) << ",";
val=static_cast<int>(input->readULong(1));
if (val) f << "unkn1=" << (val) << ",";
}
}
if (ok) {
long actPos=input->tell();
switch (type) {
case 0:
case 1:
if (type==0)
f << "int,";
else
f << "long,";
if (!input->checkPosition(actPos+2+2*type)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: unexpected size for a int\n"));
f << "###";
ok = false;
break;
}
record.m_valueType=MWAWCellContent::C_NUMBER;
content.setValue(double(input->readLong(2+2*type)));
f << "val=" << content.m_value << ",";
break;
case 2: {
f << "float,";
if (!input->checkPosition(actPos+10)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: unexpected size for a float\n"));
f << "###";
ok=false;
break;
}
record.m_valueType=MWAWCellContent::C_NUMBER;
double value;
if (input->readDouble10(value, record.m_hasNaNValue)) {
content.setValue(value);
f << "val=" << value << ",";
}
else {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: can not read a float\n"));
f << "###,";
}
break;
}
case 3: {
f << "string,";
auto stringSize = static_cast<int>(input->readULong(1));
if (!input->checkPosition(actPos+1+stringSize)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: the string(II) seems bad\n"));
f << "###";
ok=false;
break;
}
record.m_valueType=MWAWCellContent::C_TEXT;
content.m_textEntry.setBegin(input->tell());
content.m_textEntry.setLength(long(stringSize));
std::string data("");
for (int c=0; c < stringSize; ++c) data+=char(input->readULong(1));
f << "val=" << data << ",";
break;
}
// case 4: the formula is already read
case 5: // bool
if (input->checkPosition(actPos+1)) {
if (!fileFormat)
format.m_format=MWAWCell::F_BOOLEAN;
record.m_valueType=MWAWCellContent::C_NUMBER;
content.setValue(double(input->readLong(1)));
f << "val=" << content.m_value << ",";
break;
}
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: can not read a bool\n"));
f << "###bool,";
break;
case 6:
if (input->checkPosition(actPos+1)) {
record.m_valueType=MWAWCellContent::C_NUMBER;
content.setValue(std::numeric_limits<double>::quiet_NaN());
record.m_hasNaNValue = true;
f << "val=nan" << input->readLong(1) << ",";
break;
}
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: can not read a nanc[formula]\n"));
f << "###nan[res],";
break;
case 7: // empty
break;
case 8: // checkme: does such cell can have data?
f << "recovered,";
break;
default:
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSSV1: unexpected type\n"));
f << "###type=" << type << ",";
ok=false;
break;
}
}
if (content.m_contentType!=MWAWCellContent::C_FORMULA)
content.m_contentType=record.m_valueType;
if (format.m_format==MWAWCell::F_UNKNOWN && content.isValueSet()) {
format.m_format=MWAWCell::F_NUMBER;
format.m_numberFormat=MWAWCell::F_NUMBER_GENERIC;
}
f << format << ",";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
if (ok) {
ascFile.addPos(input->tell());
ascFile.addNote("_");
}
return ok;
}
bool ClarisWksDbaseContent::readRecordSS(MWAWVec2i const &id, long pos, ClarisWksDbaseContent::Record &record)
{
if (m_version <= 3)
return readRecordSSV1(id, pos, record);
record=ClarisWksDbaseContent::Record();
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
f << "DBCHNK[spread" << id << "]:";
MWAWInputStreamPtr &input= m_parserState->m_input;
input->seek(pos, librevenge::RVNG_SEEK_SET);
auto sz=long(input->readULong(2));
long endPos=pos+sz+2;
if (!input->checkPosition(endPos) || sz < 4) {
f << "###sz=" << sz;
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the sz seems bad\n"));
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
return true;
}
auto type=static_cast<int>(input->readULong(1));
// next some format ?
auto val= static_cast<int>(input->readULong(1)); // 0-46
if (val) f << "format=" << std::hex << val << std::dec << ",";
record.m_style=static_cast<int>(input->readLong(2));
if (record.m_style) f << "Style-" << record.m_style << ",";
int fileFormat=0;
ClarisWksStyleManager::Style style;
ClarisWksStyleManager::CellFormat format;
if (m_document.getStyleManager()->get(record.m_style, style)) {
if (m_document.getStyleManager()->get(style.m_cellFormatId, format)) {
f << format << ",";
fileFormat=format.m_fileFormat;
}
if (style.m_fontId>=0)
m_document.getStyleManager()->get(style.m_fontId, record.m_font);
MWAWGraphicStyle graphStyle;
if (style.m_graphicId>=0 && m_document.getStyleManager()->get(style.m_graphicId, graphStyle)) {
if (graphStyle.hasSurfaceColor())
record.m_backgroundColor=graphStyle.m_surfaceColor;
}
}
switch (fileFormat) {
case 0: // general
break;
case 1:
format.m_format=MWAWCell::F_NUMBER;
format.m_numberFormat=MWAWCell::F_NUMBER_CURRENCY;
break;
case 2:
format.m_format=MWAWCell::F_NUMBER;
format.m_numberFormat=MWAWCell::F_NUMBER_PERCENT;
break;
case 3:
format.m_format=MWAWCell::F_NUMBER;
format.m_numberFormat=MWAWCell::F_NUMBER_SCIENTIFIC;
break;
case 4:
format.m_format=MWAWCell::F_NUMBER;
format.m_numberFormat=MWAWCell::F_NUMBER_DECIMAL;
break;
case 5:
case 6:
case 7:
case 8:
case 9: {
static char const *wh[]= {"%m/%d/%y", "%B %d, %y", "%B %d, %Y", "%a, %b %d %y", "%A, %B %d %Y" };
format.m_format=MWAWCell::F_DATE;
format.m_DTFormat=wh[fileFormat-5];
break;
}
case 10: // unknown
case 11:
break;
case 12:
case 13:
case 14:
case 15: {
static char const *wh[]= {"%H:%M", "%H:%M:%S", "%I:%M %p", "%I:%M:%S %p" };
format.m_format=MWAWCell::F_TIME;
format.m_DTFormat=wh[fileFormat-12];
break;
}
default: // unknown
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: find unknown format\n"));
f << "format=##" << fileFormat << ",";
break;
}
auto &content=record.m_content;
bool ok=true;
if (type==4) {
f << "formula,";
if (sz<7) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the formula seems bad\n"));
f << "##sz,";
ok = false;
}
else {
type=static_cast<int>(input->readULong(1));
val=static_cast<int>(input->readLong(1));
if (val) f << "unkn=" << val << ","; // 1: maybe an enum stored by value instead of by id
auto formSz=static_cast<int>(input->readULong(1));
long actPos=input->tell();
ascFile.addDelimiter(actPos,'|');
if (8+formSz > sz) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the formula seems bad\n"));
f << "###";
ok=false;
}
else {
std::vector<MWAWCellContent::FormulaInstruction> formula;
std::string error;
if (readFormula(id, actPos+1+formSz, formula, error)) {
content.m_contentType=MWAWCellContent::C_FORMULA;
content.m_formula=formula;
}
else {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: can not read a formule\n"));
f << "###";
}
f << "form=";
for (auto const &fo : formula)
f << fo;
f << error << ",";
/** checkme: there does not seem to be alignment, but another
variable before the result */
input->seek(actPos+formSz+1, librevenge::RVNG_SEEK_SET);
ascFile.addDelimiter(input->tell(),'|');
sz=4+int(endPos-input->tell());
}
}
}
if (ok) {
switch (type) {
case 0:
case 1:
if (type==0)
f << "int,";
else
f << "long,";
if (sz==4) {
// rare, broken file ? AppleWorks seems to display a 0, but show a different number in the formula
record.m_valueType=MWAWCellContent::C_NUMBER;
content.setValue(0);
f << "##val=" << 0 << ",";
break;
}
if (sz<6+2*type) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: unexpected size for a int\n"));
f << "###";
break;
}
record.m_valueType=MWAWCellContent::C_NUMBER;
content.setValue(double(input->readLong(2+2*type)));
f << "val=" << content.m_value << ",";
break;
case 2: {
f << "float,";
if (sz<0xe) {
if (sz>=0xc && input->checkPosition(input->tell()+10)) {
// rare, broken file ? but as we have the main 8 bytes, let continue
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: unexpected size for a float, try to read it\n"));
f << "#";
}
else {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: unexpected size for a float\n"));
f << "###";
break;
}
}
record.m_valueType=MWAWCellContent::C_NUMBER;
double value;
if (input->readDouble10(value, record.m_hasNaNValue)) {
content.setValue(value);
f << value << ",";
}
else {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: can not read a float\n"));
f << "###,";
}
break;
}
case 3: {
f << "string,";
if (sz<5) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the string seems bad\n"));
f << "###";
break;
}
auto stringSize = static_cast<int>(input->readULong(1));
if (stringSize+5>sz) {
// rare, broken file ? let try to read the beginning of the file
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the string(II) seems bad, try to read the beginning\n"));
f << "###";
if (sz==5)
break;
stringSize=int(sz)-5;
}
record.m_valueType=MWAWCellContent::C_TEXT;
content.m_textEntry.setBegin(input->tell());
content.m_textEntry.setLength(long(stringSize));
std::string data("");
for (int c=0; c < stringSize; ++c) data+=char(input->readULong(1));
f << data << ",";
break;
}
// 4: formula
case 5: // bool
if (sz>=4+1) {
if (format.m_format==MWAWCell::F_UNKNOWN)
format.m_format=MWAWCell::F_BOOLEAN;
record.m_valueType=MWAWCellContent::C_NUMBER;
content.setValue(double(input->readLong(1)));
f << "val=" << content.m_value << ",";
break;
}
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: can not read a bool\n"));
f << "###bool,";
break;
case 6:
if (sz>=4+1) {
record.m_valueType=MWAWCellContent::C_NUMBER;
content.setValue(std::numeric_limits<double>::quiet_NaN());
record.m_hasNaNValue = true;
f << "val=nan" << input->readLong(1) << ",";
break;
}
break;
case 7: // link/anchor/goto
if (sz<4) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the mark size seems bad\n"));
f << "###mark";
break;
}
f << "mark,";
break;
case 8:
case 9:
f << "type" << type << ",";
if (sz<4) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordSS: the type%d seems bad\n", type));
f << "###";
break;
}
break;
default:
f << "#type=" << type << ",";
}
}
if (content.m_contentType!=MWAWCellContent::C_FORMULA)
content.m_contentType=record.m_valueType;
record.m_format=format;
record.m_hAlign=format.m_hAlign;
record.m_borders=format.m_borders;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
ascFile.addPos(endPos);
ascFile.addNote("_");
return true;
}
bool ClarisWksDbaseContent::readRecordDB(MWAWVec2i const &id, long pos, ClarisWksDbaseContent::Record &record)
{
record=ClarisWksDbaseContent::Record();
libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
libmwaw::DebugStream f;
f << "DBCHNK[dbase" << id << "]:";
MWAWInputStreamPtr &input= m_parserState->m_input;
input->seek(pos, librevenge::RVNG_SEEK_SET);
long sz=0;
long endPos=-1;
if (m_version>3) {
sz=long(input->readULong(2));
endPos=pos+sz+2;
if (!input->checkPosition(endPos) || sz < 2) {
f << "###sz=" << sz;
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: the sz seems bad\n"));
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
return true;
}
}
auto val=static_cast<int>(input->readULong(2));
int type=(val>>12);
val = int(val&0xFFF);
MWAWCellContent &content=record.m_content;
switch (type) {
case 0: {
f << "string,";
if ((m_version<=3&&!input->checkPosition(pos+2+val)) ||
(m_version>3 && (val+2>sz || val+4<sz))) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: the string(II) seems bad\n"));
f << "###";
break;
}
content.m_contentType=MWAWCellContent::C_TEXT;
content.m_textEntry.setBegin(input->tell());
content.m_textEntry.setLength(long(val));
std::string data("");
for (int c=0; c < val; ++c) data+=char(input->readULong(1));
f << data << ",";
break;
}
case 2: {
if ((m_version<=3&&!input->checkPosition(pos+2+val+2)) ||
(m_version>3 && val+2>sz)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: can not read a text and style field\n"));
f << "##string2" << type << "[" << val << "],";
break;
}
content.m_contentType=MWAWCellContent::C_TEXT;
content.m_textEntry.setBegin(input->tell());
content.m_textEntry.setLength(long(val));
std::string data("");
for (int c=0; c < val; ++c) data+=char(input->readULong(1));
f << "string2," << data << ",";
if (val&1) input->seek(1, librevenge::RVNG_SEEK_CUR);
auto N=int(input->readULong(2));
f << "N=" << N << ",";
int fontSize = m_version<=3 ? 10 : m_version<=5 ? 12 : 18;
if ((m_version<=3 && !input->checkPosition(input->tell()+fontSize*N)) ||
(m_version>3 && input->tell()+fontSize*N>endPos)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: can not read the text's style\n"));
f << "##N";
break;
}
for (int i=0; i<N; ++i) {
MWAWFont font;
int posChar;
if (!m_document.getStyleManager()->readFontAndPos(i, posChar, font)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: can not read the text's style\n"));
f << "##font";
break;
}
record.m_posToFontMap[posChar]=font;
}
break;
}
case 4:
// find also some string here when val is not null so let test
if (val && ((m_version>3 && val+2<=sz && val+4>=sz) || (m_version<=3 && input->checkPosition(pos+2+val)))) {
content.m_contentType=MWAWCellContent::C_TEXT;
content.m_textEntry.setBegin(input->tell());
content.m_textEntry.setLength(long(val));
std::string data("");
for (int c=0; c < val; ++c) data+=char(input->readULong(1));
f << "string4," << data << ",";
break;
}
f << "intDB,";
if ((m_version<=3&&!input->checkPosition(pos+2)) || (m_version>3 && sz!=2)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: unexpected size for a int\n"));
f << "###";
break;
}
content.m_contentType=MWAWCellContent::C_NUMBER;
content.setValue(double(input->readLong(1)));
break;
case 8:
case 9: {
if (val) f << "unkn=" << std::hex << val << std::dec << ",";
f << "float" << type << ",";
if ((m_version<=3&&!input->checkPosition(pos+12)) || (m_version>3 && sz!=12)) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: unexpected size for a float\n"));
f << "###";
break;
}
double value;
if (input->readDouble10(value, record.m_hasNaNValue)) {
content.m_contentType=MWAWCellContent::C_NUMBER;
content.setValue(value);
f << value << ",";
}
else {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readRecordDB: can not read a float\n"));
f << "###,";
}
break;
}
default:
if (val) f << "unkn=" << std::hex << val << std::dec << ",";
f << "#type=" << type << ",";
}
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
if (m_version>3) {
ascFile.addPos(endPos);
ascFile.addNote("_");
}
return true;
}
bool ClarisWksDbaseContent::get(MWAWVec2i const &pos, ClarisWksDbaseContent::Record &record) const
{
auto it=m_idColumnMap.find(pos[0]);
if (it==m_idColumnMap.end()) return false;
Column const &col=it->second;
auto rIt=col.m_idRecordMap.find(pos[1]);
if (rIt==col.m_idRecordMap.end()) return false;
record=rIt->second;
if (m_isSpreadsheet) return true;
static bool first=true;
if (pos[0]>=0&&pos[0]<int(m_dbFormatList.size())) {
auto const &format=m_dbFormatList[size_t(pos[0])];
record.m_format=format;
record.m_fileFormat=format.m_fileFormat;
record.m_hAlign=format.m_hAlign;
}
else if (first) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::get: can not find format for field %d\n", pos[0]));
first=false;
}
return true;
}
bool ClarisWksDbaseContent::send(MWAWVec2i const &pos)
{
MWAWListenerPtr listener=m_parserState->getMainListener();
if (!listener) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::send: can not find the listener\n"));
return false;
}
Record record;
if (!get(pos, record)) return true;
auto const &content=record.m_content;
listener->setFont(record.m_font);
auto contentType= content.m_contentType==MWAWCellContent::C_FORMULA ? record.m_valueType : content.m_contentType;
MWAWParagraph para;
para.m_justify =
record.m_hAlign==MWAWCell::HALIGN_LEFT ? MWAWParagraph::JustificationLeft :
record.m_hAlign==MWAWCell::HALIGN_CENTER ? MWAWParagraph::JustificationCenter :
record.m_hAlign==MWAWCell::HALIGN_RIGHT ? MWAWParagraph::JustificationRight :
contentType==MWAWCellContent::C_TEXT ? MWAWParagraph::JustificationLeft :
MWAWParagraph::JustificationRight;
listener->setParagraph(para);
switch (contentType) {
case MWAWCellContent::C_NUMBER:
if (record.m_fileFormat)
send(content.m_value, record.m_hasNaNValue, ClarisWksStyleManager::CellFormat(record.m_format));
else {
std::stringstream s;
s << content.m_value;
listener->insertUnicodeString(librevenge::RVNGString(s.str().c_str()));
}
break;
case MWAWCellContent::C_TEXT:
if (content.m_textEntry.valid()) {
MWAWInputStreamPtr &input= m_parserState->m_input;
long fPos = input->tell();
input->seek(content.m_textEntry.begin(), librevenge::RVNG_SEEK_SET);
long endPos = content.m_textEntry.end();
while (!input->isEnd() && input->tell() < endPos) {
auto c=static_cast<unsigned char>(input->readULong(1));
if (c==0x9)
listener->insertTab();
else if (c==0xd || c==0xa)
listener->insertEOL();
else
listener->insertCharacter(c, input, endPos);
}
input->seek(fPos,librevenge::RVNG_SEEK_SET);
}
break;
case MWAWCellContent::C_FORMULA:
case MWAWCellContent::C_NONE:
case MWAWCellContent::C_UNKNOWN:
#if !defined(__clang__)
default:
#endif
break;
}
return true;
}
void ClarisWksDbaseContent::send(double val, bool isNotANumber, ClarisWksStyleManager::CellFormat const &format)
{
MWAWListenerPtr listener=m_parserState->getMainListener();
if (!listener)
return;
std::stringstream s;
int const &type=format.m_fileFormat;
// note: if val*0!=0, val is a NaN so better so simply print NaN
if (type <= 0 || type >=16 || type==10 || type==11 || isNotANumber) {
s << val;
listener->insertUnicodeString(librevenge::RVNGString(s.str().c_str()));
return;
}
std::string value("");
// FIXME: must not be here, change the reference date from 1/1/1904 to 1/1/1900
if (MWAWCellContent::double2String(format.m_format==MWAWCell::F_DATE ? val+1460 : val, format, value))
s << value;
else {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::send: can not convert the actual value\n"));
s << val;
}
listener->insertUnicodeString(librevenge::RVNGString(s.str().c_str()));
}
////////////////////////////////////////////////////////////
// formula
////////////////////////////////////////////////////////////
bool ClarisWksDbaseContent::readCellInFormula(MWAWVec2i const &pos, MWAWCellContent::FormulaInstruction &instr)
{
MWAWInputStreamPtr input=m_parserState->m_input;
instr=MWAWCellContent::FormulaInstruction();
instr.m_type=MWAWCellContent::FormulaInstruction::F_Cell;
bool absolute[2] = { true, true};
int cPos[2]= {0,0};
for (int i=0; i<2; ++i) {
auto val = static_cast<int>(input->readULong(2));
if (val & 0x8000) {
absolute[1-i]=false;
if (val&0x4000)
cPos[1-i] = pos[1-i]-1+(val-0xFFFF);
else
cPos[1-i] = pos[1-i]-1+(val-0x7FFF);
}
else
cPos[1-i]=val;
}
if (m_version==6) {
// checkme: what is this number
auto val=static_cast<int>(input->readLong(2));
static bool first = true;
if (val!=-1 && first) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readCellInFormula: ARGHHH value after cell is %d\n", val));
first=false;
}
}
if (cPos[0] < 0 || cPos[1] < 0) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readCellInFormula: can not read cell position\n"));
return false;
}
instr.m_position[0]=MWAWVec2i(cPos[0],cPos[1]);
instr.m_positionRelative[0]=MWAWVec2b(!absolute[0],!absolute[1]);
return true;
}
bool ClarisWksDbaseContent::readString(long endPos, std::string &res)
{
res="";
MWAWInputStreamPtr input=m_parserState->m_input;
long pos=input->tell();
auto stringSize=static_cast<int>(input->readULong(1));
if (pos+1+stringSize>endPos) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readString: can not read string size\n"));
return false;
}
for (int i=0; i<stringSize; ++i)
res += char(input->readULong(1));
return true;
}
bool ClarisWksDbaseContent::readNumber(long endPos, double &res, bool &isNan)
{
MWAWInputStreamPtr input=m_parserState->m_input;
long pos=input->tell();
if (pos+10>endPos) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readNumber: can not read a number\n"));
return false;
}
return input->readDouble10(res, isNan);
}
namespace ClarisWksDbaseContentInternal
{
struct Operators {
char const *m_name;
int m_arity;
};
static Operators const s_listOperators[] = {
{ "<", 2}, { ">", 2}, { "=", 2}, { "<=", 2},
{ ">=", 2}, { "<>", 2}, { "&", 2},{ "+", 2},
{ "-", 2}, { "*", 2}, { "/", 2}, { "^", 2},
{ "+", 1} /*checkme*/, { "-", 1}, { "(", 1}, { "", -2} /*UNKN*/,
};
}
bool ClarisWksDbaseContent::readFormula(MWAWVec2i const &cPos, long endPos, std::vector<MWAWCellContent::FormulaInstruction> &formula, std::string &error)
{
MWAWInputStreamPtr input=m_parserState->m_input;
libmwaw::DebugStream f;
bool ok=true;
long pos=input->tell();
if (input->isEnd() || pos >= endPos)
return false;
int arity=0, type=static_cast<int>(input->readULong(1));
bool isFunction=false, isOperator=false;
MWAWCellContent::FormulaInstruction instr;
switch (type) {
case 0x10:
if (pos+1+2>endPos) {
ok=false;
break;
}
instr.m_type=MWAWCellContent::FormulaInstruction::F_Long;
instr.m_longValue=double(input->readLong(2));
break;
case 0x11:
if (pos+1+4>endPos) {
ok=false;
break;
}
instr.m_type=MWAWCellContent::FormulaInstruction::F_Long;
instr.m_longValue=double(input->readLong(4));
break;
case 0x12: {
double value;
bool isNan;
ok=readNumber(endPos, value, isNan);
if (!ok) break;
instr.m_type=MWAWCellContent::FormulaInstruction::F_Double;
instr.m_doubleValue=value;
break;
}
case 0x13: {
ok=readCellInFormula(cPos,instr);
break;
}
case 0x1b:
if (pos+1+8 > endPos) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readFormula: find instruction 0x1b, unknown size\n"));
f << "##[code=1b,short],";
ok = false;
break;
}
/* found in some web files followed by a bad cell list */
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readFormula: find instruction 0x1b\n"));
f << "##[code=1b],";
instr.m_type=MWAWCellContent::FormulaInstruction::F_CellList;
input->seek(8, librevenge::RVNG_SEEK_SET);
break;
case 0x14: {
if (pos+1+8 > endPos || !readCellInFormula(cPos, instr)) {
f << "###list cell short";
ok = false;
break;
}
MWAWCellContent::FormulaInstruction instr2;
if (!readCellInFormula(cPos, instr2)) {
f << "###list cell short2";
ok = false;
break;
}
instr.m_type=MWAWCellContent::FormulaInstruction::F_CellList;
instr.m_position[1]=instr2.m_position[0];
instr.m_positionRelative[1]=instr2.m_positionRelative[0];
break;
}
case 0x15: {
std::string text;
ok=readString(endPos, text);
if (!ok) break;
instr.m_type=MWAWCellContent::FormulaInstruction::F_Text;
instr.m_content=text;
break;
}
case 0x16: {
if (pos+1+2>endPos) {
ok=false;
break;
}
isFunction=true;
auto val=static_cast<int>(input->readULong(1));
arity=static_cast<int>(input->readULong(1));
std::string name("");
if (val<0x70) {
static char const *wh[] = {
"Abs", "Acos", "Alert", "And", "Code", "Asin", "Atan", "Atan2",
"Average", "Choose", "Char", "Concatenate", "Cos", "Count", "Date", "DateToText",
"DateValue", "Day", "DayName", "DayOfYear", "Degrees", "NA"/* error*/, "Exact", "Exp",
"Frac", "FV", "HLookup", "Hour", "If", "Index", "Int", "IRR",
"IsBlank", "IsError", "IsNA", "IsNumber", "IsText", "Left", "Ln", "Log",
"Log10", "LookUp", "Lower", "Match", "Max", "Mid", "Min", "Minute",
"MIRR", "Mod", "MonthName", "NA", "Not", "Now", "NPER", "NPV",
"N" /*NumToText*/, "Or", "Pi", "PMT", "Product", "Proper", "PV", "Radians",
"Rand", "Rate", "Replace", "Right", "Round", "Second", "Sign", "Sqrt",
"StDev", "Sum", "Tan", "Rept", "Value"/*TextToNum*/, "Time", "TimeToText", "TimeValue",
"", "Trim", "Type", "Upper", "Var", "VLookUp", "WeekDay", "WeekOfYear",
"Year", "Find", "Column", "Row", "Fact", "Len", "Sin", "Month",
"Trunc", "Count2", "Macro", "Beep", "", "", "", "",
"", "", "", "", "", "", "", "",
};
name=wh[val];
}
if (name.empty()) {
f << "###";
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::readFormula: find unknown function\n"));
std::stringstream s;
s << "Funct" << std::hex << val << std::dec;
name=s.str();
}
instr.m_type=MWAWCellContent::FormulaInstruction::F_Function;
instr.m_content=name;
formula.push_back(instr);
instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
instr.m_content="(";
break;
}
case 0x18: // bool
if (pos+1+1>endPos) {
ok=false;
break;
}
instr.m_type=MWAWCellContent::FormulaInstruction::F_Long;
instr.m_longValue=static_cast<int>(input->readLong(1));
break;
case 0x19: // database, cell
if (pos+1+1>endPos) {
ok = false;
break;
}
instr.m_type=MWAWCellContent::FormulaInstruction::F_Cell;
instr.m_position[0]=MWAWVec2i(int(input->readULong(1)),0);
instr.m_positionRelative[0]=MWAWVec2b(true, false);
break;
case 0x1f: // final equal sign
return true;
default: {
std::string op("");
if (type<0x10) {
op=ClarisWksDbaseContentInternal::s_listOperators[type].m_name;
arity=ClarisWksDbaseContentInternal::s_listOperators[type].m_arity;
}
if (!op.empty()&&arity>0) {
instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
instr.m_content=op;
isOperator=true;
break;
}
f << "##type=" << std::hex << type << std::dec << ",";
ok=false;
break;
}
}
error+=f.str();
if (!ok) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
if (!isOperator || arity==1)
formula.push_back(instr);
if (isFunction && arity>1) {
instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
instr.m_content=";";
}
for (int i=0; i < arity; ++i) {
if (!readFormula(cPos, endPos, formula, error))
return false;
if (i+1==arity) break;
formula.push_back(instr);
}
if (isFunction || instr.m_content=="(") {
instr.m_type=MWAWCellContent::FormulaInstruction::F_Operator;
instr.m_content=")";
formula.push_back(instr);
}
return true;
}
////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////
void ClarisWksDbaseContent::Record::updateFormulaCells(MWAWVec2i const &removeDelta)
{
if (m_content.m_contentType!=MWAWCellContent::C_FORMULA)
return;
for (auto &instr : m_content.m_formula) {
int numCell=instr.m_type==MWAWCellContent::FormulaInstruction::F_Cell ? 1 :
instr.m_type==MWAWCellContent::FormulaInstruction::F_CellList ? 2 : 0;
for (int c=0; c<numCell; ++c) {
instr.m_position[c]-=removeDelta;
if (instr.m_position[c][0]<0 || instr.m_position[c][1]<0) {
static bool first=true;
if (first) {
MWAW_DEBUG_MSG(("ClarisWksDbaseContent::Record::updateFormulaCells: some cell's positions are bad, remove formula\n"));
first=false;
}
// revert to the basic cell type
m_content.m_contentType=m_valueType;
return;
}
}
}
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: