+typedef BOOL NGMonthSet[12];
+typedef BOOL NGMonthDaySet[32]; // 0 is unused
+
+static void NGMonthDaySet_clear(NGMonthDaySet *daySet) {
+ register unsigned i;
+
+ for (i = 1; i <= 31; i++)
+ (*daySet)[i] = NO;
+}
+
+static void NGMonthDaySet_copyOrUnion(NGMonthDaySet *base, NGMonthDaySet *new,
+ BOOL doCopy)
+{
+ register unsigned i;
+
+ if (doCopy)
+ memcpy(base, new, sizeof(NGMonthDaySet));
+ else {
+ for (i = 1; i <= 31; i++) {
+ if (!(*new)[i])
+ (*base)[i] = NO;
+ }
+ }
+}
+
+static BOOL NGMonthDaySet_fillWithByMonthDay(NGMonthDaySet *daySet,
+ NSArray *byMonthDay)
+{
+ /* list of days in the month */
+ unsigned i, count;
+ BOOL ok;
+
+ NGMonthDaySet_clear(daySet);
+
+ for (i = 0, count = [byMonthDay count], ok = YES; i < count; i++) {
+ int dayInMonth; /* -31..-1 and 1..31 */
+
+ if ((dayInMonth = [[byMonthDay objectAtIndex:i] intValue]) == 0) {
+ ok = NO;
+ continue; /* invalid value */
+ }
+ if (dayInMonth > 31) {
+ ok = NO;
+ continue; /* error, value to large */
+ }
+ if (dayInMonth < -31) {
+ ok = NO;
+ continue; /* error, value to large */
+ }
+
+ /* adjust negative days */
+
+ if (dayInMonth < 0) {
+ /* eg: -1 == last day in month, 30 days => 30 */
+ dayInMonth = 32 - dayInMonth /* because we count from 1 */;
+ }
+
+ (*daySet)[dayInMonth] = YES;
+ }
+ return ok;
+}
+
+static inline unsigned iCalDoWForNSDoW(int dow) {
+ switch (dow) {
+ case 0: return iCalWeekDaySunday;
+ case 1: return iCalWeekDayMonday;
+ case 2: return iCalWeekDayTuesday;
+ case 3: return iCalWeekDayWednesday;
+ case 4: return iCalWeekDayThursday;
+ case 5: return iCalWeekDayFriday;
+ case 6: return iCalWeekDaySaturday;
+ case 7: return iCalWeekDaySunday;
+ default: return 0;
+ }
+}
+
+#if HEAVY_DEBUG
+static NSString *dowEN[8] = {
+ @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", @"SA", @"SU-"
+};
+#endif
+
+static void NGMonthDaySet_fillWithByDayX(NGMonthDaySet *daySet,
+ unsigned dayMask,
+ unsigned firstDoWInMonth,
+ unsigned numberOfDaysInMonth,
+ int occurrence1)
+{
+ // TODO: this is called 'X' because the API doesn't allow for full iCalendar
+ // functionality. The daymask must be a list of occurence+dow
+ register unsigned dayInMonth;
+ register int dow; /* current day of the week */
+ int occurrences[7] = { 0, 0, 0, 0, 0, 0, 0 } ;
+
+ NGMonthDaySet_clear(daySet);
+
+ if (occurrence1 >= 0) {
+ for (dayInMonth = 1, dow = firstDoWInMonth; dayInMonth<=31; dayInMonth++) {
+ // TODO: complete me
+
+ if (dayMask & iCalDoWForNSDoW(dow)) {
+ if (occurrence1 == 0)
+ (*daySet)[dayInMonth] = YES;
+ else { /* occurrence1 > 0 */
+ occurrences[dow] = occurrences[dow] + 1;
+
+ if (occurrences[dow] == occurrence1)
+ (*daySet)[dayInMonth] = YES;
+ }
+ }
+
+ dow = (dow == 6 /* Sat */) ? 0 /* Sun */ : (dow + 1);
+ }
+ }
+ else {
+ int lastDoWInMonthSet;
+
+ /* get the last dow in the set (not necessarily the month!) */
+ for (dayInMonth = 1, dow = firstDoWInMonth;
+ dayInMonth < numberOfDaysInMonth;dayInMonth++)
+ dow = (dow == 6 /* Sat */) ? 0 /* Sun */ : (dow + 1);
+ lastDoWInMonthSet = dow;
+
+#if HEAVY_DEBUG
+ NSLog(@"LAST DOW IN SET: %i / %@",
+ lastDoWInMonthSet, dowEN[lastDoWInMonthSet]);
+#endif
+ /* start at the end of the set */
+ for (dayInMonth = numberOfDaysInMonth, dow = lastDoWInMonthSet;
+ dayInMonth >= 1; dayInMonth--) {
+ // TODO: complete me
+
+#if HEAVY_DEBUG
+ NSLog(@" CHECK day-of-month %02i, "
+ @" dow=%i/%@ (first=%i/%@, last=%i/%@)",
+ dayInMonth,
+ dow, dowEN[dow],
+ firstDoWInMonth, dowEN[firstDoWInMonth],
+ lastDoWInMonthSet, dowEN[lastDoWInMonthSet]
+ );
+#endif
+
+ if (dayMask & iCalDoWForNSDoW(dow)) {
+ occurrences[dow] = occurrences[dow] + 1;
+#if HEAVY_DEBUG
+ NSLog(@" MATCH %i/%@ count: %i occurences=%i",
+ dow, dowEN[dow], occurrences[dow], occurrence1);
+#endif
+
+ if (occurrences[dow] == -occurrence1) {
+#if HEAVY_DEBUG
+ NSLog(@" COUNT MATCH");
+#endif
+ (*daySet)[dayInMonth] = YES;
+ }
+ }
+
+ dow = (dow == 0 /* Sun */) ? 6 /* Sat */ : (dow - 1);
+ }
+ }
+}
+
+- (BOOL)_addInstanceWithStartDate:(NSCalendarDate *)_startDate
+ limitDate:(NSCalendarDate *)_until
+ limitRange:(NGCalendarDateRange *)_r
+ toArray:(NSMutableArray *)_ranges
+{
+ NGCalendarDateRange *r;
+ NSCalendarDate *end;
+
+ /* check whether we are still in the limits */
+
+ // TODO: I think we should check in here whether we succeeded the
+ // repeatCount. Currently we precalculate that info in the
+ // -lastInstanceStartDate method.
+ if (_until != nil) {
+ /* Note: the 'until' in the rrule is inclusive as per spec */
+ if ([_until compare:_startDate] == NSOrderedAscending)
+ /* start after until */
+ return NO; /* Note: we assume that the algorithm is sequential */
+ }
+
+ /* create end date */
+
+ end = [_startDate addTimeInterval:[self->firstRange duration]];
+ [end setTimeZone:[_startDate timeZone]];
+
+ /* create range and check whether its in the requested range */
+
+ r = [[NGCalendarDateRange alloc] initWithStartDate:_startDate endDate:end];
+ if ([_r containsDateRange:r])
+ [_ranges addObject:r];
+ [r release]; r = nil;
+
+ return YES;
+}
+