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