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