an iCal processing library
iCal.cpp
- Committer:
- WiredHome
- Date:
- 2014-04-20
- Revision:
- 1:db274b9e40cc
- Parent:
- 0:49245357cd1b
- Child:
- 2:1f5dbc624b95
File content as of revision 1:db274b9e40cc:
#include "iCal.h" #include <algorithm> //#define DEBUG "iCal" #include <cstdio> #if (defined(DEBUG) && !defined(TARGET_LPC11U24)) #define DBG(x, ...) std::printf("[DBG %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); #define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); #define ERR(x, ...) std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); #define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); #else #define DBG(x, ...) #define WARN(x, ...) #define ERR(x, ...) #define INFO(x, ...) #endif Event_T EventList[EVENT_COUNT]; int EventCount = 0; const char * RPT_DAYS[] = { "SU", "MO", "TU", "WE", "TH", "FR", "SA", "" }; int GetNumEvents(void) { return EventCount; } const char * RepeatDayAbbrev(int i) { if (i < 7) return RPT_DAYS[i]; else return RPT_DAYS[7]; } void SortEvents() { bool swapped; int e; Event_T Event; do { swapped = false; for (e=0; e<EventCount-1; e++) { if (EventList[e].Start > EventList[e+1].Start) { Event = EventList[e]; EventList[e] = EventList[e+1]; EventList[e+1] = Event; swapped = true; } } } while (swapped); } uint16_t AtoIxN(char * p, int n) { uint16_t res = 0; while (n--) { res = (res * 10) + (*p - '0'); p++; } return res; } time_t ParseDateStamp(char * string, int32_t tzoSec) { time_t tStamp; struct tm t; //INFO("ParseDateStamp(..., %s)\r\n", string); t.tm_year = AtoIxN(string, 4) - 1900; t.tm_mon = AtoIxN(string+4, 2) - 1; t.tm_mday = AtoIxN(string+6, 2); t.tm_hour = AtoIxN(string+9, 2); t.tm_min = AtoIxN(string+11, 2); t.tm_sec = AtoIxN(string+13, 2); tStamp = mktime(&t); if (string[strlen(string)-1] == 'Z') tStamp = tStamp + tzoSec; return tStamp; //int tm_sec //seconds after the minute – [0, 60][@1] (public member object) //int tm_min //minutes after the hour – [0, 59] (public member object) //int tm_hour //hours since midnight – [0, 23] (public member object) //int tm_mday //day of the month – [1, 31] (public member object) //int tm_mon //months since January – [0, 11] (public member object) //int tm_year //years since 1900 (public member object) //int tm_wday //days since Sunday – [0, 6] (public member object) //int tm_yday //days since January 1 – [0, 365] //int tm_isdst } char * FormatCTime(time_t t) { static char temp[4][80]; static int i = 0; i &= 3; strcpy(temp[i], ctime(&t)); temp[i][strlen(temp[i])-1] = '\0'; return temp[i++]; } void ShowEventInfo(Event_T & Event) { char temp[80]; INFO("*** Summary: %s", Event.Summary); strcpy(temp, ctime(&Event.Start)); temp[strlen(temp)-1] = '\0'; INFO(" Start: %d %s", Event.Start, temp); strcpy(temp, ctime(&Event.End)); temp[strlen(temp)-1] = '\0'; INFO(" End: %d %s", Event.End, temp ); INFO(" Count: %d", Event.Count); INFO(" RepeatFrq: %d", Event.RepeatFreq); INFO(" RepeatDay: %02X", Event.RepeatDays); strcpy(temp, ctime(&Event.Until)); temp[strlen(temp)-1] = '\0'; INFO(" Until: %d %s", Event.Until, temp); INFO(" Location: %s", Event.Location); INFO(" Category: %s", Event.Category); INFO(" Priority: %d", Event.Priority); } /// Computes the intersection of time1 and time2 ranges, and modifies time1 /// range to represent the intersection. /// /// @param start1 is input as the start of the time1 range, and is written /// to represent the intersection of the two ranges. /// @param end1 is input as the end of the time1 range and is written to /// represent the intersection of the two ranges. /// @param start2 is the start of the time2 range. /// @param end2 is the end of the time2 range. /// @returns true if the ranges have an intersection, and the time1 range /// values have been modified. /// bool TimeIntersects(time_t * start1, time_t * end1, time_t * start2, time_t * end2) { // |----Time1----| // |--Time2--| false // // |----Time1----| // |--Time2--| false // // |----Time1----| // |----Time2----| // |-Time1-| true // // |----Time1----| // |----Time2----| // |-Time1-| true // // |----Time1-------| // |-Time2-| // |-Time1-| true // if (*end1 < *start2 || *end2 < *start1) return false; if (max(*start1,*start2) < min(*end1,*end2)) { *start1 = max(*start1,*start2); *end1 = min(*end1,*end2); return true; } else { return false; } } time_t NextInterval(int repeatFreq, int interval) { time_t secperday = 60*60*24; const int repeatFactor[] = {1, 7, 30, 365}; INFO("freq %d, interval %d", repeatFreq, interval); return repeatFactor[repeatFreq-1] * interval * secperday; } bool RepeatIntersects(time_t * start1, time_t * end1, time_t * start2, time_t * end2, Event_T & Event) { if (Event.RepeatFreq) { INFO("%s", Event.Summary); INFO("** 1: (%s, %s)", FormatCTime(*start1), FormatCTime(*end1)); INFO(" 2: (%s, %s)", FormatCTime(*start2), FormatCTime(*end2)); if (TimeIntersects(start1, end1, start2, end2)) { return true; } else if (Event.Start < *start2 && Event.Until > *start2 ) { // Until=.... do { time_t interval = NextInterval(Event.RepeatFreq, (Event.Interval == 0) ? 1 : Event.Interval); *start1 = *start1 + interval; *end1 = *end1 + interval; INFO("** 1: (%s, %s)", FormatCTime(*start1), FormatCTime(*end1)); INFO("until (%24s, %s)", " ", FormatCTime(Event.Until)); INFO(" 2: (%s, %s)", FormatCTime(*start2), FormatCTime(*end2)); if (TimeIntersects(start1, end1, start2, end2)) { return true; } } while (*start1 < *end2 && *start1 < Event.Until); } else if (Event.Start < *start2 && Event.Count) { // Count= int count = Event.Count - 1; do { time_t interval = NextInterval(Event.RepeatFreq, (Event.Interval == 0) ? 1 : Event.Interval); *start1 = *start1 + interval; *end1 = *end1 + interval; INFO("** 1: (%s, %s) - %d", FormatCTime(*start1), FormatCTime(*end1), count); INFO(" 2: (%s, %s)", FormatCTime(*start2), FormatCTime(*end2)); if (TimeIntersects(start1, end1, start2, end2)) { return true; } } while (--count); } else if (Event.Start < *start2) { // no Count= and no Until= do { time_t interval = NextInterval(Event.RepeatFreq, (Event.Interval == 0) ? 1 : Event.Interval); *start1 = *start1 + interval; *end1 = *end1 + interval; INFO("== 1: (%s, %s)", FormatCTime(*start1), FormatCTime(*end1)); INFO(" 2: (%s, %s)", FormatCTime(*start2), FormatCTime(*end2)); if (TimeIntersects(start1, end1, start2, end2)) { return true; } } while (*start1 < *end2); } } return false; } void ParseICalStream(char * pStart, time_t gridStartTime, time_t gridEndTime, int32_t tzoSec) { Event_T Event; bool tzAdjusted = false; char * pEnd; typedef enum { idle, inTimeZone, inEvent } seekstate_t; seekstate_t seeking = idle; EventCount = 0; while (pStart && EventCount < EVENT_COUNT) { pEnd = strchr(pStart, '\n'); if (pEnd) { if (*(pEnd-1) == '\r') pEnd--; *pEnd++ = '\0'; while (*pEnd && *pEnd < ' ') { pEnd++; } // pStart now has a single null terminated line of text. //INFO("*** %s", pStart); switch (seeking) { case idle: if (strcmp(pStart, "BEGIN:VTIMEZONE") == 0) seeking = inTimeZone; else if (strcmp(pStart, "BEGIN:VEVENT") == 0) { seeking = inEvent; Event.Start = 0; Event.End = 0; Event.Until = 0; Event.Summary[0] = '\0'; Event.Location[0] = '\0'; Event.Category[0] = '\0'; Event.Count = 0; Event.Interval = 0; Event.RepeatFreq = rptfNone; Event.RepeatDays = 0; Event.Priority = 5; // 5 is Normal //ShowEventInfo(Event, pc); } break; case inTimeZone: // Can also pick up daylight savings time if (strcmp(pStart, "END:VTIMEZONE") == 0) seeking = idle; else if (strncmp(pStart, "TZID", 4) == 0) tzAdjusted = true; break; case inEvent: // inEvent if (strcmp(pStart, "END:VEVENT") == 0) { // Timezone offset if (!tzAdjusted) { Event.Start += tzoSec; Event.End += tzoSec; } // Process it ShowEventInfo(Event); if (1 || Event.Start >= gridStartTime && Event.Start < gridEndTime || Event.End >= gridStartTime && Event.End < gridEndTime) { EventList[EventCount++] = Event; INFO(" ++++++++++++ Added Event %d", EventCount); } seeking = idle; } else if (strncmp(pStart, "DTSTART:", 8) == 0) { Event.Start = ParseDateStamp(pStart+8, tzoSec); //INFO(" Start: %d\r\n", mktime(&Event.eventStart)); } else if (strncmp(pStart, "DTSTART;", 8) == 0) { char * p = strrchr(pStart, ':'); if (p) { Event.Start = ParseDateStamp(p+1, tzoSec); //INFO(" Start: %d\r\n", mktime(&Event.eventStart)); } } else if (strncmp(pStart, "DTEND:", 6) == 0) { Event.End = ParseDateStamp(pStart+6, tzoSec); //INFO(" End: %d\r\n", mktime(&Event.eventEnd)); } else if (strncmp(pStart, "DTEND;", 6) == 0) { char * p = strrchr(pStart, ':'); if (p) { Event.End = ParseDateStamp(p+1, tzoSec); //INFO(" End: %d\r\n", mktime(&Event.eventEnd)); } } else if (strncmp(pStart, "SUMMARY:", 8) == 0) { strncpy(Event.Summary, pStart+8, SUMMARY_CHARS-1); Event.Summary[SUMMARY_CHARS-1] = '\0'; //INFO(" Summary: %s\r\n", Event.Summary); } else if (strncmp(pStart, "LOCATION:", 9) == 0) { strncpy(Event.Location, pStart+9, LOCATION_CHARS-1); Event.Location[LOCATION_CHARS-1] = '\0'; //INFO(" Location: %s\r\n", Event.Location); } else if (strncmp(pStart, "PRIORITY:", 9) == 0) { Event.Priority = *(pStart+9) - '0'; //INFO(" Priority: %d\r\n", Event.Priority); } else if (strncmp(pStart, "CATEGORIES:", 11) == 0) { strncpy(Event.Category, pStart+11, CATEGORY_CHARS-1); Event.Category[CATEGORY_CHARS-1] = '\0'; //INFO(" Category: %s\r\n", Event.Category); } else if (strncmp(pStart, "RRULE:", 6) == 0) { //RRULE:FREQ=WEEKLY;UNTIL=20140502T180000;BYDAY=MO,TU,WE,TH,FR char * p1, *p2; INFO("%s", pStart); p1 = pStart + 6; // p1 = FREQ=WEEKLY;UNTIL=20140502T180000;BYDAY=MO,TU,WE,TH,FR p2 = strchr(p1, ';'); if (p2) *p2++ = '\0'; while (*p1) { INFO("%s", p1); if (strncmp(p1, "FREQ=", 5) == 0) { INFO("%s", p1); p1 += 5; // p1 = WEEKLY;UNTIL=20140502T180000;BYDAY=MO,TU,WE,TH,FR if (strncmp(p1, "WEEKLY", 6) == 0) { INFO(" %s", p1); Event.RepeatFreq = rptfWeekly; p1 += 6; } else if (strncmp(p1, "DAILY", 5) == 0) { INFO(" %s", p1); Event.RepeatFreq = rptfDaily; p1 += 5; } else if (strncmp(p1, "MONTHLY", 7) == 0) { INFO(" %s", p1); Event.RepeatFreq = rptfMonthly; p1 += 7; } else if (strncmp(p1, "YEARLY", 6) == 0) { INFO(" %s", p1); Event.RepeatFreq = rptfYearly; p1 += 7; } } else if (strncmp(p1, "INTERVAL=", 9) == 0) { // INTERVAL=2 INFO("%s", p1); p1 += 9; Event.Interval = atoi(p1); } else if (strncmp(p1, "COUNT=", 6) == 0) { // COUNT=12; INFO("%s", p1); p1 += 6; // p1 = Event.Count = atoi(p1); } else if (strncmp(p1, "UNTIL=", 6) == 0) { INFO("%s", p1); p1 += 6; // p1 = 20140502T180000;BYDAY=MO,TU,WE,TH,FR Event.Until = ParseDateStamp(p1, tzoSec); } else if (strncmp(p1, "BYDAY=", 6) == 0) { INFO("%s", p1); p1 += 6; // p1 = MO,TU,WE,TH,FR while (*p1 >= ' ') { INFO(" %s", p1); for (int d=0; d<7; d++) { if (strncmp(p1,RepeatDayAbbrev(d),2) == 0) { Event.RepeatDays |= (1 << d); INFO(" %s %02X", RepeatDayAbbrev(d), Event.RepeatDays); break; } } p1 += 3; } INFO(" RepeatDay: %02X", Event.RepeatDays); } if (!p2) break; p1 = p2; p2 = strchr(p1, ';'); if (p2) *p2++ = '\0'; } } // End of inEvent break; default: seeking = idle; break; } pStart = pEnd; } else { pStart = NULL; } } // while }