#ifndef lint
#ifdef sccs
static	char sccsid[] = "@(#)attr.c 1.1 94/10/31";
#endif
#endif

/*
 * Copyright (c) 1986 by Sun Microsystems, Inc.
 */

#include <stdio.h>
#include <sunwindow/attr_impl.h>
#include <sunwindow/sun.h>
#include <sunwindow/sv_malloc.h>

/* Note that changes to the basic attribute-value traversal
 * should also be made in attr_portable.c for portable implmentations.
 */


/* attr_create creates an avlist from the VARARGS passed
 * on the stack.
 */
/*VARARGS2*/
Attr_avlist
attr_create(listhead, listlen, va_alist)
caddr_t	*listhead; 
int	 listlen; 
va_dcl
{
   Attr_avlist	avlist;
   va_list	valist;

   va_start(valist);
   avlist = attr_make(listhead, listlen, valist);
   va_end(valist);
   return avlist;
}


/* attr_make copies the attribute-value list pointed to by valist to
 * the storage pointed to by listhead, or some new storage if that is
 * null.  If listhead is not null, then the list must be less than or equal
 * to listlen ATTR_SIZE byte chunks; if not 0 is returned.
 * The count of the avlist is returned in *count_ptr, if count_ptr is
 * not NULL.
 */
Attr_avlist
attr_make_count(listhead, listlen, valist, count_ptr) 
Attr_avlist	listhead; 
int	 	listlen; 
va_list		valist;
int		*count_ptr;
{
   u_int 	count;
   
#ifdef NON_PORTABLE
   count = (u_int) (LINT_CAST(attr_count((Attr_avlist) valist)));
#else
   count = (u_int) (LINT_CAST(valist_count(valist)));
#endif
   /* if the user supplied storage space for attributes is not big enough
    * for the attributes in the attribute list, then exit!
    */
   if (listhead)  {
	if (count > listlen)  {
            printf("Number of attributes(%d) in the attr list exceeds\n",count);
	    printf("the maximum number(%d) specified.  Exit!\n",listlen);
	    exit(1);
	}
   }    else   {
      listhead = (Attr_avlist) (LINT_CAST(sv_malloc((ATTR_SIZE * count)+1)));
   }
#ifdef NON_PORTABLE
   (void) attr_copy_avlist(listhead, (Attr_avlist) (LINT_CAST(valist))); 
#else
   (void) attr_copy_valist(listhead, valist); 
#endif

   if (count_ptr)
      *count_ptr = count;
   return(listhead);
}


#define	avlist_get(avlist)	*(avlist)++

/* Copy count elements from list to dest.
 * Advance both list and dest to the next element after
 * the last one copied.
 */
#define	avlist_copy(avlist, dest, count)	\
    { \
        bcopy((char *) avlist, (char *) dest, (int)(count * ATTR_SIZE)); \
        avlist += count; \
        dest += count; \
    }


/* A macro to copy attribute values 
 * count is the number of caddr_t size chunks to copy.
 */
#define	avlist_copy_values(avlist, dest, count) \
    if (count == 1) \
        *dest++ = avlist_get(avlist); \
    else { \
	avlist_copy(avlist, dest, count); \
    }


/* attr_copy_avlist copies the attribute-value list from 
 * avlist to dest.  Recursive lists are collapsed into dest.
 */
 
