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