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