Here are three patches to add Tasks and Memos database records. Obviously there is a lot of code duplicated from Calendar in Tasks to handle the recurrent data fields. It probably isn't used in any other databases so we could live with it, but I would propose making an class to handle the parsing and printing (for Dump()) of this data.
I would also propose refactoring the records stuff a little to reduce the repetition of a bunch of member functions in each class. I'd like to do this before adding any more database record classes, maybe even break each database type out into their own files? -edge
### Eclipse Workspace Patch 1.0
#P barry
Index: src/record.cc
===================================================================
RCS file: /cvsroot/barry/barry/src/record.cc,v
retrieving revision 1.21
diff -u -r1.21 record.cc
--- src/record.cc 11 May 2007 19:58:20 -0000 1.21
+++ src/record.cc 18 May 2007 17:57:31 -0000
@@ -1451,6 +1451,483 @@
os << Unknowns;
}
+///////////////////////////////////////////////////////////////////////////////
+// Memo Class
+
+// Memo Field Codes
+#define MEMFC_TITLE 0x01
+#define MEMFC_BODY 0x02
+#define MEMFC_CATEGORY 0x04
+#define MEMFC_END 0xffff
+
+FieldLink<Memos> MemosFieldLinks[] = {
+ { MEMFC_TITLE, "Title", 0, 0, &Memos::Title, 0, 0 },
+ { MEMFC_BODY, "Body", 0, 0, &Memos::Body, 0, 0 },
+ { MEMFC_CATEGORY, "Category", 0, 0, &Memos::Category, 0, 0 },
+ { MEMFC_END, "End of List", 0, 0, 0, 0, 0 }
+};
+
+Memos::Memos()
+{
+ Clear();
+}
+
+Memos::~Memos()
+{
+}
+
+const unsigned char* Memos::ParseField(const unsigned char *begin,
+ const unsigned char *end)
+{
+ const CommonField *field = (const CommonField *) begin;
+
+ // advance and check size
+ begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
+ if( begin > end ) // if begin==end, we are ok
+ return begin;
+
+ if( !btohs(field->size) ) // if field has no size, something's up
+ return begin;
+
+ // cycle through the type table
+ for( FieldLink<Memos> *b = MemosFieldLinks;
+ b->type != MEMFC_END;
+ b++ )
+ {
+ if( b->type == field->type ) {
+ if( b->strMember ) {
+ std::string &s = this->*(b->strMember);
+ s.assign((const char *)field->u.raw, btohs(field->size)-1);
+ return begin; // done!
+ }
+ else if( b->timeMember && btohs(field->size) == 4 ) {
+ time_t &t = this->*(b->timeMember);
+ t = min2time(field->u.min1900);
+ return begin;
+ }
+ }
+ }
+ // if still not handled, add to the Unknowns list
+ UnknownField uf;
+ uf.type = field->type;
+ uf.data.assign((const char*)field->u.raw, btohs(field->size));
+ Unknowns.push_back(uf);
+
+ // return new pointer for next field
+ return begin;
+}
+
+void Memos::ParseHeader(const Data &data, size_t &offset)
+{
+ // no header in Memos records
+}
+
+void Memos::ParseFields(const Data &data, size_t &offset)
+{
+ const unsigned char *finish = ParseCommonFields(*this,
+ data.GetData() + offset, data.GetData() + data.GetSize());
+ offset += finish - (data.GetData() + offset);
+}
+
+
+void Memos::Dump(std::ostream &os) const
+{
+ os << "Memos entry: 0x" << setbase(16) << RecordId
+ << " (" << (unsigned int)RecType << ")\n";
+ os << " Title: " << Title << "\n";
+ os << " Body: " << Body << "\n";
+ os << " Category: " << Category << "\n";
+
+ os << Unknowns;
+ os << "\n\n";
+}
+
+void Memos::Clear()
+{
+ Title.clear();
+ Body.clear();
+ Category.clear();
+
+ Unknowns.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Tasks Class
+
+// Tasks Field Codes
+#define TSKFC_TITLE 0x02
+#define TSKFC_NOTES 0x03
+#define TSKFC_START_TIME 0x05
+#define TSKFC_DUE_TIME 0x06
+#define TSKFC_STATUS 0x09
+#define TSKFC_PRIORITY 0x0a
+#define TSKFC_RECURRENCE_DATA 0x0c
+#define TSKFC_ALARM_TIME 0x0f
+#define TSKFC_TIMEZONE_CODE 0x10
+#define TSKFC_CATEGORIES 0x11
+#define TSKFC_END 0xffff
+
+FieldLink<Tasks> TasksFieldLinks[] = {
+ { TSKFC_TITLE, "Summary", 0, 0, &Tasks::Summary, 0, 0 },
+ { TSKFC_NOTES, "Notes", 0, 0, &Tasks::Notes, 0, 0 },
+ { TSKFC_START_TIME, "Start Time", 0, 0, 0, 0, &Tasks::StartTime },
+ { TSKFC_DUE_TIME, "Due Time", 0, 0, 0, 0, &Tasks::DueTime },
+ { TSKFC_ALARM_TIME, "Alarm Time", 0, 0, 0, 0, &Tasks::AlarmTime },
+ { TSKFC_CATEGORIES, "Categories", 0, 0, &Tasks::Categories, 0, 0 },
+ { TSKFC_END, "End of List", 0, 0, 0, 0, 0 },
+};
+
+Tasks::Tasks()
+{
+ Clear();
+}
+
+Tasks::~Tasks()
+{
+}
+
+const unsigned char* Tasks::ParseField(const unsigned char *begin,
+ const unsigned char *end)
+{
+ const CommonField *field = (const CommonField *) begin;
+
+ // advance and check size
+ begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
+ if( begin > end ) // if begin==end, we are ok
+ return begin;
+
+ if( !btohs(field->size) ) // if field has no size, something's up
+ return begin;
+
+ // cycle through the type table
+ for( FieldLink<Tasks> *b = TasksFieldLinks;
+ b->type != TSKFC_END;
+ b++ )
+ {
+ if( b->type == field->type ) {
+ if( b->strMember ) {
+ std::string &s = this->*(b->strMember);
+ s.assign((const char *)field->u.raw, btohs(field->size)-1);
+ return begin; // done!
+ }
+ else if( b->timeMember && btohs(field->size) == 4 ) {
+ time_t &t = this->*(b->timeMember);
+ t = min2time(field->u.min1900);
+ return begin;
+ }
+ }
+ }
+ // handle special cases
+ switch( field->type )
+ {
+ case TSKFC_PRIORITY:
+ if( field->u.raw[0] > Low ) {
+ throw Error( "Task::ParseField: priority field out of bounds" );
+ }
+ else {
+ PriorityType = (PriorityTypeFlag)field->u.raw[0];
+ }
+ return begin;
+
+ case TSKFC_STATUS:
+ if( field->u.raw[0] > Deferred ) {
+ throw Error( "Task::ParseField: priority field out of bounds" );
+ }
+ else {
+ StatusType = (StatusTypeFlag)field->u.raw[0];
+ }
+ return begin;
+ case TSKFC_TIMEZONE_CODE:
+ if( btohs(field->size) == 4 ) {
+ TimeZoneCode = btohs(field->u.code);
+ }
+ else {
+ throw Error("Task::ParseField: not enough data in time zone code field");
+ }
+ return begin;
+
+ case TSKFC_RECURRENCE_DATA:
+ if( btohs(field->size) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE ) {
+ Recurring = true;
+ ParseRecurrenceData(&field->u.raw[0]);
+ }
+ else {
+ throw Error("Task::ParseField: not enough data in recurrence data field");
+ }
+ return begin;
+ }
+ // if still not handled, add to the Unknowns list
+ UnknownField uf;
+ uf.type = field->type;
+ uf.data.assign((const char*)field->u.raw, btohs(field->size));
+ Unknowns.push_back(uf);
+
+ // return new pointer for next field
+ return begin;
+}
+
+// this function assumes the size has already been checked
+void Tasks::ParseRecurrenceData(const void *data)
+{
+ const CalendarRecurrenceDataField *rec =
+ (const CalendarRecurrenceDataField*) data;
+
+ Interval = btohs(rec->interval);
+ if( Interval < 1 )
+ Interval = 1; // must always be >= 1
+
+ if( rec->endTime == 0xffffffff ) {
+ Perpetual = true;
+ }
+ else {
+ RecurringEndTime = min2time(rec->endTime);
+ Perpetual = false;
+ }
+
+ switch( rec->type )
+ {
+ case CRDF_TYPE_DAY:
+ RecurringType = Day;
+ // no extra data
+ break;
+
+ case CRDF_TYPE_MONTH_BY_DATE:
+ RecurringType = MonthByDate;
+ DayOfMonth = rec->u.month_by_date.monthDay;
+ break;
+
+ case CRDF_TYPE_MONTH_BY_DAY:
+ RecurringType = MonthByDay;
+ DayOfWeek = rec->u.month_by_day.weekDay;
+ WeekOfMonth = rec->u.month_by_day.week;
+ break;
+
+ case CRDF_TYPE_YEAR_BY_DATE:
+ RecurringType = YearByDate;
+ DayOfMonth = rec->u.year_by_date.monthDay;
+ MonthOfYear = rec->u.year_by_date.month;
+ break;
+
+ case CRDF_TYPE_YEAR_BY_DAY:
+ RecurringType = YearByDay;
+ DayOfWeek = rec->u.year_by_day.weekDay;
+ WeekOfMonth = rec->u.year_by_day.week;
+ MonthOfYear = rec->u.year_by_day.month;
+ break;
+
+ case CRDF_TYPE_WEEK:
+ RecurringType = Week;
+
+ // Note: this simple copy is only possible since
+ // the CAL_WD_* constants are the same as CRDF_WD_* constants.
+ // If this ever changes, this code will need to change.
+ WeekDays = rec->u.week.days;
+ break;
+
+ default:
+ eout("Unknown recurrence data type: " << rec->type);
+ throw Error("Unknown recurrence data type");
+ }
+}
+
+// this function assumes there is CALENDAR_RECURRENCE_DATA_FIELD_SIZE bytes
+// available in data
+void Tasks::BuildRecurrenceData(void *data)
+{
+ if( !Recurring )
+ throw Error("Task::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record.");
+
+ CalendarRecurrenceDataField *rec = (CalendarRecurrenceDataField*) data;
+
+ // set all to zero
+ memset(data, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
+
+ rec->interval = htobs(Interval);
+ rec->startTime = time2min(StartTime);
+ if( Perpetual )
+ rec->endTime = 0xffffffff;
+ else
+ rec->endTime = time2min(RecurringEndTime);
+
+ switch( RecurringType )
+ {
+ case Day:
+ rec->type = CRDF_TYPE_DAY;
+ // no extra data
+ break;
+
+ case MonthByDate:
+ rec->type = CRDF_TYPE_MONTH_BY_DATE;
+ rec->u.month_by_date.monthDay = DayOfMonth;
+ break;
+
+ case MonthByDay:
+ rec->type = CRDF_TYPE_MONTH_BY_DAY;
+ rec->u.month_by_day.weekDay = DayOfWeek;
+ rec->u.month_by_day.week = WeekOfMonth;
+ break;
+
+ case YearByDate:
+ rec->type = CRDF_TYPE_YEAR_BY_DATE;
+ rec->u.year_by_date.monthDay = DayOfMonth;
+ rec->u.year_by_date.month = MonthOfYear;
+ break;
+
+ case YearByDay:
+ rec->type = CRDF_TYPE_YEAR_BY_DAY;
+ rec->u.year_by_day.weekDay = DayOfWeek;
+ rec->u.year_by_day.week = WeekOfMonth;
+ rec->u.year_by_day.month = MonthOfYear;
+ break;
+
+ case Week:
+ rec->type = CRDF_TYPE_WEEK;
+
+ // Note: this simple copy is only possible since
+ // the CAL_WD_* constants are the same as CRDF_WD_* constants.
+ // If this ever changes, this code will need to change.
+ rec->u.week.days = WeekDays;
+ break;
+
+ default:
+ eout("Task::BuildRecurrenceData: "
+ "Unknown recurrence data type: " << rec->type);
+ throw Error("Task::BuildRecurrenceData: Unknown recurrence data type");
+ }
+}
+
+void Tasks::ParseHeader(const Data &data, size_t &offset)
+{
+ // no header in Tasks records
+}
+
+void Tasks::ParseFields(const Data &data, size_t &offset)
+{
+ const unsigned char *finish = ParseCommonFields(*this,
+ data.GetData() + offset, data.GetData() + data.GetSize());
+ offset += finish - (data.GetData() + offset);
+}
+void Tasks::Clear()
+{
+ Summary.clear();
+ Notes.clear();
+ Categories.clear();
+ StartTime = DueTime = AlarmTime = 0;
+
+ PriorityType = (PriorityTypeFlag)0;
+ StatusType = (StatusTypeFlag)0;
+
+ Recurring = false;
+
+ TimeZoneCode = GetTimeZoneCode( 0, 0 );
+
+ Unknowns.clear();
+}
+
+void Tasks::Dump(std::ostream &os) const
+{
+ static const char *PriorityName[] = {
+ "High", "Normal", "Low" };
+ static const char *StatusName[] = {
+ "Not Started", "In Progress", "Completed", "Waiting", "Deferred" };
+ static const char *DayNames[] = { "Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat" };
+ static const char *MonthNames[] = { "Jan", "Feb", "Mar", "Apr",
+ "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+ os << "Tasks entry: 0x" << setbase(16) << RecordId
+ << " (" << (unsigned int)RecType << ")\n";
+ // cycle through the type table
+ for( const FieldLink<Tasks> *b = TasksFieldLinks;
+ b->type != TSKFC_END;
+ b++ )
+ {
+ if( b->strMember ) {
+ const std::string &s = this->*(b->strMember);
+ if( s.size() )
+ os << " " << b->name << ": " << s << "\n";
+ }
+ else if( b->timeMember ) {
+ time_t t = this->*(b->timeMember);
+ if( t > 0 )
+ os << " " << b->name << ": " << ctime(&t);
+ }
+ }
+
+ os << " Priority: " << PriorityName[PriorityType] << "\n";
+ os << " Status: " << StatusName[StatusType] << "\n";
+ // print recurrence data if available
+ os << " Recurring: " << (Recurring ? "yes" : "no") << "\n";
+ if( Recurring ) {
+ switch( RecurringType )
+ {
+ case Day:
+ os << " Every day.\n";
+ break;
+
+ case MonthByDate:
+ os << " Every month on the "
+ << DayOfMonth
+ << (DayOfMonth == 1 ? "st" : "")
+ << (DayOfMonth == 2 ? "nd" : "")
+ << (DayOfMonth == 3 ? "rd" : "")
+ << (DayOfMonth > 3 ? "th" : "")
+ << "\n";
+ break;
+
+ case MonthByDay:
+ os << " Every month on the "
+ << DayNames[DayOfWeek]
+ << " of week "
+ << WeekOfMonth
+ << "\n";
+ break;
+
+ case YearByDate:
+ os << " Every year on "
+ << MonthNames[MonthOfYear-1]
+ << " " << DayOfMonth << "\n";
+ break;
+
+ case YearByDay:
+ os << " Every year in " << MonthNames[MonthOfYear-1]
+ << " on "
+ << DayNames[DayOfWeek]
+ << " of week " << WeekOfMonth << "\n";
+ break;
+
+ case Week:
+ os << " Every week on: ";
+ if( WeekDays & CAL_WD_SUN ) os << "Sun ";
+ if( WeekDays & CAL_WD_MON ) os << "Mon ";
+ if( WeekDays & CAL_WD_TUE ) os << "Tue ";
+ if( WeekDays & CAL_WD_WED ) os << "Wed ";
+ if( WeekDays & CAL_WD_THU ) os << "Thu ";
+ if( WeekDays & CAL_WD_FRI ) os << "Fri ";
+ if( WeekDays & CAL_WD_SAT ) os << "Sat ";
+ os << "\n";
+ break;
+
+ default:
+ os << " Unknown recurrence type\n";
+ break;
+ }
+
+ os << " Interval: " << Interval << "\n";
+
+ if( Perpetual )
+ os << " Ends: never\n";
+ else
+ os << " Ends: "
+ << ctime(&RecurringEndTime);
+ }
+ os << Unknowns;
+
+ os << "\n\n";
+
+}
+
+
///////////////////////////////////////////////////////////////////////////////
// ServiceBookConfig class
Index: src/record.h
===================================================================
RCS file: /cvsroot/barry/barry/src/record.h,v
retrieving revision 1.19
diff -u -r1.19 record.h
--- src/record.h 11 May 2007 19:58:20 -0000 1.19
+++ src/record.h 18 May 2007 17:57:57 -0000
@@ -473,6 +473,142 @@
return os;
}
+class Memos
+{
+public:
+ typedef std::vector<UnknownField> UnknownsType;
+
+ uint8_t RecType;
+ uint32_t RecordId;
+
+ std::string Title;
+ std::string Body;
+ std::string Category;
+
+ UnknownsType Unknowns;
+
+public:
+ const unsigned char* ParseField(const unsigned char *begin,
+ const unsigned char *end);
+public:
+ Memos();
+ ~Memos();
+
+ // Parser / Builder API (see parser.h / builder.h)
+ uint8_t GetRecType() const { return RecType; }
+ uint32_t GetUniqueId() const { return RecordId; }
+ void SetIds(uint8_t Type, uint32_t Id) { RecType = Type; RecordId = Id; }
+ void ParseHeader(const Data &data, size_t &offset);
+ void ParseFields(const Data &data, size_t &offset);
+ void BuildHeader(Data &data, size_t &offset) const;
+
+ void Clear();
+
+ void Dump(std::ostream &os) const;
+
+ // database name
+ static const char * GetDBName() { return "Memos"; }
+ static uint8_t GetDefaultRecType() { return 0; } // or 0?
+};
+inline std::ostream& operator<<(std::ostream &os, const Memos &msg) {
+ msg.Dump(os);
+ return os;
+}
+
+class Tasks
+{
+public:
+ typedef std::vector<UnknownField> UnknownsType;
+ uint8_t RecType;
+ uint32_t RecordId;
+
+ std::string Summary;
+ std::string Notes;
+ std::string Categories;
+ std::string UID;
+
+ time_t StartTime;
+ time_t DueTime;
+ time_t AlarmTime;
+ int TimeZoneCode;
+ unsigned short Interval;
+ enum RecurringCodeType {
+ Day = 1, //< eg. every day
+ //< set: nothing
+ MonthByDate = 3, //< eg. every month on the 12th
+ //< set: DayOfMonth
+ MonthByDay = 4, //< eg. every month on 3rd Wed
+ //< set: DayOfWeek and WeekOfMonth
+ YearByDate = 5, //< eg. every year on March 5
+ //< set: DayOfMonth and MonthOfYear
+ YearByDay = 6, //< eg. every year on 3rd Wed of Jan
+ //< set: DayOfWeek, WeekOfMonth, and
+ //< MonthOfYear
+ Week = 12 //< eg. every week on Mon and Fri
+ //< set: WeekDays
+ };
+ RecurringCodeType RecurringType;
+ time_t RecurringEndTime;
+ unsigned short // recurring details, depending on type
+ DayOfWeek, // 0-6
+ WeekOfMonth, // 1-5
+ DayOfMonth, // 1-31
+ MonthOfYear; // 1-12
+ unsigned char WeekDays; // bitmask, bit 0 = sunday
+
+ int ClassType;
+ enum PriorityTypeFlag
+ {
+ High = 0,
+ Normal,
+ Low
+ };
+ PriorityTypeFlag PriorityType;
+
+ enum StatusTypeFlag
+ {
+ NotStarted = 0,
+ InProgress,
+ Completed,
+ Waiting,
+ Deferred
+ };
+ StatusTypeFlag StatusType;
+
+ bool Recurring;
+ bool Perpetual;
+
+
+ UnknownsType Unknowns;
+
+public:
+ Tasks();
+ ~Tasks();
+
+ const unsigned char* ParseField(const unsigned char *begin,
+ const unsigned char *end);
+ void ParseRecurrenceData(const void *data);
+ void BuildRecurrenceData(void *data);
+ uint8_t GetRecType() const { return RecType; }
+ uint32_t GetUniqueId() const { return RecordId; }
+ void SetIds(uint8_t Type, uint32_t Id) { RecType = Type; RecordId = Id; }
+ void ParseHeader(const Data &data, size_t &offset);
+ void ParseFields(const Data &data, size_t &offset);
+ void BuildHeader(Data &data, size_t &offset) const;
+
+ void Clear();
+
+ void Dump(std::ostream &os) const;
+
+ // database name
+ static const char * GetDBName() { return "Tasks"; }
+ static uint8_t GetDefaultRecType() { return 2; }
+
+};
+inline std::ostream& operator<<(std::ostream &os, const Tasks &msg) {
+ msg.Dump(os);
+ return os;
+}
// This is a packed field, which is a group of fields packed in
// variable length records inside one larger field of a normal record.
Index: tools/btool.cc
===================================================================
RCS file: /cvsroot/barry/barry/tools/btool.cc,v
retrieving revision 1.14
diff -u -r1.14 btool.cc
--- tools/btool.cc 24 Feb 2007 04:48:05 -0000 1.14
+++ tools/btool.cc 18 May 2007 17:58:43 -0000
@@ -218,6 +218,16 @@
new RecordParser<ServiceBook, Store<ServiceBook> > (
new Store<ServiceBook>(filename, false)));
}
+ else if( name == "Memos" ) {
+ return auto_ptr<Parser>(
+ new RecordParser<Memos, Store<Memos> > (
+ new Store<Memos>(filename, false )));
+ }
+ else if( name == "Tasks" ) {
+ return auto_ptr<Parser>(
+ new RecordParser<Tasks, Store<Tasks> > (
+ new Store<Tasks>(filename, false )));
+ }
else {
// unknown database, use null parser
return auto_ptr<Parser>( new DataDumpParser );
pgpdhLmyysmfX.pgp
Description: PGP signature
------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/
_______________________________________________ Barry-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/barry-devel
