Convert uses of XKeycodeToKeysym (deprecated) to XkbKeycodeToKeysym
[oweals/cde.git] / cde / programs / dtcm / server / repeat.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 libraries 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: repeat.c /main/7 1996/11/21 19:46:39 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 <EUSCompat.h>
32 #include <stdio.h>
33 #define XOS_USE_NO_LOCKING
34 #define X_INCLUDE_TIME_H
35 #if defined(__linux__)
36 #undef SVR4
37 #endif
38 #include <X11/Xos_r.h>
39 #include "csa.h"
40 #include "rtable4.h"
41 #include "cm.h"
42 #include "repeat.h"
43 #include "attr.h"
44 #include "iso8601.h"
45
46 #define EOT                     2147483647
47
48 extern  int     debug;
49
50 static time_t bot, eot;
51
52 typedef enum {
53         minsec          = 60,
54         fivemins        = 300,
55         hrsec           = 3600,
56         daysec          = 86400,
57         wksec           = 604800,
58         yrsec           = 31536000,
59         leapyrsec       = 31622400
60 } Unit;
61
62 static unsigned int weekdaymasks[] = {
63         0x1,    /* sunday */
64         0x2,    /* monday */
65         0x4,    /* tuesday */
66         0x8,    /* wednesday */
67         0x10,   /* thursday */
68         0x20,   /* friday */
69         0x40    /* saturday */
70 };
71
72 static int monthsecs[12] = {
73         31*daysec,      28*daysec,      31*daysec,
74         30*daysec,      31*daysec,      30*daysec,
75         31*daysec,      31*daysec,      30*daysec,
76         31*daysec,      30*daysec,      31*daysec
77 };
78
79 int monthdays[12] = {
80         31,     28,     31,
81         30,     31,     30,
82         31,     31,     30,
83         31,     30,     31
84 };
85
86 static int lastapptofweek(u_int mask);
87 static int ntimes_this_week(u_int weekmask, int firstday);
88 static boolean_t nthweekdayofmonth(time_t t, int *nth);
89 static time_t next_nmonth(time_t t, int n);
90 static int adjust_dst(time_t start, time_t next); 
91 static time_t prev_nmonth(time_t t, int n);
92 static time_t nextnyear(time_t t, int n);
93 static int timeok(time_t t);
94 static time_t prevnyear(time_t t, int n);
95 static time_t prevmonth_exactday(time_t t);
96 static time_t nextmonth_exactday(time_t t);
97 static time_t previousmonth(time_t t);
98 static int monthseconds(time_t t);
99 static int get_ndelta(time_t startdate, Period_4 period, int ntimes);
100 static time_t lastnthweekday(time_t t, int nth, int ntimes);
101 static time_t nextnthweekday(time_t t, int nth);
102 static time_t prevnthweekday(time_t t, int nth);
103 static time_t nextnday_exacttime(time_t t, int n);
104 static time_t prevnday_exacttime(time_t t, int n);
105 static time_t nextnwk_exacttime(time_t t, int n);
106 static time_t prevnwk_exacttime(time_t t, int n);
107 static time_t nextnmth_exactday(time_t t, int n);
108 static time_t prevnmth_exactday(time_t t, int n);
109 static time_t nextmonTofri(time_t t);
110 static time_t prevmonTofri(time_t t);
111 static time_t nextmonwedfri(time_t t);
112 static time_t prevmonwedfri(time_t t);
113 static time_t nexttuethur(time_t t);
114 static time_t prevtuethur(time_t t);
115 static time_t nextdaysofweek(time_t t, int weekmask);
116 static time_t prevdaysofweek(time_t t, int weekmask);
117
118 /* [ytso 1/26/93]
119  * cm now supports to up end of 1999.  This is due to the limitation
120  * of cm_getdate() which can only handle up to end of 1999.
121  * When cm_getdate() is improved to handle up to the system limit,
122  * definitions of eot and EOT need to be changed as well as some
123  * of the routines in this file and the caller of these routines.
124  */
125 extern void
126 init_time(void)
127 {
128         struct tm tm, gm;
129         time_t  t;
130         _Xltimeparams localtime_buf;
131         _Xgtimeparams gmtime_buf;
132
133         t               = time(0);
134         tm              = *_XLocaltime(&t, localtime_buf);
135         gm              = *_XGmtime(&t, gmtime_buf);
136
137         bot             = mktime(&gm) - mktime(&tm);
138
139         tm.tm_sec       =59;
140         tm.tm_min       =59;
141         tm.tm_hour      =23;
142         tm.tm_mday      =31;
143         tm.tm_mon       =11;
144         tm.tm_year      =137;                   /* Dec. 31, 2037 */
145         tm.tm_isdst = -1;
146         eot             =mktime(&tm);
147 }
148
149 extern void
150 _DtCms_adjust_appt_startdate(Appt_4 *appt)
151 {
152         struct tm *tm;
153         _Xltimeparams localtime_buf;
154
155         if (appt->period.period < monThruFri_4 ||
156             appt->period.period > tueThur_4)
157                 return;
158
159         tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf);
160         switch (appt->period.period) {
161         case monThruFri_4:
162                 if (tm->tm_wday < 1 || tm->tm_wday > 5)
163                         appt->appt_id.tick = _DtCms_next_tick_v4(
164                                                 appt->appt_id.tick,
165                                                 appt->period);
166                 break;
167         case monWedFri_4:
168                 if ((tm->tm_wday % 2) == 0)
169                         appt->appt_id.tick = _DtCms_next_tick_v4(
170                                                 appt->appt_id.tick,
171                                                 appt->period);
172                 break;
173         case tueThur_4:
174                 if (tm->tm_wday != 2 && tm->tm_wday != 4)
175                         appt->appt_id.tick = _DtCms_next_tick_v4(
176                                                 appt->appt_id.tick,
177                                                 appt->period);
178                 break;
179         }
180 }
181
182 /*
183  * Calculate the actual number of instances of the repeating event.
184  */
185 extern int
186 _DtCms_get_ninstance_v4(Appt_4 *appt)
187 {
188         struct tm *tm;
189         int i, pdelta, ndelta, ninstance, timesperweek;
190         double dninstance;
191         _Xltimeparams localtime_buf;
192
193         if (appt->ntimes == _DtCM_OLD_REPEAT_FOREVER)
194                 return(appt->ntimes);
195
196         switch (appt->period.period) {
197         case everyNthDay_4:
198         case everyNthWeek_4:
199         case everyNthMonth_4:
200                 ninstance = (appt->ntimes+(appt->period.nth-1))/appt->period.nth;
201                 break;
202
203         case monThruFri_4:
204                 tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf);
205                 pdelta = 6 - tm->tm_wday;
206                 ndelta = get_ndelta(appt->appt_id.tick, appt->period,
207                                 appt->ntimes);
208                 dninstance = (double)(appt->ntimes - 1) * 5 + pdelta - ndelta;
209                 ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER) ?
210                                 _DtCM_OLD_REPEAT_FOREVER : (int)dninstance;
211                 break;
212
213         case monWedFri_4:
214                 tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf);
215                 pdelta = (7 - tm->tm_wday) / 2;
216                 ndelta = get_ndelta(appt->appt_id.tick, appt->period,
217                                 appt->ntimes);
218                 dninstance = (double)(appt->ntimes - 1) * 3 + pdelta - ndelta;
219                 ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER)
220                                 ? _DtCM_OLD_REPEAT_FOREVER : (int)dninstance;
221                 break;
222
223         case tueThur_4:
224                 tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf);
225                 pdelta = (tm->tm_wday == 2) ? 2 : 1;
226                 ndelta = get_ndelta(appt->appt_id.tick, appt->period,
227                                 appt->ntimes);
228                 dninstance = (double)(appt->ntimes - 1) * 2 + pdelta - ndelta;
229                 ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER) ?
230                                 _DtCM_OLD_REPEAT_FOREVER : (int)dninstance;
231                 break;
232
233         case daysOfWeek_4:
234                 tm = _XLocaltime(&(appt->appt_id.tick), localtime_buf);
235                 timesperweek = ntimes_this_week((u_int)appt->period.nth, 0);
236                 pdelta = ntimes_this_week((u_int)appt->period.nth, tm->tm_wday);
237                 ndelta = get_ndelta(appt->appt_id.tick, appt->period,
238                                 appt->ntimes);
239                 dninstance = (double)(appt->ntimes-1) * timesperweek +
240                                 pdelta - ndelta;
241                 ninstance = (dninstance > _DtCM_OLD_REPEAT_FOREVER) ?
242                                 _DtCM_OLD_REPEAT_FOREVER : (int)dninstance;
243
244                 break;
245         default:
246                 ninstance = appt->ntimes;
247         }
248
249         return ninstance;
250 }
251
252 /*
253  * calculate the ntimes value which, depending on the
254  * repeating event type, may not be the same
255  * as the actual number of instances
256  */
257 extern int
258 _DtCms_get_new_ntimes_v4(Period_4 period, time_t tick, int ninstance)
259 {
260         struct tm *tm;
261         int ntimes;
262         int delta = 0, firstweek, timesperweek;
263         _Xltimeparams localtime_buf;
264
265         switch (period.period) {
266         case everyNthDay_4:
267         case everyNthWeek_4:
268         case everyNthMonth_4:
269                 ntimes = ninstance * period.nth;
270                 break;
271         case monThruFri_4:
272                 tm = _XLocaltime(&tick, localtime_buf);
273                 if (ninstance % 5)
274                         delta = ((ninstance % 5) > (6 - tm->tm_wday)) ? 2 : 1;
275                 else if (tm->tm_wday != 1)
276                         delta = 1;
277                 ntimes = (ninstance/5) + delta;
278                 break;
279         case monWedFri_4:
280                 tm = _XLocaltime(&tick, localtime_buf);
281                 if (ninstance % 3)
282                         delta = ((ninstance % 3) > ((7-tm->tm_wday)/2)) ? 2:1;
283                 else if (tm->tm_wday != 1)
284                         delta = 1;
285                 ntimes = (ninstance/3) + delta;
286                 break;
287         case tueThur_4:
288                 tm = _XLocaltime(&tick, localtime_buf);
289                 if (ninstance % 2 || tm->tm_wday != 2)
290                         delta = 1;
291                 ntimes = (ninstance/2) + delta;
292                 break;
293         case daysOfWeek_4:
294                 tm = _XLocaltime(&tick, localtime_buf);
295                 timesperweek = ntimes_this_week((u_int)period.nth, 0);
296                 firstweek=ntimes_this_week((u_int)period.nth,tm->tm_wday);
297                 if (ninstance % timesperweek)
298                         delta = ((ninstance % timesperweek) > firstweek) ? 2:1;
299                 else if (firstweek != timesperweek)
300                         delta = 1;
301                 ntimes = (ninstance/timesperweek) + delta;
302                 break;
303         default:
304                 ntimes = ninstance;
305                 break;
306         }
307
308         return ntimes;
309 }
310
311 extern time_t
312 _DtCms_first_tick_v4(time_t t, Period_4 period, int ordinal)
313 {
314         int i;
315         time_t ftick;
316
317         ftick = t;
318         for (i = 1; i < ordinal; i++)
319                 ftick = _DtCms_prev_tick_v4(ftick, period);
320
321         return(ftick);
322 }
323
324
325 /*
326  * Given a time, calculate the closest instance whose
327  * tick is later than the time.
328  * If the calculated tick does not pass timeok(), ftick is
329  * returned and ordinal set to 1.
330  */
331 extern time_t
332 _DtCms_closest_tick_v4(time_t target, time_t ftick, Period_4 period, int *ordinal)
333 {
334         time_t ctick;
335         int delta = 0;
336         int remainder = 0;
337         int ndays;
338         struct tm *tm;
339         struct tm tm1, tm2;
340         _Xltimeparams localtime_buf;
341
342         if (target <= ftick) {
343                 *ordinal = 1;
344                 return(ftick);
345         }
346
347         if (period.period < monthly_4 || period.period == everyNthDay_4 ||
348             period.period == everyNthWeek_4) {
349                 tm1 = *_XLocaltime(&ftick, localtime_buf);
350                 tm2 = *_XLocaltime(&target, localtime_buf);
351         }
352         switch(period.period) {
353         case daily_4:
354                 delta = (target - ftick) / daysec;
355                 remainder = target - ftick - daysec * delta;
356                 if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst)
357                         remainder -= hrsec;
358                 *ordinal = delta + (remainder>0?1:0) + 1;
359                 ctick = nextnday_exacttime(ftick, *ordinal - 1);
360                 break;
361         case weekly_4:
362                 delta = (target - ftick) / wksec;
363                 remainder = target - ftick - wksec * delta;
364                 if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst)
365                         remainder -= hrsec;
366                 *ordinal = delta + (remainder>0?1:0) + 1;
367                 ctick = nextnwk_exacttime(ftick, *ordinal - 1);
368                 break;
369         case biweekly_4:
370                 delta = (target - ftick) / (wksec * 2);
371                 remainder = target - ftick - wksec * 2 * delta;
372                 if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst)
373                         remainder -= hrsec;
374                 *ordinal = delta + (remainder>0?1:0) + 1;
375                 ctick = nextnwk_exacttime(ftick, 2 * (*ordinal - 1));
376                 break;
377         case monthly_4:
378                 tm = _XLocaltime(&ftick, localtime_buf);
379                 /*
380                  * Calculate the closest tick only if the date
381                  * is < 29; otherwise just return the first tick.
382                  * Use 32 to take care of dst time difference.
383                  * Without dst, we can use 31.
384                  */
385                 if (tm->tm_mday < 29) {
386                         delta = (target - ftick) / (daysec * 32);
387                         remainder = target - ftick - (daysec * 32) * delta;
388                         *ordinal = delta + (remainder>0?1:0) + 1;
389                         ctick = nextnmth_exactday(ftick, *ordinal - 1);
390                 } else {
391                         ctick = ftick;
392                         *ordinal = 1;
393                 }
394                 break;
395         case yearly_4:
396                 tm = _XLocaltime(&ftick, localtime_buf);
397                 if (tm->tm_mday == 29 && tm->tm_mon == 1) {
398                         delta = (target - ftick) / (yrsec * 4 + daysec);
399                         remainder = target - ftick - (yrsec * 4) * delta;
400                         *ordinal = delta + (remainder>0?1:0) + 1;
401                         ctick = nextnyear(ftick, (*ordinal - 1) * 4);
402                 } else {
403                         delta = (target - ftick) / yrsec;
404                         /* adjustment for leap year */
405                         remainder = tm->tm_year % 4;
406                         if (remainder == 0 || (remainder + delta) > 3)
407                                 delta--;
408                         remainder = target - ftick - yrsec * delta;
409                         *ordinal = delta + (remainder>0?1:0) + 1;
410                         ctick = nextnyear(ftick, *ordinal - 1);
411                 }
412                 break;
413         case nthWeekday_4:
414                 /* 36 is 5 weeks ==> maximum interval between 2 instances */
415                 delta = (target - ftick) / (daysec * 36);
416                 remainder = target - ftick - (daysec * 36) * delta;
417                 *ordinal = delta + (remainder>0?1:0) + 1;
418                 ctick = lastnthweekday(ftick, period.nth, *ordinal - 1);
419                 break;
420         case everyNthDay_4:
421                 delta = (target - ftick) / (daysec * period.nth);
422                 remainder = target - ftick - (daysec * period.nth) * delta;
423                 if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst)
424                         remainder -= hrsec;
425                 *ordinal = delta + (remainder>0?1:0) + 1;
426                 ctick = nextnday_exacttime(ftick,
427                                 period.nth * (*ordinal - 1));
428                 break;
429         case everyNthWeek_4:
430                 delta = (target - ftick) / (wksec * period.nth);
431                 remainder = target - ftick - (wksec * period.nth) * delta;
432                 if (tm1.tm_isdst == 1 && tm1.tm_isdst != tm2.tm_isdst)
433                         remainder -= hrsec;
434                 *ordinal = delta + (remainder>0?1:0) + 1;
435                 ctick = nextnwk_exacttime(ftick,
436                                 period.nth * (*ordinal - 1));
437                 break;
438         case everyNthMonth_4:
439                 tm = _XLocaltime(&ftick, localtime_buf);
440                 if (tm->tm_mday < 29) {
441                         delta = (target - ftick) / (daysec * 32 * period.nth);
442                         remainder = target-ftick-(daysec*32*period.nth)*delta;
443                         *ordinal = delta + (remainder>0?1:0) + 1;
444                         ctick = nextnmth_exactday(ftick,
445                                         period.nth * (*ordinal - 1));
446                 } else {
447                         ctick = ftick;
448                         *ordinal = 1;
449                 }
450                 break;
451         case monThruFri_4:
452         case monWedFri_4:
453         case tueThur_4:
454         case daysOfWeek_4:
455                 delta = (target - ftick) / wksec;
456                 tm = _XLocaltime(&ftick, localtime_buf);
457
458                 switch (period.period) {
459                 case monThruFri_4:
460                         *ordinal = delta * 5 + 6 - tm->tm_wday;
461                         break;
462                 case monWedFri_4:
463                         *ordinal = delta * 3 + (7 - tm->tm_wday) / 2;
464                         break;
465                 case tueThur_4:
466                         *ordinal = delta * 2 + ((tm->tm_wday == 2) ? 2 : 1);
467                         break;
468                 case daysOfWeek_4:
469                         *ordinal = delta * ntimes_this_week((u_int)period.nth,0)
470                                         + ntimes_this_week((u_int)period.nth,
471                                                 tm->tm_wday);
472                 }
473
474                 /* delta*daysperweek+(lastapptofweek-firstday in first week) */
475                 if (period.period == daysOfWeek_4) {
476                         ndays = delta * 7 +
477                                 lastapptofweek((u_int)period.nth) - tm->tm_wday;
478                         ctick = ftick + ndays * daysec;
479                 } else if (period.period == tueThur_4) {
480                         ndays = delta * 7 + 4 - tm->tm_wday;
481                         ctick = ftick + ndays * daysec;
482                 } else {
483                         ndays = delta * 7 + 5 - tm->tm_wday;
484                         ctick = ftick + ndays * daysec;
485                 }
486
487                 if (ctick > target) { /* need to go back 1 week */
488                         ndays -= 7;
489                         if (ndays < 0) {
490                                 *ordinal = 1;
491                                 ctick = ftick;
492                         } else {
493                                 if (period.period == monThruFri_4)
494                                         *ordinal -= 5;
495                                 else if (period.period == monWedFri_4)
496                                         *ordinal -= 3;
497                                 else if (period.period == tueThur_4)
498                                         *ordinal -= 2;
499                                 ctick -= (7 * daysec);
500                         }
501                 }
502                 ctick = adjust_dst(ftick, ctick);
503                 break;
504         default:
505                 *ordinal = 1;
506                 ctick = ftick;
507         }
508
509         if (timeok(ctick))
510                 return(ctick);
511         else {
512                 *ordinal = 1;
513                 return(ftick);
514         }
515 }
516
517 /*
518  * Calculate the tick of the last instance of a repeating event.
519  * If the calculated tick does not pass timeok(), EOT is returned.
520  */
521 extern time_t
522 _DtCms_last_tick_v4(time_t ftick, Period_4 period, int ntimes)
523 {
524         struct tm *tm;
525         double dltick;
526         time_t ltick = 0;
527         int i;
528         _Xltimeparams localtime_buf;
529
530         if (ntimes >= _DtCM_OLD_REPEAT_FOREVER)
531                 return(EOT);
532
533         if (period.enddate != 0)
534                 return(period.enddate);
535
536         switch(period.period) {
537         case weekly_4:
538                 ltick = nextnwk_exacttime(ftick, ntimes - 1);
539                 break;
540         case biweekly_4:
541                 /* 2 * (ntimes-1) won't overflow an integer since
542                  * we make sure ntimes is < EOT
543                  */
544                 ltick = nextnwk_exacttime(ftick, 2 * (ntimes - 1));
545                 break;
546         case daily_4:
547                 ltick = nextnday_exacttime(ftick, ntimes - 1);
548                 break;
549         case monthly_4:
550                 tm = _XLocaltime(&ftick, localtime_buf);
551                 /*
552                  * calculate the last tick only if the date
553                  * is < 29; otherwise return EOT to force calculation
554                  */
555                 if (tm->tm_mday < 29)
556                         ltick = nextnmth_exactday(ftick, ntimes - 1);
557                 else
558                         ltick = EOT;
559                 break;
560         case yearly_4:
561                 /* 2038 is the last year that can be represented.
562                  * this check is to prevent (ntimes-1)*4 from integer overflow
563                  */
564                 if (ntimes > 2038)
565                         ltick = EOT;
566                 else {
567                         tm = _XLocaltime(&ftick, localtime_buf);
568                         if (tm->tm_mday == 29 && tm->tm_mon == 1)
569                                 ltick = nextnyear(ftick, (ntimes - 1) * 4);
570                         else
571                                 ltick = nextnyear(ftick, ntimes - 1);
572                 }
573                 break;
574         case nthWeekday_4:
575                 ltick = lastnthweekday(ftick, period.nth, ntimes - 1);
576                 break;
577         case everyNthDay_4:
578                 ltick = nextnday_exacttime(ftick, period.nth * 
579                         (((ntimes+(period.nth-1))/period.nth) - 1));
580                 break;
581         case everyNthWeek_4:
582                 ltick = nextnwk_exacttime(ftick, period.nth *
583                         (((ntimes+(period.nth-1))/period.nth) - 1));
584                 break;
585         case everyNthMonth_4:
586                 tm = _XLocaltime(&ftick, localtime_buf);
587                 if (tm->tm_mday < 29)
588                         ltick = nextnmth_exactday(ftick, period.nth *
589                                 (((ntimes+(period.nth-1))/period.nth) -1));
590                 else
591                         ltick = EOT;
592                 break;
593         case monThruFri_4:
594         case monWedFri_4:
595         case tueThur_4:
596         case daysOfWeek_4:
597                 tm = _XLocaltime(&ftick, localtime_buf);
598
599                 /* (ntimes-1)*daysperweek+(lastapptofweek-fstapptofFstweek) */
600                 if (period.period == daysOfWeek_4)
601                         dltick = ftick +
602                                 ((double)(ntimes - 1) * 7 +
603                                 lastapptofweek((u_int)period.nth) - tm->tm_wday)
604                                 * daysec;
605                 else if (period.period == tueThur_4)
606                         dltick = ftick +
607                                 ((double)(ntimes - 1) * 7 + (4 - tm->tm_wday)) *
608                                 daysec;
609                 else
610                         dltick = ftick +
611                                 ((double)(ntimes - 1) * 7 + (5 - tm->tm_wday)) *
612                                 daysec;
613
614                 if (dltick >= EOT || dltick < 0)
615                         ltick = EOT;
616                 else
617                         ltick = adjust_dst(ftick, (time_t)dltick);
618                 break;
619         default:
620                 break;
621         }
622         if(timeok(ltick))
623                 return(ltick);
624         else
625                 return(EOT);
626 }
627
628 /*
629  * Calculate the tick of next instance.
630  * If the calculated tick does not pass timeok(), EOT is returned.
631  */
632 extern time_t
633 _DtCms_next_tick_v4(time_t tick, Period_4 period)
634 {
635         time_t next = 0;
636         struct tm *tm;
637         _Xltimeparams localtime_buf;
638
639         switch(period.period) {
640                 case weekly_4:
641                         next = nextnwk_exacttime(tick, 1);
642                         break;
643                 case biweekly_4:
644                         next = nextnwk_exacttime(tick, 2);
645                         break;
646                 case daily_4:
647                         next = nextnday_exacttime(tick, 1);
648                         break;
649                 case monthly_4:
650                         next = nextmonth_exactday(tick);
651                         break;
652                 case yearly_4:
653                         tm = _XLocaltime(&tick, localtime_buf);
654                         if (tm->tm_mday == 29 && tm->tm_mon == 1)
655                                 next = nextnyear(tick, 4);
656                         else
657                                 next = nextnyear(tick, 1);
658                         break;
659                 case nthWeekday_4:
660                         next = nextnthweekday(tick, period.nth);
661                         break;
662                 case everyNthDay_4:
663                         next = nextnday_exacttime(tick, period.nth);
664                         break;
665                 case everyNthWeek_4:
666                         next = nextnwk_exacttime(tick, period.nth);
667                         break;
668                 case everyNthMonth_4:
669                         next = nextnmth_exactday(tick, period.nth);
670                         break;
671                 case monThruFri_4:
672                         next = nextmonTofri(tick);
673                         break;
674                 case monWedFri_4:
675                         next = nextmonwedfri(tick);
676                         break;
677                 case tueThur_4:
678                         next = nexttuethur(tick);
679                         break;
680                 case daysOfWeek_4:
681                         next = nextdaysofweek(tick, period.nth);
682                         break;
683                 default:
684                         break;
685         }
686         if(next != tick && timeok(next)) return(next);
687         else return(EOT);
688 }
689
690 /*
691  * Calculate the tick of previous instance.
692  * If the calculated tick does not pass timeok(), bot-1 is returned.
693  */
694 extern time_t
695 _DtCms_prev_tick_v4(time_t tick, Period_4 period)
696 {
697         time_t prev = 0;
698         struct tm *tm;
699         _Xltimeparams localtime_buf;
700
701         switch(period.period) {
702                 case weekly_4:
703                         prev = prevnwk_exacttime(tick, 1);
704                         break;
705                 case biweekly_4:
706                         prev = prevnwk_exacttime(tick, 2);
707                         break;
708                 case daily_4:
709                         prev = prevnday_exacttime(tick, 1);
710                         break;
711                 case monthly_4:
712                         prev = prevmonth_exactday(tick);
713                         break;
714                 case yearly_4:
715                         tm = _XLocaltime(&tick, localtime_buf);
716                         if (tm->tm_mday == 29 && tm->tm_mon == 1)
717                                 prev = prevnyear(tick, 4);
718                         else
719                                 prev = prevnyear(tick, 1);
720                         break;
721                 case nthWeekday_4:
722                         prev = prevnthweekday(tick, period.nth);
723                         break;
724                 case everyNthDay_4:
725                         prev = prevnday_exacttime(tick, period.nth);
726                         break;
727                 case everyNthWeek_4:
728                         prev = prevnwk_exacttime(tick, period.nth);
729                         break;
730                 case everyNthMonth_4:
731                         prev = prevnmth_exactday(tick, period.nth);
732                         break;
733                 case monThruFri_4:
734                         prev = prevmonTofri(tick);
735                         break;
736                 case monWedFri_4:
737                         prev = prevmonwedfri(tick);
738                         break;
739                 case tueThur_4:
740                         prev = prevtuethur(tick);
741                         break;
742                 case daysOfWeek_4:
743                         prev = prevdaysofweek(tick, period.nth);
744                         break;
745                 default:
746                         break;
747         }
748         if(prev != tick && timeok(prev)) return(prev);
749         else return(bot-1);
750 }
751
752 /*
753  * dont_care_cancel:
754  *      TRUE - it is a match regard_DtCmsIsLess the event is cancelled, 
755  *      FALSE - it is not a match if the event is cancelled.
756  */
757 extern int
758 _DtCms_in_repeater(Id_4 *key, Appt_4 *p_appt, boolean_t dont_care_cancel)
759 {
760         Period_4 period;
761         int     ordinal;
762         int     ntimes;
763         time_t  tick;
764
765         ntimes = _DtCms_get_ninstance_v4(p_appt);
766         period = p_appt->period;
767         tick = _DtCms_closest_tick_v4(key->tick, p_appt->appt_id.tick, period, &ordinal);
768         ordinal--;
769         while (++ordinal <= ntimes)
770         {
771                 if (tick > key->tick)           /* out-of-bound */
772                         break;
773                 if (tick == key->tick)
774                 {
775                         if (dont_care_cancel)
776                                 return (ordinal);
777                         if (!_DtCms_marked_4_cancellation (p_appt, ordinal))
778                                 return (ordinal);
779                 }
780                 tick = _DtCms_next_tick_v4 (tick, period);
781         }
782
783         return (0);
784 }
785
786 extern int
787 _DtCms_marked_4_cancellation(Appt_4 *a, int i)
788 {
789         Except_4 *p;
790
791         if (a==NULL)
792                 return(0);
793
794         p = a->exception;       /* in descending order for faster access */
795         while (p!=NULL) {
796                 if (i > p->ordinal)
797                         break;
798                 if (i == p->ordinal)
799                         return(1);
800                 p = p->next;
801         }
802         return(0);
803 }
804
805 extern time_t
806 next_ndays(time_t t, int n)
807 {
808         time_t next;
809         struct tm tm;
810         _Xltimeparams localtime_buf;
811
812         tm              = *_XLocaltime(&t, localtime_buf);
813         tm.tm_sec       = 0;
814         tm.tm_min       = 0;
815         tm.tm_hour      = 0;
816
817 #ifdef SVR4
818         next            = mktime(&tm);
819 #else
820         next            = timelocal(&tm);
821 #endif /* SVR4 */
822         next            = next + n * daysec;
823         next            = adjust_dst(t, next);
824         return(next);
825 }
826
827 extern time_t
828 next_nmins(time_t t, int m)
829 {
830         time_t next;
831         struct tm tm;
832         _Xltimeparams localtime_buf;
833
834         tm              = *_XLocaltime(&t, localtime_buf);
835         tm.tm_sec       = 0;
836         tm.tm_min       = 0;
837  
838         next            = mktime(&tm);
839         next            = next + m * minsec;
840         next            = adjust_dst(t, next);
841         return(next);
842 }
843
844 extern time_t
845 _DtCmsBeginOfDay(time_t t)
846 {
847         struct tm tm;
848         _Xltimeparams localtime_buf;
849
850         tm              =  *_XLocaltime(&t, localtime_buf);
851         tm.tm_sec       =  0;
852         tm.tm_min       =  0;
853         tm.tm_hour      =  0;
854         return(mktime(&tm));
855 }
856
857 extern time_t
858 _DtCmsTimeOfDay(time_t t)
859 {
860         struct tm tm;
861         _Xltimeparams localtime_buf;
862
863         tm              =  *_XLocaltime(&t, localtime_buf);
864         tm.tm_sec       =  0;
865         tm.tm_min       =  0;
866         tm.tm_hour      =  0;
867         return(t - mktime(&tm));
868 }
869
870 /*
871  * Given a weekmask, find the last appointment in the week
872  */
873 static int
874 lastapptofweek(u_int mask)
875 {
876         int n;
877
878         if (mask == 0)
879                 return -1;
880
881         for (n = -1; mask != 0; n++, mask = mask >> 1);
882
883         return n;
884 }
885
886 /*
887  * Given a weekmask and the first day of the week, calculate
888  * the number of times outstanding in the week.
889  */
890 static int
891 ntimes_this_week(u_int weekmask, int firstday)
892 {
893         int i, ntimes, weekdaymask = 1 << firstday;
894
895         if (weekmask == 0)
896                 return 0;
897
898         for (i=firstday, ntimes=0; i < 7; i++, weekdaymask <<= 1) {
899                 if (weekdaymask & weekmask)
900                         ntimes++;
901         }
902         return ntimes;
903 }
904
905 static boolean_t
906 nthweekdayofmonth(time_t t, int *nth)
907 {
908         struct tm tm, tm2, tmfirstday;
909         time_t  firstday;
910         _Xltimeparams localtime_buf;
911
912         tmfirstday = tm = *_XLocaltime(&t, localtime_buf);
913
914         *nth = (12 + tm.tm_mday - tm.tm_wday)/7;
915
916         tmfirstday.tm_hour = 0;
917         tmfirstday.tm_min = 0;
918         tmfirstday.tm_sec = 0;
919         tmfirstday.tm_mday = 1;
920         firstday = mktime(&tmfirstday);
921         tmfirstday = *_XLocaltime(&firstday, localtime_buf);
922
923         if (tm.tm_wday < tmfirstday.tm_wday)
924                 (*nth)--;
925
926         if (*nth < 4)
927                 return B_FALSE;
928         else {
929                 t += (7 * daysec);
930                 tm2 = *_XLocaltime(&t, localtime_buf);
931
932                 return((tm.tm_mon == tm2.tm_mon) ? B_FALSE : B_TRUE);
933         }
934 }
935
936 /*
937  * If the result falls beyond the system limit, -1 is returned by mktime().
938  */
939 static time_t
940 next_nmonth(time_t t, int n)
941 {
942         struct tm tm;
943         int     n12;
944         _Xltimeparams localtime_buf;
945
946         n12 = n/12;
947         n = n%12;
948
949         tm = *_XLocaltime(&t, localtime_buf);
950         tm.tm_hour=0;
951         tm.tm_min=0;
952         tm.tm_sec=0;
953         tm.tm_mday=1;
954         if (n12 > 0)
955                 tm.tm_year += n12;
956
957         if ((tm.tm_mon = tm.tm_mon + n) > 11) {
958                 tm.tm_mon -= 12;
959                 tm.tm_year++;
960         }
961
962         tm.tm_isdst = -1;
963         return(mktime(&tm));
964 }
965
966 static int
967 adjust_dst(time_t start, time_t next) 
968 {
969         struct tm oldt;
970         struct tm newt;
971         _Xltimeparams localtime_buf;
972
973         oldt = *_XLocaltime(&start, localtime_buf);
974         newt = *_XLocaltime(&next, localtime_buf);
975
976         if (oldt.tm_isdst == newt.tm_isdst) {
977                 return (next);
978         } else if (oldt.tm_isdst == 1) {
979                 return (next + (int)hrsec);
980         } else {
981                 return (next - (int)hrsec);
982         }
983 }
984
985 static time_t
986 prev_nmonth(time_t t, int n)
987 {
988         struct tm tm;
989         int     n12;
990         _Xltimeparams localtime_buf;
991
992         n12 = n/12;
993         n = n%12;
994
995         tm = *_XLocaltime(&t, localtime_buf);
996         tm.tm_hour=0;
997         tm.tm_min=0;
998         tm.tm_sec=0;
999         tm.tm_mday=1;
1000         if (n12 > 0)
1001                 tm.tm_year -= n12;
1002
1003         if ((tm.tm_mon = tm.tm_mon - n) < 0) {
1004                 tm.tm_mon += 12;
1005                 tm.tm_year--;
1006         }
1007 #ifdef SVR4
1008         tm.tm_isdst = -1;
1009         return(mktime(&tm));
1010 #else
1011         return(timelocal(&tm));
1012 #endif /* SVR4 */
1013 }
1014
1015 extern int
1016 leapyr(int y)
1017 {
1018         return
1019          (y % 4 == 0 && y % 100 !=0 || y % 400 == 0);
1020 }
1021
1022 extern int
1023 monthlength(Tick t)
1024 {
1025         int mon;
1026         struct tm tm;
1027         _Xltimeparams localtime_buf;
1028
1029         tm = *_XLocaltime(&t, localtime_buf);
1030         mon = tm.tm_mon;
1031         return(((mon==1) && leapyr(tm.tm_year+1900))? 29 : monthdays[mon]);
1032 }
1033
1034 extern int /* find dow(0-6) that 1st dom falls on */
1035 fdom(Tick t)
1036 {
1037         struct tm tm;
1038         _Xltimeparams localtime_buf;
1039
1040         tm              = *_XLocaltime(&t, localtime_buf);
1041         tm.tm_mday      = 1;
1042         tm.tm_isdst     = -1;
1043         t               = mktime(&tm);
1044         tm              = *_XLocaltime(&t, localtime_buf);
1045         return(tm.tm_wday);
1046 }
1047
1048 extern int
1049 ldom(Tick t /* find dow(0-6) that last dom falls on */ )
1050 {
1051         struct tm tm;
1052         _Xltimeparams localtime_buf;
1053
1054         tm              = *_XLocaltime(&t, localtime_buf);
1055         tm.tm_mday      = monthlength(t);
1056         tm.tm_isdst     = -1;
1057         t               = mktime(&tm);
1058         tm              = *_XLocaltime(&t, localtime_buf);
1059         return(tm.tm_wday);
1060 }
1061
1062 extern boolean_t
1063 _DtCmsInExceptionList(cms_entry *eptr, time_t tick)
1064 {
1065         CSA_date_time_entry     *dt = NULL;
1066         time_t                  time;
1067
1068         if (eptr->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value)
1069                 dt = eptr->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value->\
1070                         item.date_time_list_value;
1071
1072         for (; dt != NULL; dt = dt->next) {
1073                 if (_csa_iso8601_to_tick(dt->date_time, &time))
1074                         continue;
1075
1076                 if (time == tick)
1077                         return (B_TRUE);
1078         }
1079
1080         return (B_FALSE);
1081 }
1082
1083 static time_t
1084 nextnyear(time_t t, int n)
1085 {
1086         struct tm tm;
1087         _Xltimeparams localtime_buf;
1088
1089         tm      = *_XLocaltime(&t, localtime_buf);
1090         tm.tm_year += n;
1091 #ifdef SVR4
1092         return(mktime(&tm));
1093 #else
1094         return(timelocal(&tm));
1095 #endif /* SVR4 */
1096 }
1097
1098 static int
1099 timeok(time_t t)
1100 {
1101         int r =((t >= bot) &&(t <= eot));
1102         return(r);
1103 }
1104
1105 static time_t
1106 prevnyear(time_t t, int n)
1107 {
1108         struct tm tm;
1109         _Xltimeparams localtime_buf;
1110
1111         tm = *_XLocaltime(&t, localtime_buf);
1112         tm.tm_year -= n;
1113 #ifdef SVR4
1114         return(mktime(&tm));
1115 #else
1116         return(timelocal(&tm));
1117 #endif /* SVR4 */
1118 }
1119
1120 static time_t
1121 prevmonth_exactday(time_t t)
1122 {
1123         time_t prev; int day;
1124         struct tm tm;
1125         int sdelta;
1126         _Xltimeparams localtime_buf;
1127
1128         tm = *_XLocaltime(&t, localtime_buf);
1129         sdelta = tm.tm_hour * hrsec + tm.tm_min * minsec + tm.tm_sec; 
1130         day = tm.tm_mday;
1131         if((tm.tm_mday < 31 && tm.tm_mon != 0) ||       /* at least 30 days everywhere, except Feb.*/
1132            (tm.tm_mday==31 && tm.tm_mon==6)    ||       /* two 31s -- Jul./Aug.         */
1133            (tm.tm_mday==31 && tm.tm_mon==11)   ||       /* two 31s -- Dec./Jan.         */
1134            (tm.tm_mon == 0 &&(tm.tm_mday < 29  ||(tm.tm_mday==29 && leapyr(tm.tm_year+1900))))) {       
1135                 prev = t-monthseconds(previousmonth(t));
1136                 prev = adjust_dst(t, prev);
1137         }
1138         else {  /* brute force */
1139                 prev = previousmonth(previousmonth(t));         /* hop over the month */
1140                 tm = *_XLocaltime(&prev, localtime_buf);
1141                 tm.tm_mday = day;
1142 #ifdef SVR4
1143                 tm.tm_isdst = -1;
1144                 prev =(mktime(&tm)) + sdelta;
1145 #else
1146                 prev =(timelocal(&tm)) + sdelta;
1147 #endif /* SVR4 */
1148
1149         }
1150         return(prev);
1151 }
1152
1153 static time_t
1154 nextmonth_exactday(time_t t)
1155 {
1156         time_t next; int day;
1157         struct tm tm;
1158         int sdelta;
1159         _Xltimeparams localtime_buf;
1160
1161         tm = *_XLocaltime(&t, localtime_buf);
1162         sdelta = tm.tm_hour * hrsec + tm.tm_min * minsec + tm.tm_sec; 
1163         day = tm.tm_mday;
1164         if((tm.tm_mday < 31 && tm.tm_mon != 0) ||       /* at least 30 days everywhere, except Feb.*/
1165            (tm.tm_mday==31 && tm.tm_mon==6)    ||       /* two 31s -- Jul./Aug.         */
1166            (tm.tm_mday==31 && tm.tm_mon==11)   ||       /* two 31s -- Dec./Jan.         */
1167            (tm.tm_mon == 0 &&(tm.tm_mday < 29  ||(tm.tm_mday==29 && leapyr(tm.tm_year+1900))))) {       
1168                 next = t+monthseconds(t);
1169                 next = adjust_dst(t, next);
1170         }
1171         else {  /* brute force */
1172                 next = next_nmonth(t, 2);               /* hop over the month */
1173                 tm = *_XLocaltime(&next, localtime_buf);
1174                 tm.tm_mday = day;
1175 #ifdef SVR4
1176                 tm.tm_isdst = -1;
1177                 next = mktime(&tm) + sdelta;
1178 #else
1179                 next =(timelocal(&tm)) + sdelta;
1180 #endif /* SVR4 */
1181         }
1182         return(next);
1183 }
1184
1185 static time_t
1186 previousmonth(time_t t)
1187 {
1188         struct tm tm;
1189         _Xltimeparams localtime_buf;
1190
1191         tm = *_XLocaltime(&t, localtime_buf);
1192         tm.tm_hour=0;
1193         tm.tm_min=0;
1194         tm.tm_sec=0;
1195         if(tm.tm_mon==0) {
1196                 tm.tm_mon=11;
1197                 tm.tm_mday=1;
1198                 tm.tm_year--;
1199         }
1200         else {
1201                 tm.tm_mday=1;
1202                 tm.tm_mon--;
1203         }
1204 #ifdef SVR4
1205         tm.tm_isdst = -1;
1206         return(mktime(&tm));
1207 #else
1208         return(timelocal(&tm));
1209 #endif /* SVR4 */
1210 }
1211
1212 static int
1213 monthseconds(time_t t)
1214 {
1215         int mon;
1216         struct tm tm;
1217         _Xltimeparams localtime_buf;
1218         
1219         tm = *_XLocaltime(&t, localtime_buf);
1220         mon = tm.tm_mon;
1221         return(((mon==1) && leapyr(tm.tm_year+1900)) ?
1222                 29*daysec : monthsecs[mon]);
1223 }
1224
1225 /*
1226  * find the number of instances to be subtracted
1227  */
1228 static int
1229 get_ndelta(time_t startdate, Period_4 period, int ntimes)
1230 {
1231         struct tm *tm;
1232         int ndelta = 0;
1233         time_t lastdate;
1234         double dlastdate;
1235         _Xltimeparams localtime_buf;
1236
1237         if (period.enddate == 0)
1238                 return(ndelta);
1239
1240         /* find last day of the series */
1241         dlastdate = startdate + (double)wksec * (ntimes - 1); /* last week */
1242         if (dlastdate > EOT)
1243                 return(ndelta);
1244         else
1245                 lastdate = (time_t)dlastdate;
1246
1247         tm = _XLocaltime(&lastdate, localtime_buf);
1248         if (period.period == monThruFri_4 || period.period == monWedFri_4)
1249                 lastdate = lastdate + daysec * (5 - tm->tm_wday);
1250         else if (period.period == tueThur_4)
1251                 lastdate = lastdate + daysec * (4 - tm->tm_wday);
1252         else if (period.period == daysOfWeek_4)
1253                 lastdate = lastdate + daysec *
1254                         (lastapptofweek((u_int)period.nth) - tm->tm_wday);
1255
1256         if (period.enddate > lastdate)
1257                 return(ndelta);
1258
1259         tm = _XLocaltime(&period.enddate, localtime_buf);
1260         switch (period.period) {
1261         case monThruFri_4:
1262                 ndelta = 5 - tm->tm_wday;
1263                 break;
1264         case monWedFri_4:
1265                 if (tm->tm_wday < 3)
1266                         ndelta = 2;
1267                 else if (tm->tm_wday < 5)
1268                         ndelta = 1;
1269                 break;
1270         case tueThur_4:
1271                 if (tm->tm_wday < 2)
1272                         ndelta = 2;
1273                 else if (tm->tm_wday < 4)
1274                         ndelta = 1;
1275                 break;
1276         case daysOfWeek_4:
1277                 ndelta = ntimes_this_week((u_int)period.nth, tm->tm_wday) - 1;
1278                 break;
1279         }
1280         return(ndelta);
1281 }
1282
1283 static time_t
1284 lastnthweekday(time_t t, int nth, int ntimes)
1285 {
1286         struct tm tm1, tm2;
1287         time_t tick, ntick;
1288         int delta;
1289         int sdelta;
1290         _Xltimeparams localtime_buf;
1291
1292         /*
1293          * if nth is not specified, assume it's the
1294          * 4th week for the ambiguous case.
1295          */
1296         if (nth == 0) {
1297                 nthweekdayofmonth(t, &nth);
1298                 if (nth > 4)
1299                         nth = -1;
1300         }
1301
1302         tm1 = *_XLocaltime(&t, localtime_buf);
1303         sdelta = tm1.tm_hour * hrsec + tm1.tm_min * minsec + tm1.tm_sec; 
1304
1305         if (nth > 0) {
1306                 if ((tick = next_nmonth(t, ntimes)) == EOT || tick < 0)
1307                         return(EOT);
1308
1309                 tm2 = *_XLocaltime(&tick, localtime_buf);
1310
1311                 delta = tm1.tm_wday - tm2.tm_wday;
1312                 if (delta < 0)
1313                         delta += 7;
1314
1315                 ntick = tick + (((nth - 1) * 7 + delta) * daysec) + sdelta;
1316         } else {
1317                 if ((tick = next_nmonth(t, ntimes + 1)) == EOT || tick < 0)
1318                         return(EOT);
1319
1320                 tm2 = *_XLocaltime(&tick, localtime_buf);
1321
1322                 delta = tm2.tm_wday - tm1.tm_wday;
1323                 if (tm1.tm_wday >= tm2.tm_wday)
1324                         delta += 7;
1325
1326                 ntick = tick - (delta * daysec) + sdelta;
1327         }
1328         ntick = adjust_dst(tick, ntick);
1329
1330         return (ntick);
1331 }
1332
1333 static time_t
1334 nextnthweekday(time_t t, int nth)
1335 {
1336         struct tm tm1, tm2;
1337         time_t tick, ntick;
1338         int delta;
1339         int sdelta;
1340         _Xltimeparams localtime_buf;
1341
1342         /*
1343          * if nth is not specified, assume it's the
1344          * 4th week for the ambiguous case.
1345          */
1346         if (nth == 0) {
1347                 nthweekdayofmonth(t, &nth);
1348                 if (nth > 4)
1349                         nth = -1;
1350         }
1351
1352         tm1 = *_XLocaltime(&t, localtime_buf);
1353         sdelta = tm1.tm_hour * hrsec + tm1.tm_min * minsec + tm1.tm_sec; 
1354
1355         if (nth > 0) {
1356                 tick = next_nmonth(t, 1);
1357                 tm2 = *_XLocaltime(&tick, localtime_buf);
1358
1359                 delta = tm1.tm_wday - tm2.tm_wday;
1360                 if (delta < 0)
1361                         delta += 7;
1362
1363                 ntick = tick + (((nth - 1) * 7 + delta) * daysec) + sdelta;
1364         } else {
1365                 tick = next_nmonth(t, 2);
1366                 tm2 = *_XLocaltime(&tick, localtime_buf);
1367
1368                 delta = tm2.tm_wday - tm1.tm_wday;
1369                 if (tm1.tm_wday >= tm2.tm_wday)
1370                         delta += 7;
1371
1372                 ntick = tick - (delta * daysec) + sdelta;
1373         }
1374         ntick = adjust_dst(tick, ntick);
1375
1376         return (ntick);
1377 }
1378
1379 static time_t
1380 prevnthweekday(time_t t, int nth)
1381 {
1382         struct tm tm1, tm2;
1383         time_t tick, ptick;
1384         int delta;
1385         int sdelta;
1386         _Xltimeparams localtime_buf;
1387
1388         /*
1389          * if nth is not specified, assume it's the
1390          * 4th week for the ambiguous case.
1391          */
1392         if (nth == 0) {
1393                 nthweekdayofmonth(t, &nth);
1394                 if (nth > 4)
1395                         nth = -1;
1396         }
1397
1398         tm1 = *_XLocaltime(&t, localtime_buf);
1399         sdelta = tm1.tm_hour * hrsec + tm1.tm_min * minsec + tm1.tm_sec; 
1400
1401         if (nth > 0) {
1402                 tick = prev_nmonth(t, 1);
1403                 tm2 = *_XLocaltime(&tick, localtime_buf);
1404
1405                 delta = tm1.tm_wday - tm2.tm_wday;
1406                 if (delta < 0)
1407                         delta += 7;
1408
1409                 ptick = tick + (((nth - 1) * 7 + delta) * daysec) + sdelta;
1410         } else {
1411                 tick = prev_nmonth(next_nmonth(t, 1), 1);
1412                 tm2 = *_XLocaltime(&tick, localtime_buf);
1413
1414                 delta = tm2.tm_wday - tm1.tm_wday;
1415                 if (tm1.tm_wday >= tm2.tm_wday)
1416                         delta += 7;
1417
1418                 ptick = tick - (delta * daysec) + sdelta;
1419         }
1420         ptick = adjust_dst(tick, ptick);
1421
1422         return (ptick);
1423 }
1424
1425 /* use double in this routine to avoid integer overflow
1426  * in case n is very large.
1427  */
1428 static time_t
1429 nextnday_exacttime(time_t t, int n)
1430 {
1431         double next;
1432
1433         next    = t + (double)n * daysec;
1434         if (next >= EOT || next < 0)
1435                 return(EOT);
1436         else {
1437                 next = adjust_dst(t, (time_t)next);
1438                 return((time_t)next);
1439         }
1440 }
1441
1442 /*
1443  * This is defined in the private library and is used also by the front
1444  * end -- should it be here?
1445  */
1446 static time_t
1447 prevnday_exacttime(time_t t, int n)
1448 {
1449         time_t prev;
1450
1451         prev    = t - (n * daysec);
1452         prev    = adjust_dst(t, prev);
1453         return(prev);
1454 }
1455
1456 /* use double in this routine to avoid integer overflow
1457  * in case n is very large.
1458  */
1459 static time_t
1460 nextnwk_exacttime(time_t t, int n)
1461 {
1462         double next;
1463
1464         next    = t + (double)n * 7 * daysec;
1465         if (next >= EOT || next < 0)
1466                 return(EOT);
1467         else {
1468                 next = adjust_dst(t, (time_t)next);
1469                 return((time_t)next);
1470         }
1471 }
1472
1473 static time_t
1474 prevnwk_exacttime(time_t t, int n)
1475 {
1476         time_t prev;
1477
1478         prev    = t - n * 7 * daysec;
1479         prev    = adjust_dst(t, prev);
1480         return(prev);
1481 }
1482
1483 static time_t
1484 nextnmth_exactday(time_t t, int n)
1485 {
1486         struct tm tm1, tm2;
1487         boolean_t done = B_FALSE;
1488         time_t next;
1489         _Xltimeparams localtime_buf;
1490
1491         tm1 = *_XLocaltime(&t, localtime_buf);
1492         while (!done) {
1493                 if ((next = next_nmonth(t, n)) == EOT || next < 0)
1494                         return(EOT);
1495
1496                 tm2 = *_XLocaltime(&next, localtime_buf);
1497
1498                 /* 1. at least 30 days except feb
1499                  * 2. 2/29 on leap year
1500                  * 3. 31st on the appropriate month
1501                  */
1502                 if ((tm1.tm_mday < 31 && tm2.tm_mon != 1) ||
1503                     (tm2.tm_mon == 1 && (tm1.tm_mday < 29 ||
1504                       (tm1.tm_mday == 29 && leapyr(tm2.tm_year + 1900)))) ||
1505                     (tm1.tm_mday == 31 && ((tm2.tm_mon > 6 && tm2.tm_mon % 2) ||
1506                       ((tm2.tm_mon <= 6 && (tm2.tm_mon % 2 == 0)))))) {
1507                         tm2.tm_sec = tm1.tm_sec;
1508                         tm2.tm_min = tm1.tm_min;
1509                         tm2.tm_hour = tm1.tm_hour;
1510                         tm2.tm_mday = tm1.tm_mday;
1511                         done = B_TRUE;
1512                 } else
1513                         t = next;
1514         }
1515
1516 #ifdef SVR4
1517         tm2.tm_isdst = -1;
1518         next = mktime(&tm2);
1519 #else
1520         next = (timelocal(&tm2));
1521 #endif
1522         return(next);
1523 }
1524
1525 static time_t
1526 prevnmth_exactday(time_t t, int n)
1527 {
1528         struct tm tm1, tm2;
1529         boolean_t done = B_FALSE;
1530         time_t prev;
1531         _Xltimeparams localtime_buf;
1532
1533         tm1 = *_XLocaltime(&t, localtime_buf);
1534         while (!done) {
1535                 prev = prev_nmonth(t, n);
1536                 tm2 = *_XLocaltime(&prev, localtime_buf);
1537
1538                 if ((tm1.tm_mday < 30 && tm2.tm_mon != 1) ||
1539                     (tm2.tm_mon == 1 && (tm1.tm_mday < 29 ||
1540                       (tm1.tm_mday == 29 && leapyr(tm2.tm_year + 1900)))) ||
1541                     (tm1.tm_mday == 31 && ((tm2.tm_mon > 6 && tm2.tm_mon % 2) ||
1542                       ((tm2.tm_mon <= 6 && (tm2.tm_mon % 2 == 0)))))) {
1543                         tm2.tm_sec = tm1.tm_sec;
1544                         tm2.tm_min = tm1.tm_min;
1545                         tm2.tm_hour = tm1.tm_hour;
1546                         tm2.tm_mday = tm1.tm_mday;
1547                         done = B_TRUE;
1548                 } else
1549                         t = prev;
1550         }
1551
1552 #ifdef SVR4
1553         tm2.tm_isdst = -1;
1554         prev = mktime(&tm2);
1555 #else
1556         prev = (timelocal(&tm2));
1557 #endif
1558         return(prev);
1559 }
1560
1561 static time_t
1562 nextmonTofri(time_t t)
1563 {
1564         struct tm *tm;
1565         time_t next;
1566         _Xltimeparams localtime_buf;
1567
1568         tm = _XLocaltime(&t, localtime_buf);
1569
1570         if (tm->tm_wday < 5)
1571                 next = t + (int)daysec;
1572         else
1573                 next = t + (int)daysec * (8 - tm->tm_wday);
1574
1575         next = adjust_dst(t, next);
1576         return(next);
1577 }
1578
1579 static time_t
1580 prevmonTofri(time_t t)
1581 {
1582         struct tm *tm;
1583         time_t prev;
1584         _Xltimeparams localtime_buf;
1585
1586         tm = _XLocaltime(&t, localtime_buf);
1587
1588         if (tm->tm_wday > 1)
1589                 prev = t - (int)daysec;
1590         else
1591                 prev = t - (int)daysec * (2 + tm->tm_wday);
1592
1593         prev = adjust_dst(t, prev);
1594         return(prev);
1595 }
1596
1597 static time_t
1598 nextmonwedfri(time_t t)
1599 {
1600         struct tm *tm;
1601         time_t next;
1602         _Xltimeparams localtime_buf;
1603
1604         tm = _XLocaltime(&t, localtime_buf);
1605
1606         if (tm->tm_wday == 5)
1607                 next = t + (int)daysec * 3;
1608         else if (tm->tm_wday % 2 || tm->tm_wday == 6)
1609                 next = t + (int)daysec * 2;
1610         else
1611                 next = t + (int)daysec;
1612
1613         next = adjust_dst(t, next);
1614         return(next);
1615 }
1616
1617 static time_t
1618 prevmonwedfri(time_t t)
1619 {
1620         struct tm *tm;
1621         time_t prev;
1622         _Xltimeparams localtime_buf;
1623
1624         tm = _XLocaltime(&t, localtime_buf);
1625
1626         if (tm->tm_wday == 1)
1627                 prev = t - (int)daysec * 3;
1628         else if (tm->tm_wday % 2 || tm->tm_wday == 0)
1629                 prev = t - (int)daysec * 2;
1630         else
1631                 prev = t - (int)daysec;
1632
1633         prev = adjust_dst(t, prev);
1634         return(prev);
1635 }
1636
1637 static time_t
1638 nexttuethur(time_t t)
1639 {
1640         struct tm *tm;
1641         time_t next;
1642         _Xltimeparams localtime_buf;
1643
1644         tm = _XLocaltime(&t, localtime_buf);
1645
1646         if (tm->tm_wday < 4) {
1647                 if (tm->tm_wday % 2)
1648                         next = t + (int)daysec;
1649                 else
1650                         next = t + (int)daysec * 2;
1651         } else
1652                 next = t + (int)daysec * (9 - tm->tm_wday);
1653
1654         next = adjust_dst(t, next);
1655         return(next);
1656 }
1657
1658 static time_t
1659 prevtuethur(time_t t)
1660 {
1661         struct tm *tm;
1662         time_t prev;
1663         _Xltimeparams localtime_buf;
1664
1665         tm = _XLocaltime(&t, localtime_buf);
1666
1667         if (tm->tm_wday > 2) {
1668                 if (tm->tm_wday % 2)
1669                         prev = t - (int)daysec;
1670                 else
1671                         prev = t - (int)daysec * 2;
1672         } else
1673                 prev = t - (int)daysec * (3 + tm->tm_wday);
1674
1675         prev = adjust_dst(t, prev);
1676         return(prev);
1677 }
1678
1679 /*
1680  * the 7-bit mask should be put in the last 7 bits of the int
1681  */
1682 static time_t
1683 nextdaysofweek(time_t t, int weekmask)
1684 {
1685         unsigned int doublemask;
1686         struct tm *tm; 
1687         int i, ndays, daymask;
1688         time_t next;
1689         _Xltimeparams localtime_buf;
1690
1691         doublemask = weekmask | (weekmask << 7);
1692         tm = _XLocaltime(&t, localtime_buf);
1693         daymask = weekdaymasks[tm->tm_wday] << 1;
1694
1695         for (i = 0, ndays = 1; i < 7; i++) {
1696                 if (daymask & doublemask)
1697                         break;
1698                 else {
1699                         ndays++;
1700                         doublemask >>= 1;
1701                 }
1702         }
1703
1704         next = t + (int)daysec * ndays;
1705         next = adjust_dst(t, next);
1706         return(next);
1707 }
1708
1709 static time_t
1710 prevdaysofweek(time_t t, int weekmask)
1711 {
1712         unsigned int doublemask, daymask;
1713         struct tm *tm; 
1714         int i, ndays;
1715         time_t prev;
1716         _Xltimeparams localtime_buf;
1717
1718         doublemask = weekmask | (weekmask << 7);
1719         tm = _XLocaltime(&t, localtime_buf);
1720         daymask = weekdaymasks[tm->tm_wday] << 6; 
1721
1722         for (i = 0, ndays = 1; i < 7; i++) {
1723                 if (daymask & doublemask)
1724                         break;
1725                 else {
1726                         ndays++;
1727                         doublemask <<= 1;
1728                 }
1729         }
1730
1731         prev = t - (int)daysec * ndays;
1732         prev = adjust_dst(t, prev);
1733         return(prev);
1734 }
1735