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