/* * palm-db-tools: Read/write DB-format databases * Copyright (C) 1999-2001 by Tom Dyas (tdyas@users.sourceforge.net) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifh Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "../strop.h" #include "DB.h" #include #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 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 (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& chunks = m_chunks[CHUNK_LISTVIEW_DEFINITION]; for (std::vector::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 (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 = const_cast(reinterpret_cast (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& chunks = m_chunks[CHUNK_FIELD_DATA]; pi_uint16_t field_num = 0; bool find = false; std::vector::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 (*(ptr + 2))) - 1; date.tm_mday = static_cast (*(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 (*(ptr)); t.tm_min = static_cast (*(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& ptrs, std::vector& 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 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 ptrs; std::vector 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 (*(ptrs[j] + 2)); f.v_date.day = static_cast (*(ptrs[j] + 3)); break; case PalmLib::FlatFile::Field::TIME: f.type = PalmLib::FlatFile::Field::TIME; f.v_time.hour = static_cast (*(ptrs[j])); f.v_time.minute = static_cast (*(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 (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 (fieldData.v_date.month & 0xFF); *p++ = static_cast (fieldData.v_date.day & 0xFF); break; case PalmLib::FlatFile::Field::TIME: *p++ = static_cast (fieldData.v_time.hour & 0xFF); *p++ = static_cast (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& 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 (type & 0xFF); PalmLib::set_short(p, date.tm_year + 1900); p += sizeof(pi_uint16_t); *p++ = static_cast ((date.tm_mon + 1) & 0xFF); *p++ = static_cast (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 (type & 0xFF); *p++ = static_cast (t.tm_hour & 0xFF); *p++ = static_cast (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 items; std::string::size_type pos = 0, next; std::vector::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& 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& 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& 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& chunks, PalmLib::Block& appinfo) const { std::vector::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 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 PalmLib::FlatFile::DB::field_argumentf(int i, std::string& format) { std::vector 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; }