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