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