dtcm: Change to ANSI function definitions
[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(register char *s1, register 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(register char *s)
186 {
187         register 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         register 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                 return(B_FALSE);
877
878         if (strcmp(str1, str2)) {
879                 free(str1);
880                 free(str2);
881                 return(B_FALSE);
882         }
883         free(str1);
884         free(str2);
885
886         /* if only user name is specified, don't need to check domain */
887         str2 = strchr(user2, '@');
888         if (str2 == NULL)
889                 return(B_TRUE);
890
891         /* first assume user2=user@domain */
892         str1 = strchr(user1, '.');
893         if (str1 == NULL) {
894                 if (same_path(cm_get_local_domain(), ++str2))
895                         return(B_TRUE);
896         } else {
897                 if (same_path(++str1, ++str2))
898                         return(B_TRUE);
899         }
900
901         /* assume user2=user@host[.domain] */
902         if (str1 == NULL) {
903                 str1 = strchr(user1, '@');
904                 sprintf(buf, "%s.%s", ++str1, cm_get_local_domain());
905                 str1 = buf;
906         } else {
907                 str1 = strchr(user1, '@');
908                 str1++;
909         }
910
911         if (same_path(str1, str2))
912                 return(B_TRUE);
913         else
914                 return(B_FALSE);
915 }
916
917 /*
918  * A blank line is one that consists of only \b, \t or \n.
919  */
920 extern int
921 blank_buf(char *buf)
922 {
923         char *ptr = buf;
924
925         if (ptr == NULL) return B_TRUE;
926         while (ptr && (*ptr == ' ' || *ptr == '\t' || *ptr == '\n'))
927                 ptr++;
928         if (*ptr == '\0')
929                 return B_TRUE;
930         else
931                 return B_FALSE;
932 }
933
934 extern int
935 embedded_blank(char *buf)
936 {
937         char *ptr = buf;
938  
939         if (ptr == NULL) return B_TRUE;
940         while (ptr && *ptr) {
941                 if ((*ptr == ' ') || (*ptr == '\t'))
942                         return B_TRUE;
943                 *ptr++;
944         }
945  
946         return B_FALSE;
947 }
948
949 extern int
950 get_data_version(CSA_session_handle session) {
951         int             ver = 0;
952         Dtcm_calendar   *c;
953         CSA_attribute_reference names[1];
954         CSA_uint32      number_attrs_returned;
955         CSA_attribute   *attrs_returned;
956
957         names[0] = CSA_X_DT_CAL_ATTR_DATA_VERSION;
958
959
960         if (csa_read_calendar_attributes(session, 
961                                          1, 
962                                          names, 
963                                          &number_attrs_returned, 
964                                          &attrs_returned, 
965                                          NULL) == CSA_SUCCESS) {
966                 ver = attrs_returned[0].value->item.uint32_value;
967                 csa_free(attrs_returned);
968         }
969
970         return ver;
971 }
972
973 extern int
974 get_server_version(CSA_session_handle session) {
975         int             ver = 0;
976         Dtcm_calendar   *c;
977         CSA_attribute_reference names[1];
978         CSA_uint32      number_attrs_returned;
979         CSA_attribute   *attrs_returned;
980
981         names[0] = CSA_X_DT_CAL_ATTR_SERVER_VERSION;
982
983         if (csa_read_calendar_attributes(session, 
984                                          1, 
985                                          names, 
986                                          &number_attrs_returned, 
987                                          &attrs_returned, 
988                                          NULL) == CSA_SUCCESS) {
989                 ver = attrs_returned[0].value->item.uint32_value;
990                 csa_free(attrs_returned);
991         }
992
993         return ver;
994 }
995
996 extern CSA_sint32
997 privacy_set(Dtcm_appointment *appt) {
998
999         CSA_sint32      privacy = CSA_CLASS_PUBLIC;
1000
1001         if (!appt)
1002                 return(privacy);
1003
1004         if (!appt->private)
1005                 return(privacy);
1006
1007         if (!appt->private->value)
1008                 return(privacy);
1009
1010         privacy = appt->private->value->item.sint32_value;
1011         return(privacy);
1012
1013 }
1014
1015 extern CSA_sint32
1016 showtime_set(Dtcm_appointment *appt) {
1017  
1018         CSA_sint32      showtime = 0;
1019  
1020         if (!appt)
1021                 return(showtime);
1022  
1023         if (!appt->show_time)
1024                 return(showtime);
1025  
1026         if (!appt->show_time->value)
1027                 return(showtime);
1028  
1029         showtime = appt->show_time->value->item.sint32_value;
1030         return(showtime);
1031  
1032 }
1033
1034 /*
1035 **  Parse the date string and get the month, day, and year
1036 */
1037 extern int
1038 parse_date(OrderingType order, SeparatorType sep, char *datestr, char *m,
1039         char *d, char *y) {
1040  
1041         char *first, *second, *third;
1042         char *tmp_date, *str = separator_str(sep);
1043         _Xstrtokparams strtok_buf;
1044  
1045         m[0] = '\0';
1046         d[0] = '\0';
1047         y[0] = '\0';
1048  
1049         if (datestr == NULL)
1050                 return 0;
1051  
1052         tmp_date = cm_strdup(datestr);
1053         first = _XStrtok(tmp_date, str, strtok_buf);
1054                 /*
1055                 ** Check to see if the date entered has legit separator
1056                 */
1057                 if ( strcoll(first, datestr) == 0 ) {
1058                         free(tmp_date);
1059                         return 0;
1060                 }
1061         second = _XStrtok(NULL, str, strtok_buf);
1062         third = _XStrtok(NULL, str, strtok_buf);
1063  
1064         switch (order) {
1065         case ORDER_DMY:
1066                 if (second)
1067                         cm_strcpy(m, second);
1068                 if (first)
1069                         cm_strcpy(d, first);
1070                 if (third)
1071                         cm_strcpy(y, third);
1072                 break;
1073         case ORDER_YMD:
1074                 if (second)
1075                         cm_strcpy(m, second);
1076                 if (third)
1077                         cm_strcpy(d, third);
1078                 if (first)
1079                         cm_strcpy(y, first);
1080                 break;
1081         case ORDER_MDY:
1082         default:
1083                 if (first)
1084                         cm_strcpy(m, first);
1085                 if (second)
1086                         cm_strcpy(d, second);
1087                 if (third)
1088                         cm_strcpy(y, third);
1089                 break;
1090         }
1091         free(tmp_date);
1092                 return 1;
1093 }
1094
1095 /*
1096 **  Reformat the date string into m/d/y format and write it into the buffer
1097 */
1098 extern int
1099 datestr2mdy(char *datestr, OrderingType order, SeparatorType sep, char *buf) {
1100         char m[3], d[3], y[5];
1101  
1102         buf[0] = '\0';
1103         if (datestr == NULL)
1104                 return 0;
1105  
1106         if (order == ORDER_MDY && sep == SEPARATOR_SLASH)
1107                 cm_strcpy(buf, datestr);
1108         else {
1109                 if ( parse_date(order, sep, datestr, m, d, y) ) {
1110                         sprintf(buf, "%s/%s/%s", m, d, y);
1111                 } else {
1112                         return 0;
1113                 }
1114  
1115         }
1116         return 1;
1117 }
1118
1119 /*
1120 **  Format the date according to display property and write it into buffer
1121 */
1122 extern void
1123 format_tick(Tick tick, OrderingType order, SeparatorType sep, char *buff) {
1124         char            *str = separator_str(sep);
1125         struct tm       *tm;
1126         _Xltimeparams   localtime_buf;
1127  
1128         buff[0] = '\0';
1129         tm = _XLocaltime(&tick, localtime_buf);
1130  
1131         switch (order) {
1132         case ORDER_DMY:
1133                 sprintf(buff, "%d%s%d%s%d", tm->tm_mday, str,
1134                         tm->tm_mon+1, str, tm->tm_year+1900);
1135                 break;
1136         case ORDER_YMD:
1137                 sprintf(buff, "%d%s%d%s%d", tm->tm_year+1900, str,
1138                         tm->tm_mon+1, str, tm->tm_mday);
1139                 break;
1140         case ORDER_MDY:
1141         default:
1142                 sprintf(buff, "%d%s%d%s%d", tm->tm_mon+1, str,
1143                         tm->tm_mday, str, tm->tm_year+1900);
1144                 break;
1145         }
1146 }
1147
1148 extern void
1149 format_time(Tick t, DisplayType dt, char *buffer) {
1150         int             hr = hour(t);
1151         boolean_t       am;
1152
1153         if (t == 0) {
1154                 buffer[0] = '\0';
1155
1156         } else if (dt == HOUR12) {
1157                 am = adjust_hour(&hr);
1158                 sprintf(buffer, "%2d:%02d%s",
1159                         hr, minute(t), (am) ? "am" : "pm");
1160         } else
1161                 sprintf(buffer, "%02d%02d", hr, minute(t));
1162 }
1163
1164 /*
1165  * The V5 back end uses arrays to pass attributes back and forth.  However,
1166  * keeping hard coded references into those arrays (i.e. declaring the tick
1167  * value will always be into position 0 of the array, the what value in
1168  * position 3, etc.) is a bad idea and hard to maintain.
1169  *
1170  * Thus these convenience functions will translate from an attribute array
1171  * received from the back end into defined structure which the front end can
1172  * use.
1173  *
1174  * IF YOU UPDATE THE STRUCTURES, MAKE SURE YOU UPDATE THESE COUNT CONSTANTS!!
1175  */
1176 static const int APPT_ATTR_COUNT = 35;
1177 static const int RW_APPT_ATTR_COUNT = 15;
1178 static const int CAL_ATTR_COUNT = 12;
1179 static const int RW_CAL_ATTR_COUNT = 2;
1180 static const int DEF_V5_APPT_ATTR_COUNT = 22;
1181 static const int DEF_V4_APPT_ATTR_COUNT = 20;
1182 static const int DEF_V3_APPT_ATTR_COUNT = 17;
1183 static const int DEF_CAL_ATTR_COUNT = 6;
1184 static const int default_appt_attrs[] = {CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I,
1185                         CSA_ENTRY_ATTR_LAST_UPDATE_I,
1186                         CSA_ENTRY_ATTR_ORGANIZER_I,
1187                         CSA_ENTRY_ATTR_START_DATE_I,
1188                         CSA_ENTRY_ATTR_TYPE_I,
1189                         CSA_ENTRY_ATTR_SUBTYPE_I,
1190                         CSA_ENTRY_ATTR_CLASSIFICATION_I,
1191                         CSA_ENTRY_ATTR_END_DATE_I,
1192                         CSA_X_DT_ENTRY_ATTR_SHOWTIME_I,
1193                         CSA_ENTRY_ATTR_SUMMARY_I,
1194                         CSA_ENTRY_ATTR_STATUS_I,
1195                         CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE_I,
1196                         CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES_I,
1197                         CSA_ENTRY_ATTR_AUDIO_REMINDER_I,
1198                         CSA_ENTRY_ATTR_FLASHING_REMINDER_I,
1199                         CSA_ENTRY_ATTR_MAIL_REMINDER_I,
1200                         CSA_ENTRY_ATTR_POPUP_REMINDER_I,
1201                         CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM_I,
1202                         CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL_I,
1203                         CSA_X_DT_ENTRY_ATTR_SEQUENCE_END_DATE_I,
1204                         CSA_ENTRY_ATTR_RECURRENCE_RULE_I,
1205                         CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I
1206                         };
1207 static const int default_cal_attrs[] = {CSA_CAL_ATTR_ACCESS_LIST_I,
1208                                         CSA_CAL_ATTR_CALENDAR_NAME_I,
1209                                         CSA_CAL_ATTR_CALENDAR_SIZE_I,
1210                                         CSA_CAL_ATTR_NUMBER_ENTRIES_I,
1211                                         CSA_CAL_ATTR_TIME_ZONE_I,
1212                                         CSA_X_DT_CAL_ATTR_DATA_VERSION_I
1213                                         };
1214
1215 /*
1216  * NOTE that this loop is dependent on the first appointment attribute define
1217  * (DT_CM_ATTR_IDENTIFER_I) in the api - if that is changed and is no longer
1218  * the first appointment define, this needs to be changed.
1219  *
1220  * NOTE that this function checks if the api indexes specified are read-only:
1221  * This assumes that if you need value space (and have set the need_value_space
1222  * flag to B_TRUE), you're setting attributes and since you can't set read-only
1223  * attributes, it will ignore read-only attributes if the need_value_space flag
1224  * is B_TRUE.
1225  */
1226 Dtcm_appointment *allocate_appt_struct (Allocation_reason reason, int version, ...) {
1227         int                     idx = 0, api_idx;
1228         va_list                 pvar;
1229         CmDataList              *api_ids = CmDataListCreate();
1230         Dtcm_appointment        *appt;
1231         int                     def_attr_count;
1232
1233         /*
1234          * The Dtcm_appointment wrapper array
1235          */
1236         idx = sizeof(Dtcm_appointment);
1237         appt = (Dtcm_appointment *)ckalloc(idx);
1238         memset(appt, 0, idx);
1239         appt->reason = reason;
1240         appt->version = version;
1241
1242         /*
1243          * Step through the variable argument list and build the list of
1244          * attributes we're looking for
1245          */
1246         va_start(pvar, version);
1247         api_idx = va_arg(pvar, int);
1248         while (api_idx) {
1249                 if ((reason == appt_read) || !entry_ident_index_ro(api_idx, version))
1250                         CmDataListAdd(api_ids, (void *) (intptr_t) api_idx, 0);
1251                 api_idx = va_arg(pvar, int);
1252         }
1253         va_end(pvar);
1254
1255         /*
1256          * No attributes specified, assume the caller wanted all of them
1257          */
1258         if (api_ids->count <= 0) {
1259
1260                 if ((version == DATAVER2) || (version == DATAVER1))
1261                         def_attr_count = DEF_V3_APPT_ATTR_COUNT;
1262                 else if (version == DATAVER3)
1263                         def_attr_count = DEF_V4_APPT_ATTR_COUNT;
1264                 else if (version == DATAVER4)
1265                         def_attr_count = DEF_V5_APPT_ATTR_COUNT;
1266                 else if (version == DATAVER_ARCHIVE)
1267                         def_attr_count = DEF_V5_APPT_ATTR_COUNT;
1268
1269                 for (idx = 0; idx < def_attr_count; idx++) {
1270                         if ((reason == appt_write) && entry_ident_index_ro(default_appt_attrs[idx], version))
1271                                 continue;
1272                         CmDataListAdd(api_ids, (void *) (intptr_t) default_appt_attrs[idx], 0);
1273                 }
1274         }
1275
1276         /*
1277          * We've determined the number of attributes we're retrieving, so
1278          * allocate the name array, and the attribute array (if we are 
1279          * going to be writing attributes).
1280          */
1281
1282         appt->num_names = api_ids->count;
1283         idx = sizeof(CSA_attribute_reference *) * appt->num_names;
1284         appt->names = (CSA_attribute_reference *)ckalloc(idx);
1285         memset(appt->names, 0, idx);
1286
1287         appt->count = api_ids->count;
1288         if (reason == appt_write) {
1289                 idx = sizeof(CSA_attribute) * appt->count;
1290                 appt->attrs = (CSA_attribute *)ckalloc(idx);
1291                 memset(appt->attrs, 0, idx);
1292         }
1293
1294         /*
1295          * Now loop through and set the names and initialize the attributes
1296          */
1297         for (idx = 0; idx < appt->count; idx++) {
1298                 api_idx = (int) (intptr_t) CmDataListGetData(api_ids, idx + 1);
1299                 appt->names[idx] = strdup(_CSA_entry_attribute_names[api_idx]);
1300                 if (reason == appt_write)
1301                         initialize_entry_attr(api_idx, &appt->attrs[idx], reason, version);
1302         }
1303
1304         if (reason == appt_write)
1305                 set_appt_links(appt);
1306
1307         CmDataListDestroy(api_ids, 0);
1308         return appt;
1309 }
1310
1311 CSA_return_code
1312 query_appt_struct(CSA_session_handle session, 
1313                   CSA_entry_handle entry_handle, 
1314                   Dtcm_appointment *appt) {
1315
1316         CSA_return_code         status;
1317
1318         /* if there is old query material laying around, toss it */
1319
1320         if (appt->filled) {
1321                 csa_free(appt->attrs);
1322                 appt->filled = False;
1323         }
1324
1325
1326         if ((status = csa_read_entry_attributes(session, 
1327                                          entry_handle, 
1328                                          appt->num_names, 
1329                                          appt->names, 
1330                                          &appt->count, 
1331                                          &appt->attrs, 
1332                                          NULL)) == CSA_SUCCESS) {
1333                 set_appt_links(appt);
1334                 appt->filled = True;
1335         }
1336
1337         return(status);
1338 }
1339
1340
1341 /*
1342  * NOTE that this function checks if the api indexes specified are read-only:
1343  * This assumes that if you need value space (and have set the need_value_space
1344  * flag to B_TRUE), you're setting attributes and since you can't set read-only
1345  * attributes, it will ignore read-only attributes if the need_value_space flag
1346  * is B_TRUE.
1347  */
1348 Dtcm_calendar*
1349 allocate_cal_struct(Allocation_reason reason, int version, ...) {
1350         int             idx = 0, api_idx;
1351         va_list         pvar;
1352         CmDataList      *api_ids = CmDataListCreate();
1353         Dtcm_calendar   *cal;
1354
1355         /*
1356          * The Dtcm_apopintment wrapper array
1357          */
1358         idx = sizeof(Dtcm_calendar);
1359         cal = (Dtcm_calendar *)ckalloc(idx);
1360         memset(cal, 0, idx);
1361         cal->reason = reason;
1362         cal->version = version;
1363
1364         /*
1365          * Step through the variable argument list and build the list of
1366          * attributes we're looking for
1367          */
1368         va_start(pvar, version);
1369         api_idx = va_arg(pvar, int);
1370         while (api_idx) {
1371                 if ((reason == appt_read) || !cal_ident_index_ro(api_idx, version))
1372                         CmDataListAdd(api_ids, (void *) (intptr_t) api_idx, 0);
1373                 api_idx = va_arg(pvar, int);
1374         }
1375         va_end(pvar);
1376
1377         /*
1378          * No attributes specified, assume the caller wanted all of them
1379          */
1380         if (api_ids->count <= 0) {
1381                 for (idx = 0; idx < DEF_CAL_ATTR_COUNT; idx++) {
1382                         if ((reason == appt_write) && cal_ident_index_ro(default_cal_attrs[idx], version))
1383                                 continue;
1384                         CmDataListAdd(api_ids, (void *) (intptr_t) default_cal_attrs[idx], 0);
1385                 }
1386         }
1387
1388         /*
1389          * We've determined the number of attributes we're retrieving, so
1390          * allocate the name arrya, and the attribute array (if we are 
1391          * going to be writing attributes).
1392          */
1393
1394         cal->num_names = api_ids->count;
1395         idx = sizeof(CSA_attribute_reference) * cal->num_names;
1396         cal->names = (CSA_attribute_reference *)ckalloc(idx);
1397         memset(cal->names, 0, idx);
1398
1399         cal->count = api_ids->count;
1400         if (reason == appt_write) {
1401                 idx = sizeof(CSA_attribute) * cal->count;
1402                 cal->attrs = (CSA_attribute *)ckalloc(idx);
1403                 memset(cal->attrs, 0, idx);
1404         }
1405
1406         /*
1407          * Now loop through and set the names and initialize the attributes
1408          */
1409         for (idx = 0; idx < cal->count; idx++) {
1410                 api_idx = (int) (intptr_t) CmDataListGetData(api_ids, idx + 1);
1411                 cal->names[idx] = strdup(_CSA_calendar_attribute_names[api_idx]);
1412                 if (reason == appt_write)
1413                         initialize_cal_attr(api_idx, &cal->attrs[idx], reason, version);
1414         }
1415
1416         if (reason == appt_write) 
1417                 set_cal_links(cal);
1418
1419         CmDataListDestroy(api_ids, 0);
1420
1421
1422         return cal;
1423 }
1424
1425 CSA_return_code
1426 query_cal_struct(CSA_session_handle session, 
1427                  Dtcm_calendar *cal) {
1428
1429         CSA_return_code status;
1430
1431         /* if there is old query material laying around, toss it */
1432
1433         if (cal->filled) {
1434                 csa_free(cal->attrs);
1435                 cal->filled = False;
1436         }
1437
1438         if ((status = csa_read_calendar_attributes(session, 
1439                                          cal->num_names, 
1440                                          cal->names, 
1441                                          &cal->count, 
1442                                          &cal->attrs, 
1443                                          NULL)) == CSA_SUCCESS) {
1444                 set_cal_links(cal);
1445                 cal->filled = True;
1446         }
1447
1448         return(status);
1449 }
1450
1451 extern void
1452 scrub_cal_attr_list(Dtcm_calendar *cal) {
1453
1454         int     i;
1455
1456         for (i = 0; i < cal->count; i++) {
1457                 if (cal->attrs[i].value->type == CSA_VALUE_REMINDER) {
1458                         if ((cal->attrs[i].value->item.reminder_value->lead_time == NULL) || 
1459                              (cal->attrs[i].value->item.reminder_value->lead_time[0] == '\0')) {
1460                                 free(cal->attrs[i].name);
1461                                 cal->attrs[i].name = NULL;
1462                         }
1463                 }
1464                 else if ((cal->attrs[i].value->type == CSA_VALUE_ACCESS_LIST) && (cal->attrs[i].value->item.access_list_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_STRING) && (cal->attrs[i].value->item.string_value == NULL)) {
1469                         free(cal->attrs[i].name);
1470                         cal->attrs[i].name = NULL;
1471                 }
1472                 else if ((cal->attrs[i].value->type == CSA_VALUE_DATE_TIME) && (cal->attrs[i].value->item.date_time_value == NULL)) {
1473                         free(cal->attrs[i].name);
1474                         cal->attrs[i].name = NULL;
1475                 }
1476         }
1477 }
1478
1479 extern boolean_t
1480 cal_ident_index_ro(int id, int version) {
1481         boolean_t       r_ro;
1482
1483         switch(id) {
1484         case CSA_CAL_ATTR_CALENDAR_NAME_I:
1485         case CSA_CAL_ATTR_CALENDAR_OWNER_I:
1486         case CSA_CAL_ATTR_CALENDAR_SIZE_I:
1487         case CSA_CAL_ATTR_CHARACTER_SET_I:
1488         case CSA_CAL_ATTR_NUMBER_ENTRIES_I:
1489         case CSA_CAL_ATTR_DATE_CREATED_I:
1490         case CSA_CAL_ATTR_PRODUCT_IDENTIFIER_I:
1491         case CSA_X_DT_CAL_ATTR_DATA_VERSION_I:
1492         case CSA_CAL_ATTR_TIME_ZONE_I:
1493                 r_ro = B_TRUE;
1494                 break;
1495         default:
1496                 r_ro = B_FALSE;
1497                 break;
1498         }
1499
1500         return r_ro;
1501 }
1502
1503 extern boolean_t
1504 entry_ident_index_ro(int id, int version) {
1505         boolean_t       r_ro;
1506
1507         switch(id) {
1508         case CSA_ENTRY_ATTR_DATE_CREATED_I:
1509         case CSA_ENTRY_ATTR_LAST_UPDATE_I:
1510         case CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I:
1511         case CSA_ENTRY_ATTR_ORGANIZER_I:
1512         case CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I:
1513         case CSA_ENTRY_ATTR_SEQUENCE_NUMBER_I:
1514                 r_ro = B_TRUE;
1515                 break;
1516         case CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE_I:
1517         case CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES_I:
1518         case CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL_I:
1519         case CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM_I:
1520         case CSA_X_DT_ENTRY_ATTR_SEQUENCE_END_DATE_I:
1521                 if (version >= DATAVER4)
1522                         r_ro = B_TRUE;
1523                 else
1524                         r_ro = B_FALSE;
1525                 break;
1526         default:
1527                 r_ro = B_FALSE;
1528                 break;
1529         }
1530
1531         return r_ro;
1532 }
1533
1534 extern CSA_enum
1535 cal_ident_index_tag(int id) {
1536         CSA_enum        r_tag;
1537
1538         switch(id) {
1539         case CSA_CAL_ATTR_CALENDAR_NAME_I:
1540         case CSA_CAL_ATTR_CHARACTER_SET_I:
1541         case CSA_CAL_ATTR_COUNTRY_I:
1542         case CSA_CAL_ATTR_PRODUCT_IDENTIFIER_I:
1543         case CSA_CAL_ATTR_TIME_ZONE_I:
1544         case CSA_CAL_ATTR_LANGUAGE_I:
1545                 r_tag = CSA_VALUE_STRING;
1546                 break;
1547         case CSA_CAL_ATTR_CALENDAR_OWNER_I:
1548                 r_tag = CSA_VALUE_CALENDAR_USER;
1549                 break;
1550         case CSA_CAL_ATTR_DATE_CREATED_I:
1551                 r_tag = CSA_VALUE_DATE_TIME;
1552                 break;
1553         case CSA_CAL_ATTR_CALENDAR_SIZE_I:
1554         case CSA_CAL_ATTR_NUMBER_ENTRIES_I:
1555         case CSA_X_DT_CAL_ATTR_DATA_VERSION_I:
1556                 r_tag = CSA_VALUE_UINT32;
1557                 break;
1558         case CSA_CAL_ATTR_ACCESS_LIST_I:
1559                 r_tag = CSA_VALUE_ACCESS_LIST;
1560                 break;
1561         case CSA_CAL_ATTR_WORK_SCHEDULE_I:
1562         default:
1563                 r_tag = CSA_VALUE_OPAQUE_DATA;
1564                 break;
1565         }
1566
1567         return r_tag;
1568 }
1569
1570 extern CSA_enum
1571 entry_ident_index_tag(int id) {
1572         CSA_enum        r_tag;
1573
1574         switch(id) {
1575         case CSA_ENTRY_ATTR_DESCRIPTION_I:
1576         case CSA_ENTRY_ATTR_EXCEPTION_RULE_I:
1577         case CSA_ENTRY_ATTR_RECURRENCE_RULE_I:
1578         case CSA_ENTRY_ATTR_SUBTYPE_I:
1579         case CSA_ENTRY_ATTR_SUMMARY_I:
1580                 r_tag = CSA_VALUE_STRING;
1581                 break;
1582         case CSA_ENTRY_ATTR_DATE_COMPLETED_I:
1583         case CSA_ENTRY_ATTR_DATE_CREATED_I:
1584         case CSA_ENTRY_ATTR_DUE_DATE_I:
1585         case CSA_ENTRY_ATTR_END_DATE_I:
1586         case CSA_ENTRY_ATTR_LAST_UPDATE_I:
1587         case CSA_ENTRY_ATTR_START_DATE_I:
1588         case CSA_X_DT_ENTRY_ATTR_SEQUENCE_END_DATE_I:
1589                 r_tag = CSA_VALUE_DATE_TIME;
1590                 break;
1591         case CSA_ENTRY_ATTR_EXCEPTION_DATES_I:
1592         case CSA_ENTRY_ATTR_RECURRING_DATES_I:
1593                 r_tag = CSA_VALUE_DATE_TIME_LIST;
1594                 break;
1595         case CSA_ENTRY_ATTR_CLASSIFICATION_I:
1596         case CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I:
1597         case CSA_ENTRY_ATTR_PRIORITY_I:
1598         case CSA_ENTRY_ATTR_SEQUENCE_NUMBER_I:
1599         case CSA_ENTRY_ATTR_STATUS_I:
1600         case CSA_ENTRY_ATTR_TYPE_I:
1601         case CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES_I:
1602         case CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL_I:
1603                 r_tag = CSA_VALUE_UINT32;
1604                 break;
1605         case CSA_ENTRY_ATTR_TIME_TRANSPARENCY_I:
1606         case CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE_I:
1607         case CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM_I:
1608         case CSA_X_DT_ENTRY_ATTR_SHOWTIME_I:
1609                 r_tag = CSA_VALUE_SINT32;
1610                 break;
1611         case CSA_ENTRY_ATTR_AUDIO_REMINDER_I:
1612         case CSA_ENTRY_ATTR_FLASHING_REMINDER_I:
1613         case CSA_ENTRY_ATTR_MAIL_REMINDER_I:
1614         case CSA_ENTRY_ATTR_POPUP_REMINDER_I:
1615                 r_tag = CSA_VALUE_REMINDER;
1616                 break;
1617         case CSA_ENTRY_ATTR_ORGANIZER_I:
1618         case CSA_ENTRY_ATTR_SPONSOR_I:
1619                 r_tag = CSA_VALUE_CALENDAR_USER;
1620                 break;
1621         case CSA_ENTRY_ATTR_ATTENDEE_LIST_I:
1622                 r_tag = CSA_VALUE_ATTENDEE_LIST;
1623                 break;
1624         case CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I:
1625         default:
1626                 r_tag = CSA_VALUE_OPAQUE_DATA;
1627                 break;
1628         }
1629
1630         return r_tag;
1631 }
1632
1633 extern boolean_t
1634 ident_name_ro(char *name, int version) {
1635         boolean_t       r_ro = B_FALSE;
1636
1637         if (strcmp(name, CSA_CAL_ATTR_CALENDAR_NAME) == 0 ||
1638             strcmp(name, CSA_CAL_ATTR_CALENDAR_OWNER) == 0 ||
1639             strcmp(name, CSA_CAL_ATTR_CALENDAR_SIZE) == 0 ||
1640             strcmp(name, CSA_CAL_ATTR_DATE_CREATED) == 0 ||
1641             strcmp(name, CSA_CAL_ATTR_PRODUCT_IDENTIFIER) == 0 ||
1642             strcmp(name, CSA_X_DT_CAL_ATTR_DATA_VERSION) == 0 ||
1643             strcmp(name, CSA_ENTRY_ATTR_SEQUENCE_NUMBER) == 0 ||
1644             strcmp(name, CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER) == 0 ||
1645             strcmp(name, CSA_ENTRY_ATTR_ORGANIZER) == 0 ||
1646             strcmp(name, CSA_ENTRY_ATTR_LAST_UPDATE) == 0 ||
1647             strcmp(name, CSA_ENTRY_ATTR_DATE_CREATED) == 0 ||
1648             strcmp(name, CSA_ENTRY_ATTR_NUMBER_RECURRENCES) == 0)
1649                 r_ro = B_TRUE;
1650
1651         if ((version >= DATAVER4) &&
1652             (strcmp(name, CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE) == 0 ||
1653              strcmp(name, CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES) == 0 ||
1654              strcmp(name, CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL) == 0 ||
1655              strcmp(name, CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM) == 0))
1656                 r_ro = B_TRUE;
1657
1658         return r_ro;
1659 }
1660
1661 extern void
1662 initialize_cal_attr(int id, CSA_attribute *attrs, Allocation_reason reason, int version) {
1663         int     size;
1664
1665         attrs->name = cm_strdup(_CSA_calendar_attribute_names[id]);
1666         if ((reason == appt_write) && !cal_ident_index_ro(id, version)) {
1667                 size = sizeof(CSA_attribute_value);
1668                 attrs->value = (CSA_attribute_value *)ckalloc(size);
1669                 memset(attrs->value, 0, size);
1670                 attrs->value->type = cal_ident_index_tag(id);
1671                 if (attrs->value->type == CSA_VALUE_REMINDER)
1672                         attrs->value->item.reminder_value = (CSA_reminder *) calloc(sizeof(CSA_reminder), 1);
1673         }
1674 }
1675
1676 static void
1677 free_attr(CSA_attribute *attr) {
1678
1679         if (attr == NULL)
1680                 return;
1681
1682         if (attr->name)
1683                 free(attr->name);
1684
1685
1686         if (attr->value){
1687                 if ((attr->value->type == CSA_VALUE_STRING) && attr->value->item.string_value != NULL)
1688                         free(attr->value->item.string_value);
1689                 else if ((attr->value->type == CSA_VALUE_DATE_TIME) && attr->value->item.date_time_value != NULL)
1690                         free(attr->value->item.date_time_value);
1691                 else if ((attr->value->type == CSA_VALUE_REMINDER) && attr->value->item.reminder_value != NULL) {
1692                         if (attr->value->item.reminder_value->lead_time)
1693                                 free(attr->value->item.reminder_value->lead_time);
1694                         if (attr->value->item.reminder_value->reminder_data.data)
1695                                 free(attr->value->item.reminder_value->reminder_data.data);
1696
1697                         free(attr->value->item.reminder_value);
1698                 }
1699
1700                 free(attr->value);
1701
1702         }
1703 }
1704
1705 extern void
1706 initialize_entry_attr(int id, CSA_attribute *attrs, Allocation_reason reason, int version) {
1707         int     size;
1708
1709         attrs->name = cm_strdup(_CSA_entry_attribute_names[id]);
1710         if ((reason == appt_write) && !entry_ident_index_ro(id, version)) {
1711                 size = sizeof(CSA_attribute_value);
1712                 attrs->value = (CSA_attribute_value *)ckalloc(size);
1713                 memset(attrs->value, 0, size);
1714                 attrs->value->type = entry_ident_index_tag(id);
1715                 if (attrs->value->type == CSA_VALUE_REMINDER)
1716                         attrs->value->item.reminder_value = (CSA_reminder *) calloc(sizeof(CSA_reminder), 1);
1717         }
1718 }
1719
1720 extern void
1721 free_appt_struct(Dtcm_appointment **appt) {
1722         int     i;
1723
1724         if (!appt)
1725                 return;
1726
1727         if ((*appt)->names) {
1728                 for (i = 0; i < (*appt)->num_names; i++)
1729                         if ((*appt)->names[i])
1730                                 free((*appt)->names[i]);
1731
1732                 free((*appt)->names);
1733         }
1734
1735         /* potential memory leak here.  We must be careful, as results 
1736            from querys should be thrown away with csa_free(), while 
1737            structures we've set up to do update/write operations were 
1738            allocated by the client, and need to be freed by that client. */
1739
1740         if (((*appt)->reason == appt_read) && ((*appt)->filled == True))
1741                 csa_free((*appt)->attrs);
1742         else 
1743                 if ((*appt)->attrs) {
1744                         for (i = 0; i < (*appt)->count; i++) 
1745                                 free_attr(&((*appt)->attrs[i]));
1746
1747                         free((*appt)->attrs);
1748                 }
1749
1750         free(*appt);
1751         *appt = NULL;
1752 }
1753
1754 extern void
1755 free_cal_struct(Dtcm_calendar **cal) {
1756         int     i;
1757
1758         if (!cal)
1759                 return;
1760
1761         if ((*cal)->names) {
1762                 for (i = 0; i < (*cal)->num_names; i++)
1763                         if ((*cal)->names[i])
1764                                 free((*cal)->names[i]);
1765
1766                 free((*cal)->names);
1767         }
1768
1769         /* potential memory leak here.  We must be careful, as results 
1770            from querys should be thrown away with csa_free(), while 
1771            structures we've set up to do update/write operations were 
1772            allocated by the client, and need to be freed by that client. */
1773
1774         if (((*cal)->reason == appt_read) && ((*cal)->filled == True))
1775                 csa_free((*cal)->attrs);
1776         else
1777                 if ((*cal)->attrs) {
1778                         for (i = 0; i < (*cal)->count; i++)
1779                                 free_attr(&((*cal)->attrs[i]));
1780                         free((*cal)->attrs);
1781                 }
1782
1783         free(*cal);
1784         *cal = NULL;
1785 }
1786
1787 extern void
1788 set_appt_links(Dtcm_appointment *appt) {
1789         int     idx;
1790         char    *idx_name;
1791
1792         appt->identifier = NULL;
1793         appt->modified_time = NULL;
1794         appt->author = NULL;
1795         appt->number_recurrence = NULL;
1796         appt->time = NULL;
1797         appt->type = NULL;
1798         appt->subtype = NULL;
1799         appt->private = NULL;
1800         appt->end_time = NULL;
1801         appt->show_time = NULL;
1802         appt->what = NULL;
1803         appt->state = NULL;
1804         appt->repeat_type = NULL;
1805         appt->repeat_times = NULL;
1806         appt->repeat_interval = NULL;
1807         appt->repeat_week_num = NULL;
1808         appt->recurrence_rule = NULL;
1809         appt->beep = NULL;
1810         appt->flash = NULL;
1811         appt->mail = NULL;
1812         appt->popup = NULL;
1813         appt->sequence_end_date = NULL;
1814
1815         for (idx = 0; idx < appt->count; idx++) {
1816                 idx_name = appt->attrs[idx].name;
1817                 if (!idx_name)
1818                         continue;
1819                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER) == 0)
1820                         appt->identifier = &appt->attrs[idx];
1821                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_LAST_UPDATE) == 0)
1822                         appt->modified_time = &appt->attrs[idx];
1823                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_ORGANIZER) == 0)
1824                         appt->author = &appt->attrs[idx];
1825                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_NUMBER_RECURRENCES) == 0)
1826                         appt->number_recurrence = &appt->attrs[idx];
1827                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_START_DATE) == 0)
1828                         appt->time = &appt->attrs[idx];
1829                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_TYPE) == 0)
1830                         appt->type = &appt->attrs[idx];
1831                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_SUBTYPE) == 0)
1832                         appt->subtype = &appt->attrs[idx];
1833                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_CLASSIFICATION) == 0)
1834                         appt->private = &appt->attrs[idx];
1835                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_END_DATE) == 0)
1836                         appt->end_time = &appt->attrs[idx];
1837                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_SHOWTIME) == 0)
1838                         appt->show_time = &appt->attrs[idx];
1839                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_SUMMARY) == 0)
1840                         appt->what = &appt->attrs[idx];
1841                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_STATUS) == 0)
1842                         appt->state = &appt->attrs[idx];
1843                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE) == 0)
1844                         appt->repeat_type = &appt->attrs[idx];
1845                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES) == 0)
1846                         appt->repeat_times = &appt->attrs[idx];
1847                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL) == 0)
1848                         appt->repeat_interval = &appt->attrs[idx];
1849                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM) == 0)
1850                         appt->repeat_week_num = &appt->attrs[idx];
1851                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_RECURRENCE_RULE) == 0)
1852                         appt->recurrence_rule = &appt->attrs[idx];
1853                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_AUDIO_REMINDER) == 0)
1854                         appt->beep = &appt->attrs[idx];
1855                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_FLASHING_REMINDER) == 0)
1856                         appt->flash = &appt->attrs[idx];
1857                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_MAIL_REMINDER) == 0)
1858                         appt->mail = &appt->attrs[idx];
1859                 else if (strcmp(idx_name, CSA_ENTRY_ATTR_POPUP_REMINDER) == 0)
1860                         appt->popup = &appt->attrs[idx];
1861                 else if (strcmp(idx_name, CSA_X_DT_ENTRY_ATTR_SEQUENCE_END_DATE) == 0)
1862                         appt->sequence_end_date = &appt->attrs[idx];
1863         }
1864 }
1865
1866 extern void
1867 set_cal_links(Dtcm_calendar *cal) {
1868         int     idx;
1869         char    *idx_name;
1870
1871         for (idx = 0; idx < cal->count; idx++) {
1872                 idx_name = cal->attrs[idx].name;
1873                 if (strcmp(idx_name, CSA_CAL_ATTR_CALENDAR_NAME) == 0)
1874                         cal->cal_name = &cal->attrs[idx];
1875                 else if (strcmp(idx_name, CSA_X_DT_CAL_ATTR_DATA_VERSION) == 0)
1876                         cal->server_version = &cal->attrs[idx];
1877                 else if (strcmp(idx_name, CSA_CAL_ATTR_NUMBER_ENTRIES) == 0)
1878                         cal->num_entries = &cal->attrs[idx];
1879                 else if (strcmp(idx_name, CSA_CAL_ATTR_CALENDAR_SIZE) == 0)
1880                         cal->cal_size = &cal->attrs[idx];
1881                 else if (strcmp(idx_name, CSA_CAL_ATTR_ACCESS_LIST) == 0)
1882                         cal->access_list = &cal->attrs[idx];
1883                 else if (strcmp(idx_name, CSA_CAL_ATTR_TIME_ZONE) == 0)
1884                         cal->time_zone = &cal->attrs[idx];
1885         }
1886 }
1887
1888 extern void
1889 setup_range(CSA_attribute **attrs, CSA_enum **ops, int *count, time_t start,
1890             time_t stop, CSA_sint32 type, CSA_sint32 state, boolean_t use_state,
1891             int version) {
1892         int             a_size, o_size;
1893         CSA_enum        *op_ptr;
1894         CSA_attribute   *attr_ptr;
1895
1896
1897         if (use_state)
1898                 *count = 4;
1899         else
1900                 *count = 3;
1901
1902         a_size = sizeof(CSA_attribute) * (*count);
1903         attr_ptr = (CSA_attribute *)ckalloc(a_size);
1904         memset(attr_ptr, 0, a_size);
1905
1906         o_size = sizeof(CSA_enum) * (*count);
1907         op_ptr = (CSA_enum *)ckalloc(o_size);
1908         memset(op_ptr, 0, o_size);
1909
1910         initialize_entry_attr(CSA_ENTRY_ATTR_START_DATE_I, &attr_ptr[0], appt_write, version);
1911         attr_ptr[0].value->item.string_value = malloc(BUFSIZ);
1912         _csa_tick_to_iso8601(start, attr_ptr[0].value->item.string_value);
1913
1914         op_ptr[0] = CSA_MATCH_GREATER_THAN_OR_EQUAL_TO;
1915
1916         initialize_entry_attr(CSA_ENTRY_ATTR_START_DATE_I, &attr_ptr[1], appt_write, version);
1917         attr_ptr[1].value->item.string_value = malloc(BUFSIZ);
1918         _csa_tick_to_iso8601(stop, attr_ptr[1].value->item.string_value);
1919         op_ptr[1] = CSA_MATCH_LESS_THAN_OR_EQUAL_TO;
1920
1921         initialize_entry_attr(CSA_ENTRY_ATTR_TYPE_I, &attr_ptr[2], appt_write, version);
1922         attr_ptr[2].value->item.sint32_value = type;
1923         op_ptr[2] = CSA_MATCH_EQUAL_TO;
1924
1925         if (use_state) {
1926                 initialize_entry_attr(CSA_ENTRY_ATTR_STATUS_I, &attr_ptr[3], appt_write, version);
1927                 attr_ptr[3].value->item.sint32_value = state;
1928                 op_ptr[3] = CSA_MATCH_EQUAL_TO;
1929         }
1930
1931         *attrs = attr_ptr;
1932         *ops = op_ptr;
1933 }
1934
1935 extern void
1936 free_range(CSA_attribute **attrs, CSA_enum **ops, int count) {
1937         int     i;
1938
1939         for (i = 0; i < count; i++) {
1940                 free((*attrs)[i].name);
1941                 if (((*attrs)[i].value->type == CSA_VALUE_STRING) ||
1942                     ((*attrs)[i].value->type == CSA_VALUE_DATE_TIME))
1943                         if ((*attrs)[i].value->item.string_value)
1944                                 free((*attrs)[i].value->item.string_value);
1945
1946                 free((*attrs)[i].value);
1947         }
1948
1949         /* This memory was allocated by the client, and must be freed 
1950            by the client */
1951
1952         free(*attrs);
1953
1954         *attrs = NULL;
1955
1956         free(*ops);
1957         *ops = NULL;
1958 }
1959
1960 /*
1961  * In Motif you can't associate user data with items in a list.  To get around
1962  * this we have the following simple functions (CmDataList*) that maintain
1963  * a list of user data.  We follow the intrinscs coding style to re-inforce
1964  * the relationship these routines have to the XmList* functions.
1965  */
1966
1967 /*
1968  * Create a list to store user data
1969  */
1970 CmDataList *
1971 CmDataListCreate(void)
1972
1973 {
1974         return (CmDataList *)calloc(1, sizeof(CmDataList));
1975 }
1976
1977 /*
1978  * Destroy list
1979  */
1980 void
1981 CmDataListDestroy(CmDataList *list, int free_data)
1982
1983 {
1984         CmDataListDeleteAll(list, free_data);
1985         free(list);
1986 }
1987
1988 /*
1989  * Create node to hold data in list.
1990  */
1991 static CmDataItem *
1992 CmDataItemCreate(void)
1993
1994 {
1995         return (CmDataItem *)calloc(1, sizeof(CmDataItem));
1996 }
1997
1998 /*
1999  * Add user data to list at specified position. Note that this
2000  * routine must be called for every item added to a list.
2001  * If the item has no user data, just pass NULL.
2002  *
2003  *      list            List to add data to
2004  *      data            User data. NULL for no data.
2005  *      position        Where to insert data, starting with 1 for the
2006  *                      first item.  0 to append to end of list.
2007  *
2008  * Returns
2009  *              1       Success
2010  *              -1      Invalid position
2011  */
2012 int
2013 CmDataListAdd(CmDataList *list, void *data, int position)
2014
2015 {
2016         CmDataItem      *item, *p;
2017         int             n;
2018
2019         /* Create new node to hold data */
2020         item = CmDataItemCreate();
2021         item->data = data;
2022
2023         /* Insert node into list at appropriate spot */
2024         if (list->head == NULL) {
2025                 list->head = item;
2026         } else if (position == 0) {
2027                 /* Special case.  0 means append to end */
2028                 list->tail->next = item;
2029         } else if (position == 1) {
2030                 item->next = list->head;
2031                 list->head = item;
2032         } else {
2033                 for (n = 2, p = list->head; p != NULL && n < position;
2034                      p = p->next, n++)
2035                         ;
2036
2037                 if (p == NULL) {
2038                         return -1;
2039                 }
2040
2041                 item->next = p->next;
2042                 p->next = item;
2043         }
2044
2045         /* If new item is at the end of the list, update tail */
2046         if (item->next == NULL) {
2047                 list->tail = item;
2048         }
2049
2050         list->count++;
2051
2052         return 1;
2053 }
2054
2055 /*
2056  * Delete user data from a position in the list.  If free_data is
2057  * True then this routine will call free(3C) on the user data, otherwise
2058  * the user data is returned so that the caller can dispose of it.
2059  * This routine should be called anytime you delete an item from a
2060  * scrolling list.
2061  *
2062  *      list            List to delete data from
2063  *      position        Location of item to delete. 0 for last item
2064  *      free_data       True if you want this routine to call free()
2065  *                      on the data for you.  Otherwise this routine
2066  *                      will return the address of the data.
2067  *
2068  * Returns
2069  *      NULL    No data found for item at the specified position
2070  *      1       Success (free_data was True)
2071  *      other   Address of data for item at the specified position.
2072  *              (free_data was False)
2073  */
2074 void *
2075 CmDataListDeletePos(CmDataList *list, int position, int free_data)
2076
2077 {
2078         void            *data;
2079         CmDataItem      *p, *item;
2080         int             n;
2081
2082         /* Special case. 0 means delete last item */
2083         if (position == 0) {
2084                 position = list->count;
2085         }
2086
2087         if (list->head == NULL) {
2088                 return NULL;
2089         } else if (position == 1) {
2090                 item = list->head;
2091                 list->head = item->next;
2092                 if (list->tail == item) {
2093                         list->tail = item->next;
2094                 }
2095         } else {
2096                 for (n = 2, p = list->head; p->next != NULL && n < position;
2097                      p = p->next, n++)
2098                         ;
2099                 
2100                 if (p->next == NULL) {
2101                         return NULL;
2102                 }
2103
2104                 item = p->next;
2105                 p->next = item->next;
2106                 if (list->tail == item) {
2107                         list->tail = p;
2108                 }
2109         }
2110
2111         list->count--;
2112
2113         data = item->data;
2114         free(item);
2115
2116         if (free_data) {
2117                 if (data != NULL) 
2118                         free(data);
2119                 return (void *)1;
2120         } else {
2121                 return data;
2122         }
2123 }
2124
2125 /* 
2126  * Delete all nodes in the list.
2127  *
2128  *      list            List to delete nodes from
2129  *      free_data       True if you want this routine to call free()
2130  *                      on the data for you.
2131  */
2132 void
2133 CmDataListDeleteAll(CmDataList *list, int free_data)
2134
2135 {
2136         CmDataItem      *p, *tmp;
2137
2138         p = list->head;
2139         while (p != NULL) {
2140                 if (free_data && p->data != NULL) {
2141                         free(p->data);
2142                 }
2143
2144                 tmp = p;
2145                 p = p->next;
2146                 free(tmp);
2147         }
2148
2149         list->count = 0;
2150         list->head = NULL;
2151         list->tail = NULL;
2152
2153         return;
2154 }
2155
2156 /*
2157  * Get data for the item at a particular position in a list.
2158  */
2159 void *
2160 CmDataListGetData(CmDataList *list, int position)
2161
2162 {
2163         void            *data;
2164         CmDataItem      *p, *item;
2165         int             n;
2166
2167         if (list->head == NULL) {
2168                 return NULL;
2169         } else if (position == 0) {
2170                 data = list->tail->data;
2171         } else {
2172                 for (n = 1, p = list->head; p != NULL && n < position;
2173                      p = p->next, n++)
2174                         ;
2175                 if (p == NULL) {
2176                         return NULL;
2177                 }
2178                 data = p->data;
2179         }
2180
2181         return data;
2182 }
2183
2184 #ifdef DEBUG
2185 /*
2186  * For dumping contents of list
2187  */
2188 void
2189 CmDataListDump(CmDataList *list)
2190
2191 {
2192         CmDataItem      *p;
2193         int             n;
2194
2195         printf("***** %d items:\n", list->count);
2196
2197         for (p = list->head, n = 1; p != NULL; p = p->next, n++) {
2198                 printf("%3d: %s\n", n, p->data ? (char *)p->data : "<nil>");
2199         }
2200
2201         return;
2202 }
2203 #endif