Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / server / reminder.c
1 /* $XConsortium: reminder.c /main/4 1995/11/09 12:48:20 rswiston $ */
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 <EUSCompat.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <time.h>
14 #include "cmscalendar.h"
15 #include "reminder.h"           
16 #include "appt4.h"
17 #include "repeat.h"
18 #include "v4ops.h"
19 #include "v5ops.h"
20 #include "rerule.h"
21 #include "reutil.h"
22 #include "iso8601.h"
23
24 /******************************************************************************
25  * forward declaration of static functions used within the file
26  ******************************************************************************/
27
28 static _DtCmsRemInfo *_BuildReminder4Entry(
29                         cms_entry       *entry,
30                         int             aindex,
31                         List_node       *lnode,
32                         time_t          cutoff,
33                         _DtCmsRemInfo   **active);
34
35 static void _InsertReminder(
36                         _DtCmsRemInfo   **head,
37                         _DtCmsRemInfo   *rem);
38
39 static CSA_return_code _GetNextReminders(
40                         _DtCmsRemQueue          *remq,
41                         time_t                  tick,
42                         cms_reminder_ref        **rems);
43
44 static CSA_return_code _GetNextRemindersFromQ(
45                         _DtCmsRemQueue          *remq,
46                         int                     qindex,
47                         time_t                  tick,
48                         cms_reminder_ref        **rems);
49
50 static cms_reminder_ref *_GetReminderRefFromInfo(
51                         _DtCmsRemInfo   *rem,
52                         time_t          starttime,
53                         time_t          runtime);
54
55 static void _RemoveReminderFromQ(
56                         _DtCmsRemQueue  *remq,
57                         int             qindex,
58                         cms_entry       *entry,
59                         List_node       *lnode,
60                         time_t          starttime,
61                         boolean_t       delfwd);
62
63 static _DtCmsRemInfo *_RemoveReminderFromList(
64                         _DtCmsRemInfo   **remq,
65                         cms_entry       *entry,
66                         time_t          starttime,
67                         boolean_t       delfwd);
68
69 static long _GetNextActiveTick(
70                         cms_entry       *entry,
71                         time_t          target,
72                         time_t          lasttick,
73                         RepeatEvent     *re);
74
75 static void _UpdateReminderQ(_DtCmsRemQueue *remq, int qindex);
76
77 static CSA_return_code _GetNextRemindersFromList(
78                         _DtCmsRemInfo           *rlist,
79                         time_t                  giventime,
80                         cms_reminder_ref        **rf_r);
81
82 static void _DtCmsAddReminder4EntryToQ(
83                         _DtCmsRemQueue  *remq,
84                         cms_entry       *entry,
85                         int             aindex,
86                         List_node       *lnode);
87
88 /*****************************************************************************
89  * extern functions used in the library
90  *****************************************************************************/
91
92 extern void
93 _DtCmsAddReminderV4(Rm_que **qhead, Rm_que *p_reminder)
94 {
95         Rm_que  *p_prev;
96         Rm_que  *p_node;
97
98         if (p_reminder == NULL)
99                 return;
100
101         p_prev = NULL;
102         p_node = *qhead;
103         while (p_node != NULL)
104         {
105                 if (p_reminder->remind_at < p_node->remind_at)
106                         break;
107                 p_prev = p_node;
108                 p_node = p_node->next;
109         }
110
111         if (p_prev == NULL) {
112                 p_reminder->next = *qhead;
113                 *qhead = p_reminder;
114         } else {
115                 p_reminder->next = p_prev->next;
116                 p_prev->next = p_reminder;
117         }
118 }
119
120 extern Rm_que *
121 _DtCmsRemoveReminderV4(Rm_que **qhead, Rm_que *p_prev, Rm_que *p_curr)
122 {
123         if (p_prev == NULL)
124                 *qhead = p_curr->next;
125         else
126                 p_prev->next = p_curr->next;
127         return (p_curr->next);
128 }
129
130 extern Rm_que *
131 build_reminder(
132         time_t  current_time,
133         Appt_4  *p_appt,
134         Attr_4  p_attr,
135         time_t  start_tick,
136         u_int   start_ord)
137 {
138         int     ntimes;
139         Period_4 period;
140         Rm_que  *p_reminder = NULL;
141
142         /* Ignore the expired or unqualified reminder. */
143         p_reminder = NULL;
144         if (is_appointment(p_appt)) {
145                 /* The event is not expired yet, build the reminder */
146                 if (start_tick >= current_time)
147                 {
148                         if ((p_reminder = (Rm_que *)calloc(1, sizeof(Rm_que)))
149                            == NULL)
150                                 return (NULL);
151
152                         p_reminder->remind_ord = 0;
153                 }
154         } else {
155                 period = p_appt->period;
156                 ntimes = _DtCms_get_ninstance_v4(p_appt);
157                 while (start_ord <= ntimes) {
158                         /* Event is not expired */
159                         if (start_tick >= current_time) {
160                                 /* Event is not cancelled */
161                                 if (!_DtCms_marked_4_cancellation (p_appt, start_ord))
162                                 {
163                                         if ((p_reminder = (Rm_que *)calloc(1,
164                                             sizeof(Rm_que))) == NULL)
165                                                 return (NULL);
166
167                                         p_reminder->remind_ord = start_ord;
168                                         break;
169                                 }
170                         }
171                         /* Event is expired, advance to next event */
172                         start_tick = _DtCms_next_tick_v4 (start_tick, period);
173                         start_ord++;
174                 }
175         }
176
177         if (p_reminder != NULL) {
178                 p_reminder->remind_at = start_tick;
179                 p_reminder->appt = p_appt;
180                 p_reminder->attr = p_attr;
181         }
182
183         return (p_reminder);
184 }
185
186 extern Reminder_4 *
187 _DtCmsGetReminderInfoV4(Rm_que  *original)
188 {
189         Reminder_4 *copy;
190
191         if (original == NULL)
192                 return (NULL);
193         
194         if ((copy = calloc(1, sizeof(Reminder_4))) != NULL) {
195                 copy->tick = original->remind_at;
196                 copy->next = NULL;
197
198                 if ((copy->attr.attr = strdup(original->attr->attr)) == NULL) {
199                         free(copy);
200                         return (NULL);
201                 }
202
203                 if ((copy->attr.value = strdup(original->attr->value)) == NULL)
204                 {
205                         _DtCm_free_reminder4(copy);
206                         return (NULL);
207                 }
208
209                 if ((copy->attr.clientdata = strdup(original->attr->clientdata))
210                     == NULL) {
211                         _DtCm_free_reminder4(copy);
212                         return (NULL);
213                 }
214
215                 copy->attr.next = NULL;
216
217                 copy->appt_id.tick = copy->tick + atol (copy->attr.value);
218                 copy->appt_id.key = original->appt->appt_id.key;
219         }
220
221         return (copy);
222 }
223
224 extern void
225 _DtCmsPrintReminderListV4(Rm_que *qhead)
226 {
227         Rm_que  *p_node = qhead;
228         char    *temp=NULL;
229
230         if (qhead == NULL)
231                 return;
232
233         fprintf (stderr, "--- Active Reminder Queue ---\n");
234         while (p_node != NULL) {
235
236                 if (temp = strchr(p_node->appt->what, '\n'))
237                         *temp = '\0';
238
239                 fprintf(stderr, "%s (%d) %s: %s\n", ctime(&p_node->remind_at),
240                         p_node->remind_ord, p_node->attr->attr,
241                         p_node->appt->what);
242
243                 if (temp)
244                         *temp = '\n';
245
246                 p_node = p_node->next;
247         }
248 }
249
250 /*
251  * Obsolete all reminders (iff ord == 0) whose parent appointment matches a
252  * given appointment.  If ord != 0, then obsolete all active reminders whose
253  * serving ordinal matches ord in additional to the matching of its parent
254  * appointment.  The reminder of the next available instance will be put on the
255  * reminder queue.
256  */
257 extern void
258 _DtCmsObsoleteReminderV4(
259         Rm_que **qhead, 
260         Appt_4 *p_appt, 
261         int ord, 
262         boolean_t delforward)
263 {
264         Rm_que *p_prev;
265         Rm_que *p_next;
266         Rm_que *p_node;
267         Rm_que *p_hdr = NULL;
268
269         p_prev = NULL;
270         p_node = *qhead;
271         while (p_node != NULL) {
272
273                 if ((p_node->appt != p_appt) ||
274                     ((ord != 0) && (ord != p_node->remind_ord)) ||
275                     ((ord != 0) && delforward && p_node->remind_ord < ord)) {
276
277                         p_next = p_node->next;
278
279                 } else {
280                         /* Found the obsolete reminder. */
281                         p_next = _DtCmsRemoveReminderV4 (qhead,p_prev,p_node);
282
283                         if (ord == 0)
284                                 free (p_node);
285                         else {
286                                 /* Chain the obsolete reminders together to
287                                  * re-calculate the new reminders.
288                                  */
289                                 p_node->next = p_hdr;
290                                 p_hdr = p_node;
291                         }
292
293                         p_node = p_prev;
294                 }
295
296                 p_prev = p_node;
297                 p_node = p_next;
298         }
299
300         /* Build the reminders of the next instance from obsoleted reminders.
301          * Note, we can't put this code in the above 'while'-loop because it
302          * may confuse the loop.
303          */
304         while (p_hdr != NULL) {
305
306                 p_next = p_hdr->next;
307                 p_node = build_reminder(p_hdr->remind_at+1,
308                                         p_hdr->appt, p_hdr->attr,
309                                         p_hdr->remind_at, ord);
310                 _DtCmsAddReminderV4 (qhead, p_node);
311                 free (p_hdr);
312                 p_hdr = p_next;
313         }
314 }
315
316 #define _DtCms_NUM_REMINDERS    4
317
318 extern void
319 _DtCmsAddReminders4Entry(
320         _DtCmsRemQueue  **remq,
321         cms_entry       *entry,
322         List_node       *lnode)
323 {
324         _DtCmsRemQueue  *queue;
325         int             i;
326
327         if (*remq == NULL) {
328                 if ((queue = (_DtCmsRemQueue *)calloc(1,
329                     sizeof(_DtCmsRemQueue))) == NULL)
330                         return;
331
332                 /* initialize queues for the cde defined reminders */
333                 if ((queue->aindex = (int *)malloc(sizeof(int) *
334                     _DtCms_NUM_REMINDERS)) == NULL) {
335                         free(queue);
336                         return;
337                 }
338
339                 if ((queue->names = (char **)malloc(sizeof(char *) *
340                     _DtCms_NUM_REMINDERS)) == NULL) {
341                         free(queue->aindex);
342                         free(queue);
343                         return;
344                 }
345
346                 if ((queue->active = (_DtCmsRemInfo **)calloc(1, 
347                     sizeof(_DtCmsRemInfo) * _DtCms_NUM_REMINDERS)) == NULL) {
348                         free(queue->names);
349                         free(queue->aindex);
350                         free(queue);
351                         return;
352                 }
353
354                 if ((queue->oldhead = (_DtCmsRemInfo **)calloc(1, 
355                     sizeof(_DtCmsRemInfo) * _DtCms_NUM_REMINDERS)) == NULL) {
356                         free(queue->active);
357                         free(queue->names);
358                         free(queue->aindex);
359                         free(queue);
360                         return;
361                 }
362
363                 queue->num_queues = _DtCms_NUM_REMINDERS;
364                 queue->aindex[0] = CSA_ENTRY_ATTR_AUDIO_REMINDER_I;
365                 queue->names[0] = CSA_ENTRY_ATTR_AUDIO_REMINDER;
366                 queue->aindex[1] = CSA_ENTRY_ATTR_FLASHING_REMINDER_I;
367                 queue->names[1] = CSA_ENTRY_ATTR_FLASHING_REMINDER;
368                 queue->aindex[2] = CSA_ENTRY_ATTR_MAIL_REMINDER_I;
369                 queue->names[2] = CSA_ENTRY_ATTR_MAIL_REMINDER;
370                 queue->aindex[3] = CSA_ENTRY_ATTR_POPUP_REMINDER_I;
371                 queue->names[3] = CSA_ENTRY_ATTR_POPUP_REMINDER;
372
373                 /* set cutoff to be half an hour earlier than now
374                  * to compensate time difference between machines
375                  */
376                 queue->cutoff = time(0) - 60 * 30;
377
378                 *remq = queue;
379         }
380
381         /* Add the qualified reminder attrs to the reminder queue */
382         for (i = 1; i <= entry->num_attrs; i++) {
383
384                 if (entry->attrs[i].value == NULL ||
385                     entry->attrs[i].value->type != CSA_VALUE_REMINDER) 
386                         continue;
387
388                 _DtCmsAddReminder4EntryToQ(*remq, entry, i, lnode);
389         }
390 }
391
392 extern void
393 _DtCmsObsoleteReminder4Entry(
394         _DtCmsRemQueue  *remq,
395         cms_entry       *entry,
396         List_node       *lnode,
397         time_t          starttime,
398         boolean_t       delfwd)
399 {
400         int     i;
401
402         for (i = 0; i < remq->num_queues; i++) {
403                 if (entry->attrs[remq->aindex[i]].value)
404                         _RemoveReminderFromQ(remq, i, entry, lnode,
405                                 starttime, delfwd);
406         }
407 }
408
409 extern CSA_return_code
410 _DtCmsLookupReminder(
411         _DtCmsRemQueue          *remq,
412         time_t                  tick,
413         uint                    num_names,
414         cms_attr_name           *names,
415         cms_reminder_ref        **rems)
416 {
417         int             i, j;
418         CSA_return_code stat;
419
420         *rems = NULL;
421
422         if (remq == NULL)
423                 return (CSA_SUCCESS);
424
425         if (num_names == 0)
426                 return (_GetNextReminders(remq, tick, rems));
427
428         for (i = 0; i < num_names; i++) {
429                 for (j = 0; j < remq->num_queues; j++) {
430                         if (strcmp(names[i].name, remq->names[j]) == 0) {
431                                 names[i].num = remq->aindex[j];
432
433                                 if ((stat = _GetNextRemindersFromQ(remq, i,
434                                     tick, rems)) != CSA_SUCCESS) {
435                                         if (*rems)
436                                                 _DtCmsFreeReminderRef(*rems);
437                                         return (stat);
438                                 }
439                         }
440                 }
441         }
442
443         return (CSA_SUCCESS);
444 }
445
446 extern void
447 _DtCmsFreeReminderRef(cms_reminder_ref *rems)
448 {
449         cms_reminder_ref        *next;
450
451         while (rems != NULL) {
452                 next = rems->next;
453
454                 if (rems->reminder_name)
455                         free(rems->reminder_name);
456                 if (rems->entryid)
457                         free(rems->entryid);
458
459                 free(rems);
460                 rems = next;
461         }
462 }
463
464 extern void
465 _DtCmsUpdateReminders(_DtCmsRemQueue *remq)
466 {
467         int     i;
468
469         remq->cutoff = time(0) - 60*30;
470
471         for (i = 0; i < remq->num_queues; i++)
472                 _UpdateReminderQ(remq, i);
473 }
474
475 /*****************************************************************************
476  * static functions used within the file
477  *****************************************************************************/
478
479 static _DtCmsRemInfo *
480 _BuildReminder4Entry(
481         cms_entry       *entry,
482         int             aindex,
483         List_node       *lnode,
484         time_t          cutoff,
485         _DtCmsRemInfo   **active)
486 {
487         _DtCmsRemInfo   *rptr, *rptr2;
488         time_t          lead;
489         time_t          tick;
490         RepeatEventState *restate;
491
492         if (active) *active = NULL;
493
494         if ((rptr = (_DtCmsRemInfo *)calloc(1, sizeof(_DtCmsRemInfo))) == NULL)
495                 return (NULL);
496
497         rptr->lnode = lnode;
498         rptr->isentry = B_TRUE;
499         rptr->data.e = entry;
500         rptr->rem.i = entry->attrs[aindex].name.num;
501
502         _csa_iso8601_to_duration(entry->attrs[aindex].value->item.\
503                         reminder_value->lead_time, &lead);
504
505         if (lnode == NULL) {
506                 rptr->starttime = entry->key.time;
507                 rptr->runtime = entry->key.time - lead;
508         } else {
509                 if (lnode->lasttick == 0) {
510                         lnode->lasttick = LastTick(entry->key.time, lnode->re);
511                         lnode->duration = _DtCmsGetDuration(entry);
512                 }
513                 rptr->lasttick = lnode->lasttick;
514
515                 /* calculate first tick */
516                 tick = _GetNextActiveTick(entry, entry->key.time,
517                         lnode->lasttick, lnode->re);
518
519                 rptr->starttime = tick;
520                 rptr->runtime = tick - lead;
521
522                 /* need to calculate the active tick if
523                  * 1. runtime of first tick is before cutoff time,
524                  * 2. runtime of last tick is after cutoff time, and
525                  * 3. there's reminders for instances after the cutoff time
526                  */
527                 if (active && rptr->runtime < cutoff &&
528                     (lnode->lasttick - lead >= cutoff) &&
529                     (tick = _GetNextActiveTick(entry, cutoff + lead,
530                     lnode->lasttick, lnode->re)) > 0)
531                 {
532                         if ((rptr2 = (_DtCmsRemInfo *)calloc(1,
533                             sizeof(_DtCmsRemInfo))) == NULL) {
534                                 free(rptr);
535                                 return (NULL);
536                         }
537
538                         rptr2->lnode = lnode;
539                         rptr2->isentry = B_TRUE;
540                         rptr2->data.e = entry;
541                         rptr2->rem.i = entry->attrs[aindex].name.num;
542                         rptr2->lasttick = lnode->lasttick;
543                         rptr2->starttime = tick;
544                         rptr2->runtime = tick - lead; 
545                         *active = rptr2;
546                 }
547         }
548
549         return (rptr);
550 }
551
552 static void
553 _InsertReminder(
554         _DtCmsRemInfo   **head,
555         _DtCmsRemInfo   *rem)
556 {
557         _DtCmsRemInfo   *rptr, *prev;
558
559         for (rptr = *head, prev = NULL; rptr != NULL;
560             prev = rptr, rptr = rptr->next) {
561                 if (rem->runtime < rptr->runtime)
562                         break;
563         }
564
565         if (rptr == NULL) {
566                 if (*head == NULL) {
567                         *head = rem;
568                 } else
569                         prev->next = rem;
570         } else {
571                 rem->next = rptr;
572
573                 if (prev == NULL)
574                         *head = rem;
575                 else
576                         prev->next = rem;
577         }
578 }
579
580 static CSA_return_code
581 _GetNextReminders(_DtCmsRemQueue *remq, time_t tick, cms_reminder_ref **rems)
582 {
583         CSA_return_code         stat;
584         cms_reminder_ref        *rptr, *head, *tail;
585         int                     i;
586
587         for (i = 0, head = NULL; i < remq->num_queues; i++) {
588                 rptr = NULL;
589
590                 if ((stat = _GetNextRemindersFromQ(remq, i, tick, &rptr))
591                     != CSA_SUCCESS) {
592                         if (head)
593                                 _DtCmsFreeReminderRef(head);
594                         return (stat);
595                 }
596
597                 if (rptr == NULL)
598                         continue;
599
600                 if (head == NULL)
601                         head = tail = rptr;
602                 else if (rptr->runtime == head->runtime) {
603                         /* combine the list */
604                         for (; tail->next != NULL; tail = tail->next);
605
606                         tail->next = rptr;
607                         tail = rptr;
608                 } else if (rptr->runtime < head->runtime) {
609                         _DtCmsFreeReminderRef(head);
610                         head = tail = rptr;
611                 } else
612                         _DtCmsFreeReminderRef(rptr);
613         }
614
615         if (head) {
616                 *rems = head;
617         }
618
619         return (CSA_SUCCESS);
620 }
621
622 static CSA_return_code
623 _GetNextRemindersFromQ(
624         _DtCmsRemQueue          *remq,
625         int                     qindex,
626         time_t                  tick,
627         cms_reminder_ref        **rems)
628 {
629         CSA_return_code         stat;
630         cms_reminder_ref        *rem = *rems;
631
632         if (tick >= remq->cutoff)
633                 return (_GetNextRemindersFromList(remq->active[qindex], tick,
634                         rems));
635         else {
636                 if ((stat = _GetNextRemindersFromList(remq->oldhead[qindex],
637                     tick, rems)) == CSA_SUCCESS && rem == *rems)
638                         return (_GetNextRemindersFromList(remq->active[qindex],
639                                 tick, rems));
640                 else
641                         return (stat);
642         }
643 }
644
645 static cms_reminder_ref *
646 _GetReminderRefFromInfo(_DtCmsRemInfo *rem, time_t starttime, time_t runtime)
647 {
648         cms_reminder_ref        *rptr;
649         cms_entry               *entry = rem->data.e;
650         int                     size;
651
652         if ((rptr = (cms_reminder_ref *)calloc(1, sizeof(cms_reminder_ref)))
653             == NULL)
654                 return (NULL);
655
656         if ((rptr->reminder_name = strdup(entry->attrs[rem->rem.i].name.name))
657             == NULL) {
658                 free(rptr);
659                 return (NULL);
660         }
661
662         size = entry->attrs[CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I].value->item.\
663                 opaque_data_value->size;
664
665         if ((rptr->entryid = malloc(size)) == NULL) {
666                 free(rptr->reminder_name);
667                 free(rptr);
668                 return (NULL);
669         } else
670                 strncpy(rptr->entryid, (char *)entry->attrs\
671                         [CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I].value->item.\
672                         opaque_data_value->data, size);
673
674         rptr->key.id = entry->key.id;
675         rptr->key.time = starttime ? starttime : rem->starttime;
676
677         rptr->runtime = runtime ? runtime : rem->runtime;
678
679         return (rptr);
680 }
681
682 static void
683 _RemoveReminderFromQ(
684         _DtCmsRemQueue  *remq,
685         int             qindex,
686         cms_entry       *entry,
687         List_node       *lnode, /* zero for one time entries */
688         time_t          starttime,
689         boolean_t       delfwd)
690 {
691         _DtCmsRemInfo   *rptr, *rptr1 = NULL, *rptr2 = NULL;
692         time_t          lead;
693         time_t          tick;
694         boolean_t       do_old, do_new;
695         RepeatEventState *restate;
696
697         _csa_iso8601_to_duration(entry->attrs[remq->aindex[qindex]].value->\
698                 item.reminder_value->lead_time, &lead);
699
700         if (lnode == 0) {
701                 if (entry->key.time - lead < remq->cutoff)
702                         _RemoveReminderFromList(&remq->oldhead[qindex], entry,
703                                 starttime, delfwd);
704                 else
705                         _RemoveReminderFromList(&remq->active[qindex], entry,
706                                 starttime, delfwd);
707         } else {
708                 tick = ClosestTick(entry->key.time, entry->key.time, lnode->re,
709                         &restate);
710                 if (do_old = (tick - lead < remq->cutoff))
711                         rptr1 = _RemoveReminderFromList(&remq->oldhead[qindex],
712                                 entry, starttime, delfwd);
713
714                 if (do_new = (lnode->lasttick - lead >= remq->cutoff))
715                         rptr2 = _RemoveReminderFromList(&remq->active[qindex],
716                                 entry, starttime, delfwd);
717
718                 if (rptr = rptr1 ? rptr1 : rptr2) {
719                         if (do_old && do_new) {
720                                 /* need to clean up the other queue
721                                  * since add reminder will add to both
722                                  * queue
723                                  */
724                                 if (rptr1 == NULL)
725                                         _RemoveReminderFromList(
726                                                 &remq->oldhead[qindex], entry,
727                                                 0, B_FALSE);
728                                 else
729                                         _RemoveReminderFromList(
730                                                 &remq->active[qindex], entry,
731                                                 0, B_FALSE);
732                         }
733
734                         _DtCmsAddReminder4EntryToQ(remq, entry,
735                                 remq->aindex[qindex], rptr->lnode);
736
737                         if (rptr1) free(rptr1);
738                         if (rptr2) free(rptr2);
739                 }
740         }
741 }
742
743 static _DtCmsRemInfo *
744 _RemoveReminderFromList(
745         _DtCmsRemInfo   **qhead,
746         cms_entry       *entry,
747         time_t          starttime,
748         boolean_t       delfwd)
749 {
750         _DtCmsRemInfo   *rptr, *prev;
751
752         /* find reminder in list */
753         for (rptr = *qhead, prev = NULL; rptr != NULL;
754             prev = rptr, rptr = rptr->next) {
755                 if (rptr->data.e != entry ||
756                     (starttime > 0 && !delfwd && rptr->starttime != starttime)||
757                     (starttime > 0 && delfwd && rptr->starttime < starttime))
758                 {
759                         continue;
760                 }
761
762                 if (prev == NULL)
763                         *qhead = rptr->next;
764                 else
765                         prev->next = rptr->next;
766
767                 if (starttime == 0 || delfwd) {
768                         free(rptr);
769                         return (NULL);
770                 } else
771                         return (rptr);
772
773         }
774
775         return (NULL);
776 }
777
778 /*
779  * move all reminders in active queue whose runtime < cutoff
780  * to old queue
781  */
782 static void
783 _UpdateReminderQ(_DtCmsRemQueue *remq, int qindex)
784 {
785         _DtCmsRemInfo   *rptr, *nptr;
786         cms_entry       *entry;
787         time_t          lead, tick;
788         RepeatEventState *restate;
789
790         for (; (rptr = remq->active[qindex]) != NULL &&
791             rptr->runtime < remq->cutoff; ) {
792
793                 remq->active[qindex] = rptr->next;
794
795                 rptr->next = NULL;
796
797                 if (rptr->lnode == NULL)
798                         _InsertReminder(&remq->oldhead[qindex], rptr);
799                 else {
800                         lead = rptr->starttime - rptr->runtime;
801                         entry = rptr->data.e;
802                         tick = ClosestTick(entry->key.time, entry->key.time,
803                                 rptr->lnode->re, &restate);
804
805                         if (tick == rptr->starttime) {
806                                 /* add this to old queue */
807                                 _InsertReminder(&remq->oldhead[qindex], rptr);
808
809                                 /* make copy of rptr */
810                                 nptr = (_DtCmsRemInfo *)calloc(1,
811                                         sizeof(_DtCmsRemInfo));
812                                 *nptr = *rptr;
813                                 rptr = nptr;
814                         }
815
816                         if ((rptr->lasttick - lead < remq->cutoff) ||
817                             (tick = _GetNextActiveTick(entry,
818                             remq->cutoff + lead, rptr->lasttick,
819                             rptr->lnode->re)) <= 0)
820                         {
821                                 free(rptr);
822                         } else {
823                                 rptr->starttime = tick;
824                                 rptr->runtime = tick - lead; 
825                                 _InsertReminder(&remq->active[qindex], rptr);
826                         }
827                 } 
828         }
829 }
830
831 static long
832 _GetNextActiveTick(
833         cms_entry       *entry,
834         time_t          target,
835         time_t          lasttick,
836         RepeatEvent     *re)
837 {
838         RepeatEventState        *restate;
839         time_t                  tick;
840
841         for (tick = ClosestTick(target, entry->key.time, re, &restate);
842             tick <= lasttick;
843             tick = NextTick(tick, entry->key.time, re, restate))
844         {
845                 if (tick <= 0 || !_DtCmsInExceptionList(entry, tick))
846                         break;
847         }
848         return (tick);
849 }
850
851 /*
852  * The reminders found will be linked with the list
853  * contained in rf_f
854  */
855 static CSA_return_code
856 _GetNextRemindersFromList(
857         _DtCmsRemInfo           *rlist,
858         time_t                  giventime,
859         cms_reminder_ref        **rf_r)
860 {
861         cms_reminder_ref        *rptr, *head = NULL, *tail;
862         time_t                  tick;
863         int                     lead;
864
865         /* get from active queue */
866         for (; rlist != NULL; rlist = rlist->next) {
867
868                 if (giventime < rlist->runtime)
869                         break;
870                 else if (rlist->lnode) {
871
872                         /* check the next active tick */
873                         lead = rlist->starttime - rlist->runtime;
874                         tick = _GetNextActiveTick(rlist->data.e,
875                                 giventime + lead + 1, rlist->lasttick,
876                                 rlist->lnode->re);
877
878                         if (tick > 0 &&
879                             (head == NULL || (tick-lead <= head->runtime)))
880                         {
881                                 if ((rptr = _GetReminderRefFromInfo(rlist, tick,
882                                     tick-lead)) == NULL) {
883                                         if (head)
884                                                 _DtCmsFreeReminderRef(head);
885                                         return (CSA_E_INSUFFICIENT_MEMORY);
886                                 }
887
888                                 if (head == NULL)
889                                         head = rptr;
890                                 else if (head->runtime = rptr->runtime) {
891                                         rptr->next = head;
892                                         head = rptr;
893                                 } else {
894                                         _DtCmsFreeReminderRef(head);
895                                         head = rptr;
896                                 }
897                         }
898                 }
899         }
900
901         if (rlist) {
902                 if (head) {
903                         if (head->runtime > rlist->runtime) {
904                                 _DtCmsFreeReminderRef(head);
905                                 head = NULL;
906                         } else if (head->runtime < rlist->runtime)
907                                 goto _done;
908                 }
909
910                 /* now do lookup in the remaining list */
911
912                 while (rlist != NULL) {
913                         if (rptr = _GetReminderRefFromInfo(rlist, 0, 0)) {
914                                 rptr->next = head;
915                                 head = rptr;
916                         } else {
917                                 if (head) _DtCmsFreeReminderRef(head);
918                                 return (CSA_E_INSUFFICIENT_MEMORY);
919                         }
920
921                         if (rlist->next &&
922                             rlist->next->runtime == rlist->runtime)
923                                 rlist = rlist->next;
924                         else
925                                 break;
926                 }
927         }
928
929 _done:
930         /* find tail */
931         if (head) {
932                 for (tail = head; tail->next != NULL; tail = tail->next) ;
933                 tail->next = *rf_r;
934                 *rf_r = head;
935         }
936
937         return (CSA_SUCCESS);
938 }
939
940 static void
941 _DtCmsAddReminder4EntryToQ(
942         _DtCmsRemQueue  *remq,
943         cms_entry       *entry,
944         int             aindex,
945         List_node       *lnode)
946 {
947         _DtCmsRemInfo   *rptr, *rptr2;
948         int             i;
949
950         if ((rptr = _BuildReminder4Entry(entry, aindex, lnode, remq->cutoff,
951             &rptr2)) == NULL)
952                 return;
953
954         for (i = 0; i < remq->num_queues; i++) {
955                 if (remq->aindex[i] == aindex) {
956                         if (rptr->runtime >= remq->cutoff)
957                                 _InsertReminder(&remq->active[i], rptr);
958                         else
959                                 _InsertReminder(&remq->oldhead[i], rptr);
960
961                         if (rptr2)
962                                 _InsertReminder(&remq->active[i], rptr2);
963                         break;
964                 }
965         }
966
967         if (i == remq->num_queues) {
968                 /* expand the queue */
969         }
970 }
971