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