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