Attr_avlist
attr_copy_avlist(dest, avlist) 
register Attr_avlist	dest;
register Attr_avlist	avlist;
{
   register Attr_attribute	attr;
   register u_int		cardinality;
   
   while (attr = (Attr_attribute) avlist_get(avlist)) {
       cardinality = ATTR_CARDINALITY(attr);
       switch(ATTR_LIST_TYPE(attr)) {
           case ATTR_NONE:	/* not a list */
               *dest++ = (caddr_t) attr;
               avlist_copy_values(avlist, dest, cardinality);
               break;
               
           case ATTR_NULL:	/* null terminated list */
               *dest++ = (caddr_t) attr;
               switch (ATTR_LIST_PTR_TYPE(attr)) {
                   case ATTR_LIST_IS_INLINE:
                       /* Note that this only checks the first four bytes
                        * for the null termination.
			* Copy each value element until we have copied the
			* null termination.
                        */
		       do {
		           avlist_copy_values(avlist, dest, cardinality);
		       } while (*(dest - 1));
                       break;
                       
                   case ATTR_LIST_IS_PTR:
                       *dest++ = avlist_get(avlist);
                       break;
               }
               break;
               
            case ATTR_COUNTED:		/* counted list */
               *dest++ = (caddr_t) attr;
               switch (ATTR_LIST_PTR_TYPE(attr)) {
                   case ATTR_LIST_IS_INLINE: {
			   register u_int	count;

			   *dest = avlist_get(avlist);	/* copy the count */
			    count = ((u_int) *dest++) * cardinality;
			    avlist_copy_values(avlist, dest, count);
			}
                        break;
                        
                   case ATTR_LIST_IS_PTR:
                       *dest++ = avlist_get(avlist);
                       break;
               }
               break;
               
            case ATTR_RECURSIVE:	/* recursive attribute-value list */
                if (cardinality != 0)	/* don't strip it */
                    *dest++ = (caddr_t) attr;
                    
               switch (ATTR_LIST_PTR_TYPE(attr)) {
                   case ATTR_LIST_IS_INLINE:
                       dest = attr_copy_avlist(dest, avlist);
                       if (cardinality != 0)	/* don't strip it */
                           dest++;	/* move past the null terminator */
                       avlist = attr_skip(attr, avlist);
                       break;
                       
                   case ATTR_LIST_IS_PTR:
                       if (cardinality != 0)	/* don't collapse inline */
                           *dest++ = avlist_get(avlist);
                       else {
			   Attr_avlist new_avlist = (Attr_avlist) 
			   	(LINT_CAST(avlist_get(avlist)));
			   if (new_avlist)
			       /* Copy the list inline -- don't 
				* move past the null termintor.
				* Here both the attribute and null
				* terminator will be stripped away.
				*/
			       dest = attr_copy_avlist(dest, new_avlist);
		       }
	               break;
	       }
	       break;
	 }
    }
    *dest = 0; 
    return(dest);
}


/* attr_count counts the number of slots in the av-list avlist.
 * Recursive lists are counted as being collapsed inline.
 */
int
attr_count(avlist)
Attr_avlist avlist; 
{
   /* count the null termination */
   return(attr_count_avlist(avlist, 0) + 1);
}

static char *attr_names[ATTR_PKG_LAST-ATTR_PKG_FIRST+1] = {
    "Generic",
    "Panel",
    "Scrollbar",
    "Cursor",
    "Menu",
    "Textsw",
    "Tool",
    "Seln_base",
    "Entity",
    "Image",
    "Win",
    "Frame",
    "Canvas",
    "Tty",
    "Icon",
    "NOP",
    "Pixwin",
    "Alert",
    "Help"
};


char *
attr_sprint(s, attr)
char *s;
register Attr_attribute attr;
{
    static char msgbuf[100];
    extern char	*sprintf();
    
    if (!s) s = msgbuf;
    if (((int) ATTR_PKG(attr)) >= ((int) ATTR_PKG_FIRST) && 
	((int) ATTR_PKG(attr)) <= ((int) ATTR_PKG_LAST)) {
	char *name = attr_names[((int) ATTR_PKG(attr)) - ((int)ATTR_PKG_FIRST)];

	if (!name) name = "Syspkg";
	(void) sprintf(s, "Attr pkg= %s, id= %d, cardinality= %d (0x%x)",
		name, ATTR_ORDINAL(attr), ATTR_CARDINALITY(attr), attr);
    } else {
	(void) sprintf(s, "Attr pkg= %s, id= %d, cardinality= %d (0x%x)",
	    ATTR_PKG(attr), ATTR_ORDINAL(attr), ATTR_CARDINALITY(attr), attr);
    }
    return(s);
}

void
attr_check_pkg(last_attr, attr)
    Attr_attribute	last_attr, attr;
{
    if (!ATTR_VALID_PKG_ID(attr)) {
	char	expand_attr[100];

	(void) fprintf(stderr,
		"Malformed or non-terminated attribute-value list.\n");
	(void) fprintf(stderr, "Last valid attribute was %s.\n",
		attr_sprint(expand_attr, last_attr));
	abort();
    }
}


