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