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