Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / server / v4ops.c
1 /* $XConsortium: v4ops.c /main/5 1996/10/02 17:21:09 drk $ */
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 <sys/stat.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <pwd.h>
16 #include <time.h>
17 #include <values.h>
18 #ifdef SunOS
19 #include <sys/systeminfo.h>
20 #endif
21 #include "v4ops.h"
22 #include "rtable4.h"
23 #include "cmscalendar.h"
24 #include "tree.h"
25 #include "list.h"
26 #include "log.h"
27 #include "appt4.h"              /* Internal appointment data structure */
28 #include "reminder.h"
29 #include "access.h"
30 #include "laccess.h"
31 #include "lookup.h"
32 #include "cmsconvert.h"
33 #include "cmsdata.h"
34 #include "repeat.h"
35 #include "misc.h"
36 #include "utility.h"
37
38 extern  int     debug;
39 extern  char    *pgname;
40
41 /*****************************************************************************
42  * forward declaration of static functions used within the file
43  *****************************************************************************/
44 static Exception_4 append_exception_list(Appt_4 *p_appt, int ordinal);
45 static void trunc_exception_list(Appt_4 *p_appt, int ordinal);
46 static int num_exception(Appt_4 *p_appt);
47
48 /*****************************************************************************
49  * extern functions used in the library
50  *****************************************************************************/
51
52 /*
53  * Insert one appointment into the single appointment tree structure
54  * or the repeat appointment list structure.
55  * The passed in appointment is stored in the structure.
56  */
57 extern CSA_return_code
58 _DtCmsInsertAppt(_DtCmsCalendar *cal, Appt_4 *appt4)
59 {
60         CSA_return_code stat;
61         Rb_Status       rb_stat;
62         time_t          current_time;
63         Attr_4          p_attr;
64
65         if (cal == NULL || appt4 == NULL)
66                 return (CSA_E_INVALID_PARAMETER);
67
68         /* assign key if this is a new appointment */
69         _DtCmsGenerateKey(cal, &(appt4->appt_id.key));
70
71         if (debug) {
72                 fprintf(stderr, "Insert appt4: (%ld)%s\n",
73                         appt4->appt_id.key, ctime(&(appt4->appt_id.tick)));
74         }
75
76         /* Add the appointment into the data structure */
77         if (appt4->period.period == single_4)
78                 rb_stat = rb_insert (cal->tree, (caddr_t)appt4,
79                                 (caddr_t)&(appt4->appt_id));
80         else
81                 rb_stat = hc_insert (REPT_LIST(cal), (caddr_t)appt4,
82                                 (caddr_t)&(appt4->appt_id), NULL, NULL);
83
84         if (rb_stat == rb_ok) {
85                 /* Add the qualified reminder attrs to the reminder queue */
86                 current_time = time(0);
87                 p_attr = appt4->attr;
88                 while (p_attr != NULL)
89                 {
90                         time_t  tick;
91                         Rm_que  *p_reminder;
92
93                         tick = appt4->appt_id.tick - atol(p_attr->value);
94                         p_reminder = build_reminder (current_time, appt4,
95                                                         p_attr, tick, 1);
96                         if (p_reminder != NULL)
97                                 _DtCmsAddReminderV4(&cal->rm_queue, p_reminder);
98                         p_attr = p_attr->next;
99                 }
100         }
101
102         return (_DtCmsRbToCsaStat(rb_stat));
103 }
104
105 /*
106  * If p_auth is null, the initiator is the owner of the calendar.  Permission
107  * to delete any appointment is always granted.  If p_auth is not null, we need
108  * to check if it matches the author of the deleting appointment.  Only the
109  * author can delete his/her appointment.
110  */
111 extern CSA_return_code
112 _DtCmsDeleteAppt(
113         _DtCmsCalendar  *cal,
114         char            *user,
115         uint            access,
116         Id_4            *p_key,
117         Appt_4          **appt_r)
118 {
119         Appt_4          *p_appt;
120         List_node       *p_lnode;
121         Tree_node       *p_node;
122
123         /*
124          * Before we delete an event from the single appointment tree, we
125          * need to check if the initiator is the author of the appointment.
126          */
127         if ((user != NULL) && (p_appt = (Appt_4 *)rb_lookup(APPT_TREE(cal),
128             (caddr_t)p_key)) != NULL) {
129                 if (!(access & (access_delete_4|CSA_OWNER_RIGHTS)) &&
130                     !_DtCmIsSameUser(user, p_appt->author))
131                         return (CSA_E_NO_AUTHORITY);
132         }
133
134         if ((p_node = rb_delete (APPT_TREE(cal), (caddr_t)p_key)) != NULL) {
135                 p_appt = (Appt_4*)p_node->data;
136                 _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, 0, B_FALSE);
137                 free (p_node);
138                 if (debug)
139                 {
140                         fprintf (stderr, "Deleted (%ld)%s\n",
141                                 APPT_KEY(p_appt),
142                                 ctime(&(p_appt->appt_id.tick)));
143                 }
144                 if (appt_r != NULL)
145                         *appt_r = p_appt;
146                 else
147                         _DtCm_free_appt4(p_appt);
148                 return (CSA_SUCCESS);
149         }
150
151         /* Attempt to delete the event from the repeating appointment list */
152         p_lnode = (List_node *)hc_lookup_node(REPT_LIST(cal),
153                         (caddr_t)p_key);
154
155         if (p_lnode != NULL) {
156                 p_appt = (Appt_4 *)p_lnode->data;
157
158                 if (user != NULL &&
159                     !(access & (access_delete_4|CSA_OWNER_RIGHTS)) &&
160                     !_DtCmIsSameUser(user, p_appt->author))
161                         return (CSA_E_NO_AUTHORITY);
162
163                 _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, 0, B_FALSE);
164                 (void) hc_delete_node (REPT_LIST(cal), p_lnode);
165                 free (p_lnode);
166                 if (debug)
167                 {
168                         fprintf (stderr, "Deleted (%ld)%s\n",
169                                 APPT_KEY(p_appt),
170                                 ctime(&(p_appt->appt_id.tick)));
171                 }
172                 if (appt_r != NULL)
173                         *appt_r = p_appt;
174                 else
175                         _DtCm_free_appt4(p_appt);
176                 return (CSA_SUCCESS);
177         }
178         return (CSA_E_USER_NOT_FOUND | CSA_E_INVALID_ENTRY_HANDLE);
179 }
180
181 extern CSA_return_code
182 _DtCmsDeleteApptAndLog(
183         _DtCmsCalendar  *cal,
184         char            *user,
185         uint            access,
186         Id_4            *key,
187         Appt_4          **oldappt)
188 {
189         CSA_return_code stat;
190         Appt_4 *appt;
191
192         if ((stat = _DtCmsDeleteAppt(cal, user, access, key, &appt))
193             == CSA_SUCCESS) {
194
195                 /* Transact the log */
196                 if ((stat = v4_transact_log(cal->calendar, appt,
197                     _DtCmsLogRemove)) != CSA_SUCCESS) {
198
199                         (void)_DtCmsInsertAppt(cal, appt);
200
201                 } else if (oldappt) {
202                         *oldappt = appt;
203                 } else {
204                         _DtCm_free_appt4(appt);
205                 }
206         }
207
208         return (stat);
209 }
210
211 extern CSA_return_code
212 _DtCmsDeleteApptInstancesAndLog(
213         _DtCmsCalendar  *cal,
214         char            *source,
215         uint            access,
216         Id_4            *key,
217         Options_4       option,
218         int             *remain,
219         Appt_4          **oldappt)
220 {
221         List_node       *p_lnode;
222         CSA_return_code stat = CSA_SUCCESS;
223         Appt_4          *p_appt, *oldcopy;
224         int             ordinal;
225         int             f, file_size, ntimes, ninstance, status = 0;
226         struct stat     info;
227
228         p_lnode = (List_node *)hc_lookup_node (REPT_LIST(cal), (caddr_t)key);
229         if (p_lnode == NULL)
230                 return (CSA_X_DT_E_ENTRY_NOT_FOUND);
231
232         p_appt = (Appt_4*)p_lnode->data;
233         if (!(access & (access_delete_4 | CSA_OWNER_RIGHTS)) &&
234             !_DtCmIsSameUser(source, p_appt->author))
235                 return (CSA_E_NO_AUTHORITY);
236
237         if ((ordinal = _DtCms_in_repeater (key, p_appt, B_TRUE)) == 0)
238                 return (CSA_X_DT_E_ENTRY_NOT_FOUND);
239
240         if (debug)
241                 fprintf(stderr,"Delete instance: Ordinal=%d\n",ordinal);
242
243         /*
244          * save file size in case the first log transaction
245          * succeeds but the second log transaction fails.
246          */
247         if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
248             != CSA_SUCCESS)
249                 return (stat);
250
251         /* remove from log */
252         if ((stat = v4_transact_log(cal->calendar, p_appt, _DtCmsLogRemove))
253             != 0) {
254                 return (stat);
255         }
256         ninstance = _DtCms_get_ninstance_v4(p_appt);
257
258         /* calculate the ntimes value for the part
259          * of the sequence that start from the ordinal to the end
260          */
261         if (remain != NULL)
262                 *remain = _DtCms_get_new_ntimes_v4(p_appt->period, key->tick,
263                                 (ninstance - ordinal + 1));
264
265         /*
266          * make a copy of the original appointment,
267          * in case we need a rollback
268          */
269         if ((oldcopy = _DtCm_copy_one_appt4(p_appt)) == NULL) {
270                 stat = CSA_E_INSUFFICIENT_MEMORY;
271                 goto delete_cleanup;
272         }
273
274         /* remove from memory */
275         if (option == do_one_4) {
276                 if (!append_exception_list (p_appt, ordinal)) {
277                         stat = CSA_E_INSUFFICIENT_MEMORY;
278                         goto delete_cleanup;
279                 }
280         } else {
281                 ninstance = ordinal - 1;
282                 if (ninstance == 1) {
283                         /* convert to one-time event */
284                         p_appt->period.period = single_4;
285                         p_appt->period.nth = 0;
286                         p_appt->period.enddate = 0;
287                         p_appt->ntimes = 0;
288                         if (p_appt->exception) {
289                                 _DtCm_free_excpt4(p_appt->exception);
290                                 p_appt->exception = NULL;
291                         }
292                         stat = _DtCmsRbToCsaStat(rb_insert(cal->tree,
293                                 (caddr_t)p_appt, (caddr_t)&(p_appt->appt_id)));
294                 } else {
295                         p_appt->ntimes = _DtCms_get_new_ntimes_v4(
296                                         p_appt->period, p_appt->appt_id.tick,
297                                         ninstance);
298
299                         /* update enddate just for M-F, MWF, TTh
300                          * and weekdaycombo
301                          */
302                         switch (p_appt->period.period) {
303                         case monThruFri_4:
304                         case monWedFri_4:
305                         case tueThur_4:
306                         case daysOfWeek_4:
307                                 p_appt->period.enddate = _DtCms_prev_tick_v4(
308                                                                 key->tick,
309                                                                 p_appt->period);
310                         }
311                         trunc_exception_list(p_appt, ordinal);
312                 }
313         }
314
315         /* The last one from the series has been deleted, no more left. */
316         if (ninstance == num_exception (p_appt))
317                 ordinal = 0;
318
319         if (ordinal == 0) {
320                 /* Obsolete the reminders which match the ordinal */
321                 _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, ordinal,
322                         (option == do_one_4 ? B_FALSE : B_TRUE));
323                 _DtCm_free_appt4(p_appt);
324                 hc_delete_node (REPT_LIST(cal), p_lnode);
325                 free (p_lnode);
326         } else {
327                 /* Write out the series with new exception list. */
328                 if (stat || (stat = v4_transact_log(cal->calendar, p_appt,
329                     _DtCmsLogAdd)) != CSA_SUCCESS) {
330                         /* reverse memory update */
331                         p_appt->ntimes = oldcopy->ntimes;
332                         p_appt->period = oldcopy->period;
333                         _DtCm_free_excpt4(p_appt->exception);
334                         p_appt->exception = oldcopy->exception;
335                         oldcopy->exception = NULL;
336                         goto delete_cleanup;
337                 }
338
339                 if (ninstance == 1) {
340                         time_t  current_time;
341                         Attr_4  p_attr;
342
343                         _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, 0,
344                                 (option == do_one_4 ? B_FALSE : B_TRUE));
345                         hc_delete_node(REPT_LIST(cal), p_lnode);
346                         free(p_lnode);
347
348                         /* Add the qualified reminder attrs to
349                          * the reminder queue
350                          */
351                         current_time = time(0);
352                         p_attr = p_appt->attr;
353                         while (p_attr != NULL) {
354                                 time_t  tick;
355                                 Rm_que  *p_reminder;
356
357                                 tick = p_appt->appt_id.tick-atol(p_attr->value);
358                                 p_reminder = build_reminder(current_time,
359                                                 p_appt, p_attr, tick, 1);
360                                 if (p_reminder != NULL)
361                                         _DtCmsAddReminderV4(&cal->rm_queue,
362                                                 p_reminder);
363                                 p_attr = p_attr->next;
364                         }
365
366                 } else {
367                         _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt,
368                                 ordinal,
369                                 (option == do_one_4 ? B_FALSE : B_TRUE));
370                 }
371         }
372
373         if (oldappt)
374                 *oldappt = oldcopy;
375         else
376                 _DtCm_free_appt4(oldcopy);
377
378         return (CSA_SUCCESS);
379
380 delete_cleanup:
381         /* rollback log file */
382         _DtCmsTruncateFile(cal->calendar, file_size);
383
384         if (oldcopy)
385                 _DtCm_free_appt4(oldcopy);
386
387         return (stat);
388 }
389
390 extern CSA_return_code
391 _DtCmsChangeAll(
392         _DtCmsCalendar  *cal,
393         char            *source,
394         uint            access,
395         Id_4            *p_key,
396         Appt_4          *newa,
397         Appt_4          **oldappt)
398 {
399         CSA_return_code stat = CSA_SUCCESS;
400         Appt_4  *olda, *newcopy = NULL;
401         int     ordinal;
402         int     file_size;
403
404         if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
405             != CSA_SUCCESS)
406                 return (stat);
407
408         /*
409          * first, remove the old appointment from internal data structure
410          * and the callog file
411          */
412         if ((stat = _DtCmsDeleteApptAndLog(cal, source, access, p_key, &olda))
413             != CSA_SUCCESS)
414                 return (stat);
415
416         if (is_repeater(olda) && is_repeater(newa)) {
417                 if (_DtCmsBeginOfDay(APPT_TICK(newa)) ==
418                     _DtCmsBeginOfDay(p_key->tick) &&
419                     olda->period.period == newa->period.period)
420                 {
421                         /* keep the start day of the original
422                          * appointment if the date of the
423                          * key matches that of the new
424                          * appointment and the interval
425                          * is not changed
426                          */
427                         APPT_TICK(newa) -= _DtCmsBeginOfDay(APPT_TICK(newa)) -
428                                         _DtCmsBeginOfDay(APPT_TICK(olda));
429                 } else {
430                         /* otherwise, calculate new parent */
431                         if ((ordinal = _DtCms_in_repeater(p_key, olda, B_TRUE))
432                             == 0) {
433                                 stat = CSA_X_DT_E_ENTRY_NOT_FOUND;
434                                 goto change_all_cleanup;
435                         } else {
436                                 APPT_TICK(newa) = _DtCms_first_tick_v4(
437                                                         APPT_TICK(newa),
438                                                         newa->period, ordinal);
439                                 /* if enddate exist, adjust it */
440                                 if (olda->period.enddate &&
441                                     newa->period.period == olda->period.period
442                                     && newa->period.nth == olda->period.nth) {
443                                         newa->period.enddate +=
444                                             (_DtCmsBeginOfDay(APPT_TICK(newa)) -
445                                              _DtCmsBeginOfDay(APPT_TICK(olda)));
446                                 }
447                         }
448                 }
449
450                 /* ????? */
451                 /* We use the same exception list for the
452                  * new appt.  ?? is this reasonable for
453                  * all cases ??
454                  */
455                 newa->exception = _DtCm_copy_excpt4(olda->exception);
456         }
457
458         /* adjust start date */
459         _DtCms_adjust_appt_startdate(newa);
460
461         /* make copy of new appointment */
462         if ((newcopy = _DtCm_copy_one_appt4(newa)) == NULL) {
463                 stat = CSA_E_INSUFFICIENT_MEMORY;
464                 goto change_all_cleanup;
465         }
466
467         /* reuse the key */
468         newcopy->appt_id.key = olda->appt_id.key;
469
470         /* keep the original author */
471         free(newcopy->author);
472         if ((newcopy->author = strdup(olda->author)) == NULL) {
473                 stat = CSA_E_INSUFFICIENT_MEMORY;
474                 goto change_all_cleanup;
475         }
476
477         if ((stat = _DtCmsInsertApptAndLog(cal, newcopy)) != CSA_SUCCESS) {
478                 goto change_all_cleanup;
479         }
480         newa->appt_id.key = newcopy->appt_id.key;
481
482         if (oldappt)
483                 *oldappt = olda;
484         else
485                 _DtCm_free_appt4(olda);
486
487         return (stat);
488
489 change_all_cleanup:
490         if (newcopy)
491                 _DtCm_free_appt4(newcopy);
492         _DtCmsTruncateFile(cal->calendar, file_size);
493         (void)_DtCmsInsertAppt(cal, olda);
494         return (stat);
495 }
496
497 extern CSA_return_code
498 _DtCmsChangeSome(
499         _DtCmsCalendar  *cal,
500         char            *source,
501         uint            access,
502         Id_4            *p_key,
503         Appt_4          *newa,
504         Options_4       option,
505         Appt_4          **oldappt)
506 {
507         CSA_return_code stat;
508         Appt_4          *olda, *newcopy = NULL;
509         List_node       *lnode;
510         Exception_4     *newexcept;
511         int             file_size;
512         int             remain;
513
514         if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
515             != CSA_SUCCESS)
516                 return (stat);
517
518         /*
519          * first, remove the old appointment from internal data structure
520          * and the callog file
521          */
522         if ((stat = _DtCmsDeleteApptInstancesAndLog(cal, source, access,
523             p_key, option, &remain, &olda)) != CSA_SUCCESS)
524                 return (stat);
525
526         /* adjust start date */
527         _DtCms_adjust_appt_startdate(newa);
528
529         /* make copy of new appointment */
530         newcopy = _DtCm_copy_one_appt4(newa);
531
532         if (option == do_forward_4) {
533                 /* if repeating info is not changed, replace
534                  * the deleted part with the new series, i.e.,
535                  * ntimes of new series _DtCmsIsEquals to the number
536                  * of deleted instances. Also get new 
537                  * exception list.      
538                  */
539                 if (olda->period.period == newa->period.period &&
540                     olda->period.nth == newa->period.nth &&
541                     olda->ntimes == newa->ntimes) { 
542
543                         newcopy->ntimes = remain;
544                         if (olda->exception != NULL) {
545                                 _DtCm_free_excpt4(newcopy->exception);
546                                 if ((stat = _DtCmsTruncateElist(olda, remain,
547                                     &newcopy->exception)) != CSA_SUCCESS) {
548                                         goto change_some_cleanup;
549                                 }
550                         }
551                 }
552         }
553
554         /* keep the original author */
555         free(newcopy->author);
556         if ((newcopy->author = strdup(olda->author)) == NULL) {
557                 stat = CSA_E_INSUFFICIENT_MEMORY;
558                 goto change_some_cleanup;
559         }
560
561         APPT_KEY(newcopy) = 0;
562
563         if ((stat = _DtCmsInsertApptAndLog(cal, newcopy)) != CSA_SUCCESS) {
564                 goto change_some_cleanup;
565         } else
566                 newa->appt_id.key = newcopy->appt_id.key;
567
568         if (oldappt)
569                 *oldappt = olda;
570         else
571                 _DtCm_free_appt4(olda);
572
573         return (stat);
574
575 change_some_cleanup:
576         if (newcopy)
577                 _DtCm_free_appt4(newcopy);
578         _DtCmsTruncateFile(cal->calendar, file_size);
579         (void)_DtCmsInsertAppt(cal, olda);
580         return (stat);
581 }
582
583 /*
584  * the passed in appt is stored in the internal data structure
585  */
586 extern CSA_return_code
587 _DtCmsInsertApptAndLog(_DtCmsCalendar *cal, Appt_4 *appt)
588 {
589         CSA_return_code stat;
590
591         if ((stat = _DtCmsInsertAppt(cal, appt)) == CSA_SUCCESS) {
592
593                 /* Add the new appointment to the log */
594                 if ((stat = v4_transact_log(cal->calendar, appt,
595                     _DtCmsLogAdd)) != CSA_SUCCESS) {
596
597                         (void)_DtCmsDeleteAppt(cal, NULL, 0, &appt->appt_id,
598                                 &appt);
599                 }
600         }
601         return (stat);
602 }
603
604 extern _DtCmsComparisonResult
605 _DtCmsCompareAppt(Id_4 *key, caddr_t data)
606 {
607         Appt_4 *appt = (Appt_4 *)data;
608
609         /* Composite key */
610         if (key->tick < appt->appt_id.tick)
611                 return (_DtCmsIsLess);
612         if (key->tick > appt->appt_id.tick)
613                 return (_DtCmsIsGreater);
614
615         /* tick's are _DtCmsIsEqual */
616         if (key->key < appt->appt_id.key)
617                 return (_DtCmsIsLess);
618         if (key->key > appt->appt_id.key)
619                 return (_DtCmsIsGreater);
620
621         return (_DtCmsIsEqual);
622 }
623
624 extern _DtCmsComparisonResult
625 _DtCmsCompareRptAppt(Id_4 *key, caddr_t data)
626 {
627         Appt_4 *appt = (Appt_4 *)data;
628
629         if (key->key < appt->appt_id.key)
630                 return (_DtCmsIsLess);
631         if (key->key > appt->appt_id.key)
632                 return (_DtCmsIsGreater);
633         return (_DtCmsIsEqual);
634 }
635
636 extern caddr_t
637 _DtCmsGetApptKey (caddr_t data)
638 {
639         return ((caddr_t) &(((Appt_4 *) data)->appt_id));
640 }
641
642 extern CSA_return_code
643 v4_transact_log(char *calendar, Appt_4 *a, _DtCmsLogOps op)
644 {
645         CSA_return_code stat;
646         char    *log=NULL;
647
648         if ((log = _DtCmsGetLogFN(calendar)) == NULL)
649                  return (CSA_E_INSUFFICIENT_MEMORY);
650         else {
651                 stat = _DtCmsAppendAppt4ByFN(log, a, op);
652                 free(log);
653         }
654         return(stat);
655 }
656
657 extern CSA_return_code
658 _DtCmsLookupRangeV4(
659         _DtCmsCalendar  *cal,
660         char            *user,
661         uint            access,
662         Range_4         *p_range,
663         boolean_t       no_end_time_range,
664         long            end1,
665         long            end2,
666         boolean_t       (*match_func)(),
667         uint            num_attrs,
668         cms_attribute   *attrs,
669         CSA_enum        *ops,
670         Appt_4          **appt_r,
671         Abb_Appt_4      **abbr_r)
672 {
673         CSA_return_code stat = CSA_SUCCESS;
674         Period_4        period;
675         caddr_t         ilp = NULL;
676         int             tmp_tick, endtick;
677         Id_4            lo_key;
678         int             n;
679         List_node       *p_lnode;
680         time_t          hi_tick, lo_tick;
681         int             tick;
682         int             ordinal;
683         int             ntimes;
684         Appt_4          *p_appt;
685         cms_entry       *entries;
686         CSA_return_code (*add_list_func)();
687
688         /* do lookup on new format calendar */
689         if (cal->fversion > 1) {
690                 if ((stat = _DtCmsLookupEntries(cal, user, access,
691                     p_range->key1, p_range->key2, no_end_time_range, end1, end2,
692                     num_attrs, attrs, ops, &entries)) != CSA_SUCCESS) {
693                         return (stat);
694                 }
695
696                 if (appt_r)
697                         stat = _DtCmsCmsentriesToAppt4ForClient(entries,
698                                 appt_r);
699                 else
700                         stat = _DtCmsCmsentriesToAbbrAppt4ForClient(entries,
701                                 abbr_r);
702
703                 _DtCm_free_cms_entries(entries);
704                 return (stat);
705         }
706
707         if (appt_r)
708                 add_list_func = _AddToLinkedAppts;
709         else
710                 add_list_func = _AddToLinkedAbbrAppts;
711
712         while (p_range != NULL)
713         {
714                 lo_key.key = MAXINT;
715                 lo_key.tick = p_range->key1;
716                 hi_tick = p_range->key2;
717
718                 if (debug)
719                 {
720                         fprintf(stderr,"Range lookup from %s",
721                                 ctime(&lo_key.tick));
722                         fprintf(stderr, " to %s\n", ctime(&hi_tick));
723                 }
724
725                 n = 0;
726
727                 /* Get a range of appointments in single appointment tree */
728                 while ((p_appt = (Appt_4 *)rb_lookup_next_larger(
729                     APPT_TREE(cal),(caddr_t)&lo_key))
730                     && (APPT_TICK(p_appt) < hi_tick))
731                 {
732                         n++;
733
734                         endtick = p_appt->appt_id.tick + p_appt->duration;
735                         if ((!no_end_time_range && (p_appt->duration == 0 ||
736                             endtick <= end1 || endtick >= end2)) ||
737                             (match_func && !match_func(p_appt, num_attrs,
738                             attrs, ops))) {
739                                 lo_key.key = APPT_KEY(p_appt);
740                                 lo_key.tick = APPT_TICK(p_appt);
741                                 continue;
742                         }
743
744                         if ((stat = (*add_list_func)(p_appt, user,
745                             access,&ilp)) != CSA_SUCCESS) {
746                                 if (ilp != NULL) {
747                                         if (appt_r)
748                                                 _DtCm_free_appt4((Appt_4 *)ilp);
749                                         else
750                                                 _DtCm_free_abbrev_appt4(
751                                                         (Abb_Appt_4 *)ilp);
752                                         ilp = NULL;
753                                 }
754                                 break;
755                         }
756
757                         lo_key.key = APPT_KEY(p_appt);
758                         lo_key.tick = APPT_TICK(p_appt);
759                 }
760
761                 /* Get a range of events from repeating appointment list */
762                 p_lnode = REPT_LIST(cal)->root;
763                 while (p_lnode != NULL)
764                 {
765                         lo_tick = p_range->key1;
766                         hi_tick = p_range->key2;
767
768                         p_appt = (Appt_4*)p_lnode->data;
769
770                         /* calculate the last tick */
771                         if (p_lnode->lasttick == 0)
772                                 p_lnode->lasttick = _DtCms_last_tick_v4(
773                                                         APPT_TICK(p_appt),
774                                                         p_appt->period,
775                                                         p_appt->ntimes);
776
777                         if (p_lnode->lasttick <= lo_tick ||
778                             APPT_TICK(p_appt) >= hi_tick ||
779                             (!no_end_time_range && p_appt->duration == 0) ||
780                             (!no_end_time_range &&
781                              (p_lnode->lasttick+p_appt->duration) <= end1) ||
782                             (!no_end_time_range &&
783                              (p_appt->appt_id.tick+p_appt->duration) >= end2) ||
784                             (match_func &&
785                              !match_func(p_appt, num_attrs, attrs, ops)) ||
786                             _GetAccessLevel(user, access, p_appt) == private_4)
787                         { 
788                                 p_lnode = hc_lookup_next (p_lnode);
789                                 continue;
790                         }
791
792                         if (!no_end_time_range &&
793                             lo_tick < (end1 - p_appt->duration))
794                                 lo_tick = end1 - p_appt->duration;
795
796                         if (!no_end_time_range &&
797                             ((end2 - p_appt->duration) < hi_tick))
798                                 hi_tick = end2 - p_appt->duration;
799
800                         ntimes = _DtCms_get_ninstance_v4(p_appt);
801                         period = p_appt->period;
802                         for (tick = _DtCms_closest_tick_v4(lo_tick,
803                                 APPT_TICK(p_appt), period, &ordinal), ordinal--;
804                              tick < hi_tick;
805                              tick = _DtCms_next_tick_v4(tick, period))
806                         {
807                                 /* Repeater but beyond the scope */
808                                 if (++ordinal > ntimes ||
809                                     tick > p_lnode->lasttick)
810                                         break;
811
812                                 if (tick <= lo_tick ||
813                                     _DtCms_marked_4_cancellation(p_appt,
814                                     ordinal))
815                                         continue;
816
817                                 n++;
818
819                                 /* Replace the parent key by the
820                                  * current tick for the repeating event
821                                  */
822                                 tmp_tick = APPT_TICK(p_appt);
823                                 APPT_TICK(p_appt) = tick; 
824
825                                 /* Add to list, restore parent key */
826                                 stat = (*add_list_func)(p_appt, user,
827                                         access, &ilp);
828
829                                 APPT_TICK(p_appt) = tmp_tick;
830
831                                 if (stat != CSA_SUCCESS) {
832                                         if (ilp != NULL) {
833                                                 if (appt_r)
834                                                     _DtCm_free_appt4(
835                                                         (Appt_4 *)ilp);
836                                                 else
837                                                     _DtCm_free_abbrev_appt4(
838                                                         (Abb_Appt_4 *)ilp);
839                                                 ilp = NULL;
840                                         }
841                                         break;
842                                 }
843                         }
844
845                         p_lnode = hc_lookup_next (p_lnode);
846                 }
847
848                 p_range = p_range->next;
849         }
850
851         if (debug)
852                 fprintf (stderr, "Found %d entries in range lookup\n", n);
853
854         if (stat == CSA_SUCCESS) {
855                 if (appt_r)
856                         *appt_r = (Appt_4 *)ilp;
857                 else
858                         *abbr_r = (Abb_Appt_4 *)ilp;
859         }
860
861         return (stat);
862 }
863
864 extern CSA_return_code
865 _DtCmsLookupKeyrangeV4(
866         _DtCmsCalendar  *cal,
867         char            *user,
868         uint            access,
869         boolean_t       no_start_time_range,
870         boolean_t       no_end_time_range,
871         time_t          start1,
872         time_t          start2,
873         time_t          end1,
874         time_t          end2,
875         long            id,
876         boolean_t       (*match_func)(),
877         uint            num_attrs,
878         cms_attribute   *attrs,
879         CSA_enum        *ops,
880         Appt_4          **appt_r,
881         Abb_Appt_4      **abbr_r)
882 {
883         CSA_return_code stat = CSA_SUCCESS;
884         Period_4        period;
885         long            tmp_tick, endtick;
886         Id_4            lo_key;
887         int             n;
888         List_node       *p_lnode;
889         int             tick;
890         int             ordinal;
891         int             ntimes;
892         Appt_4          *p_appt, *tappt;
893         Abb_Appt_4      *tabbr;
894         cms_entry       *entries;
895         CSA_return_code (*add_list_func)();
896
897         /* do lookup on new format calendar */
898         if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
899                 if ((stat = _DtCmsLookupEntriesById(cal, user, access,
900                     no_start_time_range, no_end_time_range, start1, start2,
901                     end1, end2, id, num_attrs, attrs, ops, &entries))
902                     != CSA_SUCCESS) {
903                         return (stat);
904                 }
905
906                 if (appt_r) {
907                         stat = _DtCmsCmsentriesToAppt4ForClient(entries,
908                                 &tappt);
909                         *appt_r = _AddApptInOrder(*appt_r, tappt);
910                 } else {
911                         stat = _DtCmsCmsentriesToAbbrAppt4ForClient(entries,
912                                 &tabbr);
913                         *abbr_r = _AddAbbApptInOrder(*abbr_r, tabbr);
914                 }
915
916                 _DtCm_free_cms_entries(entries);
917                 return (stat);
918         }
919
920         if (appt_r)
921                 add_list_func = _AddToLinkedAppts;
922         else
923                 add_list_func = _AddToLinkedAbbrAppts;
924
925         lo_key.key = id;
926         lo_key.tick = start1;
927
928         /* Search from repeating appointments first for optimization */
929         p_lnode = hc_lookup_node (REPT_LIST(cal), (caddr_t)&lo_key);
930         if (p_lnode != NULL) {
931                 p_appt = (Appt_4*)p_lnode->data;
932                 if ((match_func && !match_func(p_appt, num_attrs, attrs, ops))
933                     || _GetAccessLevel(user, access, p_appt) == private_4)
934                         return (CSA_SUCCESS);
935
936                 /* just return the first event */
937                 if (no_start_time_range && no_end_time_range)
938                         return ((*add_list_func)(p_appt, user, access,
939                                 (appt_r ? (caddr_t)appt_r : (caddr_t)abbr_r)));
940
941                 /* Get the range of events from this appointment. */
942                 ntimes = _DtCms_get_ninstance_v4(p_appt);
943                 period = p_appt->period;
944
945                 /* calculate the last tick */
946                 if (p_lnode->lasttick == 0)
947                         p_lnode->lasttick = _DtCms_last_tick_v4(
948                                                 APPT_TICK(p_appt),
949                                                 p_appt->period, p_appt->ntimes);
950
951                 if (p_lnode->lasttick <= start1 ||
952                     p_appt->appt_id.tick >= start2 ||
953                     (!no_end_time_range && p_appt->duration == 0) ||
954                     (!no_end_time_range &&
955                      (p_lnode->lasttick+p_appt->duration) <= end1) ||
956                     (!no_end_time_range &&
957                      (p_appt->appt_id.tick+p_appt->duration) >= end2))
958                         return (SUCCESS);
959
960                 if (!no_end_time_range && (start1 < (end1 - p_appt->duration)))
961                         start1 = end1 - p_appt->duration;
962
963                 if (!no_end_time_range && ((end2 - p_appt->duration) < start2))
964                         start2 = end2 - p_appt->duration;
965
966                 for (tick = _DtCms_closest_tick_v4(start1,
967                     APPT_TICK(p_appt), period, &ordinal), ordinal--;
968                     stat == CSA_SUCCESS && tick < start2;
969                     tick = _DtCms_next_tick_v4(tick, period))
970                 {
971                         /* Repeater but beyond the scope */
972                         if (++ordinal > ntimes || tick > p_lnode->lasttick)
973                                 break;
974
975                         if (tick <= start1)
976                                 continue;
977
978                         /* If not cancelled, add to linked list */
979                         if (!_DtCms_marked_4_cancellation (p_appt, ordinal))
980                         {
981                                 n++;
982
983                                 /* Replace the parent key by
984                                  * the current tick for the
985                                  * repeating event
986                                  */
987                                 tmp_tick = APPT_TICK(p_appt);
988                                 APPT_TICK(p_appt) = tick; 
989
990                                 /* Add to list, restore parent key */
991                                 stat = (*add_list_func)(p_appt, user, access,
992                                         (appt_r ? (caddr_t)appt_r :
993                                         (caddr_t)abbr_r));
994
995                                 APPT_TICK(p_appt) = tmp_tick;
996
997                         }
998                 }
999                 return (stat);
1000         } else {
1001                 /* Check if it is in single appointment tree */
1002                 while ((p_appt = (Appt_4 *)rb_lookup_next_larger(
1003                     APPT_TREE(cal), (caddr_t) &lo_key)) &&
1004                     (APPT_TICK(p_appt) < start2))
1005                 {
1006                         if (p_appt->appt_id.key != lo_key.key) {
1007                                 lo_key.tick = APPT_TICK(p_appt);
1008                                 lo_key.key = APPT_KEY(p_appt);
1009                         } else {
1010                                 endtick = p_appt->appt_id.tick+p_appt->duration;
1011
1012                                 if ((!no_end_time_range &&
1013                                     (p_appt->duration == 0 || endtick <= end1 ||
1014                                     endtick >= end2)) ||
1015                                     (match_func && !match_func(p_appt,
1016                                     num_attrs, attrs, ops))) {
1017
1018                                         return (CSA_SUCCESS);
1019                                 } else {
1020                                         return ((*add_list_func)(p_appt, user,
1021                                                 access, (appt_r ?
1022                                                 (caddr_t)appt_r :
1023                                                 (caddr_t)abbr_r)));
1024                                 }
1025                         }
1026                 }
1027                 return (CSA_SUCCESS);
1028         }
1029 }
1030
1031 extern CSA_return_code
1032 _AddToLinkedAppts(
1033         Appt_4  *p_appt,
1034         char    *user,
1035         uint    access,
1036         caddr_t *ilp)
1037 {
1038         Appt_4  *p_prev;
1039         Appt_4  *copy;
1040
1041         switch (_GetAccessLevel(user, access, p_appt)) {
1042         case public_4:
1043                 copy = _DtCm_copy_one_appt4(p_appt);
1044                 break;
1045         case semiprivate_4:
1046                 copy = _DtCm_copy_semiprivate_appt4(p_appt);
1047                 break;
1048         default:
1049                 return (CSA_SUCCESS);
1050         }
1051
1052         if (copy == NULL)
1053                 return(CSA_E_INSUFFICIENT_MEMORY);
1054         else {
1055                 *ilp = (caddr_t)_AddApptInOrder((Appt_4 *)*ilp, copy);
1056                 return (CSA_SUCCESS);
1057         }
1058 }
1059
1060 extern CSA_return_code
1061 _AddToLinkedAbbrAppts(
1062         Appt_4  *p_appt,
1063         char    *user,
1064         uint    access,
1065         caddr_t *ilp)
1066 {
1067         Abb_Appt_4 *copy;
1068
1069         switch (_GetAccessLevel(user, access, p_appt)) {
1070         case public_4:
1071                 copy = _DtCm_appt_to_abbrev4(p_appt);
1072                 break;
1073         case semiprivate_4:
1074                 copy = _DtCm_appt_to_semiprivate_abbrev4(p_appt);
1075                 break;
1076         default:
1077                 return (CSA_SUCCESS);
1078         }
1079
1080         if (copy == NULL)
1081                 return(CSA_E_INSUFFICIENT_MEMORY);
1082         else {
1083                 *ilp = (caddr_t)_AddAbbApptInOrder((Abb_Appt_4 *)*ilp, copy);
1084                 return (CSA_SUCCESS);
1085         }
1086 }
1087
1088 extern Privacy_Level_4
1089 _GetAccessLevel(char *user, uint access, Appt_4 *p_appt)
1090 {
1091         if (access & CSA_OWNER_RIGHTS ||
1092             ((access & access_read_4) && p_appt->privacy == public_4) ||
1093             _DtCmIsSameUser(user, p_appt->author)) {
1094
1095                 return (public_4);
1096
1097         } else if (p_appt->privacy == private_4) {
1098
1099                 return (private_4);
1100
1101         } else
1102                 return (semiprivate_4);
1103
1104 }
1105
1106 extern Appt_4 *
1107 _AddApptInOrder(Appt_4 *head, Appt_4 *aptr)
1108 {
1109         Appt_4  *p_appt, *p_prev, *p_next;
1110
1111         while (aptr) {
1112                 p_next = aptr->next;
1113                 aptr->next = NULL;
1114
1115                 /* Add the item to the linked list in ascending order */
1116                 p_prev = NULL;
1117                 p_appt = head;
1118                 while (p_appt != NULL)
1119                 {
1120                         if (APPT_TICK(aptr) <= APPT_TICK(p_appt))
1121                                 break;
1122                         p_prev = p_appt;
1123                         p_appt = p_appt->next;
1124                 }
1125
1126                 if (p_prev == NULL)
1127                 {
1128                         aptr->next = p_appt;
1129                         head = aptr;
1130                 } else {
1131                         aptr->next = p_prev->next;
1132                         p_prev->next = aptr;
1133                 }
1134
1135                 aptr = p_next;
1136         }
1137
1138         return (head);
1139 }
1140
1141 extern Abb_Appt_4 *
1142 _AddAbbApptInOrder(Abb_Appt_4 *head, Abb_Appt_4 *aptr)
1143 {
1144         Abb_Appt_4      *p_appt, *p_prev, *p_next;
1145
1146         while (aptr) {
1147                 p_next = aptr->next;
1148                 aptr->next = NULL;
1149
1150                 /* Add the item to the linked list in ascending order */
1151                 p_prev = NULL;
1152                 p_appt = head;
1153                 while (p_appt != NULL)
1154                 {
1155                         if (APPT_TICK(aptr) <= APPT_TICK(p_appt))
1156                                 break;
1157                         p_prev = p_appt;
1158                         p_appt = p_appt->next;
1159                 }
1160
1161                 if (p_prev == NULL)
1162                 {
1163                         aptr->next = p_appt;
1164                         head = aptr;
1165                 } else {
1166                         aptr->next = p_prev->next;
1167                         p_prev->next = aptr;
1168                 }
1169
1170                 aptr = p_next;
1171         }
1172
1173         return (head);
1174 }
1175
1176 extern CSA_return_code
1177 _DtCmsSetV4AccessListAndLog(_DtCmsCalendar *cal, Access_Entry_4 *alist)
1178 {
1179         CSA_return_code stat;
1180         char    *name, *log;
1181         int     file_size;
1182
1183         /* update access list for old format calendar */
1184         if ((name = _DtCmsTarget2Name(cal->calendar)) == NULL)
1185                 return (CSA_E_INSUFFICIENT_MEMORY);
1186
1187         if ((log = _DtCmsGetLogFN(name)) == NULL) {
1188                 free(name);
1189                 return (CSA_E_INSUFFICIENT_MEMORY);
1190         }
1191
1192         /*
1193          * save file size in case the first log transaction
1194          * succeeds but the second log transaction fails.
1195          */
1196         if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
1197             != CSA_SUCCESS) {
1198                 free (name);
1199                 free (log);
1200                 return (stat);
1201         }
1202
1203         /* Set to the data structure */
1204         if ((stat = _DtCmsSetV4AccessListInCal(cal, alist)) == CSA_SUCCESS) {
1205
1206                 if (((stat = _DtCmsAppendAccessByFN(log, access_read_4,
1207                     GET_R_ACCESS(cal))) != CSA_SUCCESS) ||
1208                     ((stat = _DtCmsAppendAccessByFN(log, access_write_4,
1209                     GET_W_ACCESS(cal))) != CSA_SUCCESS) ||
1210                     ((stat = _DtCmsAppendAccessByFN(log, access_delete_4,
1211                     GET_D_ACCESS(cal))) != CSA_SUCCESS) ||
1212                     ((stat = _DtCmsAppendAccessByFN(log, access_exec_4,
1213                     GET_X_ACCESS(cal))) != CSA_SUCCESS)) {
1214
1215                         /* rollback log file */
1216                         _DtCmsTruncateFile(cal->calendar, file_size);
1217                 } else
1218                         cal->modified = B_TRUE;
1219         }
1220
1221         free (name);
1222         free(log);
1223         return (stat);
1224 }
1225
1226 extern CSA_return_code
1227 _DtCmsGetV4Reminders(
1228         _DtCmsCalendar  *cal,
1229         long            tick,
1230         Reminder_4      **rem_r,
1231         _DtCmsEntryId   **ids_r)
1232 {
1233         Rm_que          *p_node, *p_prev, *p_next, *p_new;
1234         Reminder_4      *rptr, *v4rem = NULL;
1235         _DtCmsEntryId   *idptr, *ids = NULL;
1236
1237         if (rem_r == NULL) return (CSA_E_INVALID_PARAMETER);
1238
1239         /* do lookup in old format calendar */
1240         p_prev = NULL;
1241         p_node = cal->rm_queue;
1242         while (p_node != NULL)
1243         {
1244                 /* It is still a future reminder. */
1245                 if (tick < p_node->remind_at)
1246                         break;
1247
1248                 /* The reminder expired.  It either needs to be recalculated
1249                  * (repeating appointment) or dropped (non-repeating appt.)
1250                  */
1251                 p_next = _DtCmsRemoveReminderV4(&cal->rm_queue, p_prev, p_node);
1252
1253                 if (is_repeater(p_node->appt)) {
1254                         /* Calculate next reminder for repeating appointment */
1255                         p_new = build_reminder(tick+1, p_node->appt,
1256                                         p_node->attr, p_node->remind_at,
1257                                         p_node->remind_ord);
1258                         if (p_new != NULL)
1259                         {
1260                                 _DtCmsAddReminderV4(&cal->rm_queue, p_new);
1261                                 if (p_new->next == p_next)
1262                                         p_prev = p_new;
1263                         }
1264                 }
1265
1266                 free (p_node);
1267                 p_node = p_next;
1268         }
1269
1270         /* Pick the first one from the active reminder queue because it is
1271          * always >= the given key.
1272          */
1273         p_node = cal->rm_queue;
1274         if (p_node != NULL)
1275         {
1276                 tick = p_node->remind_at;
1277                 do
1278                 {
1279                         rptr = _DtCmsGetReminderInfoV4(p_node);
1280                         rptr->next = v4rem;
1281                         v4rem = rptr;
1282                         if (ids_r) {
1283                                 idptr = (_DtCmsEntryId *)calloc(1,
1284                                         sizeof(_DtCmsEntryId));
1285                                 idptr->id = p_node->appt->appt_id.key;
1286                                 idptr->next = ids;
1287                                 ids = idptr;
1288                         }
1289                         p_node = p_node->next;
1290                 } while ((p_node != NULL) && (p_node->remind_at == tick));
1291         }
1292
1293         *rem_r = v4rem;
1294         if (ids_r) *ids_r = ids;
1295         return (CSA_SUCCESS);
1296 }
1297
1298 extern void
1299 _DtCmsFreeEntryIds(_DtCmsEntryId *ids)
1300 {
1301         _DtCmsEntryId *ptr;
1302
1303         while (ids != NULL) {
1304                 ptr = ids->next;
1305                 free(ids);
1306                 ids = ptr;
1307         }
1308 }
1309
1310 extern CSA_return_code
1311 _DtCmsTruncateElist(Appt_4 *parent_p, int remain, Except_4 **excpt)
1312 {
1313         int except_no, ntimes_diff;
1314         Except_4 *e, *p, *last_e = NULL, *head = NULL;
1315
1316         ntimes_diff = _DtCms_get_ninstance_v4(parent_p) - remain;
1317         p = parent_p->exception;
1318         while(p != NULL) {
1319                 if ((except_no = (p->ordinal - ntimes_diff)) > 0 && 
1320                         except_no <= remain) {
1321                         if ((e = (Except_4*)calloc(1, sizeof(Except_4)))
1322                             == NULL) {
1323                                 _DtCm_free_excpt4(head);
1324                                 return (CSA_E_INSUFFICIENT_MEMORY);
1325                         }
1326                         e->ordinal = except_no;
1327                         e->next = NULL;
1328                         if (last_e != NULL) 
1329                                 last_e->next = e;
1330                         else
1331                                 head = e;
1332                         last_e = e;
1333                 }
1334                 p = p->next;
1335         }
1336
1337         *excpt = head;
1338         return (CSA_SUCCESS);
1339 }
1340
1341 extern CSA_return_code 
1342 _DtCmsSetV4AccessListInCal(_DtCmsCalendar *cal, Access_Entry_4 *e)
1343 {
1344         Access_Entry_4  *q;
1345
1346         /* Wipe out the old access lists. */
1347         _DtCm_free_access_list4(GET_R_ACCESS (cal));
1348         _DtCm_free_access_list4(GET_W_ACCESS (cal));
1349         _DtCm_free_access_list4(GET_D_ACCESS (cal));
1350         _DtCm_free_access_list4(GET_X_ACCESS (cal));
1351         SET_R_ACCESS(cal, NULL);
1352         SET_W_ACCESS(cal, NULL);
1353         SET_D_ACCESS(cal, NULL);
1354         SET_X_ACCESS(cal, NULL);
1355         _DtCm_free_access_list4(cal->alist);
1356
1357         /* Split the access list to 3 differnt operation lists */
1358         while (e != NULL)
1359         {
1360                 if (e->access_type & access_read_4) {
1361                         q = _DtCm_make_access_entry4(e->who, e->access_type);
1362                         q->next = GET_R_ACCESS(cal);
1363                         SET_R_ACCESS(cal, q);
1364                 }
1365                 if (e->access_type & access_write_4) {
1366                         q = _DtCm_make_access_entry4(e->who, e->access_type);
1367                         q->next = GET_W_ACCESS(cal);
1368                         SET_W_ACCESS(cal, q);
1369                 }
1370                 if (e->access_type & access_delete_4) {
1371                         q = _DtCm_make_access_entry4(e->who, e->access_type);
1372                         q->next = GET_D_ACCESS(cal);
1373                         SET_D_ACCESS(cal, q);
1374                 }
1375                 if (e->access_type & access_exec_4) {
1376                         q = _DtCm_make_access_entry4(e->who, e->access_type);
1377                         q->next = GET_X_ACCESS(cal);
1378                         SET_X_ACCESS(cal, q);
1379                 }
1380                 e = e->next;
1381         }
1382
1383         cal->alist = _DtCmsCalendarAccessList(cal);
1384
1385         return (CSA_SUCCESS);
1386 }
1387
1388 /******************************************************************************
1389  * static functions used within the file
1390  ******************************************************************************/
1391
1392 static Exception_4
1393 append_exception_list(Appt_4 *p_appt, int ordinal)
1394 {
1395         Exception_4 p_excpt;
1396         Exception_4 p_prev;
1397         Exception_4 p_ex;
1398
1399         if ((p_excpt = (Exception_4)calloc(1, sizeof(*p_excpt))) == NULL)
1400                 return (NULL);
1401         p_excpt->ordinal = ordinal;
1402         p_prev = NULL;
1403         p_ex = p_appt->exception;
1404         while (p_ex != NULL)
1405         {
1406                 /* Exception list is in descending order for faster access */
1407                 if (ordinal > p_ex->ordinal)
1408                         break;
1409                 p_prev = p_ex;
1410                 p_ex = p_ex->next;
1411         }
1412         if (p_prev == NULL)
1413         {
1414                 p_excpt->next = p_appt->exception;
1415                 p_appt->exception = p_excpt;
1416         }
1417         else
1418         {
1419                 p_excpt->next = p_prev->next;
1420                 p_prev->next = p_excpt;
1421         }
1422
1423         return (p_excpt);
1424 }
1425
1426 /*
1427  * remove exceptions that are larger than ordinal
1428  */
1429 static void
1430 trunc_exception_list(Appt_4 *p_appt, int ordinal)
1431 {
1432         Exception_4 p_next;
1433         Exception_4 p_ex;
1434
1435         p_ex = p_appt->exception;
1436
1437         /* Exception list is in descending order for faster access */
1438         while ((p_ex != NULL) && (p_ex->ordinal > ordinal))
1439         {
1440                 p_next = p_ex->next;
1441                 free(p_ex);
1442                 p_ex = p_next;
1443         }
1444         p_appt->exception = p_ex;
1445 }
1446
1447 static int
1448 num_exception(Appt_4 *p_appt)
1449 {
1450         int ntimes;
1451         Except_4 *p;
1452
1453         ntimes = 0;
1454         p = p_appt->exception;
1455         while (p != NULL)
1456         {
1457                 ntimes++;
1458                 p = p->next;
1459         }
1460         return (ntimes);
1461 }
1462