57a21630b75d474b28fc1eabe308cb89e8a6e912
[oweals/cde.git] / cde / programs / dtcm / libDtCmP / util.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 /*******************************************************************************
24 **
25 **  util.c
26 **
27 **  $XConsortium: util.c /main/12 1996/11/21 19:44:40 drk $
28 **
29 **  RESTRICTED CONFIDENTIAL INFORMATION:
30 **
31 **  The information in this document is subject to special
32 **  restrictions in a confidential disclosure agreement between
33 **  HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
34 **  document outside HP, IBM, Sun, USL, SCO, or Univel without
35 **  Sun's specific written approval.  This document and all copies
36 **  and derivative works thereof must be returned or destroyed at
37 **  Sun's request.
38 **
39 **  Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
40 **
41 *******************************************************************************/
42
43 /*                                                                      *
44  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
45  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
46  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
47  * (c) Copyright 1993, 1994 Novell, Inc.                                *
48  */
49
50 #ifndef lint
51 #endif
52
53 #include <EUSCompat.h>
54 #include <stdio.h>
55 #include <stdarg.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58 #include <pwd.h> 
59 #include <netdb.h> 
60 #include <sys/utsname.h> /* SYS_NMLN */
61 #if defined(sun) || defined(USL) || defined(__uxp__)
62 #include <sys/systeminfo.h>
63 #else
64 #include <sys/dir.h>
65 #endif /* sun || USL || __uxp__ */
66 #include <sys/param.h>
67
68 #if (defined(USL) || defined(__uxp__)) && !defined(DOM_NM_LN)
69 #define DOM_NM_LN  BUFSIZ
70 #endif
71
72 #define X_INCLUDE_STRING_H
73 #define X_INCLUDE_TIME_H
74 #define XOS_USE_NO_LOCKING
75 #include <X11/Xos_r.h>
76
77 #include <errno.h>
78 #ifdef X_NOT_STDC_ENV
79 extern int errno;
80 #endif
81
82 #include "util.h"
83 #include "cm_tty.h"
84
85 extern int _csa_tick_to_iso8601(time_t, char *);
86 extern int _csa_iso8601_to_tick(char *, time_t*);
87
88 extern FILE     *popen(const char *, const char *);
89 extern int      pclose(FILE *);
90
91 /*
92  *
93  *  Function:   cm_def_printer
94  *
95  *  Purpose:    get the default printer name for SVR4   
96  *
97  *  Parameters: none     
98  *
99  *  Returns:    char* (printer name)        
100  *
101  */
102 extern char*
103 cm_def_printer()
104 {
105         FILE *fp;
106         char message[257];
107         char *tmp=NULL;
108         char *printer_name=NULL;
109
110 #ifdef SVR4
111         tmp = (char*)getenv("LPDEST");
112         if (tmp != NULL && *tmp != NULL) {
113                 printer_name = (char*)malloc(strlen(tmp)+1);
114                 strcpy(printer_name, tmp);
115         }
116         else {
117         
118         /* This is really nasty.  lpstat -d does *not* work on the AIX
119            machines.  Just fall back to "lp" here */
120
121 #ifndef AIX
122                 _Xstrtokparams strtok_buf;
123
124                 fp = (FILE *)popen("lpstat -d", "r");
125                 fread(message, 256, 1, fp);
126                 tmp = (char *)_XStrtok(message, ":", strtok_buf);
127                 tmp = (char *)_XStrtok((char *)NULL, "\n", strtok_buf);
128                 if (tmp != NULL && *tmp != NULL) {
129                         printer_name = (char*)malloc(strlen(tmp)+1);
130                         strcpy(printer_name, tmp);
131                 }
132                 else {
133                         printer_name = (char*)malloc(3);
134                         strcpy(printer_name, "lp");
135                 }
136
137                 /* close the process connection */
138                 pclose(fp);
139 #else
140                 printer_name = (char*)malloc(3);
141                 strcpy(printer_name, "lp");
142 #endif
143         }
144 #else
145         tmp = (char*)getenv("PRINTER");
146         if (tmp != NULL && *tmp != NULL) {
147                 printer_name = (char*)malloc(strlen(tmp)+1);
148                 strcpy(printer_name, tmp);
149         }
150         else {
151                 printer_name = (char*)malloc(3);
152                 strcpy(printer_name, "lw");
153         }
154 #endif
155         return printer_name;
156 }
157
158 /*--------------------------------------------------------------------------
159  * THE FOLLOWING STRING FUNCTION redefinitions are a HACK !
160  * 
161  * The cm code should be changed so that
162  *   a) the redefined functions use the same headers as in <string.h>
163  *   b) no redefinition of these library function is necessary
164  *
165  * The cm definitions use different function headers than in <string.h>
166  * Prefixing the functions will get rid of the resulting compiler error.
167  * Now cm functions will use the cm_ string functions, but library functions, 
168  * e.g. fprintf, will use strlen etc. which leads to core dumps. 
169  * As part of the bootstrapping process, I am including the below redefinitions
170  * of the system functions. This should be fixed later.
171  * [vmh - 5/31/90]
172  *--------------------------------------------------------------------------*/
173
174 extern char *
175 cm_strcpy(register char *s1, register char *s2)
176 {
177         if (s1==NULL || s2==NULL) return(NULL);
178         strcpy(s1, s2); 
179         return (s1);
180 }
181
182 extern int 
183 cm_strlen(register char *s)
184 {
185         register int n;
186  
187         if (s==NULL) return NULL;
188         return (strlen(s));
189 }
190
191 extern char *
192 cm_strdup (char *s1)
193 {
194         char *s2;
195         if (s1 == NULL) return NULL;
196         s2 = (char *) strdup(s1);
197         return (s2);
198 }
199
200 extern char *
201 cm_strcat(char *s1, char *s2)
202 {
203         if (s1==NULL || s2==NULL) return(s1);
204         strcat(s1, s2);
205         return s1;
206 }
207
208 /*      transform string patterns of \\ into \
209         \n into carriage returns and
210         \" into "       */
211
212 extern char *
213 str_to_cr(char *s)
214 {
215         int i, j, k;
216         char *newstr;
217
218         if (s==NULL) return(NULL);
219         i = cm_strlen(s);
220
221         newstr= (char *) ckalloc((unsigned)i + 1);
222         k = 0;
223         for (j=0; j<i; j++) {
224                 if (s[j]=='\\') {
225                         if (s[j+1]=='n') {
226                                 newstr[k] = '\n';
227                                 j++;
228                         }
229                         else if (s[j+1]=='\\') {
230                                 newstr[k] = '\\';
231                                 j++;
232                         }
233                         else if (s[j+1]=='\"') {
234                                 newstr[k] = '\"';
235                                 j++;
236                         }
237                         else {
238                                 newstr[k] = s[j];
239                         }
240                 }
241                 else {
242                         newstr[k] = s[j];
243                 }
244                 k++;
245         }
246         newstr[k] = NULL;
247         return(newstr);
248 }
249
250 /*      transform string patterns of \ into \\
251         carriage returns into \n, and
252         " into \"       */
253
254 extern char *
255 cr_to_str(char *s)
256 {
257         int i, j, k;
258         char *newstr;
259
260         if (s==NULL) return(NULL);
261         i = cm_strlen(s);
262
263         newstr = (char *) ckalloc((unsigned)((2 * i) + 1));
264         k = 0;
265         for (j=0; j<i; j++) {
266                 if (s[j]=='\n') {
267                         newstr[k] = '\\';
268                         newstr[k+1] = 'n';
269                         k+=2;
270                 }
271                 else if (s[j]=='\\') {
272                         newstr[k] = '\\';
273                         newstr[k+1] = '\\';
274                         k+=2;
275                 }
276                 else if (s[j]=='\"') {
277                         newstr[k] = '\\';
278                         newstr[k+1] = '\"';
279                         k+=2;
280                 }
281                 else {
282                         newstr[k] = s[j];
283                         k++;
284                 }
285         }
286         newstr[k] = NULL;
287         return(newstr);
288 }
289
290 /* VARARGS1 */
291 extern void
292 syserr(msg, a1, a2, a3)
293         char *msg;
294 {
295         /* Taken from Unix World, July 1989, p. 66 */
296         int saveerr;
297
298         /* save the error number so fprintf doesn't step on it */
299         saveerr = errno;
300
301         (void) fprintf(stderr, "cm: ");
302         /* print the actual message itself */
303         (void) fprintf(stderr, msg, a1, a2, a3);
304
305 #if 0
306         /* print the error, if any */
307         if (saveerr != 0) {
308                 if (saveerr < 0 || saveerr > sys_nerr) 
309                         (void) fprintf(stderr, ":Unknown error %d", saveerr);
310                 else 
311                         (void) fprintf(stderr, ":%s", sys_errlist[saveerr]);
312         }
313 #endif
314
315         /* thow a newline on the end */
316         (void) fprintf(stderr, "\n");
317
318         /* exit with an error */
319         if (saveerr==0)
320                 saveerr = -1;
321         exit(saveerr);
322 }
323
324
325 /*      Wrapper around standard storage allocation, to localize errors.
326         Taken from Unix World, July 1989, p. 66                         */
327 extern char *
328 ckalloc(unsigned int size)
329 {
330         register char *p;
331
332         /* try to get the memory */
333         p = (char *)calloc(1, size);
334
335         /* if it worked, return the memory directly */
336         if (p != NULL) return(p);
337
338         /* try allocation again */
339         p = (char *)calloc(1, size);
340
341         /* see if it worked the second time */
342         if (p != NULL) return(p);
343
344         /* no recovery available */
345         syserr("ckalloc: cannot allocate %d bytes", size, 0, 0);
346         return((char *)NULL);
347 }
348
349         
350 extern void
351 print_tick(Tick t)
352 {
353         char *a;
354         _Xctimeparams ctime_buf;
355  
356         a = _XCtime(&t, ctime_buf);
357         (void) fprintf (stderr, "%ld %s\n", (long)t, a);
358 }
359
360 int
361 min(int i1, int i2)
362 {
363         if (i1 > i2) return(i2);
364         if (i1 < i2) return(i1);
365         return(i1);
366 }
367
368 int
369 max(int i1, int i2)
370 {
371         if (i1 > i2) return(i1);
372         if (i1 < i2) return(i2);
373         return(i1);
374 }
375         
376 extern Lines *
377 text_to_lines(char *s, int n)
378 {
379         char *string, *line;
380         Lines *prev_l = NULL, *l = NULL, *head= NULL;
381         int i = 0;
382         char *_p;
383         int clen;
384
385         if (s == NULL || n <= 0) return NULL;
386
387         string = cm_strdup(s);
388         /*
389          * Here, look for \n, which is (in)famous character in IBM-932.
390          * Therefore, don't use strtok(). It is not i18n'ed.
391          */
392         for ( _p = string; *_p != '\0'; _p += clen ) {
393             clen = mblen( _p, MB_CUR_MAX );
394             if ( clen <= 0 ) {
395                 *_p = '\0';
396                 break;
397             }
398             if ( ( clen == 1 ) && ( *_p == '\n' ) ) {
399                 *_p = '\0';
400                 _p++;
401                 break;
402             }
403         }
404         line = string;
405         do {
406                 if (line == NULL) break;
407                 l = (Lines*)ckalloc(sizeof(Lines));
408                 if (head == NULL) head = l;
409                 if (prev_l != NULL) prev_l->next = l;
410                 l->s = cm_strdup(line);
411                 prev_l = l;
412                 i++;
413                 if ( ( *_p == '\0' ) || ( clen == -1 ) )
414                     break;
415                 line = _p;
416                 for ( ; *_p != '\0'; _p += clen ) {
417                     clen = mblen( _p, MB_CUR_MAX );
418                     if ( clen <= 0 ) {
419                         *_p = '\0';
420                         break;
421                     }
422                     if ( ( clen == 1 ) && ( *_p == '\n' ) ) {
423                         *_p = '\0';
424                         _p++;
425                         break;
426                     }
427                 }
428
429         } while (i < n);
430                 
431         free(string);
432         return head;
433 }
434  
435 extern void
436 destroy_lines(Lines *l)
437 {
438         Lines *p;
439
440         while (l != NULL) {
441                 free(l->s); l->s=NULL;
442                 p = l;
443                 l = l->next;
444                 free((char *)p); p=NULL;
445         }
446 }
447
448 /*
449  * Expand any escape characters in passed string
450  */
451 extern void
452 expand_esc_chars(char *string) {
453         char    *from, *to;
454
455         from = to = string;
456         while (from && *from) {
457                 int     len = mblen(from, MB_CUR_MAX);
458
459                 if (len <= 0) break; /* invalid char */
460                 if (len > 1) {  /* move over multibyte char */
461                         from += len;
462                         to += len;
463                         continue;
464                 }
465
466                 switch (*from++) {
467                 case '\\':
468                         switch (*from++) {
469                         case 'n':
470                                 *to++ = '\n';
471                                 break;
472                         case 't':
473                                 *to++ = '\t';
474                                 break;
475                         default:
476                                 *to++ = *(from-2);
477                                 *to++ = *(from-1);
478                                 break;
479                         }
480                         break;
481                 default:
482                         *to++ = *(from-1);
483                         break;
484                 }
485         }
486         *to = '\0';
487 }
488
489 extern char *
490 get_head(char *str, char sep)
491 {
492         static char buf[BUFSIZ];
493         char *ptr;
494
495         if (str == NULL)
496                 return(NULL);
497
498         ptr = buf;
499         while (*str && *str != sep)
500                 *ptr++ = *str++;
501         if (ptr == buf)
502                 return(NULL);
503         else {
504                 *ptr = NULL;
505                 return(cm_strdup(buf));
506         }
507 }
508
509 extern char *
510 get_tail(char *str, char sep)
511 {
512         char *ptr;
513  
514         if (str == NULL)
515                 return(NULL);
516  
517         while (*str && *str != sep)
518                 str++;
519         if (*str)
520                 return(cm_strdup(++str));
521         else
522                 return(NULL);
523 }
524
525 extern char *
526 cm_get_credentials()
527 {
528         char *name, *host;
529         static char *login = NULL;
530
531         if (login==NULL)
532         {
533                 name = (char*)cm_get_uname();
534                 host = (char*)cm_get_local_host();
535                 login = (char *) ckalloc (cm_strlen(name) + cm_strlen(host) + 2);
536                 sprintf(login, "%s@%s", name, host);
537         }
538         return (login);
539 }
540
541 extern char *
542 cm_get_local_host()
543 {
544         static char *local_host;
545
546         if (local_host == NULL) {
547 #if defined(sun) || defined(USL) || defined(__uxp__)
548                 local_host = (char *)ckalloc(MAXHOSTNAMELEN);
549                 (void) sysinfo(SI_HOSTNAME, local_host, MAXHOSTNAMELEN);
550 #else
551                 local_host = (char *)ckalloc(MAXHOSTNAMELEN);
552                 (void) gethostname(local_host, MAXHOSTNAMELEN);
553 #endif /* sun || USL || __uxp__ */
554         }
555         return local_host;
556 }
557
558 extern char *
559 cm_get_uname()
560 {
561         static char *name;
562         struct passwd *pw; 
563
564         if (name == NULL) {
565                 if ((pw = (struct passwd *)getpwuid(geteuid())) == NULL)
566                          name = (char *) cm_strdup("nobody");
567                 else
568                         name = (char *) cm_strdup(pw->pw_name);
569         }
570         return name;
571     
572 }
573
574 extern char *
575 cm_get_local_domain()
576 {
577         static char *local_domain;
578
579         if (local_domain == NULL) {
580                 local_domain = ckalloc(BUFSIZ);
581 #if defined(sun) || defined(USL) || defined(__uxp__)
582                 sysinfo(SI_SRPC_DOMAIN, local_domain, DOM_NM_LN);
583 #else
584                 (void) getdomainname(local_domain, BUFSIZ);
585 #endif /* sun || USL || __uxp__ */
586         }
587         return(local_domain);
588 }
589
590 /* partially qualified target */
591 extern char*
592 cm_pqtarget(char *name)
593 {
594         char *host, *target=NULL;
595  
596         host = (char*)strchr(name, '@');
597         if (host == NULL) {
598                 host = (char*)cm_get_local_host();
599                 target = (char *)ckalloc(cm_strlen(name) +
600                                 cm_strlen(host) + 2);
601                 sprintf(target, "%s@%s", name, host);
602         }
603         else
604                 target = (char *) cm_strdup(name);
605  
606         return target;
607 }
608 /*
609  * calendar_name@host[.domain] -> calendar_name
610  */
611 extern char *
612 cm_target2name(char *target)
613 {
614         return(get_head(target, '@'));
615 }
616  
617 /*
618  * calendar_name@host[.domain] -> host[.domain]
619  */
620 extern char *
621 cm_target2location(char *target)
622 {
623         return(get_tail(target, '@'));
624 }
625  
626 /*
627  * calendar_name@host[.domain] -> host
628  */
629 extern char *
630 cm_target2host(char *target)
631 {
632         char *location, *host;
633  
634         location = get_tail(target, '@');
635         if (location != NULL) {
636                 host = get_head(location, '.');
637                 free(location);
638                 return(host);
639         } else
640                 return(NULL);
641 }
642 /*
643  * calendar_name@host[.domain] -> domain
644  */
645 extern char *
646 cm_target2domain(char *target)
647 {
648         char *location, *domain;
649  
650         location = get_tail(target, '@');
651         if (location != NULL) {
652                 domain = get_tail(location, '.');
653                 free(location);
654                 return(domain);
655         } else
656                 return(NULL);
657 }
658
659 /*
660  * str consists of components separated by token
661  * get and copy the first component into comp and
662  * strip it out of str, so str would point to the first
663  * token or the null terminator.
664  */
665 static void
666 get_component(char **str, char *comp, char token)
667 {
668         char *ptr;
669
670         *comp = 0;
671
672         if (str == NULL)
673                 return;
674         else
675                 ptr = *str;
676
677         while (ptr && *ptr != 0 && *ptr != token)
678                 *comp++ = *ptr++;
679
680         *str = ptr;
681
682         *comp = 0;
683 }
684
685 /*
686  * head and tail points to the first and last character
687  * of a string which consists of components separated by token.
688  * get and copy the last component into comp and
689  * strip it out of the string, so tail would point to the last
690  * token or the head of the string.
691  */
692 static void
693 get_last_component(char *head, char **tail, char *comp, char token)
694 {
695         char *ptr, *cptr;
696
697         *comp = 0;
698
699         if (tail == NULL)
700                 return;
701         else
702                 cptr = *tail;
703
704         while (cptr != head && *cptr != token)
705                 cptr--;
706
707         if (*cptr == token)
708                 ptr = cptr + 1;
709         else
710                 ptr = cptr;
711
712         while (ptr != (*tail + 1))
713                 *comp++ = *ptr++;
714
715         *tail = cptr;
716
717         *comp = 0;
718 }
719
720 static boolean_t
721 match_forward(char *str1, char *str2)
722 {
723         char com1[BUFSIZ], com2[BUFSIZ];
724
725         if (str1 == NULL || str2 == NULL)
726                 return (B_FALSE);
727
728         while (B_TRUE) {
729                 get_component(&str1, com1, '.');
730                 get_component(&str2, com2, '.');
731
732                 if (*com1) {
733                         if (*com2 == NULL)
734                                 return (B_TRUE);
735                 } else {
736                         if (*com2 == NULL)
737                                 return (B_TRUE);
738                         else
739                                 return (B_FALSE);
740                 }
741
742                 if (strcasecmp(com1, com2) != 0)
743                         return (B_FALSE);
744
745                 /* take care of case: a.b a. */
746                 if (strcmp(str2, ".") == 0
747                     && (strcmp(str1, ".") != 0 || *str1 != NULL))
748                         return (B_FALSE);
749
750                 /* skip "." */
751                 if (*str1 == '.') {
752                         if (*str2 == NULL)
753                                 return (B_TRUE);
754                         else {
755                                 str1++;
756                                 str2++;
757                         }
758                 } else if (strcmp(str2, ".") == 0 || *str2 == NULL)
759                         return (B_TRUE);
760                 else
761                         return (B_FALSE);
762         }
763 }
764
765 static boolean_t
766 match_backward(char *str1, char *str2)
767 {
768         int len1, len2;
769         char *ptr1, *ptr2;
770         char com1[BUFSIZ], com2[BUFSIZ];
771
772         if (str1 == NULL || str2 == NULL)
773                 return (B_FALSE);
774
775         len1 = strlen(str1);
776         len2 = strlen(str2);
777         if (len2 > len1)
778                 return (B_FALSE);
779         else if (len2 == 0)
780                 return (B_TRUE);
781
782         ptr1 = (len1 ? (str1 + len1 - 1) : str1);
783         ptr2 = (len2 ? (str2 + len2 - 1) : str2);
784
785         if (*ptr1 == '.' && ptr1 != str1)
786                 ptr1--;
787
788         if (*ptr2 == '.' && ptr2 != str2)
789                 ptr2--;
790
791         while (B_TRUE) {
792                 get_last_component(str1, &ptr1, com1, '.');
793                 get_last_component(str2, &ptr2, com2, '.');
794
795                 if (*com1) {
796                         if (*com2 == NULL)
797                                 return (B_TRUE);
798                 } else {
799                         if (*com2 == NULL)
800                                 return (B_TRUE);
801                         else
802                                 return (B_FALSE);
803                 }
804
805                 if (strcasecmp(com1, com2) != 0)
806                         return (B_FALSE);
807
808                 /* skip "." */
809                 if (*ptr1 == '.') {
810                         if (ptr1 != str1)
811                                 ptr1--;
812                         else
813                                 return (B_FALSE); /* bad format */
814                 } else
815                         return (B_TRUE); /* done */
816
817                 if (*ptr2 == '.') {
818                         if (ptr2 != str2)
819                                 ptr2--;
820                         else
821                                 return (B_FALSE); /* bad format */
822                 } else
823                         return (B_TRUE); /* done */
824         }
825 }
826
827 /*
828  * Correct format assumed, i.e. str = label1[.label2 ...]
829  * Compare str2 against str1 which should be more fully qualified than str2
830  */
831 extern boolean_t
832 same_path(char *str1, char *str2)
833 {
834         char *ptr1,*ptr2;
835         char *user;
836         int res, n;
837
838         if (str1 == NULL || str2 == NULL)
839                 return(B_FALSE);
840
841         /* check format */
842         if (*str1 == '.' || *str2 == '.')
843                 return (B_FALSE); /* bad format */
844
845         if (match_forward(str1, str2) == B_TRUE)
846                 return (B_TRUE);
847         else
848                 return (match_backward(str1, str2));
849 }
850
851 /*
852  * compare user1 and user2
853  * user1 = user@host[.domain]
854  * user2 = any format in (user, user@host[.domain], user@domain)
855  */
856 extern boolean_t
857 same_user(char *user1, char *user2)
858 {
859         char *str1, *str2;
860         char *host, *domain;
861         char buf[BUFSIZ];
862         boolean_t res;
863
864         if (user1 == NULL || user2 == NULL)
865                 return B_FALSE;
866
867         /* compare user name */
868         str1 = get_head(user1, '@');
869         str2 = get_head(user2, '@');
870
871         if (str1 == NULL || str2 == NULL)
872                 return(B_FALSE);
873
874         if (strcmp(str1, str2)) {
875                 free(str1);
876                 free(str2);
877                 return(B_FALSE);
878         }
879         free(str1);
880         free(str2);
881
882         /* if only user name is specified, don't need to check domain */
883         str2 = strchr(user2, '@');
884         if (str2 == NULL)
885                 return(B_TRUE);
886
887         /* first assume user2=user@domain */
888         str1 = strchr(user1, '.');
889         if (str1 == NULL) {
890                 if (same_path(cm_get_local_domain(), ++str2))
891                         return(B_TRUE);
892         } else {
893                 if (same_path(++str1, ++str2))
894                         return(B_TRUE);
895         }
896
897         /* assume user2=user@host[.domain] */
898         if (str1 == NULL) {
899                 str1 = strchr(user1, '@');
900                 sprintf(buf, "%s.%s", ++str1, cm_get_local_domain());
901                 str1 = buf;
902         } else {
903                 str1 = strchr(user1, '@');
904                 str1++;
905         }
906
907         if (same_path(str1, str2))
908                 return(B_TRUE);
909         else
910                 return(B_FALSE);
911 }
912
913 /*
914  * A blank line is one that consists of only \b, \t or \n.
915  */
916 extern int
917 blank_buf(char *buf)
918 {
919         char *ptr = buf;
920
921         if (ptr == NULL) return B_TRUE;
922         while (ptr && (*ptr == ' ' || *ptr == '\t' || *ptr == '\n'))
923                 ptr++;
924         if (*ptr == '\0')
925                 return B_TRUE;
926         else
927                 return B_FALSE;
928 }
929
930 extern int
931 embedded_blank(char *buf)
932 {
933         char *ptr = buf;
934  
935         if (ptr == NULL) return B_TRUE;
936         while (ptr && *ptr) {
937                 if ((*ptr == ' ') || (*ptr == '\t'))
938                         return B_TRUE;
939                 *ptr++;
940         }
941  
942         return B_FALSE;
943 }
944
945 extern int
946 get_data_version(CSA_session_handle session) {
947         int             ver = 0;
948         Dtcm_calendar   *c;
949         CSA_attribute_reference names[1];
950         CSA_uint32      number_attrs_returned;
951         CSA_attribute   *attrs_returned;
952
953         names[0] = CSA_X_DT_CAL_ATTR_DATA_VERSION;
954
955
956         if (csa_read_calendar_attributes(session, 
957                                          1, 
958                                          names, 
959                                          &number_attrs_returned, 
960                                          &attrs_returned, 
961                                          NULL) == CSA_SUCCESS) {
962                 ver = attrs_returned[0].value->item.uint32_value;
963                 csa_free(attrs_returned);
964         }
965
966         return ver;
967 }
968
969 extern int
970 get_server_version(CSA_session_handle session) {
971         int             ver = 0;
972         Dtcm_calendar   *c;
973         CSA_attribute_reference names[1];
974         CSA_uint32      number_attrs_returned;
975         CSA_attribute   *attrs_returned;
976
977         names[0] = CSA_X_DT_CAL_ATTR_SERVER_VERSION;
978
979         if (csa_read_calendar_attributes(session, 
980                                          1, 
981                                          names, 
982                                          &number_attrs_returned, 
983                                          &attrs_returned, 
984                                          NULL) == CSA_SUCCESS) {
985                 ver = attrs_returned[0].value->item.uint32_value;
986                 csa_free(attrs_returned);
987         }
988
989         return ver;
990 }
991
992 extern CSA_sint32
993 privacy_set(Dtcm_appointment *appt) {
994
995         CSA_sint32      privacy = CSA_CLASS_PUBLIC;
996
997         if (!appt)
998                 return(privacy);
999
1000         if (!appt->private)
1001                 return(privacy);
1002
1003         if (!appt->private->value)
1004                 return(privacy);
1005
1006         privacy = appt->private->value->item.sint32_value;
1007         return(privacy);
1008
1009 }
1010
1011 extern CSA_sint32
1012 showtime_set(Dtcm_appointment *appt) {
1013  
1014         CSA_sint32      showtime = 0;
1015  
1016         if (!appt)
1017                 return(showtime);
1018  
1019         if (!appt->show_time)
1020                 return(showtime);
1021  
1022         if (!appt->show_time->value)
1023                 return(showtime);
1024  
1025         showtime = appt->show_time->value->item.sint32_value;
1026         return(showtime);
1027  
1028 }
1029
1030 /*
1031 **  Parse the date string and get the month, day, and year
1032 */
1033 extern int
1034 parse_date(OrderingType order, SeparatorType sep, char *datestr, char *m,
1035         char *d, char *y) {
1036  
1037         char *first, *second, *third;
1038         char *tmp_date, *str = separator_str(sep);
1039         _Xstrtokparams strtok_buf;
1040  
1041         m[0] = NULL;
1042         d[0] = NULL;
1043         y[0] = NULL;
1044  
1045         if (datestr == NULL)
1046                 return 0;
1047  
1048         tmp_date = cm_strdup(datestr);
1049         first = _XStrtok(tmp_date, str, strtok_buf);
1050                 /*
1051                 ** Check to see if the date entered has legit separator
1052                 */
1053                 if ( strcoll(first, datestr) == 0 ) {
1054                         free(tmp_date);
1055                         return 0;
1056                 }
1057         second = _XStrtok(NULL, str, strtok_buf);
1058         third = _XStrtok(NULL, str, strtok_buf);
1059  
1060         switch (order) {
1061         case ORDER_DMY:
1062                 if (second)
1063                         cm_strcpy(m, second);
1064                 if (first)
1065                         cm_strcpy(d, first);
1066                 if (third)
1067                         cm_strcpy(y, third);
1068                 break;
1069         case ORDER_YMD:
1070                 if (second)
1071                         cm_strcpy(m, second);
1072                 if (third)
1073                         cm_strcpy(d, third);
1074                 if (first)
1075                         cm_strcpy(y, first);
1076                 break;
1077         case ORDER_MDY:
1078         default:
1079                 if (first)
1080                         cm_strcpy(m, first);
1081                 if (second)
1082                         cm_strcpy(d, second);
1083                 if (third)
1084                         cm_strcpy(y, third);
1085                 break;
1086         }
1087         free(tmp_date);
1088                 return 1;
1089 }
1090
1091 /*
1092 **  Reformat the date string into m/d/y format and write it into the buffer
1093 */
1094 extern int
1095 datestr2mdy(char *datestr, OrderingType order, SeparatorType sep, char *buf) {
1096         char m[3], d[3], y[5];
1097  
1098         buf[0] = NULL;
1099         if (datestr == NULL)
1100                 return 0;
1101  
1102         if (order == ORDER_MDY && sep == SEPARATOR_SLASH)
1103                 cm_strcpy(buf, datestr);
1104         else {
1105                 if ( parse_date(order, sep, datestr, m, d, y) ) {
1106                         sprintf(buf, "%s/%s/%s", m, d, y);
1107                 } else {
1108                         return 0;
1109                 }
1110  
1111         }
1112         return 1;
1113 }
1114
1115 /*
1116 **  Format the date according to display property and write it into buffer
1117 */
1118 extern void
1119 format_tick(Tick tick, OrderingType order, SeparatorType sep, char *buff) {
1120         char            *str = separator_str(sep);
1121         struct tm       *tm;
1122         _Xltimeparams   localtime_buf;
1123  
1124         buff[0] = NULL;
1125         tm = _XLocaltime(&tick, localtime_buf);
1126  
1127         switch (order) {
1128         case ORDER_DMY:
1129                 sprintf(buff, "%d%s%d%s%d", tm->tm_mday, str,
1130                         tm->tm_mon+1, str, tm->tm_year+1900);
1131                 break;
1132         case ORDER_YMD:
1133                 sprintf(buff, "%d%s%d%s%d", tm->tm_year+1900, str,
1134                         tm->tm_mon+1, str, tm->tm_mday);
1135                 break;
1136         case ORDER_MDY:
1137         default:
1138                 sprintf(buff, "%d%s%d%s%d", tm->tm_mon+1, str,
1139                         tm->tm_mday, str, tm->tm_year+1900);
1140                 break;
1141         }
1142 }
1143
1144 extern void
1145 format_time(Tick t, DisplayType dt, char *buffer) {
1146         int             hr = hour(t);
1147         boolean_t       am;
1148
1149         if (t == 0) {
1150                 sprintf(buffer, "\0");
1151
1152         } else if (dt == HOUR12) {
1153                 am = adjust_hour(&hr);
1154                 sprintf(buffer, "%2d:%02d%s",
1155                         hr, minute(t), (am) ? "am" : "pm");
1156         } else
1157                 sprintf(buffer, "%02d%02d", hr, minute(t));
1158 }
1159
1160 /*
1161  * The V5 back end uses arrays to pass attributes back and forth.  However,
1162  * keeping hard coded references into those arrays (i.e. declaring the tick
1163  * value will always be into position 0 of the array, the what value in
1164  * position 3, etc.) is a bad idea and hard to maintain.
1165  *
1166  * Thus these convenience functions will translate from an attribute array
1167  * received from the back end into defined structure which the front end can
1168  * use.
1169  *
1170  * IF YOU UPDATE THE STRUCTURES, MAKE SURE YOU UPDATE THESE COUNT CONSTANTS!!
1171  */
1172 static const int APPT_ATTR_COUNT = 35;
1173 static const int RW_APPT_ATTR_COUNT = 15;
1174 static const int CAL_ATTR_COUNT = 12;
1175 static const int RW_CAL_ATTR_COUNT = 2;
1176 static const int DEF_V5_APPT_ATTR_COUNT = 22;
1177 static const int DEF_V4_APPT_ATTR_COUNT = 20;
1178 static const int DEF_V3_APPT_ATTR_COUNT = 17;
1179 static const int DEF_CAL_ATTR_COUNT = 6;
1180 static const int default_appt_attrs[] = {CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I,
1181                         CSA_ENTRY_ATTR_LAST_UPDATE_I,
1182                         CSA_ENTRY_ATTR_ORGANIZER_I,
1183                         CSA_ENTRY_ATTR_START_DATE_I,
1184                         CSA_ENTRY_ATTR_TYPE_I,
1185                         CSA_ENTRY_ATTR_SUBTYPE_I,
1186                         CSA_ENTRY_ATTR_CLASSIFICATION_I,
1187                         CSA_ENTRY_ATTR_END_DATE_I,
1188                         CSA_X_DT_ENTRY_ATTR_SHOWTIME_I,
1189                         CSA_ENTRY_ATTR_SUMMARY_I,
1190                         CSA_ENTRY_ATTR_STATUS_I,
1191                         CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE_I,
1192                         CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES_I,
1193                         CSA_ENTRY_ATTR_AUDIO_REMINDER_I,
1194                         CSA_ENTRY_ATTR_FLASHING_REMINDER_I,
1195                         CSA_ENTRY_ATTR_MAIL_REMINDER_I,
1196                         CSA_ENTRY_ATTR_POPUP_REMINDER_I,
1197                         CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM_I,
1198                         CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL_I,
1199                         CSA_X_DT_ENTRY_ATTR_SEQUENCE_END_DATE_I,
1200                         CSA_ENTRY_ATTR_RECURRENCE_RULE_I,
1201                         CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I
1202                         };
1203 static const int default_cal_attrs[] = {CSA_CAL_ATTR_ACCESS_LIST_I,
1204                                         CSA_CAL_ATTR_CALENDAR_NAME_I,
1205                                         CSA_CAL_ATTR_CALENDAR_SIZE_I,
1206                                         CSA_CAL_ATTR_NUMBER_ENTRIES_I,
1207                                         CSA_CAL_ATTR_TIME_ZONE_I,
1208                                         CSA_X_DT_CAL_ATTR_DATA_VERSION_I
1209                                         };
1210
1211 /*
1212  * NOTE that this loop is dependent on the first appointment attribute define
1213  * (DT_CM_ATTR_IDENTIFER_I) in the api - if that is changed and is no longer
1214  * the first appointment define, this needs to be changed.
1215  *
1216  * NOTE that this function checks if the api indexes specified are read-only:
1217  * This assumes that if you need value space (and have set the need_value_space
1218  * flag to B_TRUE), you're setting attributes and since you can't set read-only
1219  * attributes, it will ignore read-only attributes if the need_value_space flag
1220  * is B_TRUE.
1221  */
1222 Dtcm_appointment *allocate_appt_struct (Allocation_reason reason, int version, ...) {
1223         int                     idx = 0, api_idx;
1224         va_list                 pvar;
1225         CmDataList              *api_ids = CmDataListCreate();
1226         Dtcm_appointment        *appt;
1227         int                     def_attr_count;
1228
1229         /*
1230          * The Dtcm_appointment wrapper array
1231          */
1232         idx = sizeof(Dtcm_appointment);
1233         appt = (Dtcm_appointment *)ckalloc(idx);
1234         memset(appt, NULL, idx);
1235         appt->reason = reason;
1236         appt->version = version;
1237
1238         /*
1239          * Step through the variable argument list and build the list of
1240          * attributes we're looking for
1241          */
1242         va_start(pvar, version);
1243         api_idx = va_arg(pvar, int);
1244         while (api_idx) {
1245                 if ((reason == appt_read) || !entry_ident_index_ro(api_idx, version))
1246                         CmDataListAdd(api_ids, (void *)api_idx, 0);
1247                 api_idx = va_arg(pvar, int);
1248         }
1249         va_end(pvar);
1250
1251         /*
1252          * No attributes specified, assume the caller wanted all of them
1253          */
1254         if (api_ids->count <= 0) {
1255
1256                 if ((version == DATAVER2) || (version == DATAVER1))
1257                         def_attr_count = DEF_V3_APPT_ATTR_COUNT;
1258                 else if (version == DATAVER3)
1259                         def_attr_count = DEF_V4_APPT_ATTR_COUNT;
1260                 else if (version == DATAVER4)
1261                         def_attr_count = DEF_V5_APPT_ATTR_COUNT;
1262                 else if (version == DATAVER_ARCHIVE)
1263                         def_attr_count = DEF_V5_APPT_ATTR_COUNT;
1264
1265                 for (idx = 0; idx < def_attr_count; idx++) {
1266                         if ((reason == appt_write) && entry_ident_index_ro(default_appt_attrs[idx], version))
1267                                 continue;
1268                         CmDataListAdd(api_ids, (void *)default_appt_attrs[idx], 0);
1269                 }
1270         }
1271
1272         /*
1273          * We've determined the number of attributes we're retrieving, so
1274          * allocate the name array, and the attribute array (if we are 
1275          * going to be writing attributes).
1276          */
1277
1278         appt->num_names = api_ids->count;
1279         idx = sizeof(CSA_attribute_reference *) * appt->num_names;
1280         appt->names = (CSA_attribute_reference *)ckalloc(idx);
1281         memset(appt->names, NULL, idx);
1282
1283         appt->count = api_ids->count;
1284         if (reason == appt_write) {
1285                 idx = sizeof(CSA_attribute) * appt->count;
1286                 appt->attrs = (CSA_attribute *)ckalloc(idx);
1287                 memset(appt->attrs, NULL, idx);
1288         }
1289
1290         /*
1291          * Now loop through and set the names and initialize the attributes
1292          */
1293         for (idx = 0; idx < appt->count; idx++) {
1294                 api_idx = (int)CmDataListGetData(api_ids, idx + 1);
1295                 appt->names[idx] = strdup(_CSA_entry_attribute_names[api_idx]);
1296                 if (reason == appt_write)
1297                         initialize_entry_attr(api_idx, &appt->attrs[idx], reason, version);
1298         }
1299
1300         if (reason == appt_write)
1301                 set_appt_links(appt);
1302
1303         CmDataListDestroy(api_ids, NULL);
1304         return appt;
1305 }
1306
1307 CSA_return_code
1308 query_appt_struct(CSA_session_handle session, 
1309                   CSA_entry_handle entry_handle, 
1310                   Dtcm_appointment *appt) {
1311
1312         CSA_return_code         status;
1313
1314         /* if there is old query material laying around, toss it */
1315
1316         if (appt->filled) {
1317                 csa_free(appt->attrs);
1318                 appt->filled = False;
1319         }
1320
1321
1322         if ((status = csa_read_entry_attributes(session, 
1323                                          entry_handle, 
1324                                          appt->num_names, 
1325                                          appt->names, 
1326                                          &appt->count, 
1327                                          &appt->attrs, 
1328                                          NULL)) == CSA_SUCCESS) {
1329                 set_appt_links(appt);
1330                 appt->filled = True;
1331         }
1332
1333         return(status);
1334 }
1335
1336
1337 /*
1338  * NOTE that this function checks if the api indexes specified are read-only:
1339  * This assumes that if you need value space (and have set the need_value_space
1340  * flag to B_TRUE), you're setting attributes and since you can't set read-only
1341  * attributes, it will ignore read-only attributes if the need_value_space flag
1342  * is B_TRUE.
1343  */
1344 Dtcm_calendar*
1345 allocate_cal_struct(Allocation_reason reason, int version, ...) {
1346         int             idx = 0, api_idx;
1347         va_list         pvar;
1348         CmDataList      *api_ids = CmDataListCreate();
1349         Dtcm_calendar   *cal;
1350
1351         /*
1352          * The Dtcm_apopintment wrapper array
1353          */
1354         idx = sizeof(Dtcm_calendar);
1355         cal = (Dtcm_calendar *)ckalloc(idx);
1356         memset(cal, NULL, idx);
1357         cal->reason = reason;
1358         cal->version = version;
1359
1360         /*
1361          * Step through the variable argument list and build the list of
1362          * attributes we're looking for
1363          */
1364         va_start(pvar, version);
1365         api_idx = va_arg(pvar, int);
1366         while (api_idx) {
1367                 if ((reason == appt_read) || !cal_ident_index_ro(api_idx, version))
1368                         CmDataListAdd(api_ids, (void *)api_idx, 0);
1369                 api_idx = va_arg(pvar, int);
1370         }
1371         va_end(pvar);
1372
1373         /*
1374          * No attributes specified, assume the caller wanted all of them
1375          */
1376         if (api_ids->count <= 0) {
1377                 for (idx = 0; idx < DEF_CAL_ATTR_COUNT; idx++) {
1378                         if ((reason == appt_write) && cal_ident_index_ro(default_cal_attrs[idx], version))
1379                                 continue;
1380                         CmDataListAdd(api_ids, (void *)default_cal_attrs[idx], 0);
1381                 }
1382         }
1383
1384         /*
1385          * We've determined the number of attributes we're retrieving, so
1386          * allocate the name arrya, and the attribute array (if we are 
1387          * going to be writing attributes).
1388          */
1389
1390         cal->num_names = api_ids->count;
1391         idx = sizeof(CSA_attribute_reference) * cal->num_names;
1392         cal->names = (CSA_attribute_reference *)ckalloc(idx);
1393         memset(cal->names, NULL, idx);
1394
1395         cal->count = api_ids->count;
1396         if (reason == appt_write) {
1397                 idx = sizeof(CSA_attribute) * cal->count;
1398                 cal->attrs = (CSA_attribute *)ckalloc(idx);
1399                 memset(cal->attrs, NULL, idx);
1400         }
1401
1402         /*
1403          * Now loop through and set the names and initialize the attributes
1404          */
1405         for (idx = 0; idx < cal->count; idx++) {
1406                 api_idx = (int)CmDataListGetData(api_ids, idx + 1);
1407                 cal->names[idx] = strdup(_CSA_calendar_attribute_names[api_idx]);
1408                 if (reason == appt_write)
1409                         initialize_cal_attr(api_idx, &cal->attrs[idx], reason, version);
1410         }
1411
1412         if (reason == appt_write) 
1413                 set_cal_links(cal);
1414
1415         CmDataListDestroy(api_ids, NULL);
1416
1417
1418         return cal;
1419 }
1420
1421 CSA_return_code
1422 query_cal_struct(CSA_session_handle session, 
1423                  Dtcm_calendar *cal) {
1424
1425         CSA_return_code status;
1426
1427         /* if there is old query material laying around, toss it */
1428
1429         if (cal->filled) {
1430                 csa_free(cal->attrs);
1431                 cal->filled = False;
1432         }
1433
1434         if ((status = csa_read_calendar_attributes(session, 
1435                                          cal->num_names, 
1436                                          cal->names, 
1437                                          &cal->count, 
1438                                          &cal->attrs, 
1439                                          NULL)) == CSA_SUCCESS) {
1440                 set_cal_links(cal);
1441                 cal->filled = True;
1442         }
1443
1444         return(status);
1445 }
1446
1447 extern void
1448 scrub_cal_attr_list(Dtcm_calendar *cal) {
1449
1450         int     i;
1451
1452         for (i = 0; i < cal->count; i++) {
1453                 if (cal->attrs[i].value->type == CSA_VALUE_REMINDER) {
1454                         if ((cal->attrs[i].value->item.reminder_value->lead_time == NULL) || 
1455                              (cal->attrs[i].value->item.reminder_value->lead_time[0] == NULL)) {
1456                                 free(cal->attrs[i].name);
1457                                 cal->attrs[i].name = NULL;
1458                         }
1459                 }
1460                 else if ((cal->attrs[i].value->type == CSA_VALUE_ACCESS_LIST) && (cal->attrs[i].value->item.access_list_value == NULL)) {
1461                         free(cal->attrs[i].name);
1462                         cal->attrs[i].name = NULL;
1463                 }
1464                 else if ((cal->attrs[i].value->type == CSA_VALUE_STRING) && (cal->attrs[i].value->item.string_value == NULL)) {
1465                         free(cal->attrs[i].name);
1466                         cal->attrs[i].name = NULL;
1467                 }
1468                 else if ((cal->attrs[i].value->type == CSA_VALUE_DATE_TIME) && (cal->attrs[i].value->item.date_time_value == NULL)) {
1469                         free(cal->attrs[i].name);
1470                         cal->attrs[i].name = NULL;
1471                 }
1472         }
1473 }
1474
1475 extern boolean_t
1476 cal_ident_index_ro(int id, int version) {
1477         boolean_t       r_ro;
1478
1479         switch(id) {
1480         case CSA_CAL_ATTR_CALENDAR_NAME_I:
1481         case CSA_CAL_ATTR_CALENDAR_OWNER_I:
1482         case CSA_CAL_ATTR_CALENDAR_SIZE_I:
1483         case CSA_CAL_ATTR_CHARACTER_SET_I:
1484         case CSA_CAL_ATTR_NUMBER_ENTRIES_I:
1485         case CSA_CAL_ATTR_DATE_CREATED_I:
1486         case CSA_CAL_ATTR_PRODUCT_IDENTIFIER_I:
1487         case CSA_X_DT_CAL_ATTR_DATA_VERSION_I:
1488         case CSA_CAL_ATTR_TIME_ZONE_I:
1489                 r_ro = B_TRUE;
1490                 break;
1491         default:
1492                 r_ro = B_FALSE;
1493                 break;
1494         }
1495
1496         return r_ro;
1497 }
1498
1499 extern boolean_t
1500 entry_ident_index_ro(int id, int version) {
1501         boolean_t       r_ro;
1502
1503         switch(id) {
1504         case CSA_ENTRY_ATTR_DATE_CREATED_I:
1505         case CSA_ENTRY_ATTR_LAST_UPDATE_I:
1506         case CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I:
1507         case CSA_ENTRY_ATTR_ORGANIZER_I:
1508         case CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I:
1509         case CSA_ENTRY_ATTR_SEQUENCE_NUMBER_I:
1510                 r_ro = B_TRUE;
1511                 break;
1512         case CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE_I:
1513         case CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES_I:
1514         case CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL_I:
1515         case CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM_I:
1516         case CSA_X_DT_ENTRY_ATTR_SEQUENCE_END_DATE_I:
1517                 if (version >= DATAVER4)
1518                         r_ro = B_TRUE;
1519                 else
1520                         r_ro = B_FALSE;
1521                 break;
1522         default:
1523                 r_ro = B_FALSE;
1524                 break;
1525         }
1526
1527         return r_ro;
1528 }
1529
1530 extern CSA_enum
1531 cal_ident_index_tag(int id) {
1532         CSA_enum        r_tag;
1533
1534         switch(id) {
1535         case CSA_CAL_ATTR_CALENDAR_NAME_I:
1536         case CSA_CAL_ATTR_CHARACTER_SET_I:
1537         case CSA_CAL_ATTR_COUNTRY_I:
1538         case CSA_CAL_ATTR_PRODUCT_IDENTIFIER_I:
1539         case CSA_CAL_ATTR_TIME_ZONE_I:
1540         case CSA_CAL_ATTR_LANGUAGE_I:
1541                 r_tag = CSA_VALUE_STRING;
1542                 break;
1543         case CSA_CAL_ATTR_CALENDAR_OWNER_I:
1544                 r_tag = CSA_VALUE_CALENDAR_USER;
1545                 break;
1546         case CSA_CAL_ATTR_DATE_CREATED_I:
1547                 r_tag = CSA_VALUE_DATE_TIME;
1548                 break;
1549         case CSA_CAL_ATTR_CALENDAR_SIZE_I:
1550         case CSA_CAL_ATTR_NUMBER_ENTRIES_I:
1551         case CSA_X_DT_CAL_ATTR_DATA_VERSION_I:
1552                 r_tag = CSA_VALUE_UINT32;
1553                 break;
1554         case CSA_CAL_ATTR_ACCESS_LIST_I:
1555                 r_tag = CSA_VALUE_ACCESS_LIST;
1556                 break;
1557         case CSA_CAL_ATTR_WORK_SCHEDULE_I:
1558         default:
1559                 r_tag = CSA_VALUE_OPAQUE_DATA;
1560                 break;
1561         }
1562
1563         return r_tag;
1564 }
1565
1566 extern CSA_enum
1567 entry_ident_index_tag(int id) {
1568         CSA_enum        r_tag;
1569
1570         switch(id) {
1571         case CSA_ENTRY_ATTR_DESCRIPTION_I:
1572         case CSA_ENTRY_ATTR_EXCEPTION_RULE_I:
1573         case CSA_ENTRY_ATTR_RECURRENCE_RULE_I:
1574         case CSA_ENTRY_ATTR_SUBTYPE_I:
1575         case CSA_ENTRY_ATTR_SUMMARY_I:
1576                 r_tag = CSA_VALUE_STRING;
1577                 break;
1578         case CSA_ENTRY_ATTR_DATE_COMPLETED_I:
1579         case CSA_ENTRY_ATTR_DATE_CREATED_I:
1580         case CSA_ENTRY_ATTR_DUE_DATE_I:
1581         case CSA_ENTRY_ATTR_END_DATE_I:
1582         case CSA_ENTRY_ATTR_LAST_UPDATE_I:
1583         case CSA_ENTRY_ATTR_START_DATE_I:
1584         case CSA_X_DT_ENTRY_ATTR_SEQUENCE_END_DATE_I:
1585                 r_tag = CSA_VALUE_DATE_TIME;
1586                 break;
1587         case CSA_ENTRY_ATTR_EXCEPTION_DATES_I:
1588         case CSA_ENTRY_ATTR_RECURRING_DATES_I:
1589                 r_tag = CSA_VALUE_DATE_TIME_LIST;
1590                 break;
1591         case CSA_ENTRY_ATTR_CLASSIFICATION_I:
1592         case CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I:
1593         case CSA_ENTRY_ATTR_PRIORITY_I:
1594         case CSA_ENTRY_ATTR_SEQUENCE_NUMBER_I:
1595         case CSA_ENTRY_ATTR_STATUS_I:
1596         case CSA_ENTRY_ATTR_TYPE_I:
1597         case CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES_I:
1598         case CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL_I:
1599                 r_tag = CSA_VALUE_UINT32;
1600                 break;
1601         case CSA_ENTRY_ATTR_TIME_TRANSPARENCY_I:
1602         case CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE_I:
1603         case CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM_I:
1604         case CSA_X_DT_ENTRY_ATTR_SHOWTIME_I:
1605                 r_tag = CSA_VALUE_SINT32;
1606                 break;
1607         case CSA_ENTRY_ATTR_AUDIO_REMINDER_I:
1608         case CSA_ENTRY_ATTR_FLASHING_REMINDER_I:
1609         case CSA_ENTRY_ATTR_MAIL_REMINDER_I:
1610         case CSA_ENTRY_ATTR_POPUP_REMINDER_I:
1611                 r_tag = CSA_VALUE_REMINDER;
1612                 break;
1613         case CSA_ENTRY_ATTR_ORGANIZER_I:
1614         case CSA_ENTRY_ATTR_SPONSOR_I:
1615                 r_tag = CSA_VALUE_CALENDAR_USER;
1616                 break;
1617         case CSA_ENTRY_ATTR_ATTENDEE_LIST_I:
1618                 r_tag = CSA_VALUE_ATTENDEE_LIST;
1619                 break;
1620         case CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I:
1621         default:
1622                 r_tag = CSA_VALUE_OPAQUE_DATA;
1623                 break;
1624         }
1625
1626         return r_tag;
1627 }
1628
1629 extern boolean_t
1630 ident_name_ro(char *name, int version) {
1631         boolean_t       r_ro = B_FALSE;
1632
1633         if (strcmp(name, CSA_CAL_ATTR_CALENDAR_NAME) == 0 ||
1634             strcmp(name, CSA_CAL_ATTR_CALENDAR_OWNER) == 0 ||
1635             strcmp(name, CSA_CAL_ATTR_CALENDAR_SIZE) == 0 ||
1636             strcmp(name, CSA_CAL_ATTR_DATE_CREATED) == 0 ||
1637             strcmp(name, CSA_CAL_ATTR_PRODUCT_IDENTIFIER) == 0 ||
1638             strcmp(name, CSA_X_DT_CAL_ATTR_DATA_VERSION) == 0 ||
1639             strcmp(name, CSA_ENTRY_ATTR_SEQUENCE_NUMBER) == 0 ||
1640             strcmp(name, CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER) == 0 ||
1641             strcmp(name, CSA_ENTRY_ATTR_ORGANIZER) == 0 ||
1642             strcmp(name, CSA_ENTRY_ATTR_LAST_UPDATE) == 0 ||
1643             strcmp(name, CSA_ENTRY_ATTR_DATE_CREATED) == 0 ||
1644             strcmp(name, CSA_ENTRY_ATTR_NUMBER_RECURRENCES) == 0)
1645                 r_ro = B_TRUE;
1646
1647         if ((version >= DATAVER4) &&
1648             (strcmp(name, CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE) == 0 ||
1649              strcmp(name, CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES) == 0 ||
1650              strcmp(name, CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL) == 0 ||
1651              strcmp(name, CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM) == 0))
1652                 r_ro = B_TRUE;
1653
1654         return r_ro;
1655 }
1656
1657 extern void
1658 initialize_cal_attr(int id, CSA_attribute *attrs, Allocation_reason reason, int version) {
1659         int     size;
1660
1661         attrs->name = cm_strdup(_CSA_calendar_attribute_names[id]);
1662         if ((reason == appt_write) && !cal_ident_index_ro(id, version)) {
1663                 size = sizeof(CSA_attribute_value);
1664                 attrs->value = (CSA_attribute_value *)ckalloc(size);
1665                 memset(attrs->value, NULL, size);
1666                 attrs->value->type = cal_ident_index_tag(id);
1667                 if (attrs->value->type == CSA_VALUE_REMINDER)
1668                         attrs->value->item.reminder_value = (CSA_reminder *) calloc(sizeof(CSA_reminder), 1);
1669         }
1670 }
1671
1672 static void
1673 free_attr(CSA_attribute *attr) {
1674
1675         if (attr == NULL)
1676                 return;
1677
1678         if (attr->name)
1679                 free(attr->name);
1680
1681
1682         if (attr->value){
1683                 if ((attr->value->type == CSA_VALUE_STRING) && attr->value->item.string_value != NULL)
1684                         free(attr->value->item.string_value);
1685                 else if ((attr->value->type == CSA_VALUE_DATE_TIME) && attr->value->item.date_time_value != NULL)
1686                         free(attr->value->item.date_time_value);
1687                 else if ((attr->value->type == CSA_VALUE_REMINDER) && attr->value->item.reminder_value != NULL) {
1688                         if (attr->value->item.reminder_value->lead_time)
1689                                 free(attr->value->item.reminder_value->lead_time);
1690                         if (attr->value->item.reminder_value->reminder_data.data)
1691                                 free(attr->value->item.reminder_value->reminder_data.data);
1692
1693                         free(attr->value->item.reminder_value);
1694                 }
1695
1696                 free(attr->value);
1697
1698         }
1699 }
1700
1701 extern void
1702 initialize_entry_attr(int id, CSA_attribute *attrs, Allocation_reason reason, int version) {
1703         int     size;
1704
1705         attrs->name = cm_strdup(_CSA_entry_attribute_names[id]);
1706         if ((reason == appt_write) && !entry_ident_index_ro(id, version)) {
1707                 size = sizeof(CSA_attribute_value);
1708                 attrs->value = (CSA_attribute_value *)ckalloc(size);
1709                 memset(attrs->value, NULL, size);
1710                 attrs->value->type = entry_ident_index_tag(id);
1711                 if (attrs->value->type == CSA_VALUE_REMINDER)
1712                         attrs->value->item.reminder_value = (CSA_reminder *) calloc(sizeof(CSA_reminder), 1);
1713         }
1714 }
1715
1716 extern void
1717 free_appt_struct(Dtcm_appointment **appt) {
1718         int     i;
1719
1720         if (!appt)
1721                 return;
1722
1723         if ((*appt)->names) {
1724                 for (i = 0; i < (*appt)->num_names; i++)
1725                         if ((*appt)->names[i])
1726                                 free((*appt)->names[i]);
1727
1728                 free((*appt)->names);
1729         }
1730
1731         /* potential memory leak here.  We must be careful, as results 
1732            from querys should be thrown away with csa_free(), while 
1733            structures we've set up to do update/write operations were 
1734            allocated by the client, and need to be freed by that client. */
1735
1736         if (((*appt)->reason == appt_read) && ((*appt)->filled == True))
1737                 csa_free((*appt)->attrs);
1738         else 
1739                 if ((*appt)->attrs) {
1740                         for (i = 0; i < (*appt)->count; i++) 
1741                                 free_attr(&((*appt)->attrs[i]));
1742
1743                         free((*appt)->attrs);
1744                 }
1745
1746         free(*appt);
1747         *appt = NULL;
1748 }
1749
1750 extern void
1751 free_cal_struct(Dtcm_calendar **cal) {
1752         int     i;
1753
1754         if (!cal)
1755                 return;
1756
1757         if ((*cal)->names) {
1758                 for (i = 0; i < (*cal)->num_names; i++)
1759                         if ((*cal)->names[i])
1760                                 free((*cal)->names[i]);
1761
1762                 free((*cal)->names);
1763         }
1764
1765         /* potential memory leak here.  We must be careful, as results 
1766            from querys should be thrown away with csa_free(), while 
1767            structures we've set up to do update/write operations were 
1768            allocated by the client, and need to be freed by that client. */
1769
1770         if (((*cal)->reason == appt_read) && ((*cal)->filled == True))
1771                 csa_free((*cal)->attrs);
1772         else
1773                 if ((*cal)->attrs) {
1774                         for (i = 0; i < (*cal)->count; i++)
1775                                 free_attr(&((*cal)->attrs[i]));
1776                         free((*cal)->attrs);
1777                 }
1778
1779         free(*cal);
1780         *cal = NULL;
1781 }
1782
1783 extern void
1784 set_appt_links(Dtcm_appointment *appt) {
1785         int     idx;
1786         char    *idx_name;
1787
1788         appt->identifier = NULL;
1789         appt->modified_time = NULL;
1790         appt->author = NULL;
1791         appt->number_recurrence = NULL;
1792         appt->time = NULL;
1793         appt->type = NULL;
1794         appt->subtype = NULL;
1795         appt->private = NULL;
1796         appt->end_time = NULL;
1797         appt->show_time = NULL;
1798         appt->what = NULL;
1799         appt->state = NULL;
1800         appt->repeat_type = NULL;
1801         appt->repeat_times = NULL;
1802         appt->repeat_interval = NULL;
1803         appt->repeat_week_num = NULL;
1804         appt->recurrence_rule = NULL;
1805         appt->beep = NULL;
1806         appt->flash = NULL;
1807         appt->mail = NULL;
1808         appt->popup = NULL;
1809         appt->sequence_end_date = NULL;
1810
1811         for (idx = 0; idx < appt->count; idx++) {
1812                 idx_name = appt->attrs[idx].name;
1813                 if (!idx_name)
1814                         continue;
1815                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER) == 0)
1816                         appt->identifier = &appt->attrs[idx];
1817                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_LAST_UPDATE) == 0)
1818                         appt->modified_time = &appt->attrs[idx];
1819                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_ORGANIZER) == 0)
1820                         appt->author = &appt->attrs[idx];
1821                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_NUMBER_RECURRENCES) == 0)
1822                         appt->number_recurrence = &appt->attrs[idx];
1823                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_START_DATE) == 0)
1824                         appt->time = &appt->attrs[idx];
1825                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_TYPE) == 0)
1826                         appt->type = &appt->attrs[idx];
1827                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_SUBTYPE) == 0)
1828                         appt->subtype = &appt->attrs[idx];
1829                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_CLASSIFICATION) == 0)
1830                         appt->private = &appt->attrs[idx];
1831                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_END_DATE) == 0)
1832                         appt->end_time = &appt->attrs[idx];
1833                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_SHOWTIME) == 0)
1834                         appt->show_time = &appt->attrs[idx];
1835                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_SUMMARY) == 0)
1836                         appt->what = &appt->attrs[idx];
1837                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_STATUS) == 0)
1838                         appt->state = &appt->attrs[idx];
1839                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE) == 0)
1840                         appt->repeat_type = &appt->attrs[idx];
1841                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES) == 0)
1842                         appt->repeat_times = &appt->attrs[idx];
1843                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL) == 0)
1844                         appt->repeat_interval = &appt->attrs[idx];
1845                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM) == 0)
1846                         appt->repeat_week_num = &appt->attrs[idx];
1847                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_RECURRENCE_RULE) == 0)
1848                         appt->recurrence_rule = &appt->attrs[idx];
1849                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_AUDIO_REMINDER) == 0)
1850                         appt->beep = &appt->attrs[idx];
1851                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_FLASHING_REMINDER) == 0)
1852                         appt->flash = &appt->attrs[idx];
1853                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_MAIL_REMINDER) == 0)
1854                         appt->mail = &appt->attrs[idx];
1855                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_POPUP_REMINDER) == 0)
1856                         appt->popup = &appt->attrs[idx];
1857                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_SEQUENCE_END_DATE) == 0)
1858                         appt->sequence_end_date = &appt->attrs[idx];
1859         }
1860 }
1861
1862 extern void
1863 set_cal_links(Dtcm_calendar *cal) {
1864         int     idx;
1865         char    *idx_name;
1866
1867         for (idx = 0; idx < cal->count; idx++) {
1868                 idx_name = cal->attrs[idx].name;
1869                 if (strcmp(idx_name, CSA_CAL_ATTR_CALENDAR_NAME) == 0)
1870                         cal->cal_name = &cal->attrs[idx];
1871                 else if (strcmp(idx_name, CSA_X_DT_CAL_ATTR_DATA_VERSION) == 0)
1872                         cal->server_version = &cal->attrs[idx];
1873                 else if (strcmp(idx_name, CSA_CAL_ATTR_NUMBER_ENTRIES) == 0)
1874                         cal->num_entries = &cal->attrs[idx];
1875                 else if (strcmp(idx_name, CSA_CAL_ATTR_CALENDAR_SIZE) == 0)
1876                         cal->cal_size = &cal->attrs[idx];
1877                 else if (strcmp(idx_name, CSA_CAL_ATTR_ACCESS_LIST) == 0)
1878                         cal->access_list = &cal->attrs[idx];
1879                 else if (strcmp(idx_name, CSA_CAL_ATTR_TIME_ZONE) == 0)
1880                         cal->time_zone = &cal->attrs[idx];
1881         }
1882 }
1883
1884 extern void
1885 setup_range(CSA_attribute **attrs, CSA_enum **ops, int *count, time_t start,
1886             time_t stop, CSA_sint32 type, CSA_sint32 state, boolean_t use_state,
1887             int version) {
1888         int             a_size, o_size;
1889         CSA_enum        *op_ptr;
1890         CSA_attribute   *attr_ptr;
1891
1892
1893         if (use_state)
1894                 *count = 4;
1895         else
1896                 *count = 3;
1897
1898         a_size = sizeof(CSA_attribute) * (*count);
1899         attr_ptr = (CSA_attribute *)ckalloc(a_size);
1900         memset(attr_ptr, NULL, a_size);
1901
1902         o_size = sizeof(CSA_enum) * (*count);
1903         op_ptr = (CSA_enum *)ckalloc(o_size);
1904         memset(op_ptr, NULL, o_size);
1905
1906         initialize_entry_attr(CSA_ENTRY_ATTR_START_DATE_I, &attr_ptr[0], appt_write, version);
1907         attr_ptr[0].value->item.string_value = malloc(BUFSIZ);
1908         _csa_tick_to_iso8601(start, attr_ptr[0].value->item.string_value);
1909
1910         op_ptr[0] = CSA_MATCH_GREATER_THAN_OR_EQUAL_TO;
1911
1912         initialize_entry_attr(CSA_ENTRY_ATTR_START_DATE_I, &attr_ptr[1], appt_write, version);
1913         attr_ptr[1].value->item.string_value = malloc(BUFSIZ);
1914         _csa_tick_to_iso8601(stop, attr_ptr[1].value->item.string_value);
1915         op_ptr[1] = CSA_MATCH_LESS_THAN_OR_EQUAL_TO;
1916
1917         initialize_entry_attr(CSA_ENTRY_ATTR_TYPE_I, &attr_ptr[2], appt_write, version);
1918         attr_ptr[2].value->item.sint32_value = type;
1919         op_ptr[2] = CSA_MATCH_EQUAL_TO;
1920
1921         if (use_state) {
1922                 initialize_entry_attr(CSA_ENTRY_ATTR_STATUS_I, &attr_ptr[3], appt_write, version);
1923                 attr_ptr[3].value->item.sint32_value = state;
1924                 op_ptr[3] = CSA_MATCH_EQUAL_TO;
1925         }
1926
1927         *attrs = attr_ptr;
1928         *ops = op_ptr;
1929 }
1930
1931 extern void
1932 free_range(CSA_attribute **attrs, CSA_enum **ops, int count) {
1933         int     i;
1934
1935         for (i = 0; i < count; i++) {
1936                 free((*attrs)[i].name);
1937                 if (((*attrs)[i].value->type == CSA_VALUE_STRING) ||
1938                     ((*attrs)[i].value->type == CSA_VALUE_DATE_TIME))
1939                         if ((*attrs)[i].value->item.string_value)
1940                                 free((*attrs)[i].value->item.string_value);
1941
1942                 free((*attrs)[i].value);
1943         }
1944
1945         /* This memory was allocated by the client, and must be freed 
1946            by the client */
1947
1948         free(*attrs);
1949
1950         *attrs = NULL;
1951
1952         free(*ops);
1953         *ops = NULL;
1954 }
1955
1956 /*
1957  * In Motif you can't associate user data with items in a list.  To get around
1958  * this we have the following simple functions (CmDataList*) that maintain
1959  * a list of user data.  We follow the intrinscs coding style to re-inforce
1960  * the relationship these routines have to the XmList* functions.
1961  */
1962
1963 /*
1964  * Create a list to store user data
1965  */
1966 CmDataList *
1967 CmDataListCreate(void)
1968
1969 {
1970         return (CmDataList *)calloc(1, sizeof(CmDataList));
1971 }
1972
1973 /*
1974  * Destroy list
1975  */
1976 void
1977 CmDataListDestroy(CmDataList *list, int free_data)
1978
1979 {
1980         CmDataListDeleteAll(list, free_data);
1981         free(list);
1982 }
1983
1984 /*
1985  * Create node to hold data in list.
1986  */
1987 static CmDataItem *
1988 CmDataItemCreate(void)
1989
1990 {
1991         return (CmDataItem *)calloc(1, sizeof(CmDataItem));
1992 }
1993
1994 /*
1995  * Add user data to list at specified position. Note that this
1996  * routine must be called for every item added to a list.
1997  * If the item has no user data, just pass NULL.
1998  *
1999  *      list            List to add data to
2000  *      data            User data. NULL for no data.
2001  *      position        Where to insert data, starting with 1 for the
2002  *                      first item.  0 to append to end of list.
2003  *
2004  * Returns
2005  *              1       Success
2006  *              -1      Invalid position
2007  */
2008 int
2009 CmDataListAdd(CmDataList *list, void *data, int position)
2010
2011 {
2012         CmDataItem      *item, *p;
2013         int             n;
2014
2015         /* Create new node to hold data */
2016         item = CmDataItemCreate();
2017         item->data = data;
2018
2019         /* Insert node into list at appropriate spot */
2020         if (list->head == NULL) {
2021                 list->head = item;
2022         } else if (position == 0) {
2023                 /* Special case.  0 means append to end */
2024                 list->tail->next = item;
2025         } else if (position == 1) {
2026                 item->next = list->head;
2027                 list->head = item;
2028         } else {
2029                 for (n = 2, p = list->head; p != NULL && n < position;
2030                      p = p->next, n++)
2031                         ;
2032
2033                 if (p == NULL) {
2034                         return -1;
2035                 }
2036
2037                 item->next = p->next;
2038                 p->next = item;
2039         }
2040
2041         /* If new item is at the end of the list, update tail */
2042         if (item->next == NULL) {
2043                 list->tail = item;
2044         }
2045
2046         list->count++;
2047
2048         return 1;
2049 }
2050
2051 /*
2052  * Delete user data from a position in the list.  If free_data is
2053  * True then this routine will call free(3C) on the user data, otherwise
2054  * the user data is returned so that the caller can dispose of it.
2055  * This routine should be called anytime you delete an item from a
2056  * scrolling list.
2057  *
2058  *      list            List to delete data from
2059  *      position        Location of item to delete. 0 for last item
2060  *      free_data       True if you want this routine to call free()
2061  *                      on the data for you.  Otherwise this routine
2062  *                      will return the address of the data.
2063  *
2064  * Returns
2065  *      NULL    No data found for item at the specified position
2066  *      1       Success (free_data was True)
2067  *      other   Address of data for item at the specified position.
2068  *              (free_data was False)
2069  */
2070 void *
2071 CmDataListDeletePos(CmDataList *list, int position, int free_data)
2072
2073 {
2074         void            *data;
2075         CmDataItem      *p, *item;
2076         int             n;
2077
2078         /* Special case. 0 means delete last item */
2079         if (position == 0) {
2080                 position = list->count;
2081         }
2082
2083         if (list->head == NULL) {
2084                 return NULL;
2085         } else if (position == 1) {
2086                 item = list->head;
2087                 list->head = item->next;
2088                 if (list->tail == item) {
2089                         list->tail = item->next;
2090                 }
2091         } else {
2092                 for (n = 2, p = list->head; p->next != NULL && n < position;
2093                      p = p->next, n++)
2094                         ;
2095                 
2096                 if (p->next == NULL) {
2097                         return NULL;
2098                 }
2099
2100                 item = p->next;
2101                 p->next = item->next;
2102                 if (list->tail == item) {
2103                         list->tail = p;
2104                 }
2105         }
2106
2107         list->count--;
2108
2109         data = item->data;
2110         free(item);
2111
2112         if (free_data) {
2113                 if (data != NULL) 
2114                         free(data);
2115                 return (void *)1;
2116         } else {
2117                 return data;
2118         }
2119 }
2120
2121 /* 
2122  * Delete all nodes in the list.
2123  *
2124  *      list            List to delete nodes from
2125  *      free_data       True if you want this routine to call free()
2126  *                      on the data for you.
2127  */
2128 void
2129 CmDataListDeleteAll(CmDataList *list, int free_data)
2130
2131 {
2132         CmDataItem      *p, *tmp;
2133
2134         p = list->head;
2135         while (p != NULL) {
2136                 if (free_data && p->data != NULL) {
2137                         free(p->data);
2138                 }
2139
2140                 tmp = p;
2141                 p = p->next;
2142                 free(tmp);
2143         }
2144
2145         list->count = 0;
2146         list->head = NULL;
2147         list->tail = NULL;
2148
2149         return;
2150 }
2151
2152 /*
2153  * Get data for the item at a particular position in a list.
2154  */
2155 void *
2156 CmDataListGetData(CmDataList *list, int position)
2157
2158 {
2159         void            *data;
2160         CmDataItem      *p, *item;
2161         int             n;
2162
2163         if (list->head == NULL) {
2164                 return NULL;
2165         } else if (position == 0) {
2166                 data = list->tail->data;
2167         } else {
2168                 for (n = 1, p = list->head; p != NULL && n < position;
2169                      p = p->next, n++)
2170                         ;
2171                 if (p == NULL) {
2172                         return NULL;
2173                 }
2174                 data = p->data;
2175         }
2176
2177         return data;
2178 }
2179
2180 #ifdef DEBUG
2181 /*
2182  * For dumping contents of list
2183  */
2184 void
2185 CmDataListDump(CmDataList *list)
2186
2187 {
2188         CmDataItem      *p;
2189         int             n;
2190
2191         printf("***** %d items:\n", list->count);
2192
2193         for (p = list->head, n = 1; p != NULL; p = p->next, n++) {
2194                 printf("%3d: %s\n", n, p->data ? (char *)p->data : "<nil>");
2195         }
2196
2197         return;
2198 }
2199 #endif