diff options
Diffstat (limited to 'src/translators/pilotdb')
24 files changed, 4696 insertions, 0 deletions
diff --git a/src/translators/pilotdb/Makefile.am b/src/translators/pilotdb/Makefile.am new file mode 100644 index 0000000..cf21d12 --- /dev/null +++ b/src/translators/pilotdb/Makefile.am @@ -0,0 +1,16 @@ +####### kdevelop will overwrite this part!!! (begin)########## +noinst_LIBRARIES = libpilotdb.a + +AM_CPPFLAGS = $(all_includes) + +libpilotdb_a_METASOURCES = AUTO + +libpilotdb_a_SOURCES = pilotdb.cpp strop.cpp + +SUBDIRS = libflatfile libpalm + +EXTRA_DIST = strop.cpp strop.h portability.h pilotdb.h pilotdb.cpp + +####### kdevelop will overwrite this part!!! (end)############ + +KDE_OPTIONS = noautodist 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 ([email protected]) + * + * 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; +} diff --git a/src/translators/pilotdb/libflatfile/DB.h b/src/translators/pilotdb/libflatfile/DB.h new file mode 100644 index 0000000..dd09d36 --- /dev/null +++ b/src/translators/pilotdb/libflatfile/DB.h @@ -0,0 +1,166 @@ +/* + * This class provides access to DB-format databases. + */ + +#ifndef __PALMLIB_FLATFILE_DB_H__ +#define __PALMLIB_FLATFILE_DB_H__ + +#include <map> +#include <string> + +#include "../libpalm/Block.h" +#include "../libpalm/Database.h" +#include "Database.h" + +namespace PalmLib { + namespace FlatFile { + + class DB : public Database { + public: + /** + * Return true if this class can handle the given PalmOS + * database. + * + * @param pdb PalmOS database to check for support. + */ + static bool classify(PalmLib::Database& pdb); + + /** + * Return true if this class is the database identified by + * name. + * + * @param name A database type name to check. + */ + static bool match_name(const std::string& name); + + /** + * Default constructor for an initially empty database. + */ + DB():Database("db"), m_flags(0) { } + + /** + * Constructor which fills the flat-file structure from a + * PalmOS database. + */ + DB(PalmLib::Database&); + + // destructor + virtual ~DB() { } + + /** + * After all processing to add fields and records is done, + * outputPDB is called to create the actual file format + * used by the flat-file database product. + * + * @param pdb An instance of PalmLib::Database. + */ + virtual void outputPDB(PalmLib::Database& pdb) const; + + /** + * Return the maximum number of fields allowed in the + * database. This class returns 0 since there is no limit. + */ + virtual unsigned getMaxNumOfFields() const; + + /** + * Return true for the field types that this class + * currently supports. Returns false otherwise. + * + * @param type The field type to check for support. + */ + virtual bool supportsFieldType(const Field::FieldType& type) const; + + /** + * write the format of the field's argument in format, + * and return a strings' vector with name of each argument part. + * the format use the same display as used by printf + */ + virtual std::vector<std::string> field_argumentf(int i, std::string& format); + + /** + * Return the maximum number of views supported by this + * type of flat-file database. + */ + virtual unsigned getMaxNumOfListViews() const; + + /** + * Hook the end of the schema processing. + */ + virtual void doneWithSchema(); + + /** + * Set a extra option. + * + * @param opt_name The name of the option to set. + * @param opt_value The value to assign to this option. + */ + virtual void setOption(const std::string& name, + const std::string& value); + + /** + * Get a list of extra options. + */ + virtual options_list_t getOptions(void) const; + + // Produce a PalmOS record from a flat-file record. + void make_record(PalmLib::Record& pdb_record, + const PalmLib::FlatFile::Record& record) const; + + private: + pi_uint16_t m_flags; + + class Chunk : public PalmLib::Block { + public: + Chunk() : PalmLib::Block(), chunk_type(0) { } + Chunk(const Chunk& rhs) + : PalmLib::Block(rhs), chunk_type(rhs.chunk_type) { } + Chunk(PalmLib::Block::const_pointer data, + const PalmLib::Block::size_type size) + : PalmLib::Block(data, size), chunk_type(0) { } + Chunk& operator = (const Chunk& rhs) { + Block::operator = (rhs); + chunk_type = rhs.chunk_type; + return *this; + } + + pi_uint16_t chunk_type; + }; + + typedef std::map<pi_uint16_t, std::vector<Chunk> > chunks_t; + chunks_t m_chunks; + + // Extract the chunks from an app info block to m_chunks. + void extract_chunks(const PalmLib::Block&); + + // Extract the schema. + void extract_schema(unsigned numFields); + + // Extract the list views from the app info block. + void extract_listviews(); + + //extract the field data + std::string extract_fieldsdata(pi_uint16_t field_search, + PalmLib::FlatFile::Field::FieldType type); + + void extract_aboutinfo(); + + // Determine location and size of each field. + void parse_record(PalmLib::Record& record, + std::vector<pi_char_t *>& ptrs, + std::vector<size_t>& sizes); + + // The following routines build various types of chunks + // for the app info block and assemble them all. + void build_fieldsdata_chunks(std::vector<Chunk>& chunks) const; + void build_standard_chunks(std::vector<Chunk>&) const; + void build_listview_chunk(std::vector<Chunk>&, + const ListView&) const; + void build_about_chunk(std::vector<Chunk>& chunks) const; + void build_appinfo_block(const std::vector<Chunk>&, + PalmLib::Block&) const; + }; + + } +} + +#endif diff --git a/src/translators/pilotdb/libflatfile/Database.cpp b/src/translators/pilotdb/libflatfile/Database.cpp new file mode 100644 index 0000000..578b82d --- /dev/null +++ b/src/translators/pilotdb/libflatfile/Database.cpp @@ -0,0 +1,331 @@ +/* + * palm-db-tools: Abstract adaptor for flat-file databases. + * Copyright (C) 1999-2000 by Tom Dyas ([email protected]) + */ + +#include <iostream> +#include <sstream> +#include <stdexcept> +#include <sstream> +#include <utility> +#include <cctype> + +#include <kdebug.h> + +#include "Database.h" + +PalmLib::FlatFile::Database::Database(std::string p_Type, const PalmLib::Database& pdb) + : m_Type(p_Type) +{ + title(pdb.name()); + m_backup = pdb.backup(); + m_readonly = pdb.readonly(); + m_copy_prevention = pdb.copy_prevention(); +} + +void +PalmLib::FlatFile::Database::outputPDB(PalmLib::Database& pdb) const +{ + pdb.name(title()); + pdb.backup(m_backup); + pdb.readonly(m_readonly); + pdb.copy_prevention(m_copy_prevention); +} + +std::string +PalmLib::FlatFile::Database::title() const +{ + return m_title; +} + +void +PalmLib::FlatFile::Database::title(const std::string& title) +{ + m_title = title; +} + +unsigned +PalmLib::FlatFile::Database::getNumOfFields() const +{ + return m_fields.size(); +} + +std::string +PalmLib::FlatFile::Database::field_name(int i) const +{ + return m_fields[i].title(); +/* return m_fields[i].first;*/ +} + +PalmLib::FlatFile::Field::FieldType +PalmLib::FlatFile::Database::field_type(int i) const +{ + return m_fields[i].type(); +/* return m_fields[i].second;*/ +} + +PalmLib::FlatFile::FType +PalmLib::FlatFile::Database::field(int i) const +{ + return m_fields[i]; +} + +void +PalmLib::FlatFile::Database::appendField(PalmLib::FlatFile::FType field) +{ + if (! supportsFieldType(field.type())) { +// throw PalmLib::error("unsupported field type"); + kdDebug() << "unsupported field type" << endl; + return; + } + if (getMaxNumOfFields() != 0 && getNumOfFields() + 1 > getMaxNumOfFields()) { +// throw PalmLib::error("maximum number of fields reached"); + kdDebug() << "maximum number of fields reached" << endl; + return; + } + m_fields.push_back(field); +} + +void +PalmLib::FlatFile::Database::appendField(const std::string& name, + Field::FieldType type, std::string data) +{ + if (! supportsFieldType(type)) { + kdDebug() << "PalmLib::FlatFile::Database::appendField() - unsupported field type" << endl; + return; + } +// throw PalmLib::error("unsupported field type"); + if (getMaxNumOfFields() != 0 && getNumOfFields() + 1 > getMaxNumOfFields()) { + kdDebug() << "PalmLib::FlatFile::Database::appendField() - maximum number of fields reached" << endl; + return; + } +// throw PalmLib::error("maximum number of fields reached"); + +/* m_fields.push_back(std::make_pair(name, type));*/ +/* m_fields.push_back(PalmLib::FlatFile::make_ftype(name, type));*/ + m_fields.push_back(PalmLib::FlatFile::FType(name, type, data)); +} + +void +PalmLib::FlatFile::Database::insertField(int i, PalmLib::FlatFile::FType field) +{ + if (! supportsFieldType(field.type())) { +// throw PalmLib::error("unsupported field type"); + kdDebug() << "unsupported field type" << endl; + return; + } + if (getMaxNumOfFields() != 0 && getNumOfFields() + 1 > getMaxNumOfFields()) { +// throw PalmLib::error("maximum number of fields reached"); + kdDebug() << "maximum number of fields reached" << endl; + return; + } +/* m_fields.push_back(std::make_pair(name, type));*/ +/* m_fields.push_back(PalmLib::FlatFile::make_ftype(name, type));*/ + m_fields.insert(m_fields.begin() + i, field); +} + +void +PalmLib::FlatFile::Database::insertField(int i, const std::string& name, + Field::FieldType type, std::string data) +{ + if (! supportsFieldType(type)) { +// throw PalmLib::error("unsupported field type"); + kdDebug() << "unsupported field type" << endl; + return; + } + if (getMaxNumOfFields() != 0 && getNumOfFields() + 1 > getMaxNumOfFields()) { +// throw PalmLib::error("maximum number of fields reached"); + kdDebug() << "maximum number of fields reached" << endl; + return; + } +/* m_fields.push_back(std::make_pair(name, type));*/ +/* m_fields.push_back(PalmLib::FlatFile::make_ftype(name, type));*/ + m_fields.insert(m_fields.begin() + i, PalmLib::FlatFile::FType(name, type, data)); +} + +void +PalmLib::FlatFile::Database::removeField(int i) +{ + m_fields.erase(m_fields.begin() + i); +} + +unsigned +PalmLib::FlatFile::Database::getNumRecords() const +{ + return m_records.size(); +} + +PalmLib::FlatFile::Record +PalmLib::FlatFile::Database::getRecord(unsigned index) const +{ + if (index >= getNumRecords()) { + kdDebug() << "invalid index" << endl; + //throw std::out_of_range("invalid index"); + } + return m_records[index]; +} + +void +PalmLib::FlatFile::Database::appendRecord(PalmLib::FlatFile::Record rec) +{ + if (rec.fields().size() != getNumOfFields()) { +// throw PalmLib::error("the number of fields mismatch"); + kdDebug() << "the number of fields mismatch" << endl; + return; + } + for (unsigned int i = 0; i < getNumOfFields(); i++) { +#ifdef HAVE_VECTOR_AT + const Field field = rec.fields().at(i); +#else + const Field field = rec.fields()[i]; +#endif + if (field.type != field_type(i)) { + kdDebug() << "field " << i << " type " << field_type(i) << " mismatch: " << field.type << endl; + return; +// throw PalmLib::error(buffer.str()); + } + } + m_records.push_back(rec); +} + +void +PalmLib::FlatFile::Database::deleteRecord(unsigned index) +{ + m_records.erase(m_records.begin() + index); +} + +void +PalmLib::FlatFile::Database::clearRecords() +{ + m_records.clear(); +} + +unsigned +PalmLib::FlatFile::Database::getNumOfListViews() const +{ + return m_listviews.size(); +} + +PalmLib::FlatFile::ListView +PalmLib::FlatFile::Database::getListView(unsigned index) const +{ + return m_listviews[index]; +} + +void +PalmLib::FlatFile::Database::setListView(unsigned index, + const PalmLib::FlatFile::ListView& lv) +{ + // Ensure that the field numbers are within range. + for (PalmLib::FlatFile::ListView::const_iterator i = lv.begin(); + i != lv.end(); ++i) { + if ((*i).field >= getNumOfFields()) + return; + } + + m_listviews[index] = lv; +} + +void +PalmLib::FlatFile::Database::appendListView(const ListView& lv) +{ + // Enforce any limit of the maximum number of list views. + if (getMaxNumOfListViews() != 0 + && getNumOfListViews() + 1 > getMaxNumOfListViews()) + return; +// throw PalmLib::error("too many list views for this database type"); + + // Ensure that the field numbers are within range. + for (PalmLib::FlatFile::ListView::const_iterator i = lv.begin(); + i != lv.end(); ++i) { + if ((*i).field >= getNumOfFields()) + return; + } + m_listviews.push_back(lv); +} + +void +PalmLib::FlatFile::Database::removeListView(unsigned index) +{ + if (index < getNumOfListViews()) + m_listviews.erase( m_listviews.begin()+index); +} + +static void +strlower(std::string& str) +{ + for (std::string::iterator p = str.begin(); p != str.end(); ++p) { + if (isupper(*p)) + *p = tolower(*p); + } +} + +static bool +string2boolean(std::string str) +{ + strlower(str); + if (str == "on") + return true; + else if (str == "off") + return false; + else if (str == "true") + return true; + else if (str == "t") + return true; + else if (str == "false") + return false; + else if (str == "f") + return false; + else { + int num = 0; + + std::istringstream(str.c_str()) >> num; + return num != 0 ? true : false; + } +} + +void +PalmLib::FlatFile::Database::setOption(const std::string& name, + const std::string& value) +{ + if (name == "backup") + m_backup = string2boolean(value); + else if (name == "inROM") + m_readonly = string2boolean(value); + else if (name == "copy-prevention") + m_copy_prevention = string2boolean(value); +} + +PalmLib::FlatFile::Database::options_list_t +PalmLib::FlatFile::Database::getOptions() const +{ + PalmLib::FlatFile::Database::options_list_t set; + typedef PalmLib::FlatFile::Database::options_list_t::value_type value; + + if (m_backup) + set.push_back(value("backup", "true")); + else + set.push_back(value("backup", "false")); + + if (m_readonly) + set.push_back(value("inROM", "true")); + + if (m_copy_prevention) + set.push_back(value("copy-prevention", "true")); + + return set; +} + +void +PalmLib::FlatFile::Database::doneWithSchema() +{ + // Ensure that the database has at least one field. + if (getNumOfFields() == 0) + return; +// throw PalmLib::error("at least one field must be specified"); + + // Ensure that the database has a title. + if (title().empty()) + return; +// throw PalmLib::error("a title must be specified"); +} diff --git a/src/translators/pilotdb/libflatfile/Database.h b/src/translators/pilotdb/libflatfile/Database.h new file mode 100644 index 0000000..5bcba32 --- /dev/null +++ b/src/translators/pilotdb/libflatfile/Database.h @@ -0,0 +1,320 @@ +/* + * palm-db-tools: Abstract adaptor for flat-file databases. + * Copyright (C) 1999-2000 by Tom Dyas ([email protected]) + */ + +#ifndef __PALMLIB_FLATFILE_DATABASE_H__ +#define __PALMLIB_FLATFILE_DATABASE_H__ + +#include <vector> +#include <string> +#include <utility> + +#include "../libpalm/Database.h" +#include "Field.h" +#include "Record.h" +#include "ListView.h" +#include "FType.h" + +#define NOTETITLE_LENGTH 32 + +namespace PalmLib { + namespace FlatFile { + + // This class is an in-memory representation of a typical + // PalmOS flat-file database. The caller can request write the + // data to a real PalmLib::Database object at any time to + // actually obtain the data in a format usable on the Palm + // Pilot. + + class Database { + public: + // convenience type for the options list parsing + typedef std::vector< std::pair< std::string, std::string> > options_list_t; + + /** + * Default constructor which creates an empty + * database. Subclasses should provide a default + * constructor and an additional constructorwhich takes a + * PalmOS::Database as an argument. + */ + Database(std::string p_Type) + : m_backup(false), m_readonly(false), + m_copy_prevention(false), m_Type(p_Type) + { } + + /** + * Constructor which fills the flat-file structure from a + * PalmOS database. + * + * @param pdb PalmOS database to read from. + */ + Database(std::string p_Type, const PalmLib::Database& pdb); + + /** + * The destructor is empty since we have no other objects + * to dispose of. It is virtual since we have subclasses + * for specific flat-file database products. + */ + virtual ~Database() { } + + /** + * After all processing to add fields and records is done, + * outputPDB is called to create the actual file format + * used by the flat-file database product. This method is + * abstract since only subclasses know the specific file + * formats. + * + * @param pdb An instance of PalmLib::Database. + */ + virtual void outputPDB(PalmLib::Database& pdb) const; + + /** + * Return the title of this flat-file database. + */ + virtual std::string title() const; + + /** + * Set the title of this database. + * + * @param title New title of the database. + */ + virtual void title(const std::string& title); + + /** + * Return the maximum number of fields allowed in the + * database. The object will not allow the number of + * fields to exceed the returned value. This method is + * abstract since only the subclasses know the limit on + * fields. 0 is returned if there is no limit. + */ + virtual unsigned getMaxNumOfFields() const = 0; + + /** + * Return the number of fields in the database. + */ + virtual unsigned getNumOfFields() const; + + /** + * Accessor function for the name of a field. + */ + virtual std::string field_name(int i) const; + + /** + * Accessor function for type of a field. + */ + virtual Field::FieldType field_type(int i) const; + + /** + * Accessor function for the field informations + */ + virtual FType field(int i) const; + + /** + * write the format of the field's argument in format, + * and return a strings' vector with name of each argument part. + * the format use the same display as used by printf + */ + virtual std::vector<std::string> field_argumentf(int, std::string& format) + { format = std::string(""); return std::vector<std::string>(0, std::string(""));} + + /** + * Add a field to the flat-file database. An exception + * will be thrown if the maximum number of fields would be + * exceeded or the field type is unsupported. + * + * @param name Name of the new field. + * @param type The type of the new field. + */ + virtual void appendField(FType field); + virtual void appendField(const std::string& name, + Field::FieldType type, std::string data = std::string("")); + + /** + * Insert a field to the flat-file database. An exception + * will be thrown if the maximum number of fields would be + * exceeded or the field type is unsupported. + * + * @param name Name of the new field. + * @param type The type of the new field. + */ + virtual void insertField(int i, FType field); + virtual void insertField(int i, const std::string& name, + Field::FieldType type, std::string data = std::string("")); + + /** + * Remove a Field in the flat-file database. An Exception + * will thrown if the field doesn't exist. + */ + virtual void removeField(int i); + + /** + * Returns true if this database supports a specific field + * type. This method is abstract since only the subclasses + * know which field types are supported. + * + * @param type The field type that should be checked for support. + */ + virtual bool supportsFieldType(const Field::FieldType& type) const = 0; + + /** + * Return the number of records in the database. + */ + virtual unsigned getNumRecords() const; + + /** + * Return the record with the given index. The caller gets + * a private copy of the data and _not_ a reference to the + * data. + * + * @param index Index of the record to retrieve. + */ + virtual Record getRecord(unsigned index) const; + + /** + * Append a record to the database. An exception will be + * thrown if their are not enough fields or if field types + * mismatch. + * + * @param rec The record to append. + */ + virtual void appendRecord(Record rec); + + /** + * Remove all records from the database + */ + virtual void clearRecords(); + + /** + * Remove a record from the database + */ + virtual void deleteRecord(unsigned index); + + /** + * Return the maximum number of views supported by this + * type of flat-file database. This method is abstract + * since only the subclasses know the exact value. + */ + virtual unsigned getMaxNumOfListViews() const = 0; + + /** + * Return the actual number of views present in this + * database. + */ + virtual unsigned getNumOfListViews() const; + + /** + * Return a copy of the list view at the given index. + * + * @param index Index of the list view to return. + */ + virtual ListView getListView(unsigned index) const; + + /** + * Set the list view at the given index to the new list + * view. An exception may be thrown if field numbers are + * invalid or the list view doesn't pass muster with the + * subclass. + * + * @param index Index of the list view to set. + * @param listview The new list view. + */ + virtual void setListView(unsigned index, const ListView& listview); + + /** + * Append a new list view. This will fail if the maximum + * number of list views would be exceeded. + * + * @param listview The new list view to append. + */ + virtual void appendListView(const ListView& listview); + + /** + * Remove a list view. + * + * @param index Index of the list view to remove. + */ + virtual void removeListView(unsigned index); + + /** + * Process a special option. If the option is not + * supported, then it is silently ignored. Subclasses + * should call the base class first so that options common + * to all flat-file databases can be processed. + * + * @param name Name of the option. + * @param value String value assigned to the option. */ + virtual void setOption(const std::string& name, + const std::string& value); + + /** + * Return a list of of all extra options supported by this + * database. Subclasses should call the base class first + * and then merge any extra options. Get a list of extra + * options. + */ + virtual options_list_t getOptions(void) const; + + /** + * Hook function which should be invoked by a caller after + * all calls the meta-deta functions have completed. This + * allows the database type-specific code to do final + * checks on the meta-data. An exception will be throw if + * there is an error. Otherwise, nothing will happen. + */ + virtual void doneWithSchema(); + + /** + * Change and Return the about information + * of the database when it's supportted + */ + virtual void setAboutInformation(std::string _string) + { + about.information = _string; + } + + virtual std::string getAboutInformation() const + { + return about.information; + } + + std::string type() const + { + return m_Type; + } + + private: + // We provide a dummy copy constructor and assignment + // operator in order to prevent any copying of the object. + Database(const Database&) { } + Database& operator = (const Database&) { return *this; } + +/* typedef std::vector< std::pair< std::string, Field::FieldType > >*/ + typedef std::vector< FType> + field_list_t; + typedef std::vector<Record> record_list_t; + typedef std::vector<ListView> listview_list_t; + + typedef std::vector< std::pair< std::string, std::vector< std::string > > > + listitems_list_t; + + field_list_t m_fields; // database schema + record_list_t m_records; // the database itself + listitems_list_t m_list; // the items lists include in the database + listview_list_t m_listviews; // list views + bool m_backup; // backup flag for PDB + bool m_readonly; // readonly flag for PDB + bool m_copy_prevention; // copy prevention for PDB + std::string m_title; // name of database + class About + { + public: + std::string information; + } about; + std::string m_Type; + }; + + } // namespace FlatFile +} // namespace PalmLib + +#endif diff --git a/src/translators/pilotdb/libflatfile/FType.h b/src/translators/pilotdb/libflatfile/FType.h new file mode 100644 index 0000000..86396b3 --- /dev/null +++ b/src/translators/pilotdb/libflatfile/FType.h @@ -0,0 +1,48 @@ +/* + * palm-db-tools: Field Type definitions for flat-file database objects. + * Copyright (C) 2000 by Tom Dyas ([email protected]) + */ + +#ifndef __PALMLIB_FLATFILE_FTYPE_H__ +#define __PALMLIB_FLATFILE_FTYPE_H__ + +#include <string> +#include <utility> + +#include "../libpalm/palmtypes.h" +#include "Field.h" + +namespace PalmLib { + namespace FlatFile { + + class FType { + public: + friend class PalmLib::FlatFile::Field; + FType(std::string title, PalmLib::FlatFile::Field::FieldType type) : + m_title(title), m_type(type), m_data("") { } + + FType(std::string title, PalmLib::FlatFile::Field::FieldType type, std::string data) : + m_title(title), m_type(type), m_data(data) { } + + virtual ~FType() { } + + std::string title() const {return m_title;} + virtual PalmLib::FlatFile::Field::FieldType type() const + { return m_type;} + + virtual std::string argument() const { return m_data;} + + void set_argument( const std::string data) { m_data = data;} + + void setTitle( const std::string value) { m_title = value;} + void setType( const PalmLib::FlatFile::Field::FieldType value) { m_type = value;} + private: + std::string m_title; + PalmLib::FlatFile::Field::FieldType m_type; + + std::string m_data; + }; + } +} + +#endif diff --git a/src/translators/pilotdb/libflatfile/Field.h b/src/translators/pilotdb/libflatfile/Field.h new file mode 100644 index 0000000..583bc21 --- /dev/null +++ b/src/translators/pilotdb/libflatfile/Field.h @@ -0,0 +1,119 @@ +/* + * palm-db-tools: Field definitions for flat-file database objects. + * Copyright (C) 2000 by Tom Dyas ([email protected]) + */ + +#ifndef __PALMLIB_FLATFILE_FIELD_H__ +#define __PALMLIB_FLATFILE_FIELD_H__ + +#include <string> + +#include "../libpalm/palmtypes.h" + +namespace PalmLib { + namespace FlatFile { + + class Field { + public: + Field() : no_value(false), type(STRING), v_boolean(false), + v_integer(0), v_float(0) { } + ~Field() { } + + // True if this field has no value. (NULL in SQL terms) + bool no_value; + + enum FieldType { + STRING, + BOOLEAN, + INTEGER, + FLOAT, + DATE, + TIME, + DATETIME, + LIST, + LINK, + NOTE, + CALCULATED, + LINKED, + LAST + }; + + enum FieldType type; + + std::string v_string; + std::string v_note; + bool v_boolean; + PalmLib::pi_int32_t v_integer; + long double v_float; + struct { + int month; + int day; + int year; + } v_date; // valid for DATE and DATETIME + struct { + int hour; + int minute; + } v_time; // valid for TIME and DATETIME + + /* + friend Field operator = (const Field& y) + { + this.v_string = y.v_string; + this.v_boolean = y.v_boolean; + this.v_integer = y.v_integer; + this.v_float = y.v_float; + this.v_date.month = y.v_date.month; + this.v_date.day = y.v_date.day; + this.v_date.year = y.v_date.year; + this.v_time.hour = y.v_time.hour; + this.v_time.minute = y.v_time.minute; + this.v_note = y.v_note; + } +*/ + friend bool operator==(const Field& x, const Field& y) + { + if (x.type != y.type) + return false; + switch (x.type) { + case STRING: + return (x.v_string == y.v_string); + case BOOLEAN: + return (x.v_boolean == y.v_boolean); + case INTEGER: + return (x.v_integer == y.v_integer); + case FLOAT: + return (x.v_float == y.v_float); + case DATE: + return (x.v_date.month == y.v_date.month + && x.v_date.day == y.v_date.day + && x.v_date.year == y.v_date.year); + case TIME: + return (x.v_time.hour == y.v_time.hour + && x.v_time.minute == y.v_time.minute); + case DATETIME: + return (x.v_date.month == y.v_date.month + && x.v_date.day == y.v_date.day + && x.v_date.year == y.v_date.year + && x.v_time.hour == y.v_time.hour + && x.v_time.minute == y.v_time.minute); + case LIST: + return (x.v_string == y.v_string); + case LINK: + return (x.v_string == y.v_string); + case NOTE: + return (x.v_string == y.v_string + && x.v_note == y.v_note); + case CALCULATED: + return true; //a calculated field could be recalculate at each time + case LINKED: + return (x.v_string == y.v_string); + default: + return (x.v_string == y.v_string); + } + } + }; + + } +} + +#endif diff --git a/src/translators/pilotdb/libflatfile/ListView.h b/src/translators/pilotdb/libflatfile/ListView.h new file mode 100644 index 0000000..4229548 --- /dev/null +++ b/src/translators/pilotdb/libflatfile/ListView.h @@ -0,0 +1,77 @@ +#ifndef __PALMOS__FLATFILE__VIEW_H__ +#define __PALMOS__FLATFILE__VIEW_H__ + +#include <string> +#include <vector> + +#include "ListViewColumn.h" + +namespace PalmLib { + namespace FlatFile { + + // The ListView class represents the a "list view" as + // implemented by the major PalmOS flat-file programs. The + // main idea is a series of columns that display a field of + // the database. + // + // For fun, this class exports the STL interface of the STL + // class it uses to store the ListViewColumn classes. + + class ListView { + private: + typedef std::vector<ListViewColumn> rep_type; + rep_type rep; + + public: + typedef rep_type::value_type value_type; + typedef rep_type::iterator iterator; + typedef rep_type::const_iterator const_iterator; + typedef rep_type::reference reference; + typedef rep_type::const_reference const_reference; + typedef rep_type::size_type size_type; + typedef rep_type::difference_type difference_type; + typedef rep_type::reverse_iterator reverse_iterator; + typedef rep_type::const_reverse_iterator const_reverse_iterator; + + // global fields + std::string name; + bool editoruse; + + // STL pull-up interface (probably not complete) + iterator begin() { return rep.begin(); } + const_iterator begin() const { return rep.begin(); } + iterator end() { return rep.end(); } + const_iterator end() const { return rep.end(); } + reverse_iterator rbegin() { return rep.rbegin(); } + const_reverse_iterator rbegin() const { return rep.rbegin(); } + reverse_iterator rend() { return rep.rend(); } + const_reverse_iterator rend() const { return rep.rend(); } + size_type size() const { return rep.size(); } + size_type max_size() const { return rep.max_size(); } + bool empty() const { return rep.empty(); } + reference front() { return rep.front(); } + const_reference front() const { return rep.front(); } + reference back() { return rep.back(); } + const_reference back() const { return rep.back(); } + void push_back(const ListViewColumn& x) { rep.push_back(x); } + void pop_back() { rep.pop_back(); } + void clear() { rep.clear(); } + void resize(size_type new_size, const ListViewColumn& x) + { rep.resize(new_size, x); } + void resize(size_type new_size) + { rep.resize(new_size, ListViewColumn()); } + + ListView() : rep(), name(""), editoruse(false) { } + ListView(const ListView& rhs) : rep(rhs.rep), name(rhs.name), editoruse(false) { } + ListView& operator = (const ListView& rhs) { + name = rhs.name; + rep = rhs.rep; + return *this; + } + + }; + + } +} + +#endif diff --git a/src/translators/pilotdb/libflatfile/ListViewColumn.h b/src/translators/pilotdb/libflatfile/ListViewColumn.h new file mode 100644 index 0000000..7b5330e --- /dev/null +++ b/src/translators/pilotdb/libflatfile/ListViewColumn.h @@ -0,0 +1,19 @@ +#ifndef __PALMLIB_FLATFILE_LISTVIEWCOLUMN_H__ +#define __PALMLIB_FLATFILE_LISTVIEWCOLUMN_H__ + +namespace PalmLib { + namespace FlatFile { + + // The ListViewColumn class stores the field number and column + // width for a column in a list view. + + struct ListViewColumn { + ListViewColumn() : field(0), width(80) { } + ListViewColumn(unsigned a1, unsigned a2) : field(a1), width(a2) { } + unsigned field; + unsigned width; + }; + } +} + +#endif diff --git a/src/translators/pilotdb/libflatfile/Makefile.am b/src/translators/pilotdb/libflatfile/Makefile.am new file mode 100644 index 0000000..d3ab012 --- /dev/null +++ b/src/translators/pilotdb/libflatfile/Makefile.am @@ -0,0 +1,20 @@ +####### kdevelop will overwrite this part!!! (begin)########## +noinst_LIBRARIES = liblibflatfile.a + +AM_CPPFLAGS = $(all_includes) + +liblibflatfile_a_METASOURCES = AUTO + +liblibflatfile_a_SOURCES = DB.cpp Database.cpp + + +EXTRA_DIST = Database.cpp Database.h DB.cpp DB.h Field.h FType.h ListView.h ListViewColumn.h Record.h + +####### kdevelop will overwrite this part!!! (end)############ + +# is this the right way to do this? I need to include the strop.o object file since its +# in the parent directory +liblibflatfile_a_LIBADD = ../strop.o +CLEANFILES = strop.Po + +KDE_OPTIONS = noautodist diff --git a/src/translators/pilotdb/libflatfile/Record.h b/src/translators/pilotdb/libflatfile/Record.h new file mode 100644 index 0000000..537953e --- /dev/null +++ b/src/translators/pilotdb/libflatfile/Record.h @@ -0,0 +1,45 @@ +/* + * palm-db-tools: Field definitions for flat-file database objects. + * Copyright (C) 2000 by Tom Dyas ([email protected]) + */ + +#ifndef __PALMLIB_FLATFILE_RECORD_H__ +#define __PALMLIB_FLATFILE_RECORD_H__ + +#include <vector> + +#include "Field.h" + +namespace PalmLib { + namespace FlatFile { +// typedef std::vector<Field> Record; + + class Record{ + public: + + const std::vector<Field> fields() const { return m_Fields; } + + void appendField(Field newfield) { m_Fields.push_back(newfield); } + bool created() const { return m_New;} + void created(bool on){ m_New = on;} + bool secret() const { return m_Secret;} + void secret(bool on) { m_Secret = on;} + + bool dirty() const { return m_Dirty; } + void dirty( bool on) { m_Dirty = on; } + + pi_uint32_t unique_id() const { return m_UID; } + void unique_id(pi_uint32_t id) { m_UID = id; } + private: + + std::vector<Field> m_Fields; + bool m_Secret; + bool m_New; + + bool m_Dirty; + pi_uint32_t m_UID; + }; + } +} + +#endif diff --git a/src/translators/pilotdb/libpalm/Block.cpp b/src/translators/pilotdb/libpalm/Block.cpp new file mode 100644 index 0000000..c58f6f1 --- /dev/null +++ b/src/translators/pilotdb/libpalm/Block.cpp @@ -0,0 +1,85 @@ +/* + * palm-db-tools: Encapsulate "blocks" of data. + * Copyright (C) 2000 by Tom Dyas ([email protected]) + * + * The PalmLib::Block class represents a generic block of data. It is + * used to simplify passing arrays of pi_char_t around. + */ + +#include <cstring> + +#include "Block.h" + +void PalmLib::Block::reserve(PalmLib::Block::size_type new_size) +{ + if (new_size > capacity()) { + // Allocate a new buffer containing a copy of the old with the + // remainder zero'ed out. + pointer new_data = new pi_char_t[new_size]; + memcpy(new_data, m_data, m_size); + memset(new_data + m_size, 0, new_size - m_size); + + // Replace the existing buffer. + delete [] m_data; + m_data = new_data; + m_size = new_size; + } +} + +void PalmLib::Block::resize(size_type new_size) +{ + if (new_size < m_size) { + // Copy the data that will remain to a new buffer and switch to it. + pointer new_data = new pi_char_t[new_size]; + memcpy(new_data, m_data, new_size); + + // Replace the existing buffer. + delete [] m_data; + m_data = new_data; + m_size = new_size; + } else if (new_size > m_size) { + // Copy the data that will remain to a new buffer and switch to it. + pointer new_data = new pi_char_t[new_size]; + memcpy(new_data, m_data, m_size); + memset(new_data + m_size, 0, new_size - m_size); + + // Replace the existing buffer. + delete [] m_data; + m_data = new_data; + m_size = new_size; + } +} + +void PalmLib::Block::assign(PalmLib::Block::const_pointer data, + const PalmLib::Block::size_type size) +{ + clear(); + if (data && size > 0) { + m_size = size; + m_data = new pi_char_t[m_size]; + memcpy(m_data, data, m_size); + } +} + +void PalmLib::Block::assign(const PalmLib::Block::size_type size, + const PalmLib::Block::value_type value) +{ + clear(); + if (size > 0) { + m_size = size; + m_data = new pi_char_t[m_size]; + memset(m_data, value, m_size); + } +} + +bool operator == (const PalmLib::Block& lhs, const PalmLib::Block& rhs) +{ + if (lhs.size() == rhs.size()) { + if (lhs.data()) { + if (memcmp(lhs.data(), rhs.data(), lhs.size()) != 0) + return false; + } + return true; + } + return false; +} diff --git a/src/translators/pilotdb/libpalm/Block.h b/src/translators/pilotdb/libpalm/Block.h new file mode 100644 index 0000000..6ed6069 --- /dev/null +++ b/src/translators/pilotdb/libpalm/Block.h @@ -0,0 +1,186 @@ +/* + * palm-db-tools: Encapsulate "blocks" of data. + * Copyright (C) 2000 by Tom Dyas ([email protected]) + * + * The PalmLib::Block class represents a generic block of data. It is + * used to make passing pi_char_t buffers around very easy. The Record + * and Resource classes both inherit from this class. A STL interface + * is also attempted though it is probably not complete. + */ + +#ifndef __PALMLIB_BLOCK_H__ +#define __PALMLIB_BLOCK_H__ + +#include <algorithm> +#include <iterator> + +#include "palmtypes.h" + +namespace PalmLib { + + class Block { + public: + // STL: container type definitions + typedef PalmLib::pi_char_t value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type* iterator; + typedef const value_type* const_iterator; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + // STL: reverisible container type definitions +#ifdef __GNUG__ + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + typedef std::reverse_iterator<iterator> reverse_iterator; +#endif + + /** + * Default constructor. + */ + Block() : m_data(0), m_size(0) { } + + /** + * Constructor which fills the block from buffer "raw" with + * length "len". + */ + Block(const_pointer raw, const size_type len) : m_data(0), m_size(0) { + assign(raw, len); + } + + /** + * Constructor which takes a size and allocates a zero'ed out + * buffer of that size. (STL: Sequence: default fill + * constructor) + */ + Block(const size_type size, const value_type value = 0) + : m_data(0), m_size(0) { + assign(size, value); + } + + /** + * Constructor which takes two iterators and builds the block + * from the region between the iterators. (STL: Sequence: + * range constructor) + */ + Block(const_iterator a, const_iterator b) : m_data(0), m_size(0) { + assign(a, b - a); + } + + /** + * Copy constructor. Just copies the data from the other block + * into this block. + */ + Block(const Block& rhs) : m_data(0), m_size(0) { + assign(rhs.data(), rhs.size()); + } + + /** + * Destructor. Just frees the buffer if it exists. + */ + virtual ~Block() { clear(); } + + /** + * Assignment operator. + * + * @param rhs The block whose contents should be copied. + */ + Block& operator = (const Block& rhs) { + assign(rhs.data(), rhs.size()); + return *this; + } + + // STL: Container + iterator begin() { return m_data; } + const_iterator begin() const { return m_data; } + iterator end() { return (m_data != 0) ? (m_data + m_size) : (0); } + const_iterator end() const + { return (m_data != 0) ? (m_data + m_size) : (0); } + size_type size() const { return m_size; } + size_type max_size() const { + return size_type(-1) / sizeof(value_type); + } + bool empty() const { return m_size == 0; } + + // STL: Reversible Container +#ifdef __GNUG__ + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } +#endif + + // STL: Random Access Container + reference operator [] (size_type index) { return m_data[index]; } + const_reference operator [] (size_type index) const + { return m_data[index]; } + + // STL: Sequence (not complete) + void clear() { + if (m_data) { + delete [] m_data; + m_data = 0; + m_size = 0; + } + } + void resize(size_type n); + reference front() { return m_data[0]; } + const_reference front() const { return m_data[0]; } + + // STL: (present in vector but not part of a interface spec) + size_type capacity() const { return m_size; } + void reserve(size_type size); + + /** + * Return a pointer to the data area. If there are no + * contents, then the return value will be NULL. This is not + * an STL method but goes with this class as a singular data + * block and not a container (even though it is). + */ + iterator data() { return m_data; } + const_iterator data() const { return m_data; } + + /** + * Replace the existing contents of the Block with the buffer + * that starts at raw of size len. + * + * @param raw Pointer to the new contents. + * @param len Size of the new contents. + */ + void assign(const_pointer data, const size_type size); + + /** + * Replace the existing contents of the Block with a buffer + * consisting of size elements equal to fill. + * + * @param size The size of the new contents. + * @param value Value to fill the contents with. + */ + void assign(const size_type size, const value_type value = 0); + + // compatiblity functions (remove before final 0.3.0 release) + const_pointer raw_data() const { return data(); } + pointer raw_data() { return data(); } + size_type raw_size() const { return size(); } + void set_raw(const_pointer raw, const size_type len) + { assign(raw, len); } + + private: + pointer m_data; + size_type m_size; + }; + +} + +bool operator == (const PalmLib::Block& lhs, const PalmLib::Block& rhs); + +inline bool operator != (const PalmLib::Block& lhs, const PalmLib::Block& rhs) +{ return ! (lhs == rhs); } + +#endif diff --git a/src/translators/pilotdb/libpalm/Database.cpp b/src/translators/pilotdb/libpalm/Database.cpp new file mode 100644 index 0000000..38d896f --- /dev/null +++ b/src/translators/pilotdb/libpalm/Database.cpp @@ -0,0 +1,43 @@ +/* + * palm-db-tools: General interface to a PalmOS database. + * Copyright (C) 2000 by Tom Dyas ([email protected]) + * + * This file implens an abstract interface to PalmOS + * databases. Subclasses would include the class that reads/writes PDB + * files and possibly databases that can be accessed over the HotSync + * protocols. + */ + +#include "palmtypes.h" +#include "Record.h" +#include "Database.h" + +#ifndef __GNUG__ + +// MSVC: Visual C++ doesn't like initializers in the header ... +const PalmLib::pi_uint16_t PalmLib::Database::FLAG_HDR_RESOURCE = 0x0001; +const PalmLib::pi_uint16_t PalmLib::Database::FLAG_HDR_READ_ONLY = 0x0002; +const PalmLib::pi_uint16_t PalmLib::Database::FLAG_HDR_APPINFO_DIRTY = 0x0004; +const PalmLib::pi_uint16_t PalmLib::Database::FLAG_HDR_BACKUP = 0x0008; +const PalmLib::pi_uint16_t PalmLib::Database::FLAG_HDR_OK_TO_INSTALL_NEWER = 0x0010; +const PalmLib::pi_uint16_t PalmLib::Database::FLAG_HDR_RESET_AFTER_INSTALL = 0x0020; +const PalmLib::pi_uint16_t PalmLib::Database::FLAG_HDR_COPY_PREVENTION = 0x0040; +const PalmLib::pi_uint16_t PalmLib::Database::FLAG_HDR_STREAM = 0x0080; +const PalmLib::pi_uint16_t PalmLib::Database::FLAG_HDR_HIDDEN = 0x0100; +const PalmLib::pi_uint16_t PalmLib::Database::FLAG_HDR_LAUNCHABLE_DATA = 0x0200; +const PalmLib::pi_uint16_t PalmLib::Database::FLAG_HDR_OPEN = 0x8000; +const PalmLib::pi_char_t PalmLib::Record::FLAG_ATTR_DELETED = 0x80; +const PalmLib::pi_char_t PalmLib::Record::FLAG_ATTR_DIRTY = 0x40; +const PalmLib::pi_char_t PalmLib::Record::FLAG_ATTR_BUSY = 0x20; +const PalmLib::pi_char_t PalmLib::Record::FLAG_ATTR_SECRET = 0x10; + +#endif + +PalmLib::Database::Database(bool resourceDB) + : m_name(""), m_version(0), m_time_created(0), m_time_modified(0), + m_time_backup(0), m_modification(0), m_unique_id_seed(0) +{ + m_flags = resourceDB ? FLAG_HDR_RESOURCE : 0; + m_type = PalmLib::mktag(' ', ' ', ' ', ' '); + m_creator = PalmLib::mktag(' ', ' ', ' ', ' '); +} diff --git a/src/translators/pilotdb/libpalm/Database.h b/src/translators/pilotdb/libpalm/Database.h new file mode 100644 index 0000000..bcde8c0 --- /dev/null +++ b/src/translators/pilotdb/libpalm/Database.h @@ -0,0 +1,181 @@ +/* + * palm-db-tools: General interface to a PalmOS database. + * Copyright (C) 2000 by Tom Dyas ([email protected]) + * + * This header defines an abstract interface to PalmOS + * databases. Subclasses would include the class that reads/writes PDB + * files and possibly databases that can be accessed over the HotSync + * protocols. + */ + +#ifndef __PALMLIB_DATABASE_H__ +#define __PALMLIB_DATABASE_H__ + +#include <string> + +#include "palmtypes.h" +#include "Block.h" +#include "Record.h" +#include "Resource.h" + +namespace PalmLib { + + class Database { + public: + // Constants for bits in the flags field of a PalmOS database. +#ifdef __GNUG__ + static const pi_uint16_t FLAG_HDR_RESOURCE = 0x0001; + static const pi_uint16_t FLAG_HDR_READ_ONLY = 0x0002; + static const pi_uint16_t FLAG_HDR_APPINFO_DIRTY = 0x0004; + static const pi_uint16_t FLAG_HDR_BACKUP = 0x0008; + static const pi_uint16_t FLAG_HDR_OK_TO_INSTALL_NEWER = 0x0010; + static const pi_uint16_t FLAG_HDR_RESET_AFTER_INSTALL = 0x0020; + static const pi_uint16_t FLAG_HDR_COPY_PREVENTION = 0x0040; + static const pi_uint16_t FLAG_HDR_STREAM = 0x0080; + static const pi_uint16_t FLAG_HDR_HIDDEN = 0x0100; + static const pi_uint16_t FLAG_HDR_LAUNCHABLE_DATA = 0x0200; + static const pi_uint16_t FLAG_HDR_OPEN = 0x8000; +#else + static const pi_uint16_t FLAG_HDR_RESOURCE; + static const pi_uint16_t FLAG_HDR_READ_ONLY; + static const pi_uint16_t FLAG_HDR_APPINFO_DIRTY; + static const pi_uint16_t FLAG_HDR_BACKUP; + static const pi_uint16_t FLAG_HDR_OK_TO_INSTALL_NEWER; + static const pi_uint16_t FLAG_HDR_RESET_AFTER_INSTALL; + static const pi_uint16_t FLAG_HDR_COPY_PREVENTION; + static const pi_uint16_t FLAG_HDR_STREAM; + static const pi_uint16_t FLAG_HDR_HIDDEN; + static const pi_uint16_t FLAG_HDR_LAUNCHABLE_DATA; + static const pi_uint16_t FLAG_HDR_OPEN; +#endif + + Database(bool resourceDB = false); + virtual ~Database() { } + + bool isResourceDB() const {return (m_flags & FLAG_HDR_RESOURCE) != 0;} + + virtual pi_uint32_t type() const { return m_type; } + virtual void type(pi_uint32_t new_type) { m_type = new_type; } + + virtual pi_uint32_t creator() const { return m_creator; } + virtual void creator(pi_uint32_t new_creator) + { m_creator = new_creator; } + + virtual pi_uint16_t version() const { return m_version; } + virtual void version(pi_uint16_t v) { m_version = v; } + + virtual pi_int32_t creation_time() const { return m_time_created; } + virtual void creation_time(pi_int32_t ct) { m_time_created = ct; } + + virtual pi_uint32_t modification_time() const + { return m_time_modified; } + virtual void modification_time(pi_uint32_t mt) + { m_time_modified = mt; } + + virtual pi_uint32_t backup_time() const { return m_time_backup; } + virtual void backup_time(pi_uint32_t bt) { m_time_backup = bt; } + + virtual pi_uint32_t modnum() const { return m_modification; } + virtual void modnum(pi_uint32_t new_modnum) + { m_modification = new_modnum; } + + virtual pi_uint32_t unique_id_seed() const + { return m_unique_id_seed; } + virtual void unique_id_seed(pi_uint32_t uid_seed) + { m_unique_id_seed = uid_seed; } + + virtual pi_uint16_t flags() const { return m_flags; } + virtual void flags(pi_uint16_t flags) + { m_flags = flags & ~(FLAG_HDR_RESOURCE | FLAG_HDR_OPEN); } + + virtual std::string name() const { return m_name; } + virtual void name(const std::string& new_name) { m_name = new_name; } + + virtual bool backup() const + { return (m_flags & FLAG_HDR_BACKUP) != 0; } + virtual void backup(bool state) { + if (state) + m_flags |= FLAG_HDR_BACKUP; + else + m_flags &= ~(FLAG_HDR_BACKUP); + } + + virtual bool readonly() const + { return (m_flags & FLAG_HDR_READ_ONLY) != 0; } + virtual void readonly(bool state) { + if (state) + m_flags |= FLAG_HDR_READ_ONLY; + else + m_flags &= ~(FLAG_HDR_READ_ONLY); + } + + virtual bool copy_prevention() const + { return (m_flags & FLAG_HDR_COPY_PREVENTION) != 0; } + virtual void copy_prevention(bool state) { + if (state) + m_flags |= FLAG_HDR_COPY_PREVENTION; + else + m_flags &= ~(FLAG_HDR_COPY_PREVENTION); + } + + // Return the total number of records/resources in this + // database. + virtual unsigned getNumRecords() const = 0; + + // Return the database's application info block as a Block + // object. + virtual Block getAppInfoBlock() const { return Block(); } + + // Set the database's app info block to the contents of the + // passed Block object. + virtual void setAppInfoBlock(const Block &) { } + + // Return the database's sort info block as a Block object. + virtual Block getSortInfoBlock() const { return Block(); } + + // Set the database's sort info block to the contents of the + // passed Block object. + virtual void setSortInfoBlock(const Block &) { } + + // Return the record identified by the given index. The caller + // owns the returned RawRecord object. + virtual Record getRecord(unsigned index) const = 0; + + // Set the record identified by the given index to the given + // record. + virtual void setRecord(unsigned index, const Record& rec) = 0; + + // Append the given record to the database. + virtual void appendRecord(const Record& rec) = 0; + + // returned if the specified (type, ID) combination is not + // present in the database. The caller owns the returned + // RawRecord object. + virtual Resource getResourceByType(pi_uint32_t type, + pi_uint32_t id) const = 0; + + // Return the resource present at the given index. NULL is + // returned if the index is invalid. The caller owns the + // returned RawRecord object. + virtual Resource getResourceByIndex(unsigned index) const = 0; + + // Set the resouce at given index to passed Resource object. + virtual void setResource(unsigned index, const Resource& rsrc) = 0; + + private: + std::string m_name; + pi_uint16_t m_flags; + pi_uint16_t m_version; + pi_uint32_t m_time_created; + pi_uint32_t m_time_modified; + pi_uint32_t m_time_backup; + pi_uint32_t m_modification; + pi_uint32_t m_type; + pi_uint32_t m_creator; + pi_uint32_t m_unique_id_seed; + + }; + +} // namespace PalmLib + +#endif diff --git a/src/translators/pilotdb/libpalm/Makefile.am b/src/translators/pilotdb/libpalm/Makefile.am new file mode 100644 index 0000000..ea92331 --- /dev/null +++ b/src/translators/pilotdb/libpalm/Makefile.am @@ -0,0 +1,15 @@ +####### kdevelop will overwrite this part!!! (begin)########## +noinst_LIBRARIES = liblibpalm.a + +AM_CPPFLAGS = $(all_includes) + +liblibpalm_a_METASOURCES = AUTO + +liblibpalm_a_SOURCES = Database.cpp Block.cpp + + +EXTRA_DIST = Block.cpp Block.h palmtypes.h Record.h Resource.h Database.h Database.cpp + +####### kdevelop will overwrite this part!!! (end)############ + +KDE_OPTIONS = noautodist diff --git a/src/translators/pilotdb/libpalm/Record.h b/src/translators/pilotdb/libpalm/Record.h new file mode 100644 index 0000000..ecf19e3 --- /dev/null +++ b/src/translators/pilotdb/libpalm/Record.h @@ -0,0 +1,168 @@ +/* + * palm-db-tools: Raw PalmOS Records + * Copyright (C) 2000 by Tom Dyas ([email protected]) + */ + +#ifndef __PALMLIB_RECORD_H__ +#define __PALMLIB_RECORD_H__ + +#include "Block.h" + +namespace PalmLib { + + class Record : public Block { + public: +#ifdef __GNUG__ + static const pi_char_t FLAG_ATTR_DELETED = 0x80; + static const pi_char_t FLAG_ATTR_DIRTY = 0x40; + static const pi_char_t FLAG_ATTR_BUSY = 0x20; + static const pi_char_t FLAG_ATTR_SECRET = 0x10; +#else + static const pi_char_t FLAG_ATTR_DELETED; + static const pi_char_t FLAG_ATTR_DIRTY; + static const pi_char_t FLAG_ATTR_BUSY; + static const pi_char_t FLAG_ATTR_SECRET; +#endif + + /** + * Default constructor. + */ + Record() : Block(), m_attrs(0), m_unique_id(0) { } + + /** + * Copy constructor. + */ + Record(const Record& rhs) : Block(rhs.data(), rhs.size()) { + m_attrs = rhs.attrs(); + m_unique_id = rhs.unique_id(); + } + + /** + * Destructor. + */ + virtual ~Record() { } + + /** + * Constructor which lets the caller specify all the + * parameters. + * + * @param attrs Attribute byte (flags + category). + * @param unique_id Unique ID for this record. + * @param data Start of buffer to copy (or 0 for empty). + * @param size Size of the buffer to copy. + */ + Record(pi_char_t attrs, pi_uint32_t unique_id, + Block::const_pointer data, const Block::size_type size) + : Block(data, size), m_attrs(attrs), m_unique_id(unique_id) { } + + /** + * Constructor which lets the caller use the default fill + * constructor. + * @param attrs Attribute byte (flags + category). + * @param unique_id Unique ID for this record. + * @param size Size of buffer to generate. + * @param value Value to fill buffer with. + */ + Record(pi_char_t attrs, pi_uint32_t unique_id, + const size_type size, const value_type value = 0) + : Block(size, value), m_attrs(attrs), m_unique_id(unique_id) { } + + /** + * Assignment operator. + * + * @param rhs The PalmLib::Record we should become. */ + Record& operator = (const Record& rhs) { + Block::operator = (rhs); + m_attrs = rhs.attrs(); + m_unique_id = rhs.unique_id(); + return *this; + } + + /** + * Return the attributes byte which contains the category and + * flags. + */ + pi_char_t attrs() const { return m_attrs; } + + /** + * Return the state of the record's "deleted" flag. + */ + bool deleted() const { return (m_attrs & FLAG_ATTR_DELETED) != 0; } + + /** + * Set the state of the record's "deleted" flag. + * + * @param state New state of the "deleted" flag. + */ + void deleted(bool state) { + if (state) + m_attrs |= FLAG_ATTR_DELETED; + else + m_attrs &= ~(FLAG_ATTR_DELETED); + } + + /** + * Return the state of the record's "dirty" flag. + */ + bool dirty() const { return (m_attrs & FLAG_ATTR_DIRTY) != 0; } + + /** + * Set the state of the record's "dirty" flag. + * + * @param state New state of the "dirty" flag. + */ + void dirty(bool state) { + if (state) + m_attrs |= FLAG_ATTR_DIRTY; + else + m_attrs &= ~(FLAG_ATTR_DIRTY); + } + + /** + * Return the state of the record's "secret" flag. + */ + bool secret() const { return (m_attrs & FLAG_ATTR_SECRET) != 0; } + + /** + * Set the state of the record's "secret" flag. + * + * @param state New state of the "secret" flag. + */ + void secret(bool state) { + if (state) + m_attrs |= FLAG_ATTR_SECRET; + else + m_attrs &= ~(FLAG_ATTR_SECRET); + } + + /** + * Return the category of this record. + */ + pi_char_t category() const { return (m_attrs & 0x0F); } + + /** + * Set the category of this record. + */ + void category(pi_char_t cat) + { m_attrs &= ~(0x0F); m_attrs |= (cat & 0x0F); } + + /** + * Return the unique ID of this record. + */ + pi_uint32_t unique_id() const { return m_unique_id; } + + /** + * Set the unique ID of this record to uid. + * + * @param uid New unique ID for this record. + */ + void unique_id(pi_uint32_t uid) { m_unique_id = uid; } + + private: + pi_char_t m_attrs; + pi_uint32_t m_unique_id; + }; + +} + +#endif diff --git a/src/translators/pilotdb/libpalm/Resource.h b/src/translators/pilotdb/libpalm/Resource.h new file mode 100644 index 0000000..b98f718 --- /dev/null +++ b/src/translators/pilotdb/libpalm/Resource.h @@ -0,0 +1,85 @@ +/* + * palm-db-tools: PalmOS Resources + * Copyright (C) 2000 by Tom Dyas ([email protected]) + */ + +#ifndef __PALMLIB_RESOURCE_H__ +#define __PALMLIB_RESOURCE_H__ + +#include "Block.h" +#include "palmtypes.h" + +namespace PalmLib { + + class Resource : public Block { + public: + /** + * Default constructor. + */ + Resource() : Block(), m_type(0), m_id(0) { } + + /** + * Copy constructor. + */ + Resource(const Resource& rhs) : Block(rhs.data(), rhs.size()) { + m_type = rhs.type(); + m_id = rhs.id(); + } + + /** + * Destructor. + */ + virtual ~Resource() { } + + /** + * Constructor which lets the caller specify all the + * parameters. + * + * @param type Resource type + * @param id Resource ID + * @param data Start of buffer to copy. + * @param size Size of the buffer to copy. + */ + Resource(pi_uint32_t type, pi_uint32_t id, + const_pointer data, const size_type size) + : Block(data, size), m_type(type), m_id(id) { } + + /** + * Constructor which lets the caller use the default fill + * constructor. + * + * @param type Resource type + * @param id Resource ID + * @param size Size of buffer to generate. + * @param value Value to fill buffer with. + */ + Resource(pi_uint32_t type, pi_uint32_t id, + const size_type size, const value_type value = 0) + : Block(size, value), m_type(type), m_id(id) { } + + /** + * Assignment operator. + */ + Resource& operator = (const Resource& rhs) { + Block::operator = (rhs); + m_type = rhs.type(); + m_id = rhs.id(); + return *this; + } + + // Accessor functions for the resource type. + pi_uint32_t type() const { return m_type; } + void type(const pi_uint32_t _type) { m_type = _type; } + + // Accessor functions for the resource ID. + pi_uint32_t id() const { return m_id; } + void id(const pi_uint32_t _id) { m_id = _id; } + + private: + pi_uint32_t m_type; + pi_uint32_t m_id; + }; + +} + +#endif diff --git a/src/translators/pilotdb/libpalm/palmtypes.h b/src/translators/pilotdb/libpalm/palmtypes.h new file mode 100644 index 0000000..5c12262 --- /dev/null +++ b/src/translators/pilotdb/libpalm/palmtypes.h @@ -0,0 +1,117 @@ +/* + * This file contains type definitions and helper functions to make + * access to data in Palm Pilot order easier. + */ + +#ifndef __LIBPALM_PALMTYPES_H__ +#define __LIBPALM_PALMTYPES_H__ + +#include <stdexcept> + +#include "../portability.h" + +namespace PalmLib { + +#if SIZEOF_UNSIGNED_CHAR == 1 + typedef unsigned char pi_char_t; +#else +#error Unable to determine the size of pi_char_t. +#endif + +#if SIZEOF_UNSIGNED_LONG == 2 + typedef unsigned long pi_uint16_t; +#elif SIZEOF_UNSIGNED_INT == 2 + typedef unsigned int pi_uint16_t; +#elif SIZEOF_UNSIGNED_SHORT == 2 + typedef unsigned short pi_uint16_t; +#else +#error Unable to determine the size of pi_uint16_t. +#endif + +#if SIZEOF_LONG == 2 + typedef long pi_int16_t; +#elif SIZEOF_INT == 2 + typedef int pi_int16_t; +#elif SIZEOF_SHORT == 2 + typedef short pi_int16_t; +#else +#error Unable to determine the size of pi_int16_t. +#endif + +#if SIZEOF_UNSIGNED_LONG == 4 + typedef unsigned long pi_uint32_t; +#elif SIZEOF_UNSIGNED_INT == 4 + typedef unsigned int pi_uint32_t; +#elif SIZEOF_UNSIGNED_SHORT == 4 + typedef unsigned short pi_uint32_t; +#else +#error Unable to determine the size of pi_uint32_t. +#endif + +#if SIZEOF_LONG == 4 + typedef long pi_int32_t; +#elif SIZEOF_INT == 4 + typedef int pi_int32_t; +#elif SIZEOF_SHORT == 4 + typedef short pi_int32_t; +#else +#error Unable to determine the size of pi_int32_t. +#endif + +typedef union { + double number; +#ifdef WORDS_BIGENDIAN + struct { + PalmLib::pi_uint32_t hi; + PalmLib::pi_uint32_t lo; + } words; +#else + struct { + PalmLib::pi_uint32_t lo; + PalmLib::pi_uint32_t hi; + } words; +#endif +} pi_double_t; + + inline pi_int32_t get_long(const pi_char_t* p) { + return ( (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3] ); + } + + inline pi_int32_t get_treble(const pi_char_t* p) { + return ( (p[0] << 16) || (p[1] << 8) || p[0]); + } + + inline pi_int16_t get_short(const pi_char_t* p) { + return ( (p[0] << 8) | p[1] ); + } + + inline void set_long(pi_char_t *p, pi_int32_t v) { + p[0] = (v >> 24) & 0xFF; + p[1] = (v >> 16) & 0xFF; + p[2] = (v >> 8 ) & 0xFF; + p[3] = (v ) & 0xFF; + } + + inline void set_treble(pi_char_t *p, pi_int32_t v) { + p[0] = (v >> 16) & 0xFF; + p[1] = (v >> 8 ) & 0xFF; + p[2] = (v ) & 0xFF; + } + + inline void set_short(pi_char_t *p, pi_int16_t v) { + p[0] = (v >> 8) & 0xFF; + p[1] = (v ) & 0xFF; + } + + inline pi_uint32_t mktag(pi_char_t c1, pi_char_t c2, + pi_char_t c3, pi_char_t c4) + { return (((c1)<<24)|((c2)<<16)|((c3)<<8)|(c4)); } + + class error : public std::runtime_error { + public: + error(const std::string & what_arg) : std::runtime_error(what_arg) { } + }; + +} // namespace PalmLib + +#endif diff --git a/src/translators/pilotdb/pilotdb.cpp b/src/translators/pilotdb/pilotdb.cpp new file mode 100644 index 0000000..d7779e4 --- /dev/null +++ b/src/translators/pilotdb/pilotdb.cpp @@ -0,0 +1,277 @@ +/*************************************************************************** + copyright : (C) 2003-2006 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "pilotdb.h" +#include "strop.h" +#include "libflatfile/Record.h" + +#include <kdebug.h> + +#include <qbuffer.h> + +using namespace PalmLib; +using Tellico::Export::PilotDB; + +namespace { + static const int PI_HDR_SIZE = 78; + static const int PI_RESOURCE_ENT_SIZE = 10; + static const int PI_RECORD_ENT_SIZE = 8; +} + +PilotDB::PilotDB() : Database(false), m_app_info(), m_sort_info(), + m_next_record_list_id(0) { + pi_int32_t now = StrOps::get_current_time(); + creation_time(now); + modification_time(now); + backup_time(now); +} + +PilotDB::~PilotDB() { + for(record_list_t::iterator i = m_records.begin(); i != m_records.end(); ++i) { + delete (*i); + } +} + +QByteArray PilotDB::data() { + QBuffer b; + b.open(IO_WriteOnly); + + pi_char_t buf[PI_HDR_SIZE]; + pi_int16_t ent_hdr_size = isResourceDB() ? PI_RESOURCE_ENT_SIZE : PI_RECORD_ENT_SIZE; + std::streampos offset = PI_HDR_SIZE + m_records.size() * ent_hdr_size + 2; + + for(int i=0; i<32; ++i) { + buf[i] = 0; + } + memcpy(buf, name().c_str(), QMIN(31, name().length())); + set_short(buf + 32, flags()); + set_short(buf + 34, version()); + set_long(buf + 36, creation_time()); + set_long(buf + 40, modification_time()); + set_long(buf + 44, backup_time()); + set_long(buf + 48, modnum()); + if(m_app_info.raw_size() > 0) { + set_long(buf + 52, offset); + offset += m_app_info.raw_size(); + } else { + set_long(buf + 52, 0); + } + if(m_sort_info.raw_size() > 0) { + set_long(buf + 56, offset); + offset += m_sort_info.raw_size(); + } else { + set_long(buf + 56, 0); + } + set_long(buf + 60, type()); + set_long(buf + 64, creator()); + set_long(buf + 68, unique_id_seed()); + set_long(buf + 72, m_next_record_list_id); + set_short(buf + 76, m_records.size()); + + // Write the PDB/PRC header to the string. + b.writeBlock(reinterpret_cast<char *>(buf), sizeof(buf)); + + for(record_list_t::iterator i = m_records.begin(); i != m_records.end(); ++i) { + Block* entry = *i; + + if(isResourceDB()) { + Resource * resource = reinterpret_cast<Resource *> (entry); + set_long(buf, resource->type()); + set_short(buf + 4, resource->id()); + set_long(buf + 6, offset); + } else { + Record * record = reinterpret_cast<Record *> (entry); + set_long(buf, offset); + buf[4] = record->attrs(); + set_treble(buf + 5, record->unique_id()); + } + b.writeBlock(reinterpret_cast<char *>(buf), ent_hdr_size); + offset += entry->raw_size(); + } + + b.writeBlock("\0", 1); + b.writeBlock("\0", 1); + + if(m_app_info.raw_size() > 0) { + b.writeBlock((char *) m_app_info.raw_data(), m_app_info.raw_size()); + } + + if(m_sort_info.raw_size() > 0) { + b.writeBlock((char *) m_sort_info.raw_data(), m_sort_info.raw_size()); + } + + for(record_list_t::iterator q = m_records.begin(); q != m_records.end(); ++q) { + Block* entry = *q; + b.writeBlock((char *) entry->raw_data(), entry->raw_size()); + } + + b.close(); + return b.buffer(); +} + +// Return the record identified by the given index. The caller owns +// the returned RawRecord object. +Record PilotDB::getRecord(unsigned index) const +{ + if (index >= m_records.size()) kdDebug() << "invalid index" << endl; + return *(reinterpret_cast<Record *> (m_records[index])); +} + +// Set the record identified by the given index to the given record. +void PilotDB::setRecord(unsigned index, const Record& rec) +{ +// if (index >= m_records.size()) kdDebug() << "invalid index"); + *(reinterpret_cast<Record *> (m_records[index])) = rec; +} + +// Append the given record to the database. +void PilotDB::appendRecord(const Record& rec) +{ + Record* record = new Record(rec); + + // If this new record has a unique ID that duplicates any other + // record, then reset the unique ID to an unused value. + if (m_uid_map.find(record->unique_id()) != m_uid_map.end()) { + uid_map_t::iterator iter = max_element(m_uid_map.begin(), + m_uid_map.end()); + pi_uint32_t maxuid = (*iter).first; + + // The new unique ID becomes the max plus one. + record->unique_id(maxuid + 1); + } + + m_uid_map[record->unique_id()] = record; + m_records.push_back(record); +} + + +void PilotDB::clearRecords() +{ + m_records.erase(m_records.begin(), m_records.end()); +} + +// Return the resource with the given type and ID. NULL is returned if +// the specified (type, ID) combination is not present in the +// database. The caller owns the returned RawRecord object. +Resource PilotDB::getResourceByType(pi_uint32_t type, pi_uint32_t id) const +{ + for (record_list_t::const_iterator i = m_records.begin(); + i != m_records.end(); ++i) { + Resource* resource = reinterpret_cast<Resource *> (*i); + if (resource->type() == type && resource->id() == id) + return *resource; + } + + kdWarning() << "PilotDB::getResourceByType() - not found!" << endl; + return Resource(); +} + +// Return the resource present at the given index. NULL is returned if +// the index is invalid. The caller owns the returned RawRecord +// object. +Resource PilotDB::getResourceByIndex(unsigned index) const +{ + if (index >= m_records.size()) kdDebug() << "invalid index" << endl; + return *(reinterpret_cast<Resource *> (m_records[index])); +} + +// Set the resouce at given index to passed RawResource object. +void PilotDB::setResource(unsigned index, const Resource& resource) +{ + if (index >= m_records.size()) kdDebug() << "invalid index" << endl; + *(reinterpret_cast<Resource *> (m_records[index])) = resource; +} + +FlatFile::Field PilotDB::string2field(FlatFile::Field::FieldType type, const std::string& fldstr) { + FlatFile::Field field; + + switch (type) { + case FlatFile::Field::STRING: + field.type = FlatFile::Field::STRING; + field.v_string = fldstr; + break; + + case FlatFile::Field::BOOLEAN: + field.type = FlatFile::Field::BOOLEAN; + field.v_boolean = StrOps::string2boolean(fldstr); + break; + + case FlatFile::Field::INTEGER: + field.type = FlatFile::Field::INTEGER; + StrOps::convert_string(fldstr, field.v_integer); + break; + + case FlatFile::Field::FLOAT: + field.type = FlatFile::Field::FLOAT; + StrOps::convert_string(fldstr, field.v_float); + break; + + case FlatFile::Field::NOTE: + field.type = FlatFile::Field::NOTE; + field.v_string = fldstr.substr(0,NOTETITLE_LENGTH - 1); + field.v_note = fldstr; + break; + + case FlatFile::Field::LIST: + field.type = FlatFile::Field::LIST; + field.v_string = fldstr; + break; + + case FlatFile::Field::LINK: + field.type = FlatFile::Field::LINK; + field.v_integer = 0; + field.v_string = fldstr; + break; + + case FlatFile::Field::LINKED: + field.type = FlatFile::Field::LINKED; + field.v_string = fldstr; + break; + + case FlatFile::Field::CALCULATED: + field.type = FlatFile::Field::CALCULATED; + field.v_string = fldstr; + break; + + case FlatFile::Field::DATE: + field.type = FlatFile::Field::DATE; + struct tm time; + if (!fldstr.empty()) { +#ifdef strptime + if(!strptime(fldstr.c_str(), "%Y/%m/%d", &time)) { +#else + if(!StrOps::strptime(fldstr.c_str(), "%Y/%m/%d", &time)) { +#endif + kdDebug() << "invalid date in field" << endl; + } + field.v_date.month = time.tm_mon + 1; + field.v_date.day = time.tm_mday; + field.v_date.year = time.tm_year + 1900; + field.v_time.hour = time.tm_hour; + field.v_time.minute = time.tm_min; + } else { + field.v_date.month = 0; + field.v_date.day = 0; + field.v_date.year = 0; + field.v_time.hour = 24; + field.v_time.minute = 0; + } + break; + + default: + kdWarning() << "PilotDB::string2field() - unsupported field type" << endl; + break; + } + + return field; +} diff --git a/src/translators/pilotdb/pilotdb.h b/src/translators/pilotdb/pilotdb.h new file mode 100644 index 0000000..dd21c7b --- /dev/null +++ b/src/translators/pilotdb/pilotdb.h @@ -0,0 +1,127 @@ +/*************************************************************************** + pilotdb.h + ------------------- + begin : Thu Nov 20 2003 + copyright : (C) 2003 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef PILOTDB_H +#define PILOTDB_H + +#include <map> +#include <vector> + +#include "libpalm/Database.h" +#include "libflatfile/Field.h" + +#include <qcstring.h> + +namespace Tellico { + namespace Export { + +/** + * @author Robby Stephenson + */ +class PilotDB : public PalmLib::Database { +public: + PilotDB(); + ~PilotDB(); + + QByteArray data(); + + /** + * Return the total number of records/resources in this database. + */ + virtual unsigned getNumRecords() const { return m_records.size(); } + + /** + * Return the database's application info block as a Block + * object. The caller owns the returned object. + */ + virtual PalmLib::Block getAppInfoBlock() const { return m_app_info; } + + /** + * Set the database's app info block to the contents of the + * passed Block object. + */ + virtual void setAppInfoBlock(const PalmLib::Block& new_app_info) { m_app_info = new_app_info; } + + /** + * Return the database's sort info block as a Block + * object. The caller owns the returned object. + */ + virtual PalmLib::Block getSortInfoBlock() const { return m_sort_info; } + + /** + * Set the database's sort info block to the contents of the + * passed Block object. + */ + virtual void setSortInfoBlock(const PalmLib::Block& new_sort_info) { m_sort_info = new_sort_info; } + + /** + * Return the record identified by the given index. The caller + * owns the returned RawRecord object. + */ + virtual PalmLib::Record getRecord(unsigned index) const; + + /** + * Set the record identified by the given index to the given record. + */ + virtual void setRecord(unsigned index, const PalmLib::Record& rec); + + /** + * Append the given record to the database. + */ + virtual void appendRecord(const PalmLib::Record& rec); + + /** + * Delete all records + */ + virtual void clearRecords(); + + /** + * returned if the specified (type, ID) combination is not + * present in the database. The caller owns the returned RawRecord object. + */ + virtual PalmLib::Resource getResourceByType(PalmLib::pi_uint32_t type, PalmLib::pi_uint32_t id) const; + + /** + * Return the resource present at the given index. NULL is + * returned if the index is invalid. The caller owns the + * returned RawRecord object. + */ + virtual PalmLib::Resource getResourceByIndex(unsigned index) const; + + /** + * Set the resource at given index to passed Resource object. + */ + virtual void setResource(unsigned index, const PalmLib::Resource& rsrc); + + static PalmLib::FlatFile::Field string2field(PalmLib::FlatFile::Field::FieldType type, + const std::string& fldstr); + +protected: + typedef std::vector<PalmLib::Block *> record_list_t; + typedef std::map<PalmLib::pi_uint32_t, PalmLib::Record *> uid_map_t; + + record_list_t m_records; + uid_map_t m_uid_map; + +private: + PalmLib::Block m_app_info; + PalmLib::Block m_sort_info; + PalmLib::pi_int32_t m_next_record_list_id; +}; + + } //end namespace +} // end namespace +#endif diff --git a/src/translators/pilotdb/portability.h b/src/translators/pilotdb/portability.h new file mode 100644 index 0000000..cb41f79 --- /dev/null +++ b/src/translators/pilotdb/portability.h @@ -0,0 +1,72 @@ +/* + * palm-db-tools: Support Library: String Parsing Utility Functions + * Copyright (C) 1999-2000 by Tom Dyas ([email protected]) + * + * 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 + */ + +#ifndef __LIBSUPPORT_PORTABILITY_H__ +#define __LIBSUPPORT_PORTABILITY_H__ + +/* + * Pull in the correct configuration header. + */ + +#ifndef WIN32 +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#else +#include "win32/win32-config.h" +#endif + +#ifdef _MSC_VER +/* Borrowed from GLib: Make MSVC more pedantic, this is a recommended + * pragma list from _Win32_Programming_ by Rector and Newcomer. + */ +#pragma warning(error:4002) +#pragma warning(error:4003) +#pragma warning(1:4010) +#pragma warning(error:4013) +#pragma warning(1:4016) +#pragma warning(error:4020) +#pragma warning(error:4021) +#pragma warning(error:4027) +#pragma warning(error:4029) +#pragma warning(error:4033) +#pragma warning(error:4035) +#pragma warning(error:4045) +#pragma warning(error:4047) +#pragma warning(error:4049) +#pragma warning(error:4053) +#pragma warning(error:4071) +#pragma warning(disable:4101) +#pragma warning(error:4150) + +#pragma warning(disable:4244) /* No possible loss of data warnings */ +#pragma warning(disable:4305) /* No truncation from int to char warnings */ +#endif /* _MSC_VER */ + +/* MSVC is screwed up when it comes to calling base class virtual + * functions from a subclass. Thus, the following macro which makes + * calling the superclass nice and simple. + */ +#ifndef _MSC_VER +#define SUPERCLASS(namespace, class, function, args) namespace::class::function args +#else +#define SUPERCLASS(namespace, class, function, args) this-> class::function args +#endif + +#endif diff --git a/src/translators/pilotdb/strop.cpp b/src/translators/pilotdb/strop.cpp new file mode 100644 index 0000000..b8c7f55 --- /dev/null +++ b/src/translators/pilotdb/strop.cpp @@ -0,0 +1,589 @@ +/* + * palm-db-tools: Support Library: String Parsing Utility Functions + * Copyright (C) 1999-2000 by Tom Dyas ([email protected]) + * + * 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 <string> +#include <vector> +#include <algorithm> +#include <cctype> +#include <sstream> + +#include "strop.h" +#include <kdebug.h> + +extern std::ostream* err; + +void StrOps::lower(std::string& str) +{ + for (std::string::iterator p = str.begin(); p != str.end(); ++p) { + if (isupper(*p)) + *p = tolower(*p); + } +} + +bool StrOps::string2boolean(const std::string& str) +{ + std::string value(str); + + StrOps::lower(value); + + if (value == "on") return true; + else if (str == "off") return false; + else if (str == "true") return true; + else if (str == "t") return true; + else if (str == "false") return false; + else if (str == "f") return false; + else { + int num = 0; + + std::istringstream(str.c_str()) >> num; + return num != 0 ? true : false; + } +} + +std::string StrOps::type2string(PalmLib::FlatFile::Field::FieldType t) +{ + switch (t) { + case PalmLib::FlatFile::Field::STRING: + return "string"; + + case PalmLib::FlatFile::Field::BOOLEAN: + return "boolean"; + + case PalmLib::FlatFile::Field::INTEGER: + return "integer"; + + case PalmLib::FlatFile::Field::FLOAT: + return "float"; + + case PalmLib::FlatFile::Field::DATE: + return "date"; + + case PalmLib::FlatFile::Field::TIME: + return "time"; + + case PalmLib::FlatFile::Field::DATETIME: + return "datetime"; + + case PalmLib::FlatFile::Field::NOTE: + return "note"; + + case PalmLib::FlatFile::Field::LIST: + return "list"; + + case PalmLib::FlatFile::Field::LINK: + return "link"; + + case PalmLib::FlatFile::Field::CALCULATED: + return "calculated"; + + case PalmLib::FlatFile::Field::LINKED: + return "linked"; + + default: + // If we don't support the field type, then fake it as a string. + return "string"; + } +} + +PalmLib::FlatFile::Field::FieldType StrOps::string2type(std::string typestr) +{ + StrOps::lower(typestr); + if (typestr == "string") + return PalmLib::FlatFile::Field::STRING; + else if (typestr == "str") + return PalmLib::FlatFile::Field::STRING; + else if (typestr == "note") + return PalmLib::FlatFile::Field::NOTE; + else if (typestr == "bool") + return PalmLib::FlatFile::Field::BOOLEAN; + else if (typestr == "boolean") + return PalmLib::FlatFile::Field::BOOLEAN; + else if (typestr == "integer") + return PalmLib::FlatFile::Field::INTEGER; + else if (typestr == "int") + return PalmLib::FlatFile::Field::INTEGER; + else if (typestr == "float") + return PalmLib::FlatFile::Field::FLOAT; + else if (typestr == "date") + return PalmLib::FlatFile::Field::DATE; + else if (typestr == "time") + return PalmLib::FlatFile::Field::TIME; + else if (typestr == "datetime") + return PalmLib::FlatFile::Field::DATETIME; + else if (typestr == "list") + return PalmLib::FlatFile::Field::LIST; + else if (typestr == "link") + return PalmLib::FlatFile::Field::LINK; + else if (typestr == "linked") + return PalmLib::FlatFile::Field::LINKED; + else if (typestr == "calculated") + return PalmLib::FlatFile::Field::CALCULATED; + else + kdDebug() << "unknown field type" << endl; + return PalmLib::FlatFile::Field::STRING; +} + +std::string StrOps::strip_back(const std::string& str, const std::string& what) +{ + std::string result(str); + std::string::reverse_iterator p = result.rbegin(); + + while (p != result.rend() + && (std::find(what.begin(), what.end(), *p) != what.end())) ++p; + + result.erase(p.base(), result.end()); + + return result; +} + +std::string StrOps::strip_front(const std::string& str,const std::string& what) +{ + std::string result(str); + std::string::iterator p = result.begin(); + + while (p != result.end() + && (std::find(what.begin(), what.end(), *p) != what.end())) ++p; + + result.erase(result.begin(), p); + + return result; +} + +StrOps::string_list_t StrOps::csv_to_array(const std::string& str, char delim, bool quoted_string) +{ + enum { STATE_NORMAL, STATE_QUOTES } state; + StrOps::string_list_t result; + std::string elem; + + state = STATE_NORMAL; + for (std::string::const_iterator p = str.begin(); p != str.end(); ++p) { + switch (state) { + case STATE_NORMAL: + if (quoted_string && *p == '"') { + state = STATE_QUOTES; + } else if (*p == delim) { + result.push_back(elem); + elem = ""; + } else { + elem += *p; + } + break; + + case STATE_QUOTES: + if (quoted_string && *p == '"') { + if ((p + 1) != str.end() && *(p+1) == '"') { + ++p; + elem += '"'; + } else { + state = STATE_NORMAL; + } + } else { + elem += *p; + } + break; + } + } + + switch (state) { + case STATE_NORMAL: + result.push_back(elem); + break; + case STATE_QUOTES: + kdDebug() << "unterminated quotes" << endl; + break; + } + + return result; +} + +StrOps::string_list_t +StrOps::str_to_array(const std::string& str, const std::string& delim, + bool multiple_delim, bool handle_comments) +{ + enum { STATE_NORMAL, STATE_COMMENT, STATE_QUOTE_DOUBLE, STATE_QUOTE_SINGLE, + STATE_BACKSLASH, STATE_BACKSLASH_DOUBLEQUOTE } state; + StrOps::string_list_t result; + std::string elem; + + state = STATE_NORMAL; + for (std::string::const_iterator p = str.begin(); p != str.end(); ++p) { + switch (state) { + case STATE_NORMAL: + if (*p == '"') { + state = STATE_QUOTE_DOUBLE; + } else if (*p == '\'') { + state = STATE_QUOTE_SINGLE; + } else if (std::find(delim.begin(), delim.end(), *p) != delim.end()) { + if (multiple_delim) { + ++p; + while (p != str.end() + && std::find(delim.begin(), delim.end(), *p) != delim.end()) { + ++p; + } + --p; + } + result.push_back(elem); + elem = ""; + } else if (*p == '\\') { + state = STATE_BACKSLASH; + } else if (handle_comments && *p == '#') { + state = STATE_COMMENT; + } else { + elem += *p; + } + break; + + case STATE_COMMENT: + break; + + case STATE_QUOTE_DOUBLE: + if (*p == '"') + state = STATE_NORMAL; + else if (*p == '\\') + state = STATE_BACKSLASH_DOUBLEQUOTE; + else + elem += *p; + break; + + case STATE_QUOTE_SINGLE: + if (*p == '\'') + state = STATE_NORMAL; + else + elem += *p; + break; + + case STATE_BACKSLASH: + elem += *p; + state = STATE_NORMAL; + break; + + case STATE_BACKSLASH_DOUBLEQUOTE: + switch (*p) { + case '\\': + elem += '\\'; + break; + + case 'n': + elem += '\n'; + break; + + case 'r': + elem += '\r'; + break; + + case 't': + elem += '\t'; + break; + + case 'v': + elem += '\v'; + break; + + case '"': + elem += '"'; + break; + + case 'x': + { + char buf[3]; + + // Extract and check the first hexadecimal character. + if ((p + 1) == str.end()) + kdDebug() << "truncated escape" << endl; + if (! isxdigit(*(p + 1))) + kdDebug() << "invalid hex character" << endl; + buf[0] = *++p; + + // Extract and check the second (if any) hex character. + if ((p + 1) != str.end() && isxdigit(*(p + 1))) { + buf[1] = *++p; + buf[2] = '\0'; + } else { + buf[1] = buf[2] = '\0'; + } + + std::istringstream stream(buf); + stream.setf(std::ios::hex, std::ios::basefield); + unsigned value; + stream >> value; + + elem += static_cast<char> (value & 0xFFu); + } + break; + } + + // Escape is done. Go back to the normal double quote state. + state = STATE_QUOTE_DOUBLE; + break; + } + } + + switch (state) { + case STATE_NORMAL: + result.push_back(elem); + break; + + case STATE_QUOTE_DOUBLE: + kdDebug() << "unterminated double quotes" << endl; + break; + + case STATE_QUOTE_SINGLE: + kdDebug() << "unterminated single quotes" << endl; + break; + + case STATE_BACKSLASH: + case STATE_BACKSLASH_DOUBLEQUOTE: + kdDebug() << "an escape character must follow a backslash" << endl; + break; + + default: + break; + } + + return result; +} + +PalmLib::pi_uint32_t +StrOps::get_current_time(void) +{ + time_t now; + + time(&now); + return static_cast<PalmLib::pi_uint32_t> (now) + PalmLib::pi_uint32_t(2082844800); +} + +char * +StrOps::strptime(const char *s, const char *format, struct tm *tm) +{ + char *data = (char *)s; + char option = false; + + while (*format != 0) { + if (*data == 0) + return NULL; + switch (*format) { + case '%': + option = true; + format++; + break; + case 'd': + if (option) { + tm->tm_mday = strtol(data, &data, 10); + if (tm->tm_mday < 1 || tm->tm_mday > 31) + return NULL; + } else if (*data != 'd') { + return data; + } + option = false; + format++; + break; + case 'm': + if (option) { + /* tm_mon between 0 and 11 */ + tm->tm_mon = strtol(data, &data, 10) - 1; + if (tm->tm_mon < 0 || tm->tm_mon > 11) + return NULL; + } else if (*data != 'm') { + return data; + } + option = false; + format++; + break; + case 'y': + if (option) { + tm->tm_year = strtol(data, &data, 10); + if (tm->tm_year < 60) tm->tm_year += 100; + } else if (*data != 'y') { + return data; + } + option = false; + format++; + break; + case 'Y': + if (option) { + tm->tm_year = strtol(data, &data, 10) - 1900; + } else if (*data != 'Y') { + return data; + } + option = false; + format++; + break; + case 'H': + if (option) { + tm->tm_hour = strtol(data, &data, 10); + if (tm->tm_hour < 0 || tm->tm_hour > 23) + return NULL; + } else if (*data != 'H') { + return data; + } + option = false; + format++; + break; + case 'M': + if (option) { + tm->tm_min = strtol(data, &data, 10); + if (tm->tm_min < 0 || tm->tm_min > 59) + return NULL; + } else if (*data != 'M') { + return data; + } + option = false; + format++; + break; + default: + if (option) + return data; + if (*data != *format) + return data; + format++; + data++; + } + } + return data; +} + +// Read a line from an istream w/o concern for buffer limitations. +std::string +StrOps::readline(std::istream& stream) +{ + std::string line; + char buf[1024]; + + while (1) { + // Read the next line (or part thereof) from the stream. + stream.getline(buf, sizeof(buf)); + // Bail out of the loop if the stream has reached end-of-file. + if ((stream.eof() && !buf[0]) || stream.bad()) + break; + + // Append the data read to the result string. + line.append(buf); + + // If the stream is good, then stop. Otherwise, clear the + // error indicator so that getline will work again. + if ((stream.eof() && buf[0]) || stream.good()) + break; + stream.clear(); + } + + return line; +} + +std::string +StrOps::quote_string(std::string str, bool extended_mode) +{ + std::string result; + std::ostringstream error; + + if (extended_mode) { + result += '"'; + for (std::string::iterator c = str.begin(); c != str.end(); ++c) { + switch (*c) { + case '\\': + result += '\\'; + result += '\\'; + break; + + case '\r': + result += '\\'; + result += 'r'; + break; + + case '\n': + result += '\\'; + result += 'n'; + break; + + case '\t': + result += '\\'; + result += 't'; + break; + + case '\v': + result += '\\'; + result += 'v'; + break; + + case '"': + result += '\\'; + result += '"'; + break; + + default: + if (isprint(*c)) { + result += *c; + } else { + std::ostringstream buf; + buf.width(2); + buf.setf(std::ios::hex, std::ios::basefield); + buf.setf(std::ios::left); + buf << ((static_cast<unsigned> (*c)) & 0xFF) << std::ends; + + result += "\\x"; + result += buf.str(); + } + break; + } + } + result += '"'; + } else { + result += '"'; + for (std::string::iterator c = str.begin(); c != str.end(); ++c) { + if (*c == '"') { + result += "\"\""; + } else if (*c == '\n' || *c == '\r') { + error << "use extended csv mode for newlines\n"; + *err << error.str(); + kdDebug() << error.str().c_str() << endl; + } else { + result += *c; + } + } + result += '"'; + } + + return result; +} + +std::string +StrOps::concatenatepath(std::string p_Path, + std::string p_FileName, std::string p_Ext) +{ + std::string l_FilePath; +#ifdef WIN32 + if (p_FileName[1] == ':' || p_FileName[0] == '\\') + return p_FileName; + else if (p_Path.empty()) + l_FilePath = p_FileName; + else + l_FilePath = p_Path + std::string("\\") + p_FileName; +#else + if (p_FileName[0] == '/') + return p_FileName; + else if (p_Path.empty()) + l_FilePath = p_FileName; + else + l_FilePath = p_Path + std::string("/") + p_FileName; + +#endif + if (!p_Ext.empty() && (p_FileName.rfind(p_Ext) == std::string::npos)) + l_FilePath += p_Ext; + + return l_FilePath; +} diff --git a/src/translators/pilotdb/strop.h b/src/translators/pilotdb/strop.h new file mode 100644 index 0000000..3d9dc8d --- /dev/null +++ b/src/translators/pilotdb/strop.h @@ -0,0 +1,153 @@ +/* + * palm-db-tools: Support Library: String Parsing Utility Functions + * Copyright (C) 1999-2000 by Tom Dyas ([email protected]) + * + * 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 + */ + +#ifndef __LIBSUPPORT_STROP_H__ +#define __LIBSUPPORT_STROP_H__ + +#include <stdexcept> +#include <vector> +#include <string> +#include <sstream> +#include <time.h> +#include "libflatfile/Database.h" + +namespace StrOps { + + // This exception is thrown whenever an error is encountered in + // csv_to_array and str_to_array. + class csv_parse_error : public std::runtime_error { + public: + csv_parse_error(const std::string& msg) : std::runtime_error(msg) { } + }; + + class csv_unterminated_quote : public std::runtime_error { + public: + csv_unterminated_quote(const std::string& msg) : std::runtime_error(msg) { } + }; + + // The results of any parse are returned as this type. + typedef std::vector<std::string> string_list_t; + + + /** + * Convert all uppercase characters in a string to lowercase. + */ + void lower(std::string& str); + + /** + * Convert a string to a boolean value. + * + * @param str The string containing a boolean value to convert. + */ + bool string2boolean(const std::string& str); + + /** + * Convert a string to a FieldType value. + * + * @param typestr The string containing a FieldType value to convert. + */ + PalmLib::FlatFile::Field::FieldType string2type(std::string typestr); + + /** + * Convert a FieldType value to a string. + * + * @param t The FieldType value containing a string to convert. + */ + std::string type2string(PalmLib::FlatFile::Field::FieldType t); + + /** + * Strip trailing characters from a string. + * + * @param str The string to strip characters from. + * @param what The string containing characters to strip. + */ + std::string strip_back(const std::string& str, const std::string& what); + + /** + * Strip leading characters from a string. + * + * @param str The string to strip characters from. + * @param what The string containing characters to strip. + */ + std::string strip_front(const std::string& str, const std::string& what); + + /** + * Convert a string to a target type using a istringstream. + */ + template<class T> + inline void convert_string(const std::string& str, T& result) { + std::istringstream(str.c_str()) >> result; + } + + /** + * Parse a string in CSV (comman-seperated values) format and + * return it as a list. + * + * @param str The string containing the CSV fields. + * @param delim The field delimiter. Defaults to a comma. + */ + string_list_t csv_to_array(const std::string& str, char delim = ',', bool quoted_string = true); + + + /** + * Parse an argv-style array and return it as a list. + * + * @param str The string to parse. + * @param delim String containing the delimiter characters. + * @param multiple_delim Should multiple delimiters count as one? + * @param handle_comments Handle # as a comment character. + */ + string_list_t str_to_array(const std::string& str, + const std::string& delim, + bool multiple_delim, + bool handle_comments); + + /** + * return the current date in the palm format. + */ + PalmLib::pi_uint32_t get_current_time(void); + + /** + * fill a char array with a tm structure in the format passed. + * @param s the char array filled. + * @param format the string of the format to use to print the date. + * @param tm a pointer to time structure. + */ + char *strptime(const char *s, const char *format, struct tm *tm); + + /** + * read one line from the input stream of a file + */ + std::string readline(std::istream& stream); + + /** + * add the quotes to a string + */ + std::string quote_string(std::string str, bool extended_mode); + + /** + * concatenate the path directory, the file name and the extension + * to give the file path to a file + */ + std::string concatenatepath(std::string p_Path, std::string p_FileName, + std::string p_Ext = std::string("")); + +} // namespace StrOps + +#endif |