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