diff options
Diffstat (limited to 'src/translators/pilotdb/libflatfile/DB.cpp')
| -rw-r--r-- | src/translators/pilotdb/libflatfile/DB.cpp | 1437 |
1 files changed, 1437 insertions, 0 deletions
diff --git a/src/translators/pilotdb/libflatfile/DB.cpp b/src/translators/pilotdb/libflatfile/DB.cpp new file mode 100644 index 0000000..40e639a --- /dev/null +++ b/src/translators/pilotdb/libflatfile/DB.cpp @@ -0,0 +1,1437 @@ +/* + * palm-db-tools: Read/write DB-format databases + * Copyright (C) 1999-2001 by Tom Dyas (tdyas@users.sourceforge.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifh Floor, Boston, MA 02110-1301 USA + */ + +#include <iostream> +#include <vector> +#include <string> +#include <stdexcept> +#include <sstream> +#include <time.h> + +#include <cstring> + +#include <kdebug.h> + +#include "../strop.h" + +#include "DB.h" + +#include <kdebug.h> + +#define charSeperator '/' +#define VIEWFLAG_USE_IN_EDITVIEW 0x01 + +#define INVALID_DEFAULT 0 +#define NOW_DEFAULT 1 +#define CONSTANT_DEFAULT 2 + +using namespace PalmLib::FlatFile; +using namespace PalmLib; + +namespace { + static const pi_uint16_t CHUNK_FIELD_NAMES = 0; + static const pi_uint16_t CHUNK_FIELD_TYPES = 1; + static const pi_uint16_t CHUNK_FIELD_DATA = 2; + static const pi_uint16_t CHUNK_LISTVIEW_DEFINITION = 64; + static const pi_uint16_t CHUNK_LISTVIEW_OPTIONS = 65; + static const pi_uint16_t CHUNK_LFIND_OPTIONS = 128; + static const pi_uint16_t CHUNK_ABOUT = 254; +} + +template <class Map, class Key> +static inline bool has_key(const Map& map, const Key& key) +{ + return map.find(key) != map.end(); +} + +bool PalmLib::FlatFile::DB::classify(PalmLib::Database& pdb) +{ + return (! pdb.isResourceDB()) + && (pdb.creator() == PalmLib::mktag('D','B','O','S')) + && (pdb.type() == PalmLib::mktag('D','B','0','0')); +} + +bool PalmLib::FlatFile::DB::match_name(const std::string& name) +{ + return (name == "DB") || (name == "db"); +} + +void PalmLib::FlatFile::DB::extract_chunks(const PalmLib::Block& appinfo) +{ + size_t i; + pi_uint16_t chunk_type; + pi_uint16_t chunk_size; + + if (appinfo.size() > 4) { + // Loop through each chunk in the block while data remains. + i = 4; + while (i < appinfo.size()) { + /* Stop the loop if there is not enough room for even one + * chunk header. + */ + if (i + 4 >= appinfo.size()) { +// throw PalmLib::error("header is corrupt"); + kdDebug() << "header is corrupt" << endl; + } + // Copy the chunk type and size into the local buffer. + chunk_type = get_short(appinfo.data() + i); + chunk_size = get_short(appinfo.data() + i + 2); + i += 4; + + // Copy the chunk into seperate storage. + Chunk chunk(appinfo.data() + i, chunk_size); + chunk.chunk_type = chunk_type; + m_chunks[chunk.chunk_type].push_back(chunk); + + /* Advance the index by the size of the chunk. */ + i += chunk.size(); + } + + // If everything was correct, then we should be exactly at the + // end of the block. + if (i != appinfo.size()) { +// throw PalmLib::error("header is corrupt"); + kdDebug() << "header is corrupt" << endl; + } + } else { +// throw PalmLib::error("header is corrupt"); + kdDebug() << "header is corrupt" << endl; + } +} + +void PalmLib::FlatFile::DB::extract_schema(unsigned numFields) +{ + unsigned i; + + if (!has_key(m_chunks, CHUNK_FIELD_NAMES) + || !has_key(m_chunks, CHUNK_FIELD_TYPES)) { +// throw PalmLib::error("database is missing its schema"); + kdDebug() << "database is missing its schema" << endl; + return; + } + + Chunk names_chunk = m_chunks[CHUNK_FIELD_NAMES][0]; + Chunk types_chunk = m_chunks[CHUNK_FIELD_TYPES][0]; + pi_char_t* p = names_chunk.data(); + pi_char_t* q = types_chunk.data(); + + // Ensure that the types chunk has the expected size. + if (types_chunk.size() != numFields * sizeof(pi_uint16_t)) { +// throw PalmLib::error("types chunk is corrupt"); + kdDebug() << "types chunk is corrupt" << endl; + } + // Loop for each field and extract the name and type. + for (i = 0; i < numFields; ++i) { + PalmLib::FlatFile::Field::FieldType type; + int len; + + // Determine the length of the name string. Ensure that the + // string does not go beyond the end of the chunk. + pi_char_t* null_p = reinterpret_cast<pi_char_t*> + (memchr(p, 0, names_chunk.size() - (p - names_chunk.data()))); + if (!null_p) { +// throw PalmLib::error("names chunk is corrupt"); + kdDebug() << "names chunk is corrupt" << endl; + } + len = null_p - p; + + switch (PalmLib::get_short(q)) { + case 0: + type = PalmLib::FlatFile::Field::STRING; + break; + + case 1: + type = PalmLib::FlatFile::Field::BOOLEAN; + break; + + case 2: + type = PalmLib::FlatFile::Field::INTEGER; + break; + + case 3: + type = PalmLib::FlatFile::Field::DATE; + break; + + case 4: + type = PalmLib::FlatFile::Field::TIME; + break; + + case 5: + type = PalmLib::FlatFile::Field::NOTE; + break; + + case 6: + type = PalmLib::FlatFile::Field::LIST; + break; + + case 7: + type = PalmLib::FlatFile::Field::LINK; + break; + + case 8: + type = PalmLib::FlatFile::Field::FLOAT; + break; + + case 9: + type = PalmLib::FlatFile::Field::CALCULATED; + break; + + case 10: + type = PalmLib::FlatFile::Field::LINKED; + break; + + default: +// throw PalmLib::error("unknown field type"); + kdDebug() << "PalmLib::FlatFile::DB::extract_schema() - unknown field type" << endl; + type = PalmLib::FlatFile::Field::STRING; + break; + } + + // Inform the superclass about this field. + appendField(std::string((char *) p, len), type, extract_fieldsdata(i, type)); + + // Advance to the information on the next field. + p += len + 1; + q += 2; + } +} + +void PalmLib::FlatFile::DB::extract_listviews() +{ + if (!has_key(m_chunks, CHUNK_LISTVIEW_DEFINITION)) + return; + +/* throw PalmLib::error("no list views in database");*/ + + const std::vector<Chunk>& chunks = m_chunks[CHUNK_LISTVIEW_DEFINITION]; + + for (std::vector<Chunk>::const_iterator iter = chunks.begin(); + iter != chunks.end(); ++iter) { + const Chunk& chunk = (*iter); + PalmLib::FlatFile::ListView lv; + + if (chunk.size() < (2 + 2 + 32)) { +// throw PalmLib::error("list view is corrupt"); + kdDebug() << "list view is corrupt" << endl; + } + pi_uint16_t flags = PalmLib::get_short(chunk.data()); + pi_uint16_t num_cols = PalmLib::get_short(chunk.data() + 2); + + lv.editoruse = false; + if (flags & VIEWFLAG_USE_IN_EDITVIEW) + lv.editoruse = true; + + if (chunk.size() != static_cast<unsigned> (2 + 2 + 32 + num_cols * 4)) { +// throw PalmLib::error("list view is corrupt"); + kdDebug() << "list view is corrupt" << endl; + } + // Determine the length of the name string. + pi_char_t* null_ptr = reinterpret_cast<pi_char_t*> + (memchr(chunk.data() + 4, 0, 32)); + if (null_ptr) + lv.name = std::string((char *) (chunk.data() + 4), + null_ptr - (chunk.data() + 4)); + else + lv.name = "Unknown"; + + const pi_char_t* p = chunk.data() + 2 + 2 + 32; + for (int i = 0; i < num_cols; ++i) { + pi_uint16_t field = PalmLib::get_short(p); + pi_uint16_t width = PalmLib::get_short(p + 2); + p += 2 * sizeof(pi_uint16_t); + + if (field >= getNumOfFields()) { +// throw PalmLib::error("list view is corrupt"); + kdDebug() << "list view is corrupt" << endl; + } + PalmLib::FlatFile::ListViewColumn col(field, width); + lv.push_back(col); + } + + appendListView(lv); + } +} + +std::string PalmLib::FlatFile::DB::extract_fieldsdata(pi_uint16_t field_search, PalmLib::FlatFile::Field::FieldType type) +{ + std::ostringstream theReturn; + + if (!has_key(m_chunks, CHUNK_FIELD_DATA)) + return std::string(theReturn.str()); + + std::vector<Chunk>& chunks = m_chunks[CHUNK_FIELD_DATA]; + + pi_uint16_t field_num = 0; + bool find = false; + std::vector<Chunk>::const_iterator iter = chunks.begin(); + for ( ; iter != chunks.end(); ++iter) { + const Chunk& chunk = (*iter); + + field_num = PalmLib::get_short(chunk.data()); + + if (field_num == field_search) { + find = true; + break; + } + } + + if (find) { + const Chunk& chunk = (*iter); + + switch (type) { + + case PalmLib::FlatFile::Field::STRING: + theReturn << std::string((const char *)chunk.data()+2, chunk.size() - 2); + break; + + case PalmLib::FlatFile::Field::BOOLEAN: + break; + + case PalmLib::FlatFile::Field::INTEGER: + theReturn << PalmLib::get_long(chunk.data() + sizeof(pi_uint16_t)); + theReturn << charSeperator; + theReturn << PalmLib::get_short(chunk.data() + sizeof(pi_uint16_t) + sizeof(pi_uint32_t)); + break; + + case PalmLib::FlatFile::Field::FLOAT: { + pi_double_t value; + value.words.hi = PalmLib::get_long(chunk.data() + 2); + value.words.lo = PalmLib::get_long(chunk.data() + 6); + + theReturn << value.number; + } + break; + + case PalmLib::FlatFile::Field::DATE: + if (*(chunk.data() + sizeof(pi_uint16_t)) == NOW_DEFAULT) + theReturn << "now"; + else if (*(chunk.data() + sizeof(pi_uint16_t)) == CONSTANT_DEFAULT) { + const pi_char_t * ptr = chunk.data() + sizeof(pi_uint16_t) + 1; + struct tm date; + date.tm_year = PalmLib::get_short(ptr) - 1900; + date.tm_mon = (static_cast<int> (*(ptr + 2))) - 1; + date.tm_mday = static_cast<int> (*(ptr + 3)); + + (void) mktime(&date); + + char buf[1024]; + + // Clear out the output buffer. + memset(buf, 0, sizeof(buf)); + + // Convert and output the date using the format. + strftime(buf, sizeof(buf), "%Y/%m/%d", &date); + + theReturn << buf; + } + break; + + case PalmLib::FlatFile::Field::TIME: + if (*(chunk.data() + sizeof(pi_uint16_t)) == NOW_DEFAULT) + theReturn << "now"; + else if (*(chunk.data() + sizeof(pi_uint16_t)) == CONSTANT_DEFAULT) { + const pi_char_t * ptr = chunk.data() + sizeof(pi_uint16_t) + 1; + struct tm t; + const struct tm * tm_ptr; + time_t now; + + time(&now); + tm_ptr = localtime(&now); + memcpy(&t, tm_ptr, sizeof(tm)); + + t.tm_hour = static_cast<int> (*(ptr)); + t.tm_min = static_cast<int> (*(ptr + 1)); + t.tm_sec = 0; + + char buf[1024]; + + // Clear out the output buffer. + memset(buf, 0, sizeof(buf)); + + // Convert and output the date using the format. + strftime(buf, sizeof(buf), "%H:%M", &t); + + theReturn << buf; + } + break; + + case PalmLib::FlatFile::Field::NOTE: + break; + + case PalmLib::FlatFile::Field::LIST: { + unsigned short numItems = PalmLib::get_short(chunk.data() + sizeof(pi_uint16_t)); + int prevLength = 0; + std::string item; + + if (numItems > 0) { + for (unsigned short i = 0; i < numItems - 1; i++) { + item = std::string((const char *)chunk.data() + 3 * sizeof(pi_uint16_t) + prevLength); + theReturn << item << charSeperator; + prevLength += item.length() + 1; + } + item = std::string((const char *)chunk.data() + 3 * sizeof(pi_uint16_t) + prevLength); + theReturn << item; + } + } + break; + + case PalmLib::FlatFile::Field::LINK: + theReturn << std::string((const char *)chunk.data()+sizeof(pi_uint16_t)); +// theReturn << std::string((const char *)chunk.data()+sizeof(pi_uint16_t), chunk.size() - 2); + theReturn << charSeperator; + theReturn << PalmLib::get_short(chunk.data() + sizeof(pi_uint16_t) + 32 * sizeof(pi_char_t)); + break; + + case PalmLib::FlatFile::Field::LINKED: + theReturn << PalmLib::get_short(chunk.data() + sizeof(pi_uint16_t)); + theReturn << charSeperator; + theReturn << PalmLib::get_short(chunk.data() + 2 * sizeof(pi_uint16_t)); + break; + + case PalmLib::FlatFile::Field::CALCULATED: + break; + + default: + kdDebug() << "unknown field type" << endl; + break; + } + } + return std::string(theReturn.str()); +} + +void PalmLib::FlatFile::DB::extract_aboutinfo() +{ + if (!has_key(m_chunks, CHUNK_ABOUT)) + return; + + Chunk chunk = m_chunks[CHUNK_ABOUT][0]; + pi_char_t* header = chunk.data(); + pi_char_t* q = chunk.data() + PalmLib::get_short(header); + + setAboutInformation( (char*)q); +} + +void PalmLib::FlatFile::DB::parse_record(PalmLib::Record& record, + std::vector<pi_char_t *>& ptrs, + std::vector<size_t>& sizes) +{ + unsigned i; + + // Ensure that enough space for the offset table exists. + if (record.size() < getNumOfFields() * sizeof(pi_uint16_t)) { +// throw PalmLib::error("record is corrupt"); + kdDebug() << "record is corrupt" << endl; + } + // Extract the offsets from the record. Determine field pointers. + std::vector<pi_uint16_t> offsets(getNumOfFields()); + for (i = 0; i < getNumOfFields(); ++i) { + offsets[i] = get_short(record.data() + i * sizeof(pi_uint16_t)); + if (offsets[i] >= record.size()) { +// throw PalmLib::error("record is corrupt"); + kdDebug() << "record is corrupt" << endl; + } + ptrs.push_back(record.data() + offsets[i]); + } + + // Determine the field sizes. + for (i = 0; i < getNumOfFields() - 1; ++i) { + sizes.push_back(offsets[i + 1] - offsets[i]); + } + sizes.push_back(record.size() - offsets[getNumOfFields() - 1]); +} + +PalmLib::FlatFile::DB::DB(PalmLib::Database& pdb) + : Database("db", pdb), m_flags(0) +{ + // Split the application information block into its component chunks. + extract_chunks(pdb.getAppInfoBlock()); + + // Pull the header fields and schema out of the databasse. + m_flags = get_short(pdb.getAppInfoBlock().data()); + unsigned numFields = get_short(pdb.getAppInfoBlock().data() + 2); + extract_schema(numFields); + + // Extract all of the list views. + extract_listviews(); + + extract_aboutinfo(); + + for (unsigned i = 0; i < pdb.getNumRecords(); ++i) { + PalmLib::Record record = pdb.getRecord(i); + Record rec; + + std::vector<pi_char_t *> ptrs; + std::vector<size_t> sizes; + parse_record(record, ptrs, sizes); + for (unsigned j = 0; j < getNumOfFields(); ++j) { + PalmLib::FlatFile::Field f; + f.type = field_type(j); + + switch (field_type(j)) { + case PalmLib::FlatFile::Field::STRING: + f.type = PalmLib::FlatFile::Field::STRING; + f.v_string = std::string((char *) ptrs[j], sizes[j] - 1); + break; + + case PalmLib::FlatFile::Field::BOOLEAN: + f.type = PalmLib::FlatFile::Field::BOOLEAN; + if (*(ptrs[j])) + f.v_boolean = true; + else + f.v_boolean = false; + break; + + case PalmLib::FlatFile::Field::INTEGER: + f.type = PalmLib::FlatFile::Field::INTEGER; + f.v_integer = PalmLib::get_long(ptrs[j]); + break; + + case PalmLib::FlatFile::Field::FLOAT: { + // Place data from database in a union for conversion. + pi_double_t value; + value.words.hi = PalmLib::get_long(ptrs[j]); + value.words.lo = PalmLib::get_long(ptrs[j] + 4); + + // Fill out the information for this field. + f.type = PalmLib::FlatFile::Field::FLOAT; + f.v_float = value.number; + } + break; + + case PalmLib::FlatFile::Field::DATE: + f.type = PalmLib::FlatFile::Field::DATE; + f.v_date.year = PalmLib::get_short(ptrs[j]); + f.v_date.month = static_cast<int> (*(ptrs[j] + 2)); + f.v_date.day = static_cast<int> (*(ptrs[j] + 3)); + break; + + case PalmLib::FlatFile::Field::TIME: + f.type = PalmLib::FlatFile::Field::TIME; + f.v_time.hour = static_cast<int> (*(ptrs[j])); + f.v_time.minute = static_cast<int> (*(ptrs[j] + 1)); + break; + + case PalmLib::FlatFile::Field::NOTE: + f.type = PalmLib::FlatFile::Field::NOTE; + f.v_string = std::string((char *) ptrs[j], sizes[j] - 3); + f.v_note = std::string((char *) (record.data() + get_short(ptrs[j] + strlen(f.v_string.c_str()) + 1))); + break; + + case PalmLib::FlatFile::Field::LIST: + f.type = PalmLib::FlatFile::Field::LIST; + if (!field(j).argument().empty()) { + std::string data = field(j).argument(); + unsigned int k; + std::string::size_type pos = 0; + pi_uint16_t itemID = *ptrs[j]; // TR: a list value is stored on 1 byte + + for (k = 0; k < itemID; k++) { + if ((pos = data.find(charSeperator, pos)) == std::string::npos) { + break; + } + pos++; + } + if (pos == std::string::npos) { + f.v_string = "N/A"; + } else { + if (data.find(charSeperator, pos) == std::string::npos) { + f.v_string = data.substr( pos, std::string::npos); + } else { + f.v_string = data.substr( pos, data.find(charSeperator, pos) - pos); + } + } + } + break; + + case PalmLib::FlatFile::Field::LINK: + f.type = PalmLib::FlatFile::Field::LINK; + f.v_integer = PalmLib::get_long(ptrs[j]); + f.v_string = std::string((char *) (ptrs[j] + 4), sizes[j] - 5); + break; + + case PalmLib::FlatFile::Field::LINKED: + f.type = PalmLib::FlatFile::Field::LINKED; + f.v_string = std::string((char *) ptrs[j], sizes[j] - 1); + break; + + case PalmLib::FlatFile::Field::CALCULATED: { + std::ostringstream value; + f.type = PalmLib::FlatFile::Field::CALCULATED; + switch (ptrs[j][0]) { + case 1: //string + value << std::string((char *) ptrs[j] + 1, sizes[j] - 2); + break; + case 2: //integer + value << PalmLib::get_long(ptrs[j] + 1); + break; + case 9: //float + { + pi_double_t fvalue; + fvalue.words.hi = PalmLib::get_long(ptrs[j] + 1); + fvalue.words.lo = PalmLib::get_long(ptrs[j] + 5); + + value << fvalue.number; + } + default: + value << "N/A"; + } + f.v_string = value.str(); + } break; + + default: + kdDebug() << "unknown field type" << endl; + break; + } + + // Append this field to the record. + rec.appendField(f); + } + rec.unique_id(record.unique_id()); + // Append this record to the database. + appendRecord(rec); + } +} + +void PalmLib::FlatFile::DB::make_record(PalmLib::Record& pdb_record, + const Record& record) const +{ + unsigned int i; + + // Determine the packed size of this record. + size_t size = getNumOfFields() * sizeof(pi_uint16_t); + for (i = 0; i < getNumOfFields(); i++) { +#ifdef HAVE_VECTOR_AT + const Field field = record.fields().at(i); +#else + const Field field = record.fields()[i]; +#endif + switch (field.type) { + case PalmLib::FlatFile::Field::STRING: + size += field.v_string.length() + 1; + break; + + case PalmLib::FlatFile::Field::NOTE: + size += field.v_string.length() + 3; + size += field.v_note.length() + 1; + break; + + case PalmLib::FlatFile::Field::BOOLEAN: + size += 1; + break; + + case PalmLib::FlatFile::Field::INTEGER: + size += 4; + break; + + case PalmLib::FlatFile::Field::FLOAT: + size += 8; + break; + + case PalmLib::FlatFile::Field::DATE: + size += sizeof(pi_uint16_t) + 2 * sizeof(pi_char_t); + break; + + case PalmLib::FlatFile::Field::TIME: + size += 2 * sizeof(pi_char_t); + break; + + case PalmLib::FlatFile::Field::LIST: + size += sizeof(pi_char_t); + break; + + case PalmLib::FlatFile::Field::LINK: + size += sizeof(pi_int32_t); + size += field.v_string.length() + 1; + break; + + case PalmLib::FlatFile::Field::LINKED: + size += field.v_string.length() + 1; + break; + + case PalmLib::FlatFile::Field::CALCULATED: + size += 1; + break; + + default: + kdDebug() << "unsupported field type" << endl; + break; + } + } + + // Allocate a block for the packed record and setup the pointers. + pi_char_t* buf = new pi_char_t[size]; + pi_char_t* p = buf + getNumOfFields() * sizeof(pi_uint16_t); + pi_char_t* offsets = buf; + + // Pack the fields into the buffer. + for (i = 0; i < getNumOfFields(); i++) { + pi_char_t* noteOffsetOffset = 0; + bool setNote = false; +#ifdef HAVE_VECTOR_AT + const Field fieldData = record.fields().at(i); +#else + const Field fieldData = record.fields()[i]; +#endif + + // Mark the offset to the start of this field in the offests table. + PalmLib::set_short(offsets, static_cast<pi_uint16_t> (p - buf)); + offsets += sizeof(pi_uint16_t); + + // Pack the field. + switch (fieldData.type) { + case PalmLib::FlatFile::Field::STRING: + memcpy(p, fieldData.v_string.c_str(), fieldData.v_string.length() + 1); + p += fieldData.v_string.length() + 1; + break; + + case PalmLib::FlatFile::Field::NOTE: + if (setNote) + kdDebug() << "unsupported field type"; + memcpy(p, fieldData.v_string.c_str(), fieldData.v_string.length() + 1); + p += fieldData.v_string.length() + 1; + noteOffsetOffset = p; + p += 2; + setNote = true; + break; + + case PalmLib::FlatFile::Field::BOOLEAN: + *p++ = ((fieldData.v_boolean) ? 1 : 0); + break; + + case PalmLib::FlatFile::Field::INTEGER: + PalmLib::set_long(p, fieldData.v_integer); + p += sizeof(pi_int32_t); + break; + + case PalmLib::FlatFile::Field::FLOAT: { + // Place data the data in a union for easy conversion. + pi_double_t value; + value.number = fieldData.v_float; + PalmLib::set_long(p, value.words.hi); + p += sizeof(pi_uint32_t); + PalmLib::set_long(p, value.words.lo); + p += sizeof(pi_uint32_t); + break; + } + + case PalmLib::FlatFile::Field::DATE: + PalmLib::set_short(p, fieldData.v_date.year); + p += sizeof(pi_uint16_t); + *p++ = static_cast<pi_char_t> (fieldData.v_date.month & 0xFF); + *p++ = static_cast<pi_char_t> (fieldData.v_date.day & 0xFF); + break; + + case PalmLib::FlatFile::Field::TIME: + *p++ = static_cast<pi_char_t> (fieldData.v_time.hour & 0xFF); + *p++ = static_cast<pi_char_t> (fieldData.v_time.minute & 0xFF); + break; + + case PalmLib::FlatFile::Field::LIST: + if (!field(i).argument().empty()) { + std::string data = field(i).argument(); + std::string::size_type pos = 0, next; + unsigned int j = 0; + pi_int16_t itemID = -1; + + while ( (next = data.find(charSeperator, pos)) != std::string::npos) { + if (fieldData.v_string == data.substr( pos, next - pos)) { + itemID = j; + break; + } + j++; + pos = next + 1; + } + // TR: the following test handles the case where the field value + // equals the last item in list (bugfix) + if (itemID == -1 && fieldData.v_string == data.substr( pos, std::string::npos)) { + itemID = j; + } + p[0] = itemID; // TR: a list value is stored on 1 byte + p += sizeof(pi_char_t); + } + break; + + case PalmLib::FlatFile::Field::LINK: + PalmLib::set_long(p, fieldData.v_integer); + p += sizeof(pi_int32_t); + memcpy(p, fieldData.v_string.c_str(), fieldData.v_string.length() + 1); + p += fieldData.v_string.length() + 1; + break; + + case PalmLib::FlatFile::Field::LINKED: + memcpy(p, fieldData.v_string.c_str(), fieldData.v_string.length() + 1); + p += fieldData.v_string.length() + 1; + break; + + case PalmLib::FlatFile::Field::CALCULATED: + *p = 13; + p++; + break; + + default: + kdDebug() << "unsupported field type"; + break; + } + if (setNote) { + if (fieldData.v_note.length()) { + memcpy(p, fieldData.v_note.c_str(), fieldData.v_note.length() + 1); + PalmLib::set_short(noteOffsetOffset, (pi_uint16_t)(p - buf)); + p += fieldData.v_note.length() + 1; + } else { + PalmLib::set_short(noteOffsetOffset, 0); + } + } + } + + // Place the packed data into the PalmOS record. + pdb_record.set_raw(buf, size); + delete [] buf; +} + +void PalmLib::FlatFile::DB::build_fieldsdata_chunks(std::vector<DB::Chunk>& chunks) const +{ + pi_char_t * buf = 0, * p; + unsigned int size, i; + + for (i = 0; i < getNumOfFields(); ++i) { + size = 0; + switch (field_type(i)) { + case PalmLib::FlatFile::Field::STRING: + if (!field(i).argument().empty()) { + size = (field(i).argument().length() + 1) + 2; + buf = new pi_char_t[size]; + PalmLib::set_short(buf, i); + strcpy((char *) (buf + 2), field(i).argument().c_str()); + } + break; + + case PalmLib::FlatFile::Field::BOOLEAN: + break; + + case PalmLib::FlatFile::Field::INTEGER: + if (!field(i).argument().empty()) { + std::string data = field(i).argument(); + std::pair< PalmLib::pi_int32_t, PalmLib::pi_int16_t> values(0, 0); + + if ( data.find(charSeperator) != std::string::npos) { + StrOps::convert_string(data.substr( 0, data.find(charSeperator)), values.first); + StrOps::convert_string(data.substr( data.find(charSeperator) + 1, std::string::npos), values.second); + } else + StrOps::convert_string(data, values.first); + + size = 2 + sizeof(pi_uint32_t) + sizeof(pi_uint16_t); + buf = new pi_char_t[size]; + p = buf; + PalmLib::set_short(p, i); + p += sizeof(pi_uint16_t); + PalmLib::set_long(p, values.first); + p += sizeof(pi_uint32_t); + PalmLib::set_short(p, values.second); + p += sizeof(pi_uint16_t); + } + break; + + case PalmLib::FlatFile::Field::FLOAT: + if (!field(i).argument().empty()) { + std::string data = field(i).argument(); + pi_double_t value; + + StrOps::convert_string(data, value.number); + + size = 2 + 2 * sizeof(pi_uint32_t); + buf = new pi_char_t[size]; + p = buf; + PalmLib::set_short(p, i); + p += sizeof(pi_uint16_t); + PalmLib::set_long(p, value.words.hi); + p += sizeof(pi_uint32_t); + PalmLib::set_long(p, value.words.lo); + p += sizeof(pi_uint32_t); + } + break; + + case PalmLib::FlatFile::Field::DATE: + if (!field(i).argument().empty()) { + std::string data = field(i).argument(); + struct tm date; + pi_char_t type; + + if (data.substr(0, 3) == "now") { + type = NOW_DEFAULT; + const struct tm * tm_ptr; + time_t now; + + time(&now); + tm_ptr = localtime(&now); + memcpy(&date, tm_ptr, sizeof(tm)); + } else +#ifdef strptime + if (strptime(data.c_str(), "%Y/%m/%d", &date)) +#else + if (StrOps::strptime(data.c_str(), "%Y/%m/%d", &date)) +#endif + type = CONSTANT_DEFAULT; + else + type = INVALID_DEFAULT; + + if (type != INVALID_DEFAULT) { + size = sizeof(pi_uint16_t) + 1 + sizeof(pi_uint16_t) + 2; + buf = new pi_char_t[size]; + p = buf; + PalmLib::set_short(p, i); + p += sizeof(pi_uint16_t); + *p++ = static_cast<pi_char_t> (type & 0xFF); + PalmLib::set_short(p, date.tm_year + 1900); + p += sizeof(pi_uint16_t); + *p++ = static_cast<pi_char_t> ((date.tm_mon + 1) & 0xFF); + *p++ = static_cast<pi_char_t> (date.tm_mday & 0xFF); + } + + } + break; + + case PalmLib::FlatFile::Field::TIME: + if (!field(i).argument().empty()) { + std::string data = field(i).argument(); + struct tm t; + pi_char_t type; + + if (data == "now") { + type = NOW_DEFAULT; + const struct tm * tm_ptr; + time_t now; + + time(&now); + tm_ptr = localtime(&now); + memcpy(&t, tm_ptr, sizeof(tm)); + } else +#ifdef strptime + if (!strptime(data.c_str(), "%H/%M", &t)) +#else + if (!StrOps::strptime(data.c_str(), "%H/%M", &t)) +#endif + type = CONSTANT_DEFAULT; + else + type = INVALID_DEFAULT; + + if (type != INVALID_DEFAULT) { + size = sizeof(pi_uint16_t) + 1 + sizeof(pi_uint16_t) + 2; + buf = new pi_char_t[size]; + p = buf; + PalmLib::set_short(p, i); + p += sizeof(pi_uint16_t); + *p++ = static_cast<pi_char_t> (type & 0xFF); + *p++ = static_cast<pi_char_t> (t.tm_hour & 0xFF); + *p++ = static_cast<pi_char_t> (t.tm_min & 0xFF); + } + + } + break; + + case PalmLib::FlatFile::Field::NOTE: + break; + + case PalmLib::FlatFile::Field::LIST: + if (!field(i).argument().empty()) { + std::string data = field(i).argument(); + std::vector<std::string> items; + std::string::size_type pos = 0, next; + std::vector<std::string>::iterator iter; + size = 2 + 2 * sizeof(pi_uint16_t); + while ( (next = data.find(charSeperator, pos)) != std::string::npos) { + std::string item = data.substr( pos, next - pos); + items.push_back(item); + size += item.length() + 1; + pos = next + 1; + } + if (pos != std::string::npos) { + std::string item = data.substr( pos, std::string::npos); + items.push_back(item); + size += item.length() + 1; + } + + buf = new pi_char_t[size]; + p = buf; + PalmLib::set_short(p, i); + p += sizeof(pi_uint16_t); + PalmLib::set_short(p, items.size()); + p += sizeof(pi_uint16_t); + p += sizeof(pi_uint16_t); + for (iter = items.begin(); iter != items.end(); ++iter) { + std::string& item = (*iter); + strcpy((char *) p, item.c_str()); + p[item.length()] = 0; + p += item.length() + 1; + } + + } + break; + + case PalmLib::FlatFile::Field::LINK: + if (!field(i).argument().empty()) { + std::string data = field(i).argument(); + std::string databasename; + pi_uint16_t fieldnum; + + if ( data.find(charSeperator) != std::string::npos) { + databasename = data.substr( 0, data.find(charSeperator)); + StrOps::convert_string(data.substr( data.find(charSeperator) + 1, std::string::npos), fieldnum); + } else { + databasename = data; + fieldnum = 0; + } + + size = 2 + 32 * sizeof(pi_char_t) + sizeof(pi_uint16_t); + buf = new pi_char_t[size]; + p = buf; + PalmLib::set_short(p, i); + p += sizeof(pi_uint16_t); + strcpy((char *) p, databasename.c_str()); + p += 32 * sizeof(pi_char_t); + PalmLib::set_short(p, fieldnum); + p += sizeof(pi_uint16_t); + } + break; + + case PalmLib::FlatFile::Field::LINKED: + if (!field(i).argument().empty()) { + std::string data = field(i).argument(); + pi_uint16_t linknum; + pi_uint16_t fieldnum; + + if ( data.find(charSeperator) != std::string::npos) { + StrOps::convert_string(data.substr( 0, data.find(charSeperator)), linknum); + StrOps::convert_string(data.substr( data.find(charSeperator) + 1, std::string::npos), fieldnum); + if (field_type(linknum) != PalmLib::FlatFile::Field::LINK) { + unsigned int j = 0; + while (field_type(j) != PalmLib::FlatFile::Field::LINK && j < i) j++; + linknum = j; + } + } else { + unsigned int j = 0; + while (field_type(j) != PalmLib::FlatFile::Field::LINK && j < i) j++; + linknum = j; + fieldnum = 0; + } + + size = 2 + 2 * sizeof(pi_uint16_t); + buf = new pi_char_t[size]; + p = buf; + PalmLib::set_short(p, i); + p += sizeof(pi_uint16_t); + PalmLib::set_short(p, linknum); + p += sizeof(pi_uint16_t); + PalmLib::set_short(p, fieldnum); + p += sizeof(pi_uint16_t); + } + break; + + case PalmLib::FlatFile::Field::CALCULATED: + break; + + default: + kdDebug() << "unknown field type" << endl; + break; + } + + if (size) { + Chunk data_chunk(buf, size); + data_chunk.chunk_type = CHUNK_FIELD_DATA; + delete [] buf; + chunks.push_back(data_chunk); + } + } +} + +void PalmLib::FlatFile::DB::build_about_chunk(std::vector<DB::Chunk>& chunks) const +{ + pi_char_t* buf; + pi_char_t* p; + int headersize = 2*sizeof(pi_uint16_t); + std::string information = getAboutInformation(); + + if (!information.length()) + return; + // Build the names chunk. + buf = new pi_char_t[headersize + information.length() + 1]; + p = buf; + + PalmLib::set_short(p, headersize); + p += 2; + PalmLib::set_short(p, 1); //about type version + p += 2; + memcpy(p, information.c_str(), information.length() + 1); + p += information.length() + 1; + Chunk chunk(buf, headersize + information.length() + 1); + chunk.chunk_type = CHUNK_ABOUT; + delete [] buf; + chunks.push_back(chunk); + +} + +void PalmLib::FlatFile::DB::build_standard_chunks(std::vector<DB::Chunk>& chunks) const +{ + pi_char_t* buf; + pi_char_t* p; + unsigned i; + + // Determine the size needed for the names chunk. + size_t names_chunk_size = 0; + for (i = 0; i < getNumOfFields(); ++i) { + names_chunk_size += field_name(i).length() + 1; + } + + // Build the names chunk. + buf = new pi_char_t[names_chunk_size]; + p = buf; + for (i = 0; i < getNumOfFields(); ++i) { + const std::string name = field_name(i); + memcpy(p, name.c_str(), name.length() + 1); + p += name.length() + 1; + } + Chunk names_chunk(buf, names_chunk_size); + names_chunk.chunk_type = CHUNK_FIELD_NAMES; + delete [] buf; + + // Build the types chunk. + buf = new pi_char_t[getNumOfFields() * sizeof(pi_uint16_t)]; + p = buf; + for (i = 0; i < getNumOfFields(); ++i) { + // Pack the type of the current field. + switch (field_type(i)) { + case PalmLib::FlatFile::Field::STRING: + PalmLib::set_short(p, 0); + break; + + case PalmLib::FlatFile::Field::BOOLEAN: + PalmLib::set_short(p, 1); + break; + + case PalmLib::FlatFile::Field::INTEGER: + PalmLib::set_short(p, 2); + break; + + case PalmLib::FlatFile::Field::DATE: + PalmLib::set_short(p, 3); + break; + + case PalmLib::FlatFile::Field::TIME: + PalmLib::set_short(p, 4); + break; + + case PalmLib::FlatFile::Field::NOTE: + PalmLib::set_short(p, 5); + break; + + case PalmLib::FlatFile::Field::LIST: + PalmLib::set_short(p, 6); + break; + + case PalmLib::FlatFile::Field::LINK: + PalmLib::set_short(p, 7); + break; + + case PalmLib::FlatFile::Field::FLOAT: + PalmLib::set_short(p, 8); + break; + + case PalmLib::FlatFile::Field::CALCULATED: + PalmLib::set_short(p, 9); + break; + + case PalmLib::FlatFile::Field::LINKED: + PalmLib::set_short(p, 10); + break; + + default: + kdDebug() << "unsupported field type" << endl; + break; + } + + // Advance to the next position. + p += sizeof(pi_uint16_t); + } + Chunk types_chunk(buf, getNumOfFields() * sizeof(pi_uint16_t)); + types_chunk.chunk_type = CHUNK_FIELD_TYPES; + delete [] buf; + + // Build the list view options chunk. + buf = new pi_char_t[2 * sizeof(pi_uint16_t)]; + PalmLib::set_short(buf, 0); + PalmLib::set_short(buf + sizeof(pi_uint16_t), 0); + Chunk listview_options_chunk(buf, 2 * sizeof(pi_uint16_t)); + listview_options_chunk.chunk_type = CHUNK_LISTVIEW_OPTIONS; + delete [] buf; + + // Build the local find options chunk. + buf = new pi_char_t[sizeof(pi_uint16_t)]; + PalmLib::set_short(buf, 0); + Chunk lfind_options_chunk(buf, 1 * sizeof(pi_uint16_t)); + lfind_options_chunk.chunk_type = CHUNK_LFIND_OPTIONS; + delete [] buf; + + // Add all the chunks to the chunk list. + chunks.push_back(names_chunk); + chunks.push_back(types_chunk); + chunks.push_back(listview_options_chunk); + chunks.push_back(lfind_options_chunk); +} + +void PalmLib::FlatFile::DB::build_listview_chunk(std::vector<DB::Chunk>& chunks, + const ListView& lv) const +{ + // Calculate size and allocate space for the temporary buffer. + size_t size = 2 * sizeof(pi_uint16_t) + 32 + + lv.size() * (2 * sizeof(pi_uint16_t)); + pi_char_t* buf = new pi_char_t[size]; + + // Fill in the header details. + pi_uint16_t flags = 0; + if (lv.editoruse) { + std::cout << "editoruse\n"; + flags |= VIEWFLAG_USE_IN_EDITVIEW; + } + PalmLib::set_short(buf, flags); + PalmLib::set_short(buf + sizeof(pi_uint16_t), lv.size()); + memset((char *) (buf + 4), 0, 32); + strncpy((char *) (buf + 4), lv.name.c_str(), 32); + + // Fill in the column details. + pi_char_t* p = buf + 4 + 32; + for (ListView::const_iterator i = lv.begin(); i != lv.end(); ++i) { + const ListViewColumn& col = (*i); + PalmLib::set_short(p, col.field); + PalmLib::set_short(p + sizeof(pi_uint16_t), col.width); + p += 2 * sizeof(pi_uint16_t); + } + + // Create the chunk and place it in the chunks list. + Chunk chunk(buf, size); + chunk.chunk_type = CHUNK_LISTVIEW_DEFINITION; + delete [] buf; + chunks.push_back(chunk); +} + +void PalmLib::FlatFile::DB::build_appinfo_block(const std::vector<DB::Chunk>& chunks, PalmLib::Block& appinfo) const +{ + std::vector<Chunk>::const_iterator iter; + + // Determine the size of the final app info block. + size_t size = 2 * sizeof(pi_uint16_t); + for (iter = chunks.begin(); iter != chunks.end(); ++iter) { + const Chunk& chunk = (*iter); + size += 2 * sizeof(pi_uint16_t) + chunk.size(); + } + + // Allocate the temporary buffer. Fill in the header. + pi_char_t* buf = new pi_char_t[size]; + PalmLib::set_short(buf, m_flags); + PalmLib::set_short(buf + sizeof(pi_uint16_t), getNumOfFields()); + + // Pack the chunks into the buffer. + size_t i = 4; + for (iter = chunks.begin(); iter != chunks.end(); ++iter) { + const Chunk& chunk = (*iter); + // Set the chunk type and size. + PalmLib::set_short(buf + i, chunk.chunk_type); + PalmLib::set_short(buf + i + 2, chunk.size()); + i += 4; + + // Copy the chunk data into the buffer. + memcpy(buf + i, chunk.data(), chunk.size()); + i += chunk.size(); + } + + // Finally, move the buffer into the provided appinfo block. + appinfo.set_raw(buf, size); + delete [] buf; +} + +void PalmLib::FlatFile::DB::outputPDB(PalmLib::Database& pdb) const +{ + unsigned i; + + // Let the superclass have a chance. + SUPERCLASS(PalmLib::FlatFile, Database, outputPDB, (pdb)); + + // Set the database's type and creator. + pdb.type(PalmLib::mktag('D','B','0','0')); + pdb.creator(PalmLib::mktag('D','B','O','S')); + + // Create the app info block. + std::vector<Chunk> chunks; + build_standard_chunks(chunks); + for (i = 0; i < getNumOfListViews(); ++i) { + build_listview_chunk(chunks, getListView(i)); + } + build_fieldsdata_chunks(chunks); + build_about_chunk(chunks); + + PalmLib::Block appinfo; + build_appinfo_block(chunks, appinfo); + pdb.setAppInfoBlock(appinfo); + + // Output each record to the PalmOS database. + for (i = 0; i < getNumRecords(); ++i) { + Record record = getRecord(i); + PalmLib::Record pdb_record; + + make_record(pdb_record, record); + pdb.appendRecord(pdb_record); + } +} + +unsigned PalmLib::FlatFile::DB::getMaxNumOfFields() const +{ + return 0; +} + +bool +PalmLib::FlatFile::DB::supportsFieldType(const Field::FieldType& type) const +{ + switch (type) { + case PalmLib::FlatFile::Field::STRING: + case PalmLib::FlatFile::Field::BOOLEAN: + case PalmLib::FlatFile::Field::INTEGER: + case PalmLib::FlatFile::Field::FLOAT: + case PalmLib::FlatFile::Field::DATE: + case PalmLib::FlatFile::Field::TIME: + case PalmLib::FlatFile::Field::NOTE: + case PalmLib::FlatFile::Field::LIST: + case PalmLib::FlatFile::Field::LINK: + case PalmLib::FlatFile::Field::LINKED: + case PalmLib::FlatFile::Field::CALCULATED: + return true; + default: + return false; + } +} + +std::vector<std::string> +PalmLib::FlatFile::DB::field_argumentf(int i, std::string& format) +{ + std::vector<std::string> vtitles(0, std::string("")); + int j; + + switch (field_type(i)) { + case PalmLib::FlatFile::Field::STRING: + format = std::string("%s"); + vtitles.push_back(std::string("default value")); + break; + case PalmLib::FlatFile::Field::INTEGER: + format = std::string("%ld/%d"); + vtitles.push_back(std::string("default value")); + vtitles.push_back(std::string("increment")); + break; + case PalmLib::FlatFile::Field::FLOAT: + format = std::string("%f"); + vtitles.push_back(std::string("default value")); + break; + case PalmLib::FlatFile::Field::DATE: + format = std::string("%d/%d/%d"); + vtitles.push_back(std::string("Year (or now)")); + vtitles.push_back(std::string("Month")); + vtitles.push_back(std::string("Day in the month")); + break; + case PalmLib::FlatFile::Field::TIME: + format = std::string("%d/%d"); + vtitles.push_back(std::string("Hour (or now)")); + vtitles.push_back(std::string("Minute")); + break; + case PalmLib::FlatFile::Field::LIST: + format = std::string(""); + for (j = 0; j < 31; i++) { + format += std::string("%s/"); + std::ostringstream title; + title << "item " << j; + vtitles.push_back(title.str()); + } + format += std::string("%s"); + vtitles.push_back(std::string("item 32")); + break; + case PalmLib::FlatFile::Field::LINK: + format = std::string("%s/%d"); + vtitles.push_back(std::string("database")); + vtitles.push_back(std::string("field number")); + break; + case PalmLib::FlatFile::Field::LINKED: + format = std::string("%d/%d"); + vtitles.push_back(std::string("link field number")); + vtitles.push_back(std::string("field number")); + break; + case PalmLib::FlatFile::Field::CALCULATED: + case PalmLib::FlatFile::Field::BOOLEAN: + case PalmLib::FlatFile::Field::NOTE: + default: + format = std::string(""); + break; + } + return vtitles; +} + +unsigned PalmLib::FlatFile::DB::getMaxNumOfListViews() const +{ + return 0; +} + +void PalmLib::FlatFile::DB::doneWithSchema() +{ + // Let the superclass have a chance. + SUPERCLASS(PalmLib::FlatFile, Database, doneWithSchema, ()); +/* false from the 0.3.3 version + if (getNumOfListViews() < 1) + throw PalmLib::error("at least one list view must be specified"); +*/ +} + +void PalmLib::FlatFile::DB::setOption(const std::string& name, + const std::string& value) +{ + if (name == "find") { + if (!StrOps::string2boolean(value)) + m_flags &= ~(1); + else + m_flags |= 1; + } else if (name == "read-only" + || name == "readonly") { + if (!StrOps::string2boolean(value)) + m_flags &= ~(0x8000); + else + m_flags |= 0x8000; + } else { + SUPERCLASS(PalmLib::FlatFile, Database, setOption, (name, value)); + } +} + +PalmLib::FlatFile::Database::options_list_t +PalmLib::FlatFile::DB::getOptions(void) const +{ + typedef PalmLib::FlatFile::Database::options_list_t::value_type value; + PalmLib::FlatFile::Database::options_list_t result; + + result = SUPERCLASS(PalmLib::FlatFile, Database, getOptions, ()); + + if (m_flags & 1) + result.push_back(value("find", "true")); + + if (m_flags & 0x8000) + result.push_back(value("read-only", "true")); + + return result; +} |
