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