2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: reminder.c /main/4 1995/11/09 12:48:20 rswiston $ */
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.
31 #include <EUSCompat.h>
36 #include "cmscalendar.h"
46 /******************************************************************************
47 * forward declaration of static functions used within the file
48 ******************************************************************************/
50 static _DtCmsRemInfo *_BuildReminder4Entry(
55 _DtCmsRemInfo **active);
57 static void _InsertReminder(
61 static CSA_return_code _GetNextReminders(
64 cms_reminder_ref **rems);
66 static CSA_return_code _GetNextRemindersFromQ(
70 cms_reminder_ref **rems);
72 static cms_reminder_ref *_GetReminderRefFromInfo(
77 static void _RemoveReminderFromQ(
85 static _DtCmsRemInfo *_RemoveReminderFromList(
91 static long _GetNextActiveTick(
97 static void _UpdateReminderQ(_DtCmsRemQueue *remq, int qindex);
99 static CSA_return_code _GetNextRemindersFromList(
100 _DtCmsRemInfo *rlist,
102 cms_reminder_ref **rf_r);
104 static void _DtCmsAddReminder4EntryToQ(
105 _DtCmsRemQueue *remq,
110 /*****************************************************************************
111 * extern functions used in the library
112 *****************************************************************************/
115 _DtCmsAddReminderV4(Rm_que **qhead, Rm_que *p_reminder)
120 if (p_reminder == NULL)
125 while (p_node != NULL)
127 if (p_reminder->remind_at < p_node->remind_at)
130 p_node = p_node->next;
133 if (p_prev == NULL) {
134 p_reminder->next = *qhead;
137 p_reminder->next = p_prev->next;
138 p_prev->next = p_reminder;
143 _DtCmsRemoveReminderV4(Rm_que **qhead, Rm_que *p_prev, Rm_que *p_curr)
146 *qhead = p_curr->next;
148 p_prev->next = p_curr->next;
149 return (p_curr->next);
162 Rm_que *p_reminder = NULL;
164 /* Ignore the expired or unqualified reminder. */
166 if (is_appointment(p_appt)) {
167 /* The event is not expired yet, build the reminder */
168 if (start_tick >= current_time)
170 if ((p_reminder = (Rm_que *)calloc(1, sizeof(Rm_que)))
174 p_reminder->remind_ord = 0;
177 period = p_appt->period;
178 ntimes = _DtCms_get_ninstance_v4(p_appt);
179 while (start_ord <= ntimes) {
180 /* Event is not expired */
181 if (start_tick >= current_time) {
182 /* Event is not cancelled */
183 if (!_DtCms_marked_4_cancellation (p_appt, start_ord))
185 if ((p_reminder = (Rm_que *)calloc(1,
186 sizeof(Rm_que))) == NULL)
189 p_reminder->remind_ord = start_ord;
193 /* Event is expired, advance to next event */
194 start_tick = _DtCms_next_tick_v4 (start_tick, period);
199 if (p_reminder != NULL) {
200 p_reminder->remind_at = start_tick;
201 p_reminder->appt = p_appt;
202 p_reminder->attr = p_attr;
209 _DtCmsGetReminderInfoV4(Rm_que *original)
213 if (original == NULL)
216 if ((copy = calloc(1, sizeof(Reminder_4))) != NULL) {
217 copy->tick = original->remind_at;
220 if ((copy->attr.attr = strdup(original->attr->attr)) == NULL) {
225 if ((copy->attr.value = strdup(original->attr->value)) == NULL)
227 _DtCm_free_reminder4(copy);
231 if ((copy->attr.clientdata = strdup(original->attr->clientdata))
233 _DtCm_free_reminder4(copy);
237 copy->attr.next = NULL;
239 copy->appt_id.tick = copy->tick + atol (copy->attr.value);
240 copy->appt_id.key = original->appt->appt_id.key;
247 _DtCmsPrintReminderListV4(Rm_que *qhead)
249 Rm_que *p_node = qhead;
255 fprintf (stderr, "--- Active Reminder Queue ---\n");
256 while (p_node != NULL) {
258 if (temp = strchr(p_node->appt->what, '\n'))
261 fprintf(stderr, "%s (%d) %s: %s\n", ctime(&p_node->remind_at),
262 p_node->remind_ord, p_node->attr->attr,
268 p_node = p_node->next;
273 * Obsolete all reminders (iff ord == 0) whose parent appointment matches a
274 * given appointment. If ord != 0, then obsolete all active reminders whose
275 * serving ordinal matches ord in additional to the matching of its parent
276 * appointment. The reminder of the next available instance will be put on the
280 _DtCmsObsoleteReminderV4(
284 boolean_t delforward)
289 Rm_que *p_hdr = NULL;
293 while (p_node != NULL) {
295 if ((p_node->appt != p_appt) ||
296 ((ord != 0) && (ord != p_node->remind_ord)) ||
297 ((ord != 0) && delforward && p_node->remind_ord < ord)) {
299 p_next = p_node->next;
302 /* Found the obsolete reminder. */
303 p_next = _DtCmsRemoveReminderV4 (qhead,p_prev,p_node);
308 /* Chain the obsolete reminders together to
309 * re-calculate the new reminders.
311 p_node->next = p_hdr;
322 /* Build the reminders of the next instance from obsoleted reminders.
323 * Note, we can't put this code in the above 'while'-loop because it
324 * may confuse the loop.
326 while (p_hdr != NULL) {
328 p_next = p_hdr->next;
329 p_node = build_reminder(p_hdr->remind_at+1,
330 p_hdr->appt, p_hdr->attr,
331 p_hdr->remind_at, ord);
332 _DtCmsAddReminderV4 (qhead, p_node);
338 #define _DtCms_NUM_REMINDERS 4
341 _DtCmsAddReminders4Entry(
342 _DtCmsRemQueue **remq,
346 _DtCmsRemQueue *queue;
350 if ((queue = (_DtCmsRemQueue *)calloc(1,
351 sizeof(_DtCmsRemQueue))) == NULL)
354 /* initialize queues for the cde defined reminders */
355 if ((queue->aindex = (int *)malloc(sizeof(int) *
356 _DtCms_NUM_REMINDERS)) == NULL) {
361 if ((queue->names = (char **)malloc(sizeof(char *) *
362 _DtCms_NUM_REMINDERS)) == NULL) {
368 if ((queue->active = (_DtCmsRemInfo **)calloc(1,
369 sizeof(_DtCmsRemInfo) * _DtCms_NUM_REMINDERS)) == NULL) {
376 if ((queue->oldhead = (_DtCmsRemInfo **)calloc(1,
377 sizeof(_DtCmsRemInfo) * _DtCms_NUM_REMINDERS)) == NULL) {
385 queue->num_queues = _DtCms_NUM_REMINDERS;
386 queue->aindex[0] = CSA_ENTRY_ATTR_AUDIO_REMINDER_I;
387 queue->names[0] = CSA_ENTRY_ATTR_AUDIO_REMINDER;
388 queue->aindex[1] = CSA_ENTRY_ATTR_FLASHING_REMINDER_I;
389 queue->names[1] = CSA_ENTRY_ATTR_FLASHING_REMINDER;
390 queue->aindex[2] = CSA_ENTRY_ATTR_MAIL_REMINDER_I;
391 queue->names[2] = CSA_ENTRY_ATTR_MAIL_REMINDER;
392 queue->aindex[3] = CSA_ENTRY_ATTR_POPUP_REMINDER_I;
393 queue->names[3] = CSA_ENTRY_ATTR_POPUP_REMINDER;
395 /* set cutoff to be half an hour earlier than now
396 * to compensate time difference between machines
398 queue->cutoff = time(0) - 60 * 30;
403 /* Add the qualified reminder attrs to the reminder queue */
404 for (i = 1; i <= entry->num_attrs; i++) {
406 if (entry->attrs[i].value == NULL ||
407 entry->attrs[i].value->type != CSA_VALUE_REMINDER)
410 _DtCmsAddReminder4EntryToQ(*remq, entry, i, lnode);
415 _DtCmsObsoleteReminder4Entry(
416 _DtCmsRemQueue *remq,
424 for (i = 0; i < remq->num_queues; i++) {
425 if (entry->attrs[remq->aindex[i]].value)
426 _RemoveReminderFromQ(remq, i, entry, lnode,
431 extern CSA_return_code
432 _DtCmsLookupReminder(
433 _DtCmsRemQueue *remq,
436 cms_attr_name *names,
437 cms_reminder_ref **rems)
440 CSA_return_code stat;
445 return (CSA_SUCCESS);
448 return (_GetNextReminders(remq, tick, rems));
450 for (i = 0; i < num_names; i++) {
451 for (j = 0; j < remq->num_queues; j++) {
452 if (strcmp(names[i].name, remq->names[j]) == 0) {
453 names[i].num = remq->aindex[j];
455 if ((stat = _GetNextRemindersFromQ(remq, i,
456 tick, rems)) != CSA_SUCCESS) {
458 _DtCmsFreeReminderRef(*rems);
465 return (CSA_SUCCESS);
469 _DtCmsFreeReminderRef(cms_reminder_ref *rems)
471 cms_reminder_ref *next;
473 while (rems != NULL) {
476 if (rems->reminder_name)
477 free(rems->reminder_name);
487 _DtCmsUpdateReminders(_DtCmsRemQueue *remq)
491 remq->cutoff = time(0) - 60*30;
493 for (i = 0; i < remq->num_queues; i++)
494 _UpdateReminderQ(remq, i);
497 /*****************************************************************************
498 * static functions used within the file
499 *****************************************************************************/
501 static _DtCmsRemInfo *
502 _BuildReminder4Entry(
507 _DtCmsRemInfo **active)
509 _DtCmsRemInfo *rptr, *rptr2;
512 RepeatEventState *restate;
514 if (active) *active = NULL;
516 if ((rptr = (_DtCmsRemInfo *)calloc(1, sizeof(_DtCmsRemInfo))) == NULL)
520 rptr->isentry = B_TRUE;
521 rptr->data.e = entry;
522 rptr->rem.i = entry->attrs[aindex].name.num;
524 _csa_iso8601_to_duration(entry->attrs[aindex].value->item.\
525 reminder_value->lead_time, &lead);
528 rptr->starttime = entry->key.time;
529 rptr->runtime = entry->key.time - lead;
531 if (lnode->lasttick == 0) {
532 lnode->lasttick = LastTick(entry->key.time, lnode->re);
533 lnode->duration = _DtCmsGetDuration(entry);
535 rptr->lasttick = lnode->lasttick;
537 /* calculate first tick */
538 tick = _GetNextActiveTick(entry, entry->key.time,
539 lnode->lasttick, lnode->re);
541 rptr->starttime = tick;
542 rptr->runtime = tick - lead;
544 /* need to calculate the active tick if
545 * 1. runtime of first tick is before cutoff time,
546 * 2. runtime of last tick is after cutoff time, and
547 * 3. there's reminders for instances after the cutoff time
549 if (active && rptr->runtime < cutoff &&
550 (lnode->lasttick - lead >= cutoff) &&
551 (tick = _GetNextActiveTick(entry, cutoff + lead,
552 lnode->lasttick, lnode->re)) > 0)
554 if ((rptr2 = (_DtCmsRemInfo *)calloc(1,
555 sizeof(_DtCmsRemInfo))) == NULL) {
560 rptr2->lnode = lnode;
561 rptr2->isentry = B_TRUE;
562 rptr2->data.e = entry;
563 rptr2->rem.i = entry->attrs[aindex].name.num;
564 rptr2->lasttick = lnode->lasttick;
565 rptr2->starttime = tick;
566 rptr2->runtime = tick - lead;
576 _DtCmsRemInfo **head,
579 _DtCmsRemInfo *rptr, *prev;
581 for (rptr = *head, prev = NULL; rptr != NULL;
582 prev = rptr, rptr = rptr->next) {
583 if (rem->runtime < rptr->runtime)
602 static CSA_return_code
603 _GetNextReminders(_DtCmsRemQueue *remq, time_t tick, cms_reminder_ref **rems)
605 CSA_return_code stat;
606 cms_reminder_ref *rptr, *head, *tail;
609 for (i = 0, head = NULL; i < remq->num_queues; i++) {
612 if ((stat = _GetNextRemindersFromQ(remq, i, tick, &rptr))
615 _DtCmsFreeReminderRef(head);
624 else if (rptr->runtime == head->runtime) {
625 /* combine the list */
626 for (; tail->next != NULL; tail = tail->next);
630 } else if (rptr->runtime < head->runtime) {
631 _DtCmsFreeReminderRef(head);
634 _DtCmsFreeReminderRef(rptr);
641 return (CSA_SUCCESS);
644 static CSA_return_code
645 _GetNextRemindersFromQ(
646 _DtCmsRemQueue *remq,
649 cms_reminder_ref **rems)
651 CSA_return_code stat;
652 cms_reminder_ref *rem = *rems;
654 if (tick >= remq->cutoff)
655 return (_GetNextRemindersFromList(remq->active[qindex], tick,
658 if ((stat = _GetNextRemindersFromList(remq->oldhead[qindex],
659 tick, rems)) == CSA_SUCCESS && rem == *rems)
660 return (_GetNextRemindersFromList(remq->active[qindex],
667 static cms_reminder_ref *
668 _GetReminderRefFromInfo(_DtCmsRemInfo *rem, time_t starttime, time_t runtime)
670 cms_reminder_ref *rptr;
671 cms_entry *entry = rem->data.e;
674 if ((rptr = (cms_reminder_ref *)calloc(1, sizeof(cms_reminder_ref)))
678 if ((rptr->reminder_name = strdup(entry->attrs[rem->rem.i].name.name))
684 size = entry->attrs[CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I].value->item.\
685 opaque_data_value->size;
687 if ((rptr->entryid = malloc(size)) == NULL) {
688 free(rptr->reminder_name);
692 strncpy(rptr->entryid, (char *)entry->attrs\
693 [CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I].value->item.\
694 opaque_data_value->data, size);
696 rptr->key.id = entry->key.id;
697 rptr->key.time = starttime ? starttime : rem->starttime;
699 rptr->runtime = runtime ? runtime : rem->runtime;
705 _RemoveReminderFromQ(
706 _DtCmsRemQueue *remq,
709 List_node *lnode, /* zero for one time entries */
713 _DtCmsRemInfo *rptr, *rptr1 = NULL, *rptr2 = NULL;
716 boolean_t do_old, do_new;
717 RepeatEventState *restate;
719 _csa_iso8601_to_duration(entry->attrs[remq->aindex[qindex]].value->\
720 item.reminder_value->lead_time, &lead);
723 if (entry->key.time - lead < remq->cutoff)
724 _RemoveReminderFromList(&remq->oldhead[qindex], entry,
727 _RemoveReminderFromList(&remq->active[qindex], entry,
730 tick = ClosestTick(entry->key.time, entry->key.time, lnode->re,
732 if (do_old = (tick - lead < remq->cutoff))
733 rptr1 = _RemoveReminderFromList(&remq->oldhead[qindex],
734 entry, starttime, delfwd);
736 if (do_new = (lnode->lasttick - lead >= remq->cutoff))
737 rptr2 = _RemoveReminderFromList(&remq->active[qindex],
738 entry, starttime, delfwd);
740 if (rptr = rptr1 ? rptr1 : rptr2) {
741 if (do_old && do_new) {
742 /* need to clean up the other queue
743 * since add reminder will add to both
747 _RemoveReminderFromList(
748 &remq->oldhead[qindex], entry,
751 _RemoveReminderFromList(
752 &remq->active[qindex], entry,
756 _DtCmsAddReminder4EntryToQ(remq, entry,
757 remq->aindex[qindex], rptr->lnode);
759 if (rptr1) free(rptr1);
760 if (rptr2) free(rptr2);
765 static _DtCmsRemInfo *
766 _RemoveReminderFromList(
767 _DtCmsRemInfo **qhead,
772 _DtCmsRemInfo *rptr, *prev;
774 /* find reminder in list */
775 for (rptr = *qhead, prev = NULL; rptr != NULL;
776 prev = rptr, rptr = rptr->next) {
777 if (rptr->data.e != entry ||
778 (starttime > 0 && !delfwd && rptr->starttime != starttime)||
779 (starttime > 0 && delfwd && rptr->starttime < starttime))
787 prev->next = rptr->next;
789 if (starttime == 0 || delfwd) {
801 * move all reminders in active queue whose runtime < cutoff
805 _UpdateReminderQ(_DtCmsRemQueue *remq, int qindex)
807 _DtCmsRemInfo *rptr, *nptr;
810 RepeatEventState *restate;
812 for (; (rptr = remq->active[qindex]) != NULL &&
813 rptr->runtime < remq->cutoff; ) {
815 remq->active[qindex] = rptr->next;
819 if (rptr->lnode == NULL)
820 _InsertReminder(&remq->oldhead[qindex], rptr);
822 lead = rptr->starttime - rptr->runtime;
823 entry = rptr->data.e;
824 tick = ClosestTick(entry->key.time, entry->key.time,
825 rptr->lnode->re, &restate);
827 if (tick == rptr->starttime) {
828 /* add this to old queue */
829 _InsertReminder(&remq->oldhead[qindex], rptr);
831 /* make copy of rptr */
832 nptr = (_DtCmsRemInfo *)calloc(1,
833 sizeof(_DtCmsRemInfo));
838 if ((rptr->lasttick - lead < remq->cutoff) ||
839 (tick = _GetNextActiveTick(entry,
840 remq->cutoff + lead, rptr->lasttick,
841 rptr->lnode->re)) <= 0)
845 rptr->starttime = tick;
846 rptr->runtime = tick - lead;
847 _InsertReminder(&remq->active[qindex], rptr);
860 RepeatEventState *restate;
863 for (tick = ClosestTick(target, entry->key.time, re, &restate);
865 tick = NextTick(tick, entry->key.time, re, restate))
867 if (tick <= 0 || !_DtCmsInExceptionList(entry, tick))
874 * The reminders found will be linked with the list
877 static CSA_return_code
878 _GetNextRemindersFromList(
879 _DtCmsRemInfo *rlist,
881 cms_reminder_ref **rf_r)
883 cms_reminder_ref *rptr, *head = NULL, *tail;
887 /* get from active queue */
888 for (; rlist != NULL; rlist = rlist->next) {
890 if (giventime < rlist->runtime)
892 else if (rlist->lnode) {
894 /* check the next active tick */
895 lead = rlist->starttime - rlist->runtime;
896 tick = _GetNextActiveTick(rlist->data.e,
897 giventime + lead + 1, rlist->lasttick,
901 (head == NULL || (tick-lead <= head->runtime)))
903 if ((rptr = _GetReminderRefFromInfo(rlist, tick,
904 tick-lead)) == NULL) {
906 _DtCmsFreeReminderRef(head);
907 return (CSA_E_INSUFFICIENT_MEMORY);
912 else if (head->runtime = rptr->runtime) {
916 _DtCmsFreeReminderRef(head);
925 if (head->runtime > rlist->runtime) {
926 _DtCmsFreeReminderRef(head);
928 } else if (head->runtime < rlist->runtime)
932 /* now do lookup in the remaining list */
934 while (rlist != NULL) {
935 if (rptr = _GetReminderRefFromInfo(rlist, 0, 0)) {
939 if (head) _DtCmsFreeReminderRef(head);
940 return (CSA_E_INSUFFICIENT_MEMORY);
944 rlist->next->runtime == rlist->runtime)
954 for (tail = head; tail->next != NULL; tail = tail->next) ;
959 return (CSA_SUCCESS);
963 _DtCmsAddReminder4EntryToQ(
964 _DtCmsRemQueue *remq,
969 _DtCmsRemInfo *rptr, *rptr2;
972 if ((rptr = _BuildReminder4Entry(entry, aindex, lnode, remq->cutoff,
976 for (i = 0; i < remq->num_queues; i++) {
977 if (remq->aindex[i] == aindex) {
978 if (rptr->runtime >= remq->cutoff)
979 _InsertReminder(&remq->active[i], rptr);
981 _InsertReminder(&remq->oldhead[i], rptr);
984 _InsertReminder(&remq->active[i], rptr2);
989 if (i == remq->num_queues) {
990 /* expand the queue */