Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / server / renexttick.c
1 /* $XConsortium: renexttick.c /main/5 1996/11/21 19:46:22 drk $ */
2 /*
3  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
4  *  (c) Copyright 1993, 1994 International Business Machines Corp.
5  *  (c) Copyright 1993, 1994 Novell, Inc.
6  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
7  */
8
9
10 #define XOS_USE_NO_LOCKING
11 #define X_INCLUDE_TIME_H
12 #include <X11/Xos_r.h>
13
14 #include "rerule.h"
15 #include "repeat.h"
16 #include "reutil.h"
17
18 typedef enum {
19         SameInterval = 0,
20         NextInterval
21 } MoveIndicator; 
22
23 static Tick DoMinute(const Tick, const Tick, const RepeatEvent *,
24                         RepeatEventState *);
25 static Tick DoDay(const Tick, const Tick, const RepeatEvent *,
26                         RepeatEventState *);
27 static Tick DoWeek(const Tick, const Tick, const RepeatEvent *,
28                         RepeatEventState *);
29 static Tick DoMonthDay(const Tick, const Tick, const RepeatEvent *,
30                         RepeatEventState *);
31 static Tick DoMonthPos(const Tick, const Tick, const RepeatEvent *,
32                         RepeatEventState *);
33 static Tick DoYearByMonth(const Tick, const Tick, const RepeatEvent *,
34                         RepeatEventState *);
35 static Tick DoYearByDay(const Tick, const Tick, const RepeatEvent *,
36                         RepeatEventState *);
37
38 static Tick NextDayTick(const Tick, const Tick, const RepeatEvent *,
39                         RepeatEventState *);
40 static Tick NextWeekTick(const struct tm *, const struct tm *,
41                          const RepeatEvent *, const DayTime *,
42                          const MoveIndicator, RepeatEventState *);
43 static Tick NextMonthTick(const struct tm *, const RepeatEvent *,
44                           const WeekDayTime *, const MoveIndicator,
45                           RepeatEventState *);
46 extern void FillInRepeatEvent(const Tick, RepeatEvent *);
47
48 Tick
49 NextTick(
50         const Tick               cur_time,
51         const Tick               start_time,
52         RepeatEvent             *re,
53         RepeatEventState        *res)
54 {
55         Tick                     next_time;
56
57         if (!re) return (Tick)NULL;
58
59         FillInRepeatEvent(start_time, re);
60
61         switch (re->re_type) {
62         case RT_MINUTE:
63                 next_time = DoMinute(cur_time, start_time, re, res);
64                 break;
65         case RT_DAILY:
66                 next_time = DoDay(cur_time, start_time, re, res);
67                 break;
68         case RT_WEEKLY:
69                 next_time = DoWeek(cur_time, start_time, re, res);
70                 break;
71         case RT_MONTHLY_POSITION:
72                 next_time = DoMonthPos(cur_time, start_time, re, res);
73                 break;
74         case RT_MONTHLY_DAY:
75                 next_time = DoMonthDay(cur_time, start_time, re, res);
76                 break;
77         case RT_YEARLY_MONTH:
78                 next_time = DoYearByMonth(cur_time, start_time, re, res);
79                 break;
80         case RT_YEARLY_DAY:
81                 next_time = DoYearByDay(cur_time, start_time, re, res);
82                 break;
83         }
84
85         /*
86          * If the duration was not set (thus strictly using the end date)
87          * reset the RepeatEventState duration back to not-set.  This is
88          * cleaner than having conditionals through out the code checking
89          * to see if the duration needs to be updated.
90          */
91         if (re->re_duration == RE_NOTSET)
92                 res->res_duration = RE_NOTSET;
93
94         return next_time;
95 }
96
97 static Tick
98 DoMinute(
99         const Tick               cur_time,
100         const Tick               start_time,
101         const RepeatEvent       *re,
102         RepeatEventState        *res)
103 {
104         return (Tick)NULL;
105 }
106
107 static Tick
108 DoDay(
109         const Tick               cur_time,
110         const Tick               start_time,
111         const RepeatEvent       *re,
112         RepeatEventState        *res)
113 {
114         Tick                     next_time;
115         Duration                 res_duration = res->res_duration;
116         unsigned int             res_time = RES_DSTATE(res).res_time;
117
118         if (RE_DAILY(re)->dd_ntime) {
119                 /* Last event was on the last time for this day.  Now must
120                  * move to the next day/interval.
121                  */
122                 if (RES_DSTATE(res).res_time == RE_DAILY(re)->dd_ntime - 1) {
123                         next_time = NextDayTick(cur_time, start_time, re, res);
124                         RES_DSTATE(res).res_time = 0;
125                 } else {
126                         struct tm               *tm;
127                         _Xltimeparams           localtime_buf;
128
129                         /* There is a later valid time on this day, use it */
130                         tm = _XLocaltime(&cur_time, localtime_buf);
131                         tm->tm_hour = 
132                                 RE_DAILY(re)->
133                                       dd_time[++RES_DSTATE(res).res_time] / 100;
134                         tm->tm_min = 
135                                 RE_DAILY(re)->dd_time[RES_DSTATE(res).res_time]
136                                 % 100;
137                         tm->tm_isdst = -1;
138                         next_time = mktime(tm);
139                 }
140         } else {
141                 /* No alternate times for this day/move to the next interval. */
142                 next_time = NextDayTick(cur_time, start_time, re, res);
143         }
144
145         /* If we went beyond the end date then restore the state info, ret 0 */
146         if (re->re_end_date && re->re_end_date < next_time) {
147                 res->res_duration = res_duration;
148                 RES_DSTATE(res).res_time = res_time;
149                 return 0;
150         }
151
152         return next_time;
153 }
154
155 static Tick
156 DoWeek(
157         const Tick               cur_time,
158         const Tick               start_time,
159         const RepeatEvent       *re,
160         RepeatEventState        *res)
161 {
162         unsigned int             res_daytime = RES_WSTATE(res).res_daytime,
163                                  res_time = RES_WSTATE(res).res_time,
164                                  re_ntime,
165                                  re_ndaytime = RE_WEEKLY(re)->wd_ndaytime;
166         Duration                 res_duration = res->res_duration;
167         struct tm                cur_tm,
168                                  start_tm;
169         DayTime                 *daytime = RE_WEEKLY(re)->wd_daytime;
170         Tick                     next_time;
171         _Xltimeparams            localtime_buf;
172
173         cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
174         start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
175
176         re_ntime = daytime[0].dt_ntime;
177
178         /*
179          * We want to stop when the duration == 1 and we have handled all
180          * the days in the rule for that week.
181          */
182         if ((res->res_duration == 0) || ((res->res_duration == 1) && 
183             ((re_ndaytime - 1) == RES_WSTATE(res).res_daytime)))
184                 return (Tick)0;
185                 
186         /* If these are equal then we are at the end of the time
187          * slots for this day */
188         if ((re_ntime - 1) == res_time) {
189                 /* If these are equal then we used up all the
190                  * days for this week */
191                 if ((re_ndaytime - 1) == res_daytime) {
192
193                         /* End of week */
194                         RES_WSTATE(res).res_daytime = 0;
195                         RES_WSTATE(res).res_time = 0;
196                         next_time = NextWeekTick(&cur_tm, &start_tm, re,
197                                                  daytime, NextInterval, res);
198                 } else {
199                         /* Move to the next day event (same week), use
200                          * the earliest time.
201                          */
202                         RES_WSTATE(res).res_time = 0;
203                         RES_WSTATE(res).res_daytime++;
204                         next_time = NextWeekTick(&cur_tm, &start_tm, re,
205                                                 daytime, SameInterval, res);
206                 }
207         } else {
208                 /* Move to the next time, same day. */
209                 RES_WSTATE(res).res_time++;
210                 next_time = NextWeekTick(&cur_tm, &start_tm, re, daytime,
211                                                  SameInterval, res);
212         }
213
214         /* If we went beyond the end date then restore the state info, ret 0 */
215         if (re->re_end_date && re->re_end_date < next_time) {
216                 res->res_duration = res_duration;
217                 RES_WSTATE(res).res_time = res_time;
218                 RES_WSTATE(res).res_daytime = res_daytime;
219                 return 0;
220         }
221
222         return (next_time);
223 }
224
225 static Tick
226 DoMonthDay(
227         const Tick               cur_time,
228         const Tick               start_time,
229         const RepeatEvent       *re,
230         RepeatEventState        *res)
231 {
232         unsigned int            *md_days = RE_MONTHLY(re)->md_days,
233                                  md_nitems = RE_MONTHLY(re)->md_nitems,
234                                  res_day = RES_MSTATE(res).res_day;
235         Duration                 res_duration = res->res_duration;
236         Tick                     _cur_time;
237         struct tm                start_tm,
238                                  cur_tm;
239         int                      next_interval = FALSE;
240         _Xltimeparams            localtime_buf;
241
242         start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
243         cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
244
245         /*
246          * We want to stop when the duration == 1 and we have handled all
247          * the days listed in the rule for that month.
248          */
249         if ((res->res_duration == 0) || ((res->res_duration == 1) && 
250             ((md_nitems - 1) == RES_MSTATE(res).res_day)))
251                 return (Tick)0;
252
253         /* Check each event day in the month, move to the next interval when
254          * we run out of event days for the month.  Make sure the event day
255          * exists in that month.  e.g. the 31st of June does not exist.
256          */
257         do {
258                 if ((md_nitems - 1) == RES_MSTATE(res).res_day) {
259                         cur_tm.tm_mon += 1 * re->re_interval;
260                         RES_MSTATE(res).res_day = 0;
261                         next_interval = TRUE;
262                 } else
263                         RES_MSTATE(res).res_day++;
264
265                 cur_tm.tm_isdst = -1;
266                 cur_tm.tm_mday = 1;
267                 _cur_time = mktime(&cur_tm);
268                 cur_tm = *_XLocaltime((const time_t *)&_cur_time, localtime_buf);
269         } while (!DayExists(md_days[RES_MSTATE(res).res_day], cur_tm.tm_mon,
270                             cur_tm.tm_year));
271
272         cur_tm.tm_mday = DayOfMonth(md_days[RES_MSTATE(res).res_day],
273                                     cur_tm.tm_mon, cur_tm.tm_year);
274         cur_tm.tm_isdst = -1;
275         _cur_time = mktime(&cur_tm);
276
277         if (next_interval) res->res_duration--;
278
279         /* If we went beyond the end date then restore the state info, ret 0 */
280         if (re->re_end_date && re->re_end_date < _cur_time) {
281                 res->res_duration = res_duration;
282                 RES_MSTATE(res).res_day = res_day;
283                 return 0;
284         }
285
286         return (_cur_time);
287 }
288
289 static Tick
290 DoMonthPos(
291         const Tick               cur_time,
292         const Tick               start_time,
293         const RepeatEvent       *re,
294         RepeatEventState        *res)
295 {
296         WeekDayTime             *wdt_list = RE_MONTHLY(re)->md_weektime;
297         struct tm                start_tm,
298                                  cur_tm;
299         Tick                     _cur_time;
300         Duration                 res_duration = res->res_duration;
301         unsigned int             md_nitems = RE_MONTHLY(re)->md_nitems,
302                                  wdt_nday = wdt_list[md_nitems-1].wdt_nday,
303                                  wdt_nweek = wdt_list[md_nitems-1].wdt_nweek,
304                                  res_weektime = RES_MSTATE(res).res_weektime,
305                                  res_wday = RES_MSTATE(res).res_wday,
306                                  res_wtime = RES_MSTATE(res).res_wtime,
307                                  res_wweek = RES_MSTATE(res).res_wweek;
308         _Xltimeparams            localtime_buf;
309
310         if (res->res_duration == 0)
311                 return (Tick)0;
312                 
313         start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
314         cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
315
316         /* XXX: This assumes rules of this form only: MP<n> 1+ WE #4 */
317         if ((wdt_list[res_weektime].wdt_nday - 1) == res_wday) {
318                 if ((wdt_list[res_weektime].wdt_nweek - 1) == res_wweek) {
319                         if ((md_nitems - 1) == res_weektime) {
320                                 RES_MSTATE(res).res_weektime = 0;
321                                 RES_MSTATE(res).res_wday = 0;
322                                 RES_MSTATE(res).res_wtime = 0;
323                                 RES_MSTATE(res).res_wweek = 0;
324                                 _cur_time = NextMonthTick(&cur_tm, re, wdt_list,
325                                                          NextInterval, res);
326                         } else {
327                                 _cur_time = 0;
328                         }
329                 } else {
330                         _cur_time = 0;
331                 }
332         } else {
333                 _cur_time = 0;
334         }
335
336         /* If we went beyond the end date then restore the state info, ret 0 */
337         if (re->re_end_date && re->re_end_date < _cur_time) {
338                 res->res_duration = res_duration;
339                 RES_MSTATE(res).res_weektime = res_weektime;
340                 RES_MSTATE(res).res_wday = res_wday;
341                 RES_MSTATE(res).res_wtime = res_wtime;
342                 RES_MSTATE(res).res_wweek = res_wweek;
343                 return 0;
344         }
345
346         return (_cur_time);
347 }
348
349 static Tick
350 DoYearByMonth(
351         const Tick               cur_time,
352         const Tick               start_time,
353         const RepeatEvent       *re,
354         RepeatEventState        *res)
355 {
356         struct tm                start_tm,
357                                  cur_tm;
358         Tick                     _cur_time;
359         Duration                 res_duration = res->res_duration;
360         unsigned int            *month_list = RE_YEARLY(re)->yd_items,
361                                  nitems = RE_YEARLY(re)->yd_nitems,
362                                  res_month = RES_YSTATE(res).res_daymonth;
363         _Xltimeparams            localtime_buf;
364
365         start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
366         cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
367
368         /*
369          * We want to stop when the duration == 1 and we have handled all
370          * the months listed in the rule for that year.
371          */
372         if ((res->res_duration == 0) || ((res->res_duration == 1) && 
373             ((nitems - 1) == RES_YSTATE(res).res_daymonth)))
374                 return (Tick)0;
375
376         cur_tm.tm_mday = start_tm.tm_mday;
377         cur_tm.tm_hour = start_tm.tm_hour;
378         cur_tm.tm_min = start_tm.tm_min;
379         cur_tm.tm_sec = start_tm.tm_sec;
380
381         /* If these equal then we used up all the months for this year.  
382          * We must now move to the next interval.
383          */
384         if ((nitems - 1) == res_month) {
385                 cur_tm.tm_year += re->re_interval;
386                 cur_tm.tm_mon = month_list[0] - 1; /* 0 = January */
387                 RES_YSTATE(res).res_daymonth = 0;
388                 res->res_duration--;
389         } else {
390                 /* Take the next month in the month_list, same year */
391                 cur_tm.tm_mon = month_list[++RES_YSTATE(res).res_daymonth] - 1;
392         }
393
394         cur_tm.tm_isdst = -1;
395         _cur_time = mktime(&cur_tm);
396
397         /* If we went beyond the end date then restore the state info, ret 0 */
398         if (re->re_end_date && re->re_end_date < _cur_time) {
399                 res->res_duration = res_duration;
400                 RES_YSTATE(res).res_daymonth = res_month;
401                 return 0;
402         }
403
404         return (_cur_time);
405 }
406
407 static Tick
408 DoYearByDay(
409         const Tick               cur_time,
410         const Tick               start_time,
411         const RepeatEvent       *re,
412         RepeatEventState        *res)
413 {
414         struct tm                start_tm,
415                                  cur_tm;
416         Tick                     _cur_time;
417         Duration                 res_duration = res->res_duration;
418         unsigned int            *day_list = RE_YEARLY(re)->yd_items,
419                                  nitems = RE_YEARLY(re)->yd_nitems,
420                                  res_month = RES_YSTATE(res).res_daymonth;
421         _Xltimeparams            localtime_buf;
422
423         start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
424         cur_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
425
426         /*
427          * We want to stop when the duration == 1 and we have handled all
428          * the days listed in the rule for that year.
429          */
430         if ((res->res_duration == 0) || ((res->res_duration == 1) && 
431             ((nitems - 1) == RES_YSTATE(res).res_daymonth)))
432                 return (Tick)0;
433
434         cur_tm.tm_mday = start_tm.tm_mday;
435         cur_tm.tm_hour = start_tm.tm_hour;
436         cur_tm.tm_min = start_tm.tm_min;
437         cur_tm.tm_sec = start_tm.tm_sec;
438         cur_tm.tm_mon = 0;
439         cur_tm.tm_isdst = -1;
440
441         /* If these equal then we used up all the days for this year.  
442          * We must now move to the next interval.
443          */
444         if ((nitems - 1) == res_month) {
445                 cur_tm.tm_year += re->re_interval;
446                 cur_tm.tm_mday = day_list[0];
447                 RES_YSTATE(res).res_daymonth = 0;
448                 res->res_duration--;
449         } else {
450                 /* Take the next day in the day_list, same year */
451                 cur_tm.tm_mday = day_list[++RES_YSTATE(res).res_daymonth];
452         }
453
454         _cur_time = mktime(&cur_tm);
455
456         /* If we went beyond the end date then restore the state info, ret 0 */
457         if (re->re_end_date && re->re_end_date < _cur_time) {
458                 res->res_duration = res_duration;
459                 RES_YSTATE(res).res_daymonth = res_month;
460                 return 0;
461         }
462
463         return (_cur_time);
464 }
465
466 static Tick
467 NextDayTick(
468         const Tick               cur_time,
469         const Tick               start_time,
470         const RepeatEvent       *re,
471         RepeatEventState        *res)
472 {
473         struct tm       *tm;
474         struct tm        start_tm;
475         _Xltimeparams    localtime_buf;
476
477         if (res->res_duration == 0) return (Tick)0;
478
479         res->res_duration--;
480
481         start_tm = *_XLocaltime(&start_time, localtime_buf);
482         tm = _XLocaltime(&cur_time, localtime_buf);
483
484         tm->tm_mday += re->re_interval;
485         tm->tm_hour = start_tm.tm_hour;
486         tm->tm_min = start_tm.tm_min;
487         tm->tm_isdst = -1;
488
489         return (mktime(tm));
490 }
491
492 static Tick
493 NextWeekTick(
494         const struct tm         *current_tm,
495         const struct tm         *start_tm,
496         const RepeatEvent       *re,
497         const DayTime           *wd_daytime,
498         const MoveIndicator      move,
499         RepeatEventState        *res)
500 {
501         struct tm               *cur_tm = (struct tm *)current_tm;
502         unsigned int             res_daytime = RES_WSTATE(res).res_daytime;
503         unsigned int             res_time = RES_WSTATE(res).res_time;
504
505         /* Move forward to the next interval (at least another week) */
506         if (move == (const MoveIndicator) NextInterval) {
507                 /* Normalize the date to the beginning of the week. */
508                 cur_tm->tm_mday -= cur_tm->tm_wday;
509                 cur_tm->tm_sec = cur_tm->tm_min = cur_tm->tm_hour = 0;
510                 cur_tm->tm_isdst = -1;
511
512                 /* Add an interval */
513                 cur_tm->tm_mday += re->re_interval * 7;
514
515                 /* Put it on the correct day. */
516                 cur_tm->tm_mday += wd_daytime[0].dt_day;
517                 /* Put it on the correct time. */
518                 cur_tm->tm_hour = wd_daytime[0].dt_time[0] / 100;
519                 cur_tm->tm_min = wd_daytime[0].dt_time[0] % 100;
520
521                 res->res_duration--;
522
523                 return (mktime(cur_tm));
524         }
525
526         /* SameInterval */
527
528         /* Move the appropriate number of days forward */
529         cur_tm->tm_mday += GetWDayDiff(cur_tm->tm_wday,
530                                        wd_daytime[res_daytime].dt_day);
531                                 
532         /* Use the indicated time if available */
533         if (RE_WEEKLY(re)->wd_daytime[res_daytime].dt_time) {
534                 cur_tm->tm_hour = wd_daytime[res_daytime].dt_time[res_time]
535                                                                         / 100;
536                 cur_tm->tm_min = wd_daytime[res_daytime].dt_time[res_time]
537                                                                         % 100;
538         } else {
539                 /* Use the time from the first appt */
540                 cur_tm->tm_hour = start_tm->tm_hour; 
541                 cur_tm->tm_min = start_tm->tm_min;
542         }
543
544         cur_tm->tm_isdst = -1;
545         return (mktime(cur_tm));
546 }
547
548 static Tick
549 NextMonthTick(
550         const struct tm         *current_time,
551         const RepeatEvent       *re,
552         const WeekDayTime       *wdt_list,
553         const MoveIndicator      move,
554         RepeatEventState        *res)
555 {
556         struct tm               *cur_tm = (struct tm *)current_time;
557         unsigned int             res_weektime = RES_MSTATE(res).res_weektime,
558                                  res_wweek = RES_MSTATE(res).res_wweek,
559                                  res_wday = RES_MSTATE(res).res_wday;
560         Tick                     next_time;
561
562         /* Move forward to the next interval (at least another month) */
563         if (move == (const MoveIndicator) NextInterval) {
564                 cur_tm->tm_mday = 1;
565
566                 do {
567                         /* Add an interval */
568                         cur_tm->tm_mon += re->re_interval;
569                         cur_tm->tm_isdst = -1;
570
571                 } while (!(next_time = WeekNumberToDay(mktime(cur_tm),
572                                 wdt_list[res_weektime].wdt_week[res_wweek],
573                                 wdt_list[res_weektime].wdt_day[res_wday])));
574                 res->res_duration--;
575         } else {
576                 /* SameInterval */
577                 next_time = 0;
578         }
579
580         return (next_time);
581 }