1 /* $XConsortium: reminder.c /main/4 1995/11/09 12:48:20 rswiston $ */
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.
14 #include "cmscalendar.h"
24 /******************************************************************************
25 * forward declaration of static functions used within the file
26 ******************************************************************************/
28 static _DtCmsRemInfo *_BuildReminder4Entry(
33 _DtCmsRemInfo **active);
35 static void _InsertReminder(
39 static CSA_return_code _GetNextReminders(
42 cms_reminder_ref **rems);
44 static CSA_return_code _GetNextRemindersFromQ(
48 cms_reminder_ref **rems);
50 static cms_reminder_ref *_GetReminderRefFromInfo(
55 static void _RemoveReminderFromQ(
63 static _DtCmsRemInfo *_RemoveReminderFromList(
69 static long _GetNextActiveTick(
75 static void _UpdateReminderQ(_DtCmsRemQueue *remq, int qindex);
77 static CSA_return_code _GetNextRemindersFromList(
80 cms_reminder_ref **rf_r);
82 static void _DtCmsAddReminder4EntryToQ(
88 /*****************************************************************************
89 * extern functions used in the library
90 *****************************************************************************/
93 _DtCmsAddReminderV4(Rm_que **qhead, Rm_que *p_reminder)
98 if (p_reminder == NULL)
103 while (p_node != NULL)
105 if (p_reminder->remind_at < p_node->remind_at)
108 p_node = p_node->next;
111 if (p_prev == NULL) {
112 p_reminder->next = *qhead;
115 p_reminder->next = p_prev->next;
116 p_prev->next = p_reminder;
121 _DtCmsRemoveReminderV4(Rm_que **qhead, Rm_que *p_prev, Rm_que *p_curr)
124 *qhead = p_curr->next;
126 p_prev->next = p_curr->next;
127 return (p_curr->next);
140 Rm_que *p_reminder = NULL;
142 /* Ignore the expired or unqualified reminder. */
144 if (is_appointment(p_appt)) {
145 /* The event is not expired yet, build the reminder */
146 if (start_tick >= current_time)
148 if ((p_reminder = (Rm_que *)calloc(1, sizeof(Rm_que)))
152 p_reminder->remind_ord = 0;
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))
163 if ((p_reminder = (Rm_que *)calloc(1,
164 sizeof(Rm_que))) == NULL)
167 p_reminder->remind_ord = start_ord;
171 /* Event is expired, advance to next event */
172 start_tick = _DtCms_next_tick_v4 (start_tick, period);
177 if (p_reminder != NULL) {
178 p_reminder->remind_at = start_tick;
179 p_reminder->appt = p_appt;
180 p_reminder->attr = p_attr;
187 _DtCmsGetReminderInfoV4(Rm_que *original)
191 if (original == NULL)
194 if ((copy = calloc(1, sizeof(Reminder_4))) != NULL) {
195 copy->tick = original->remind_at;
198 if ((copy->attr.attr = strdup(original->attr->attr)) == NULL) {
203 if ((copy->attr.value = strdup(original->attr->value)) == NULL)
205 _DtCm_free_reminder4(copy);
209 if ((copy->attr.clientdata = strdup(original->attr->clientdata))
211 _DtCm_free_reminder4(copy);
215 copy->attr.next = NULL;
217 copy->appt_id.tick = copy->tick + atol (copy->attr.value);
218 copy->appt_id.key = original->appt->appt_id.key;
225 _DtCmsPrintReminderListV4(Rm_que *qhead)
227 Rm_que *p_node = qhead;
233 fprintf (stderr, "--- Active Reminder Queue ---\n");
234 while (p_node != NULL) {
236 if (temp = strchr(p_node->appt->what, '\n'))
239 fprintf(stderr, "%s (%d) %s: %s\n", ctime(&p_node->remind_at),
240 p_node->remind_ord, p_node->attr->attr,
246 p_node = p_node->next;
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
258 _DtCmsObsoleteReminderV4(
262 boolean_t delforward)
267 Rm_que *p_hdr = NULL;
271 while (p_node != NULL) {
273 if ((p_node->appt != p_appt) ||
274 ((ord != 0) && (ord != p_node->remind_ord)) ||
275 ((ord != 0) && delforward && p_node->remind_ord < ord)) {
277 p_next = p_node->next;
280 /* Found the obsolete reminder. */
281 p_next = _DtCmsRemoveReminderV4 (qhead,p_prev,p_node);
286 /* Chain the obsolete reminders together to
287 * re-calculate the new reminders.
289 p_node->next = p_hdr;
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.
304 while (p_hdr != NULL) {
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);
316 #define _DtCms_NUM_REMINDERS 4
319 _DtCmsAddReminders4Entry(
320 _DtCmsRemQueue **remq,
324 _DtCmsRemQueue *queue;
328 if ((queue = (_DtCmsRemQueue *)calloc(1,
329 sizeof(_DtCmsRemQueue))) == NULL)
332 /* initialize queues for the cde defined reminders */
333 if ((queue->aindex = (int *)malloc(sizeof(int) *
334 _DtCms_NUM_REMINDERS)) == NULL) {
339 if ((queue->names = (char **)malloc(sizeof(char *) *
340 _DtCms_NUM_REMINDERS)) == NULL) {
346 if ((queue->active = (_DtCmsRemInfo **)calloc(1,
347 sizeof(_DtCmsRemInfo) * _DtCms_NUM_REMINDERS)) == NULL) {
354 if ((queue->oldhead = (_DtCmsRemInfo **)calloc(1,
355 sizeof(_DtCmsRemInfo) * _DtCms_NUM_REMINDERS)) == NULL) {
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;
373 /* set cutoff to be half an hour earlier than now
374 * to compensate time difference between machines
376 queue->cutoff = time(0) - 60 * 30;
381 /* Add the qualified reminder attrs to the reminder queue */
382 for (i = 1; i <= entry->num_attrs; i++) {
384 if (entry->attrs[i].value == NULL ||
385 entry->attrs[i].value->type != CSA_VALUE_REMINDER)
388 _DtCmsAddReminder4EntryToQ(*remq, entry, i, lnode);
393 _DtCmsObsoleteReminder4Entry(
394 _DtCmsRemQueue *remq,
402 for (i = 0; i < remq->num_queues; i++) {
403 if (entry->attrs[remq->aindex[i]].value)
404 _RemoveReminderFromQ(remq, i, entry, lnode,
409 extern CSA_return_code
410 _DtCmsLookupReminder(
411 _DtCmsRemQueue *remq,
414 cms_attr_name *names,
415 cms_reminder_ref **rems)
418 CSA_return_code stat;
423 return (CSA_SUCCESS);
426 return (_GetNextReminders(remq, tick, rems));
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];
433 if ((stat = _GetNextRemindersFromQ(remq, i,
434 tick, rems)) != CSA_SUCCESS) {
436 _DtCmsFreeReminderRef(*rems);
443 return (CSA_SUCCESS);
447 _DtCmsFreeReminderRef(cms_reminder_ref *rems)
449 cms_reminder_ref *next;
451 while (rems != NULL) {
454 if (rems->reminder_name)
455 free(rems->reminder_name);
465 _DtCmsUpdateReminders(_DtCmsRemQueue *remq)
469 remq->cutoff = time(0) - 60*30;
471 for (i = 0; i < remq->num_queues; i++)
472 _UpdateReminderQ(remq, i);
475 /*****************************************************************************
476 * static functions used within the file
477 *****************************************************************************/
479 static _DtCmsRemInfo *
480 _BuildReminder4Entry(
485 _DtCmsRemInfo **active)
487 _DtCmsRemInfo *rptr, *rptr2;
490 RepeatEventState *restate;
492 if (active) *active = NULL;
494 if ((rptr = (_DtCmsRemInfo *)calloc(1, sizeof(_DtCmsRemInfo))) == NULL)
498 rptr->isentry = B_TRUE;
499 rptr->data.e = entry;
500 rptr->rem.i = entry->attrs[aindex].name.num;
502 _csa_iso8601_to_duration(entry->attrs[aindex].value->item.\
503 reminder_value->lead_time, &lead);
506 rptr->starttime = entry->key.time;
507 rptr->runtime = entry->key.time - lead;
509 if (lnode->lasttick == 0) {
510 lnode->lasttick = LastTick(entry->key.time, lnode->re);
511 lnode->duration = _DtCmsGetDuration(entry);
513 rptr->lasttick = lnode->lasttick;
515 /* calculate first tick */
516 tick = _GetNextActiveTick(entry, entry->key.time,
517 lnode->lasttick, lnode->re);
519 rptr->starttime = tick;
520 rptr->runtime = tick - lead;
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
527 if (active && rptr->runtime < cutoff &&
528 (lnode->lasttick - lead >= cutoff) &&
529 (tick = _GetNextActiveTick(entry, cutoff + lead,
530 lnode->lasttick, lnode->re)) > 0)
532 if ((rptr2 = (_DtCmsRemInfo *)calloc(1,
533 sizeof(_DtCmsRemInfo))) == NULL) {
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;
554 _DtCmsRemInfo **head,
557 _DtCmsRemInfo *rptr, *prev;
559 for (rptr = *head, prev = NULL; rptr != NULL;
560 prev = rptr, rptr = rptr->next) {
561 if (rem->runtime < rptr->runtime)
580 static CSA_return_code
581 _GetNextReminders(_DtCmsRemQueue *remq, time_t tick, cms_reminder_ref **rems)
583 CSA_return_code stat;
584 cms_reminder_ref *rptr, *head, *tail;
587 for (i = 0, head = NULL; i < remq->num_queues; i++) {
590 if ((stat = _GetNextRemindersFromQ(remq, i, tick, &rptr))
593 _DtCmsFreeReminderRef(head);
602 else if (rptr->runtime == head->runtime) {
603 /* combine the list */
604 for (; tail->next != NULL; tail = tail->next);
608 } else if (rptr->runtime < head->runtime) {
609 _DtCmsFreeReminderRef(head);
612 _DtCmsFreeReminderRef(rptr);
619 return (CSA_SUCCESS);
622 static CSA_return_code
623 _GetNextRemindersFromQ(
624 _DtCmsRemQueue *remq,
627 cms_reminder_ref **rems)
629 CSA_return_code stat;
630 cms_reminder_ref *rem = *rems;
632 if (tick >= remq->cutoff)
633 return (_GetNextRemindersFromList(remq->active[qindex], tick,
636 if ((stat = _GetNextRemindersFromList(remq->oldhead[qindex],
637 tick, rems)) == CSA_SUCCESS && rem == *rems)
638 return (_GetNextRemindersFromList(remq->active[qindex],
645 static cms_reminder_ref *
646 _GetReminderRefFromInfo(_DtCmsRemInfo *rem, time_t starttime, time_t runtime)
648 cms_reminder_ref *rptr;
649 cms_entry *entry = rem->data.e;
652 if ((rptr = (cms_reminder_ref *)calloc(1, sizeof(cms_reminder_ref)))
656 if ((rptr->reminder_name = strdup(entry->attrs[rem->rem.i].name.name))
662 size = entry->attrs[CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I].value->item.\
663 opaque_data_value->size;
665 if ((rptr->entryid = malloc(size)) == NULL) {
666 free(rptr->reminder_name);
670 strncpy(rptr->entryid, (char *)entry->attrs\
671 [CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I].value->item.\
672 opaque_data_value->data, size);
674 rptr->key.id = entry->key.id;
675 rptr->key.time = starttime ? starttime : rem->starttime;
677 rptr->runtime = runtime ? runtime : rem->runtime;
683 _RemoveReminderFromQ(
684 _DtCmsRemQueue *remq,
687 List_node *lnode, /* zero for one time entries */
691 _DtCmsRemInfo *rptr, *rptr1 = NULL, *rptr2 = NULL;
694 boolean_t do_old, do_new;
695 RepeatEventState *restate;
697 _csa_iso8601_to_duration(entry->attrs[remq->aindex[qindex]].value->\
698 item.reminder_value->lead_time, &lead);
701 if (entry->key.time - lead < remq->cutoff)
702 _RemoveReminderFromList(&remq->oldhead[qindex], entry,
705 _RemoveReminderFromList(&remq->active[qindex], entry,
708 tick = ClosestTick(entry->key.time, entry->key.time, lnode->re,
710 if (do_old = (tick - lead < remq->cutoff))
711 rptr1 = _RemoveReminderFromList(&remq->oldhead[qindex],
712 entry, starttime, delfwd);
714 if (do_new = (lnode->lasttick - lead >= remq->cutoff))
715 rptr2 = _RemoveReminderFromList(&remq->active[qindex],
716 entry, starttime, delfwd);
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
725 _RemoveReminderFromList(
726 &remq->oldhead[qindex], entry,
729 _RemoveReminderFromList(
730 &remq->active[qindex], entry,
734 _DtCmsAddReminder4EntryToQ(remq, entry,
735 remq->aindex[qindex], rptr->lnode);
737 if (rptr1) free(rptr1);
738 if (rptr2) free(rptr2);
743 static _DtCmsRemInfo *
744 _RemoveReminderFromList(
745 _DtCmsRemInfo **qhead,
750 _DtCmsRemInfo *rptr, *prev;
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))
765 prev->next = rptr->next;
767 if (starttime == 0 || delfwd) {
779 * move all reminders in active queue whose runtime < cutoff
783 _UpdateReminderQ(_DtCmsRemQueue *remq, int qindex)
785 _DtCmsRemInfo *rptr, *nptr;
788 RepeatEventState *restate;
790 for (; (rptr = remq->active[qindex]) != NULL &&
791 rptr->runtime < remq->cutoff; ) {
793 remq->active[qindex] = rptr->next;
797 if (rptr->lnode == NULL)
798 _InsertReminder(&remq->oldhead[qindex], rptr);
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);
805 if (tick == rptr->starttime) {
806 /* add this to old queue */
807 _InsertReminder(&remq->oldhead[qindex], rptr);
809 /* make copy of rptr */
810 nptr = (_DtCmsRemInfo *)calloc(1,
811 sizeof(_DtCmsRemInfo));
816 if ((rptr->lasttick - lead < remq->cutoff) ||
817 (tick = _GetNextActiveTick(entry,
818 remq->cutoff + lead, rptr->lasttick,
819 rptr->lnode->re)) <= 0)
823 rptr->starttime = tick;
824 rptr->runtime = tick - lead;
825 _InsertReminder(&remq->active[qindex], rptr);
838 RepeatEventState *restate;
841 for (tick = ClosestTick(target, entry->key.time, re, &restate);
843 tick = NextTick(tick, entry->key.time, re, restate))
845 if (tick <= 0 || !_DtCmsInExceptionList(entry, tick))
852 * The reminders found will be linked with the list
855 static CSA_return_code
856 _GetNextRemindersFromList(
857 _DtCmsRemInfo *rlist,
859 cms_reminder_ref **rf_r)
861 cms_reminder_ref *rptr, *head = NULL, *tail;
865 /* get from active queue */
866 for (; rlist != NULL; rlist = rlist->next) {
868 if (giventime < rlist->runtime)
870 else if (rlist->lnode) {
872 /* check the next active tick */
873 lead = rlist->starttime - rlist->runtime;
874 tick = _GetNextActiveTick(rlist->data.e,
875 giventime + lead + 1, rlist->lasttick,
879 (head == NULL || (tick-lead <= head->runtime)))
881 if ((rptr = _GetReminderRefFromInfo(rlist, tick,
882 tick-lead)) == NULL) {
884 _DtCmsFreeReminderRef(head);
885 return (CSA_E_INSUFFICIENT_MEMORY);
890 else if (head->runtime = rptr->runtime) {
894 _DtCmsFreeReminderRef(head);
903 if (head->runtime > rlist->runtime) {
904 _DtCmsFreeReminderRef(head);
906 } else if (head->runtime < rlist->runtime)
910 /* now do lookup in the remaining list */
912 while (rlist != NULL) {
913 if (rptr = _GetReminderRefFromInfo(rlist, 0, 0)) {
917 if (head) _DtCmsFreeReminderRef(head);
918 return (CSA_E_INSUFFICIENT_MEMORY);
922 rlist->next->runtime == rlist->runtime)
932 for (tail = head; tail->next != NULL; tail = tail->next) ;
937 return (CSA_SUCCESS);
941 _DtCmsAddReminder4EntryToQ(
942 _DtCmsRemQueue *remq,
947 _DtCmsRemInfo *rptr, *rptr2;
950 if ((rptr = _BuildReminder4Entry(entry, aindex, lnode, remq->cutoff,
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);
959 _InsertReminder(&remq->oldhead[i], rptr);
962 _InsertReminder(&remq->active[i], rptr2);
967 if (i == remq->num_queues) {
968 /* expand the queue */