Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / server / recount.c
1 /* $XConsortium: recount.c /main/6 1996/11/21 19:45:46 drk $ */
2 /*
3  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
4  *  (c) Copyright 1993, 1994 International Business Machines Corp.
5  *  (c) Copyright 1993, 1994 Novell, Inc.
6  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
7  */
8
9 #define XOS_USE_NO_LOCKING
10 #define X_INCLUDE_TIME_H
11 #include <X11/Xos_r.h>
12
13 #include <EUSCompat.h>
14 #include "csa.h"
15 #include "rerule.h"
16 #include "repeat.h"
17 #include "reutil.h"
18 #include "iso8601.h"
19
20 static int InitialEventsToExclude(time_t, RepeatEvent *);
21 static int EventsPerMonth(RepeatEvent *);
22 static int DoBruteForce(const time_t, RepeatEvent       *);
23
24 /*
25  * Give a start time, a parsed rule and a list of exception dates, determine
26  * the number of events that will be generated.
27  */
28 int
29 CountEvents(
30         Tick                     start_time,
31         RepeatEvent             *re,
32         CSA_date_time_entry     *dte)
33 {
34         time_t                   exclude_time,
35                                  close_time;
36         int                      excluded_days = 0;
37         unsigned int             nevents1 = (unsigned int)-1,
38                                  nevents2 = (unsigned int)-1;
39
40         if (!re || !start_time) return RE_ERROR;
41
42         if (!re->re_end_date && re->re_duration == RE_INFINITY)
43                 return RE_INFINITY;
44
45         /*
46          * Count the number of times an excluded time hits an event time
47          * generated by the rule.
48          */
49         for (; dte; dte = dte->next) {
50                 RepeatEventState        *res;
51
52                 if (_csa_iso8601_to_tick(dte->date_time, &exclude_time) == -1)
53                         continue;
54
55                 if (!(close_time = ClosestTick(exclude_time, start_time, re,
56                                                                        &res))) {
57                         time_t          last_time;
58
59                         last_time = LastTick(start_time, re);
60                         if (last_time == exclude_time)
61                                 excluded_days++;
62                 } else {
63                         if (close_time == exclude_time)
64                                 excluded_days++;
65                 }
66                 
67                 _DtCm_free_re_state(res);
68         }
69
70         /*
71          * If there is an end date, then we must calculate the total number
72          * of events via the brute force method.
73          */
74         if (re->re_end_date) {
75                 nevents1 = DoBruteForce(start_time, re) - excluded_days;
76         }
77
78         if (re->re_duration == RE_NOTSET)
79                 return nevents1;
80
81         switch (re->re_type) {
82         case RT_MINUTE:
83                 break;
84         case RT_DAILY:
85                 nevents2 = re->re_duration;             
86                 break;
87         case RT_WEEKLY:
88                 if (!RE_WEEKLY(re)->wd_ndaytime)
89                         nevents2 = re->re_duration;
90                 else
91                         nevents2 = re->re_duration *
92                                                 RE_WEEKLY(re)->wd_ndaytime;
93
94                 nevents2 -= InitialEventsToExclude(start_time, re);
95                 break;
96         case RT_MONTHLY_POSITION: {
97                 int     events_per_month = EventsPerMonth(re);
98
99                 if (!events_per_month) {
100                         nevents2 = DoBruteForce(start_time, re);
101                 } else {
102                         nevents2 = re->re_duration * events_per_month -
103                                 InitialEventsToExclude(start_time, re);
104                 }
105                 break;
106         }
107         case RT_MONTHLY_DAY: {
108                 int              ndays = RE_MONTHLY(re)->md_nitems;
109                 struct tm       *start_tm;
110                 _Xltimeparams    localtime_buf;
111
112                 start_tm = _XLocaltime((const time_t *)&start_time, localtime_buf);
113                 /* 
114                  * Need to do this by brute force if they want days that may
115                  * not exist in a given month.
116                  */
117                 if (((!ndays) && start_tm->tm_mday > 28) ||
118                     (ndays && RE_MONTHLY(re)->md_days[ndays - 1] > 28)) {
119                         nevents2 = DoBruteForce(start_time, re);
120                 } else {
121                         if (!ndays)
122                                 nevents2 = re->re_duration;
123                         else
124                                 nevents2 = re->re_duration * ndays;
125
126                         nevents2 -= InitialEventsToExclude(start_time, re);
127                 }
128                 break;
129         }
130         case RT_YEARLY_MONTH:
131                 if (!RE_YEARLY(re)->yd_nitems)
132                         nevents2 = re->re_duration;
133                 else
134                         nevents2 = re->re_duration * 
135                                                 RE_YEARLY(re)->yd_nitems;
136                 nevents2 -= InitialEventsToExclude(start_time, re);
137                 break;
138         case RT_YEARLY_DAY:
139                 if (!RE_YEARLY(re)->yd_nitems)
140                         nevents2 = re->re_duration;
141                 else
142                         nevents2 = re->re_duration * 
143                                                 RE_YEARLY(re)->yd_nitems;
144                 nevents2 -= InitialEventsToExclude(start_time, re);
145                 break;
146         }
147
148         nevents2 -= excluded_days;
149
150         /*
151          * If both a duration and and enddate are set the policy is to use
152          * the lesser of the two.
153          */
154         if (nevents1 < nevents2)
155                 return nevents1;
156
157         return nevents2;
158 }
159
160 /* 
161  * If the rule is a weekly or monthly style with specific weekdays listed,
162  * such as W1 MO WE FR and the start_time indicates the rule starts on say a
163  * WE, then the first MO would not count as an event day so it must be
164  * excluded from the total count.
165  */
166 static int
167 InitialEventsToExclude(
168         time_t           start_time,
169         RepeatEvent     *re)
170 {
171         struct tm       *start_tm;
172         _Xltimeparams    localtime_buf;
173
174         start_tm = _XLocaltime((const time_t *)&start_time, localtime_buf);
175
176         if (re->re_type == RT_WEEKLY) {
177                 DayTime *daytime = (DayTime *)RE_WEEKLY(re)->wd_daytime;
178                 int      nevent_days = RE_WEEKLY(re)->wd_ndaytime,
179                          i;
180
181                 if (!nevent_days) return 0;
182
183                 for (i = 0; i < nevent_days; i++) {
184                         if (daytime[i].dt_day >= start_tm->tm_wday)
185                                 return i;
186                 }
187                 return (nevent_days);
188         } else if (re->re_type == RT_MONTHLY_POSITION) {
189                 WeekDayTime *wdt = (WeekDayTime *)RE_MONTHLY(re)->md_weektime;
190                 int          i,
191                              ndays = 0;
192
193                 for (i = 0; i < RE_MONTHLY(re)->md_nitems; i++) {
194                         int     j, k;
195                         time_t  date;
196
197                         for (j = 0; 
198                             j < RE_MONTHLY(re)->md_weektime[i].wdt_nweek; 
199                             j++) {
200
201                                 for (k = 0; 
202                                     k < RE_MONTHLY(re)->md_weektime[i].wdt_nday;
203                                     k++) { 
204
205                                         date = WeekNumberToDay(start_time,
206                                                 RE_MONTHLY(re)->md_weektime[i].
207                                                                    wdt_week[j],
208                                                 RE_MONTHLY(re)->md_weektime[i].
209                                                                    wdt_day[k]);
210                                         if (!date || date < start_time)
211                                                 ndays++;
212                                 }
213                         }
214                 }
215                 return (ndays);
216         } else if (re->re_type == RT_MONTHLY_DAY) {
217                 int i;
218
219                 if (!RE_MONTHLY(re)->md_nitems) return 0;
220
221                 for (i = 0; i < RE_MONTHLY(re)->md_nitems; i++) {
222                         if (RE_MONTHLY(re)->md_days[i] >= start_tm->tm_mday)
223                                 return i;
224                 }
225                 return (RE_MONTHLY(re)->md_nitems);
226         } else if (re->re_type == RT_YEARLY_MONTH) {
227                 int i;
228
229                 if (!RE_YEARLY(re)->yd_nitems) return 0;
230
231                 for (i = 0; i < RE_YEARLY(re)->yd_nitems; i++) {
232                         if (RE_YEARLY(re)->yd_items[i] >= (start_tm->tm_mon +1))
233                                 return i;
234                 }
235                 return (RE_YEARLY(re)->yd_nitems);
236         } else if (re->re_type == RT_YEARLY_DAY) {
237                 int i;
238
239                 if (!RE_YEARLY(re)->yd_nitems) return 0;
240
241                 for (i = 0; i < RE_YEARLY(re)->yd_nitems; i++) {
242                         if (RE_YEARLY(re)->yd_items[i] >= 
243                                                         (start_tm->tm_yday + 1))
244                                 return i;
245                 }
246                 return (RE_YEARLY(re)->yd_nitems);
247         }
248
249         return 0;
250 }
251         
252 /*
253  * Given a parsed MP rule determine the number of events it would generate
254  * in a month.  If the rule suggests events should occure on the 5th week
255  * which means the number of events generated in a given month is not
256  * constant, we return 0.
257  */
258 static int
259 EventsPerMonth(
260         RepeatEvent     *re)
261 {
262         int              nevents = 1,
263                          i;
264
265         for (i = 0, nevents = 0; i < RE_MONTHLY(re)->md_nitems; i++) {
266                 int j;
267
268                 /* If 5+ or 5- is used, we must compute count by brute force */
269                 for (j = 0; j < RE_MONTHLY(re)->md_weektime[i].wdt_nweek; j++) {
270                         if ((RE_MONTHLY(re)->md_weektime[i].wdt_week[j] == 
271                                                                        WK_F5) ||
272                             (RE_MONTHLY(re)->md_weektime[i].wdt_week[j] == 
273                                                                        WK_L5))
274                                 return 0;
275                 }
276
277                 nevents += RE_MONTHLY(re)->md_weektime[i].wdt_nday *
278                            RE_MONTHLY(re)->md_weektime[i].wdt_nweek;
279         }
280
281         return nevents;
282 }
283
284 /*
285  * Given a start time and a parsed rule determine the number events generated
286  * by walking the event stream until we reach the end.
287  */
288 static int
289 DoBruteForce(
290         const time_t             start_time, 
291         RepeatEvent             *re)
292 {
293         RepeatEventState        *res;
294         time_t                   cur_time;
295         int                      nevents = 0;
296         
297
298         if (!(cur_time = ClosestTick(start_time, start_time, re, &res)))
299                 return nevents;
300
301         nevents = 1;
302
303         while ((cur_time = NextTick(cur_time, start_time, re, res))) {
304                 nevents++;
305         }
306
307         _DtCm_free_re_state(res);
308
309         return nevents;
310 }