Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / server / reutil.c
1 /* $XConsortium: reutil.c /main/7 1996/11/21 19:47:12 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 #include <time.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include "rerule.h"
13 #include "reutil.h"
14 #include "repeat.h"
15
16 #define XOS_USE_NO_LOCKING
17 #define X_INCLUDE_TIME_H
18 #include <X11/Xos_r.h>
19
20 extern int monthdays[12];
21 extern RepeatEvent      *_DtCm_repeat_info;
22 extern char             *_DtCm_rule_buf;
23 extern void              _DtCm_rule_parser();
24 extern char             *ReToString(RepeatEvent *);
25
26
27 /*
28  * Check to make sure the current interval number is not greater than the
29  * duration.
30  */
31 int
32 InTimeRange(
33         const unsigned int    ninterval,
34         const Duration        duration)
35 {
36         if (duration == RE_INFINITY || duration == RE_NOTSET) return TRUE;
37
38         if (ninterval >= duration)
39                 return FALSE;
40
41         return TRUE;
42 }
43
44 /*
45  * Given two days of the week, compute the forward difference between them. 
46  */
47 int
48 GetWDayDiff(
49         const int     start,
50         const int     end)
51 {
52         if (start <= end)
53                 return end - start;
54
55         return (7 - (start - end));
56 }
57
58 /*
59  * Returns true if a day exists in the specified month and year.
60  */
61 int
62 DayExists(
63         const int     day,
64         const int     month,
65         const int     year)
66 {
67         int valid_day = DayOfMonth(day, month, year);
68
69         if (valid_day < 29) return TRUE;
70
71         /* month = 0 = January */
72         if ((month == 1) && leapyr(year + 1900)) {
73                 if (valid_day == 29)
74                         return TRUE;
75                 else
76                         return FALSE;
77         }
78
79         if (valid_day <= monthdays[month]) 
80                 return TRUE;
81
82         return FALSE;
83 }
84
85 /* 
86  * Given a date (specifically a month and year) determine if any of the
87  * occurences of a weekday (e.g. 4th Sunday) exist in the given month.
88  */
89 int
90 OccurenceExists(
91         const WeekDayTime       *wdt_list,
92         const unsigned int       nwdt_list,
93         const Tick               date)
94 {
95         int              i, j, k;
96
97         for (i = 0; i < nwdt_list; i++) {
98                 for (j = 0; j < wdt_list[i].wdt_nweek; j++) {
99                         for (k = 0; k < wdt_list[i].wdt_nday; k++) {
100                                 if (WeekNumberToDay(date,
101                                                 wdt_list[i].wdt_week[j],
102                                                 wdt_list[i].wdt_day[k])) {
103                                         return TRUE;
104                                 }
105                         }
106                 }
107         }
108
109         return FALSE;
110 }
111
112 WeekNumber
113 GetWeekNumber(
114         const Tick date)
115 {
116         switch (DayToWeekNumber(date)) {
117         case 1:
118                 return WK_F1;
119         case 2:
120                 return WK_F2;
121         case 3:
122                 return WK_F3;
123         case 4:
124                 return WK_F4;
125         case 5:
126                 return WK_F5;
127         default:
128                 return WK_L1;
129         }
130 }
131
132 /*
133  * Calculate the position of a week day (e.g. SU) in the month:
134  *      7/1/94  would return 1 since this is the first Friday of the month.
135  *      7/6/94  would return 1 even though it is in the second week since this
136  *              is the first Wed of the month.
137  *      7/15/94 would return 3.
138  */
139 WeekNumber
140 DayToWeekNumber(
141         const Tick date)
142 {
143         _Xltimeparams    localtime_buf;
144         struct tm       *date_tm = _XLocaltime(&date, localtime_buf);
145         int              week_number;
146
147         week_number = date_tm->tm_mday / 7;
148  
149         if (date_tm->tm_mday % 7)
150                 week_number++;
151  
152         return week_number;
153 }
154
155 /*
156  * Given a week number and a day of the week determine what day of the month
157  * it falls on given a month in ``date''.
158  */
159 Tick
160 WeekNumberToDay(
161         const Tick            date,
162         const WeekNumber      week,
163         const WeekDay         weekday)
164 {
165         _Xltimeparams    localtime_buf;
166         struct tm       *date_tm = _XLocaltime(&date, localtime_buf);
167         int              first_weekday,
168                          last_weekday,
169                          day_of_month,
170                          initial_month_number = date_tm->tm_mon;
171         Tick             _date;
172
173         /* From the first day (or last day in the WK_L* cases) of the month
174          * work forward (or backward) to find the weekday requested. 
175          */
176         if (week <= (const WeekDay)WK_F5) {
177                 day_of_month = 1;
178                 first_weekday = fdom(date);
179                 if (weekday != first_weekday)
180                         day_of_month += GetWDayDiff(first_weekday, weekday);
181         } else {
182                 day_of_month = monthlength(date);
183                 last_weekday = ldom(date);
184                 if (weekday != last_weekday)
185                         day_of_month -= GetWDayDiff(weekday, last_weekday);
186         }
187
188         /* Now move forward or backward through the month added or subtracting
189          * the appropriate number of weeks to get to the correct location.
190          */
191         if (week <= (const WeekDay)WK_F5) {
192                 date_tm->tm_mday = day_of_month + (int)week * 7;
193         } else {
194                 /* ((int)week - WK_L1) normalizes the WK_L* to the values
195                  * of 0 to 4.  See the WeekNumber enum.
196                  */
197                 date_tm->tm_mday = day_of_month - ((int)week - WK_L1) * 7;
198         } 
199
200         date_tm->tm_isdst = -1;
201         _date = mktime(date_tm);
202         date_tm = _XLocaltime(&_date, localtime_buf);
203
204         /* It is possible that the requested week number is not in this
205          * month.
206          */
207         if (date_tm->tm_mon != initial_month_number)
208                 return ((Tick)NULL);
209
210         return (_date);
211 }
212
213 unsigned int
214 DayOfMonth(
215         const int     day,
216         const int     month,
217         const int     year)
218 {
219
220         if (day != RE_LASTDAY) return day;
221
222         /* month = 0 = January */
223         if ((month == 1) && leapyr(year + 1900))
224                 return 29;
225         else
226                 return monthdays[month];
227 }
228
229 int
230 same_week(
231         struct tm       *tm1,
232         struct tm       *tm2)
233 {
234         struct tm        tm11 = *tm1;
235         struct tm        tm22 = *tm2;
236         Tick             time1, time2;
237         _Xltimeparams    localtime_buf;
238
239         tm11.tm_mday -= tm11.tm_wday;
240         tm22.tm_mday -= tm22.tm_wday;
241         time1 = mktime(&tm11);
242         time2 = mktime(&tm22);
243         tm11 = *_XLocaltime(&time1, localtime_buf);
244         tm22 = *_XLocaltime(&time2, localtime_buf);
245
246         if (tm11.tm_yday == tm22.tm_yday)
247                 return TRUE;
248
249         return FALSE;
250 }
251
252 Tick
253 DeriveNewStartTime(
254         const Tick       start_time,
255         RepeatEvent     *old_re,
256         const Tick       current_time,
257         const Tick       target_time,
258         RepeatEvent     *new_re)
259 {
260         Tick             end_date;
261         Tick             an_event;
262         int              num_events;
263         RepeatEventState *res;
264
265         /* Count the number of events from the start time to the current time */
266         end_date = old_re->re_end_date;
267         old_re->re_end_date = current_time;
268         /* XXX: Need to deal with excluded events */
269         num_events = CountEvents(start_time, old_re, NULL);
270         old_re->re_end_date = end_date;
271  
272         if (!ClosestTick(current_time, start_time, old_re, &res)) {
273                 /* XXX: Are we at the last tick or are there other problems? */
274                 return 0;
275         }
276
277         an_event = target_time;
278  
279         /* Walk backwards from the new target time to where the new start
280          * time should be.
281          */
282         while (--num_events &&
283                (an_event = PrevTick(an_event, NULL, new_re, res))) {
284                ;
285         }
286
287         return an_event;
288 }
289
290 /*
291  * Return True if rule1 is the same as rule2.  This function returns True
292  * if the rule portion of the rules are == reguardless of the duration or 
293  * end_date's. 
294  * re1 should point to the RepeatEvent struct for rule1. If you pass in
295  * NULL for re1 the function *may* parse rule1 for you and return the
296  * RepeatEvent struct through re1.  You will need to free it.
297  * The same applies for re2.
298  */
299 boolean_t
300 RulesMatch(
301         char            *rule1,
302         char            *rule2)
303 {
304         Duration         old_duration;
305         time_t           old_end_date;
306         char            *new_rule1,
307                         *new_rule2;
308         RepeatEvent     *re1,
309                         *re2;
310
311         /* If rules are the same then we are done */
312         if (!strcmp(rule1, rule2))
313                 return TRUE;
314
315         _DtCm_rule_buf = rule1;
316         _DtCm_rule_parser();
317         if (!_DtCm_repeat_info) {
318                 /* Bad rule - fail */
319                 return FALSE;
320         }
321         re1 = _DtCm_repeat_info;
322
323         _DtCm_rule_buf = rule2;
324         _DtCm_rule_parser();
325         if (!_DtCm_repeat_info) {
326                 /* Bad rule - fail */
327                 _DtCm_free_re(re1);
328                 return FALSE;
329         }
330         re2 = _DtCm_repeat_info;
331
332         /* If rule1 != rule2 and the duration and end_date are the same
333          * then the rules themselves must be different.
334          */
335         if (re1->re_duration == re2->re_duration &&
336             re1->re_end_date == re2->re_end_date) {
337                 _DtCm_free_re(re1);
338                 _DtCm_free_re(re2);
339                 return FALSE;
340         }
341
342         
343         /* If the duration or end_date are different, the rules themselves
344          * may still be different.  So we make the durations and end_dates
345          * the same and reconstruct the rules.
346          */
347         old_duration = re2->re_duration;
348         old_end_date = re2->re_end_date;
349
350         re2->re_duration = re1->re_duration;
351         re2->re_end_date = re1->re_end_date;
352
353         new_rule1 = ReToString(re1);
354         new_rule2 = ReToString(re2);
355
356         re2->re_duration = old_duration;
357         re2->re_end_date = old_end_date;
358
359         if (!strcmp(new_rule1, new_rule2)) {
360                 _DtCm_free_re(re1);
361                 _DtCm_free_re(re2);
362                 free(new_rule1);
363                 free(new_rule2);
364                 return TRUE;
365         }
366         free(new_rule1);
367         free(new_rule2);
368         _DtCm_free_re(re1);
369         _DtCm_free_re(re2);
370         return FALSE;
371 }