Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / server / relasttick.c
1 /* $XConsortium: relasttick.c /main/6 1996/11/21 19:46:04 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 <stdlib.h>
14 #include "rerule.h"
15 #include "repeat.h"
16 #include "reutil.h"
17
18 static Tick DoMinute(const Tick, const RepeatEvent *);
19 static Tick DoDay(const Tick, const RepeatEvent *);
20 static Tick DoWeek(const Tick, RepeatEvent *);
21 static Tick DoMonthDay(const Tick, const RepeatEvent *);
22 static Tick DoMonthPos(const Tick, RepeatEvent *);
23 static Tick DoYearByMonth(const Tick, RepeatEvent *);
24 static Tick DoYearByDay(const Tick, RepeatEvent *);
25 static Tick LastOccurence(const Tick, const WeekDayTime *, const unsigned int);
26 static Tick LastTickFromEndDate(const Tick, const RepeatEvent *);
27 static int LastDayExists(const struct tm *, const unsigned int, 
28                          const unsigned int *);
29 extern void FillInRepeatEvent(const Tick, RepeatEvent *);
30 extern Tick ClosestTick(const Tick, const Tick, RepeatEvent *, 
31                         RepeatEventState **);
32 extern Tick PrevTick(const Tick, const Tick, RepeatEvent *, RepeatEventState *);
33
34 Tick
35 LastTick(
36         const Tick               start_time,
37         RepeatEvent             *re)
38 {
39         Tick                     last_time;
40
41         if (!re) return (Tick)NULL;
42
43         if (re->re_duration == RE_INFINITY) return EOT;
44
45         FillInRepeatEvent(start_time, re);
46
47         switch (re->re_type) {
48         case RT_MINUTE:
49                 last_time = DoMinute(start_time, re);
50                 break;
51         case RT_DAILY:
52                 last_time = DoDay(start_time, re);
53                 break;
54         case RT_WEEKLY:
55                 last_time = DoWeek(start_time, re);
56                 break;
57         case RT_MONTHLY_POSITION:
58                 last_time = DoMonthPos(start_time, re);
59                 break;
60         case RT_MONTHLY_DAY:
61                 last_time = DoMonthDay(start_time, re);
62                 break;
63         case RT_YEARLY_MONTH:
64                 last_time = DoYearByMonth(start_time, re);
65                 break;
66         case RT_YEARLY_DAY:
67                 last_time = DoYearByDay(start_time, re);
68                 break;
69         }
70
71         return last_time;
72 }
73
74 static Tick
75 DoMinute(
76         const Tick               start_time,
77         const RepeatEvent       *re)
78 {
79         return (Tick)NULL;
80 }
81
82 static Tick
83 DoDay(
84         const Tick               start_time,
85         const RepeatEvent       *re)
86 {
87         Tick                     last_time1 = EOT,
88                                  last_time2 = EOT;
89
90         if (re->re_end_date) {
91                 last_time1 = LastTickFromEndDate(start_time, re);
92         }
93
94         if (re->re_duration != RE_NOTSET) {
95                 struct tm       *start_tm;
96                 _Xltimeparams    localtime_buf;
97
98                 start_tm = _XLocaltime((const time_t *)&start_time, localtime_buf);
99
100                 /* Go to the last day an event can happen on. */
101                 start_tm->tm_mday += (re->re_duration - 1) * re->re_interval;
102                 start_tm->tm_isdst = -1;
103
104                 /* Go to the last time an event can happen on the last day. */
105                 if (RE_DAILY(re)->dd_ntime) {
106                         start_tm->tm_hour =
107                         RE_DAILY(re)->dd_time[RE_DAILY(re)->dd_ntime - 1] / 100;
108                         start_tm->tm_min = 
109                         RE_DAILY(re)->dd_time[RE_DAILY(re)->dd_ntime - 1] % 100;
110                 }
111                 last_time2 = mktime(start_tm);
112         }
113
114         return ((last_time1 < last_time2) ? last_time1 : last_time2);
115 }
116
117 static Tick
118 DoWeek(
119         const Tick               start_time,
120         RepeatEvent             *re)
121 {
122         struct tm               *start_tm;
123         unsigned int             wd_ndaytime = RE_WEEKLY(re)->wd_ndaytime,
124                                  dt_ntime,
125                                  start_hour,
126                                  start_min;
127         Tick                     _start_time = start_time,
128                                  last_time1 = EOT,
129                                  last_time2 = EOT;
130         RepeatEventState        *res;
131         _Xltimeparams            localtime_buf;
132
133         if (re->re_end_date) {
134                 last_time1 = LastTickFromEndDate(start_time, re);
135
136                 if (re->re_duration == RE_NOTSET)
137                         return last_time1;
138         }
139
140         /* Find the real start time */
141         _start_time = ClosestTick(start_time, start_time, re, &res);
142         free(res);
143         start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
144
145         start_hour = start_tm->tm_hour;
146         start_min = start_tm->tm_min;
147
148         /* Go to the last day an event can happen on. */
149         start_tm->tm_mday += (re->re_duration - 1) * re->re_interval * 7;
150         start_tm->tm_isdst = -1;
151
152         if (wd_ndaytime) {
153                 _start_time = mktime(start_tm);
154                 start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
155
156                 /* Normalize to the beginning of the week */
157                 start_tm->tm_mday -= start_tm->tm_wday;
158                 start_tm->tm_sec = start_tm->tm_min = start_tm->tm_hour = 0;
159                 start_tm->tm_isdst = -1;
160                 _start_time = mktime(start_tm);
161                 start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
162
163                 /* Move forward to the proper week day */
164                 start_tm->tm_mday += 
165                         RE_WEEKLY(re)->wd_daytime[wd_ndaytime - 1].dt_day;
166
167                 dt_ntime = RE_WEEKLY(re)->wd_daytime[wd_ndaytime - 1].dt_ntime;
168
169                 /* Set the proper time */
170                 if (dt_ntime) {
171                         start_tm->tm_hour = RE_WEEKLY(re)->
172                               wd_daytime[wd_ndaytime - 1].dt_time[dt_ntime - 1]
173                               / 100;
174                         start_tm->tm_min = RE_WEEKLY(re)->
175                               wd_daytime[wd_ndaytime - 1].dt_time[dt_ntime - 1]
176                               % 100;
177                 } else {
178                         start_tm->tm_hour = start_hour;
179                         start_tm->tm_min = start_min;
180                 }
181         } 
182
183         start_tm->tm_isdst = -1;
184         last_time2 = mktime(start_tm);
185
186         return ((last_time1 < last_time2) ? last_time1 : last_time2);
187 }
188
189 static Tick
190 DoMonthDay(
191         const Tick               start_time,
192         const RepeatEvent       *re)
193 {
194         struct tm                start_tm,
195                                 *cur_tm;
196         unsigned int             md_nitems = RE_MONTHLY(re)->md_nitems,
197                                 *md_days = RE_MONTHLY(re)->md_days,
198                                  interval = 1;
199         Tick                     cur_time,
200                                  last_time1 = EOT,
201                                  last_time2 = EOT;
202         _Xltimeparams            localtime_buf;
203
204         if (re->re_end_date) {
205                 last_time1 = LastTickFromEndDate(start_time, re);
206
207                 if (re->re_duration == RE_NOTSET)
208                         return last_time1;
209         }
210
211         start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
212         cur_tm = _XLocaltime((const time_t *)&start_time, localtime_buf);
213         start_tm.tm_isdst = -1;
214
215         /* The 28th - 31st may not exist in a given month thus if only these
216          * days are specified in a rule it is necessary to calculate the
217          * correct month by brute force versus as a mathimatical calculation.
218          */
219         if (md_days[0] > 28) {
220                 cur_tm->tm_mday = 1;
221
222                 /* Compute last event by brute force */
223                 do {
224                         cur_tm->tm_mon += re->re_interval;
225                         cur_tm->tm_isdst = -1;
226                         cur_time = mktime(cur_tm);
227                         cur_tm = _XLocaltime((const time_t *)&cur_time, localtime_buf);
228
229                         if (DayExists(
230                                 DayOfMonth(md_days[0], cur_tm->tm_mon,
231                                            cur_tm->tm_year),
232                                 cur_tm->tm_mon, cur_tm->tm_year))
233                                 interval++;
234                 } while (interval < re->re_duration);
235
236                 start_tm.tm_mon = cur_tm->tm_mon;
237                 start_tm.tm_year = cur_tm->tm_year;
238                 start_tm.tm_mday = LastDayExists(cur_tm, md_nitems, md_days); 
239
240         } else if (md_nitems) {
241                 start_tm.tm_mon += (re->re_duration - 1) * re->re_interval;
242                 start_tm.tm_mday = 1;
243                 /* Have the year and month updated */
244                 cur_time = mktime(&start_tm);
245                 start_tm = *_XLocaltime((const time_t *)&cur_time, localtime_buf);
246                 /* Get the right day (LASTDAY converted to a real day) */
247                 start_tm.tm_isdst = -1;
248                 start_tm.tm_mday = DayOfMonth(
249                                 md_days[md_nitems - 1],
250                                 start_tm.tm_mon, start_tm.tm_year);
251         } else 
252                 start_tm.tm_mon += (re->re_duration - 1) * re->re_interval;
253
254         last_time2 = mktime(&start_tm);
255         return ((last_time1 < last_time2) ? last_time1 : last_time2);
256 }
257
258 static Tick
259 DoMonthPos(
260         const Tick               _start_time,
261         RepeatEvent             *re)
262 {
263         struct tm                start_tm,
264                                  cur_tm;
265         Tick                     last_time1 = EOT,
266                                  last_time2 = EOT,
267                                  start_time = _start_time;
268         unsigned int             nwdt_list = RE_MONTHLY(re)->md_nitems,
269                                  num_intervals = 1,
270                                  i, j,
271                                  brute_force = TRUE;
272         WeekDayTime             *wdt_list = RE_MONTHLY(re)->md_weektime;
273         RepeatEventState        *res;
274         _Xltimeparams            localtime_buf;
275
276         if (re->re_end_date) {
277                 last_time1 = LastTickFromEndDate(start_time, re);
278
279                 if (re->re_duration == RE_NOTSET)
280                         return last_time1;
281         }
282
283         /* Find the real start time */
284         start_time = ClosestTick(start_time, start_time, re, &res);
285         free(res);
286         start_tm = *_XLocaltime((const time_t *)&start_time, localtime_buf);
287
288         for (i = 0; i < nwdt_list; i++) {
289                 for (j = 0; j < wdt_list[i].wdt_nweek; j++) {
290                         if ((wdt_list[i].wdt_week[j] != WK_F5) &&
291                             (wdt_list[i].wdt_week[j] != WK_L5)) {
292                                 brute_force = FALSE;
293                                 break;
294                         }
295                 }
296                 if (brute_force == FALSE) break;
297         }
298
299         if (brute_force) {
300                 cur_tm = start_tm;
301                 cur_tm.tm_mday = 1;
302
303                 while (num_intervals < re->re_duration) {
304                         cur_tm.tm_mon += re->re_interval;
305                         cur_tm.tm_isdst = -1;
306                         last_time2 = mktime(&cur_tm);
307                         cur_tm = *_XLocaltime((const time_t *)&last_time2, 
308                                             localtime_buf);
309
310                         if (OccurenceExists(wdt_list, nwdt_list, last_time2))
311                                 num_intervals++;
312
313                         if (!InTimeRange(num_intervals, re->re_duration))
314                                 break;
315                 }
316         } else {
317                 start_tm.tm_mon += (re->re_duration - 1) * re->re_interval;
318                 start_tm.tm_isdst = -1;
319                 start_tm.tm_mday = 1;
320                 last_time2 = mktime(&start_tm);
321         }
322
323         /*
324          * Given the occurence information, find the last valid day in the
325          * month.
326          */
327         last_time2 = LastOccurence(last_time2, wdt_list, nwdt_list);
328
329         return ((last_time1 < last_time2) ? last_time1 : last_time2);
330 }
331
332 static Tick
333 DoYearByMonth(
334         const Tick               start_time,
335         RepeatEvent             *re)
336 {
337         struct tm               *start_tm;
338         int                      start_day; 
339         Tick                     _start_time,
340                                  last_time1 = EOT,
341                                  last_time2 = EOT;
342         RepeatEventState        *res;
343         _Xltimeparams            localtime_buf;
344
345         if (re->re_end_date) {
346                 last_time1 = LastTickFromEndDate(start_time, re);
347
348                 if (re->re_duration == RE_NOTSET)
349                         return last_time1;
350         }
351
352         /* Find the real start time */
353         _start_time = ClosestTick(start_time, start_time, re, &res);
354         free(res);
355         start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
356         start_day = start_tm->tm_mday;
357
358         /* Go to the last day an event can happen on. */
359         start_tm->tm_year += (re->re_duration - 1) * re->re_interval;
360         start_tm->tm_isdst = -1;
361
362         /* XXX: If the only month used is Feb and the date is the 29th, then
363          * we must use a special case.
364          */
365
366         /* Go to the last time an event can happen on on the last month. */
367         if (RE_YEARLY(re)->yd_nitems) {
368                 int     i;
369
370                 _start_time = mktime(start_tm);
371                 start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
372
373                 for (i = RE_YEARLY(re)->yd_nitems - 1; i >= 0; i++) {
374                         if (DayExists(start_day, RE_YEARLY(re)->yd_items[i],
375                                                         start_tm->tm_year)) {
376                                 start_tm->tm_mon = RE_YEARLY(re)->yd_items[i]-1;
377                                 start_tm->tm_isdst = -1;
378                                 return (mktime(start_tm));
379                         }
380                 }
381                 /* No months have a day that can be used */
382                 return ((Tick)NULL); 
383         }
384
385         last_time2 = mktime(start_tm);
386         return ((last_time1 < last_time2) ? last_time1 : last_time2);
387 }
388
389 static Tick
390 DoYearByDay(
391         const Tick               start_time,
392         RepeatEvent             *re)
393 {
394         struct tm               *start_tm;
395         int                      start_day; 
396         Tick                     _start_time,
397                                  last_time1 = EOT,
398                                  last_time2 = EOT;
399         RepeatEventState        *res;
400         _Xltimeparams            localtime_buf;
401
402         if (re->re_end_date) {
403                 last_time1 = LastTickFromEndDate(start_time, re);
404
405                 if (re->re_duration == RE_NOTSET)
406                         return last_time1;
407         }
408
409         /* Find the real start time */
410         _start_time = ClosestTick(start_time, start_time, re, &res);
411         free(res);
412         start_tm = _XLocaltime((const time_t *)&_start_time, localtime_buf);
413
414         /* Go to the last year an event can happen on. */
415         start_tm->tm_year += (re->re_duration - 1) * re->re_interval;
416         start_tm->tm_isdst = -1;
417
418         /* Go to the last time an event can happen on. */
419         if (RE_YEARLY(re)->yd_nitems) {
420                 start_tm->tm_mon = 0; 
421                 start_tm->tm_mday =
422                         RE_YEARLY(re)->yd_items[RE_YEARLY(re)->yd_nitems - 1];
423         }
424
425         last_time2 = mktime(start_tm);
426         return ((last_time1 < last_time2) ? last_time1 : last_time2);
427 }
428
429 /*
430  * Given a month/year (from cur_tm), and a list of days of the month
431  * determine the last day in that list that is valid in that month.
432  */
433 static int
434 LastDayExists(
435         const struct tm         *cur_tm,
436         const unsigned int       md_nitems,
437         const unsigned int      *md_days)
438 {
439         int     i;
440         int     day;
441
442         for (i = md_nitems - 1; i >= 0; i--) {  
443                 day = DayOfMonth(md_days[i], cur_tm->tm_mon, cur_tm->tm_year);
444                 if (DayExists(day, cur_tm->tm_mon, cur_tm->tm_year))
445                         return(day);
446         }
447
448         return 0;
449 }
450
451 /*
452  * Given a month/year (in cur_time) determine the last occurence of week/day/
453  * time in the month.
454  */
455 static Tick
456 LastOccurence(
457         const Tick               cur_time,
458         const WeekDayTime       *wdt_list,
459         const unsigned int       nwdt_list)
460 {
461         int     i, j, k;
462         Tick    oldest_time = 0,
463                 current_time;
464  
465         for (i = 0; i < nwdt_list; i++) {
466                 for (j = 0; j < wdt_list[i].wdt_nweek; j++) {
467                         for (k = 0; k < wdt_list[i].wdt_nday; k++) {
468                                 if (current_time = WeekNumberToDay(cur_time,
469                                                 wdt_list[i].wdt_week[j],
470                                                 wdt_list[i].wdt_day[k])) {
471                                         if (current_time > oldest_time)
472                                                 oldest_time = current_time;
473                                         
474                                 }
475                         }
476                 }
477         }
478
479         return oldest_time;
480 }
481
482 /*
483  * Given a time and a rule find the last tick before the end date.
484  */
485 static Tick
486 LastTickFromEndDate(
487         const Tick               cur_time,
488         const RepeatEvent       *re)
489 {
490         RepeatEventState        *res;
491         RepeatEvent             *_re = (RepeatEvent *)re;
492         Tick                     end_date = re->re_end_date,
493                                  last_time;
494         Duration                 duration = re->re_duration;
495  
496         /* Take the end date out of the equation. */
497         _re->re_end_date = 0;
498         _re->re_duration = RE_INFINITY;
499  
500         /* Use the end date to get the closest tick after it, then
501          * step back one tick to get the last tick before the
502          * end date.
503          */
504         last_time = ClosestTick(end_date, cur_time, _re, &res);
505         /*
506          * An event that occurs at the same time as the end_date is an
507          * event.
508          */
509         if (last_time != end_date)
510                 last_time = PrevTick(last_time, cur_time, _re, res);
511  
512         /* Return the re to its original state. */
513         _re->re_end_date = end_date;
514         _re->re_duration = duration;
515         free (res);
516
517         return last_time;
518 }