int
attr_count_avlist(avlist, last_attr) 
register Attr_avlist	avlist; 
register Attr_attribute	last_attr;
{
   register Attr_attribute	attr;
   register u_int		count = 0;
   register u_int		num;
   register u_int		cardinality;
   
   while (attr = (Attr_attribute) *avlist++) {
       count++;			/* count the attribute */
       cardinality = ATTR_CARDINALITY(attr);
       attr_check_pkg(last_attr, attr);
       last_attr = attr;
       switch(ATTR_LIST_TYPE(attr)) {
           case ATTR_NONE:	/* not a list */
               count += cardinality;
               avlist += cardinality;
               break;
               
           case ATTR_NULL:	/* null terminated list */
               switch (ATTR_LIST_PTR_TYPE(attr)) {
                   case ATTR_LIST_IS_INLINE:
                       /* Note that this only checks the first four bytes
                        * for the null termination.
                        */
                       while (*avlist++)
                           count++;
                       count++;			/* count the null terminator */
                       break;
               
                   case ATTR_LIST_IS_PTR:
                       count++;
                       avlist++;
                       break;
               }
               break;
               
            case ATTR_COUNTED:		/* counted list */
                switch (ATTR_LIST_PTR_TYPE(attr)) {
                    case ATTR_LIST_IS_INLINE:
                       num = ((u_int)(*avlist)) * cardinality + 1;
                       count += num;
                       avlist += num;
                       break;
                    case ATTR_LIST_IS_PTR:
                        count++;
                        avlist++;
                        break;
                }
                break;      
               
            case ATTR_RECURSIVE:	/* recursive attribute-value list */
                if (cardinality == 0)	/* don't include the attribute */
                    count--;
                    
                switch (ATTR_LIST_PTR_TYPE(attr)) {
                    case ATTR_LIST_IS_INLINE:
                        count += attr_count_avlist(avlist, attr);
                        if (cardinality != 0)	/* count the null terminator */
                            count++;
                        avlist = attr_skip(attr, avlist);
                        break;
                       
                    case ATTR_LIST_IS_PTR:
                        if (cardinality != 0) {	/* don't collapse inline */
                            count++;
                            avlist++;
                        } else if (*avlist)
                            /* Here we count the elements of the
                             * recursive list as being inline.
                             * Don't count the null terminator.
                             */
	                    count += attr_count_avlist((Attr_avlist) (LINT_CAST
			    	(*avlist++)), attr);
	               else
	                   avlist++;
		       break;
                }
            break;
        }    
    }
    return count;
}


/* attr_skip_value returns a pointer to the attribute after 
 * the value pointed to by avlist.  attr should be the 
 * attribute which describes the value at avlist.
 */
Attr_avlist
attr_skip_value(attr, avlist)
register Attr_attribute	 attr;
register Attr_avlist	 avlist;
{ 
    switch (ATTR_LIST_TYPE(attr)) {
        case ATTR_NULL:
            if (ATTR_LIST_PTR_TYPE(attr) == ATTR_LIST_IS_PTR)
                avlist++;
            else
 	        while (*avlist++);
 	    break;
 	        
 	case ATTR_RECURSIVE:
            if (ATTR_LIST_PTR_TYPE(attr) == ATTR_LIST_IS_PTR)
	        avlist++;
            else
                while (attr = (Attr_attribute) *avlist++)
                    avlist = attr_skip_value(attr, avlist);
 	    break;
 	        
 	case ATTR_COUNTED:
            if (ATTR_LIST_PTR_TYPE(attr) == ATTR_LIST_IS_PTR)
                avlist++;
            else
 	        avlist += ((int) *avlist) * ATTR_CARDINALITY(attr) + 1;
 	    break;
 	        
 	case ATTR_NONE:
 	    avlist += ATTR_CARDINALITY(attr);
 	    break;
     }
     return avlist;
}
 	
/* attr_free frees the attribute-value list
 */
void
attr_free(avlist)
Attr_avlist avlist; 
{
   free((char *) avlist);
}
