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 );

Attachment: 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
Barry-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/barry-devel

Reply via email to