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