/*
 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * mslp_list.c : Minimal SLP v2 string list manipulation utilities
 *
 *  All reads, writes, message composing and decomposing is done here.
 *
 * Version: 1.7
 * Date:    03/30/99
 *
 * Licensee will, at its expense,  defend and indemnify Sun Microsystems,
 * Inc.  ("Sun")  and  its  licensors  from  and  against any third party
 * claims, including costs and reasonable attorneys' fees,  and be wholly
 * responsible for  any liabilities  arising  out  of  or  related to the
 * Licensee's use of the Software or Modifications.   The Software is not
 * designed  or intended for use in  on-line  control  of  aircraft,  air
 * traffic,  aircraft navigation,  or aircraft communications;  or in the
 * design, construction, operation or maintenance of any nuclear facility
 * and Sun disclaims any express or implied warranty of fitness  for such
 * uses.  THE SOFTWARE IS PROVIDED TO LICENSEE "AS IS" AND ALL EXPRESS OR
 * IMPLIED CONDITION AND WARRANTIES, INCLUDING  ANY  IMPLIED  WARRANTY OF
 * MERCHANTABILITY,   FITNESS  FOR  WARRANTIES,   INCLUDING  ANY  IMPLIED
 * WARRANTY  OF  MERCHANTABILITY,  FITNESS FOR PARTICULAR PURPOSE OR NON-
 * INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT WILL SUN BE LIABLE HEREUNDER
 * FOR ANY DIRECT DAMAGES OR ANY INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL
 * OR CONSEQUENTIAL DAMAGES OF ANY KIND.
 *
 * (c) Sun Microsystems, 1998, All Rights Reserved.
 * Author: Erik Guttman
 */
 /*
	Portions Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>

#include "mslp_sd.h"
#include "slp.h"
#include "mslp.h"

/*
 * list_intersection
 *
 * return:
 *   1 if there is an overlap, 0 otherwise.
 */
EXPORT int list_intersection(const char *pcL1, const char *pcL2)
{
    int i1 = 0, i2 = 0;
    char *pcS1, *pcS2;
    char c;
    
    if (!pcL1 || !pcL2) 
    {
        LOG(SLP_LOG_ERR,"list_intersection: got NULL value as a parameter!");
        return 0;
    }
    
    if (*pcL1 == '\0' && *pcL2 == '\0') 
        return 1;
    
    for ( pcS1=get_next_string(",",pcL1,&i1,&c); pcS1; pcS1=get_next_string(",",pcL1,&i1,&c) ) 
    {
        i2 = 0;
        for (pcS2=get_next_string(",",pcL2,&i2,&c); pcS2;pcS2=get_next_string(",",pcL2,&i2,&c))
        {
            int result = SDstrcasecmp(pcS1,pcS2);
            SLPFree(pcS2);
            if (result==0)
            {
                SLPFree(pcS1);
                return 1;
            }
        }
        
        SLPFree(pcS1);
    }
    
    return 0;
}

/*
 * list_merge
 *
 *   This function will build a list of unique elements.  It assumes
 *   that the items arriving with pcNewList are packed, for efficiency.
 *   If the list grows too large, it will be grown to take in the new
 *   string and 'breathing room' for further expansion.
 *
 *     pcNewList    The new list to merge in with the old one.
 *     ppcList      A pointer to the buffer with the list to be built onto.
 *     piListLen    The max size of the list list to be build onto.
 *     iCheck       If this is 0, don't check for duplicates.
 *
 * Returns:  None.
 * 
 * Side Effects:
 *
 *   A call to this function can result in *ppcList (the buffer) being
 *   reallocated and *piListLen (the max buffer size) being expanded.)
 */
EXPORT void list_merge(const char *pcNewList, char **ppcList, int *piListLen, int iCheck)
{
    int offset = 0;
    char c, *pcScope;
    int initial = 0;
    
    if (!pcNewList) 
        return;
    
    if (!ppcList)
        return;
    
    if (!*ppcList)
    {
        *ppcList = safe_malloc(strlen(pcNewList)+1,pcNewList,strlen(pcNewList));
        *piListLen = strlen(*ppcList);
        return;
    }
    
    if (*piListLen == 0) 
        initial = 1; /* suppresses initial comma in new lists */
    
    if (!iCheck) 
    {
        int iSLen = strlen(pcNewList);
        if ((iSLen + (int) strlen(*ppcList)+1) >= *piListLen) 
        { /* too big? */
            char *pcOld = *ppcList;
            *piListLen += iSLen + LISTINCR;
            *ppcList = safe_malloc(*piListLen,*ppcList,strlen(*ppcList));
            SLPFree(pcOld);
        }
        
        if (!initial) 
            slp_strcat(*ppcList,",");
            
        slp_strcat(*ppcList,pcNewList);
        return;
    }
    
    while((pcScope = get_next_string(",",pcNewList,&offset,&c))) 
    {
        if (!list_intersection(pcScope,*ppcList)) 
        {
            int iSLen = strlen(pcScope);
        
            if ((iSLen + (int) strlen(*ppcList)+1) >= *piListLen) 
            { /* too big? */
                char *pcOld = *ppcList;
                *piListLen += iSLen + LISTINCR;
                *ppcList = safe_malloc(*piListLen,*ppcList,strlen(*ppcList));
                SLPFree(pcOld);
            }
    
            if (initial != 1) 
                slp_strcat(*ppcList,","); /* supress initial comma */
        
            slp_strcat(*ppcList,pcScope); /* append the scope not already on list */
        }
        
        SLPFree(pcScope);
    }   
}

/*
 * list_subset
 *
 *    Compares two lists.  All the elements in the first list must be
 *    in the second and neither may be empty lists.
 *
 *  pcSub    The list which must have all elements in pcSuper
 *  pcSuper  The list which must contain all or more elements than in pcSub
 *
 * Returns:
 *    0 if not a subset, 1 if it is a subset.
 *
 * Side effects:
 *    None.
 */
EXPORT int list_subset(const char *pcSub, const char *pcSuper) {

  int offset = 0;
  char c, *pcScope;

  if (!pcSub || !pcSuper ||                          /* either are NULL */
      (pcSub[0] == '\0' && pcSuper[0] != '\0')) {    /* sub empty, super not */
    return 0;
  }
     
  while((pcScope = get_next_string(",",pcSub,&offset,&c))) {
    if (!list_intersection(pcScope,pcSuper)) {
      SLPFree(pcScope);
      return 0;
    }
    SLPFree(pcScope);
  }

  return 1;
}

/*
 * Take any extraneous spaces out of a string or string list for the
 * sake of comparison.
 *
 * The function is smart enough to deal with attribute lists too.
 *
 * Will create a copy of a list, and pack the values into it, returning
 * that one. 
 */
EXPORT char * list_pack(const char *pc) {

  const char *pcSrc;
  char *pcTemp,  *pcDest;
  
  if ( !pc || !*pc) return safe_malloc(1,0,0);
  pcTemp = safe_malloc(strlen(pc)+1,0,0); /* clone */
  pcDest = pcTemp;
  pcSrc  = pc;
  
  while(*pcSrc) {
    char *pcStart = NULL;
    /* initial space */
    while (*pcSrc && isspace(*pcSrc)) 
      pcSrc++; /* advance past the initial space and do not copy */

    if (*pcSrc == ',' || *pcSrc == '(' || *pcSrc == ')' || *pcSrc == '=') {
      *pcDest++ = *pcSrc++;
      continue;
    }
    
    pcStart = NULL;
    /* nonspace value */
    while (*pcSrc) {
      if (*pcSrc == ',' || *pcSrc == '(' || *pcSrc == ')' || *pcSrc == '=') {
	if (pcStart) {
	  *--pcDest = *pcSrc++; /* overwrite terminal 'space' */
	  pcDest++;
	  pcStart = NULL;
	} else {
	  *pcDest++ = *pcSrc++;  
	}
	break;
      }
      if (*pcSrc && !isspace(*pcSrc)) {
	*pcDest++ = *pcSrc++;
	pcStart = NULL; /* we might have several series of ' ' */
      } else if (pcStart == NULL) {
	/* only copy ONE space internal to values */
	/* we also have to save where it starts so that trailing
	   spaces can be eliminated before the end of the string
	   or the end of terms (ie. followed by a ',' '(' ')' or '=') */
	pcStart = pcDest; 
	*pcDest++ = ' '; /* don't use pcDest as it might be a CR or HT */
	pcSrc++;
	while (*pcSrc && isspace(*pcSrc)) pcSrc++; /* do not copy */
      } else {
	pcSrc++;
      }
    }
    if (!*pcSrc) {
      /* there has been a NULL in the Src stream in an item */
      if (pcStart) { 
        *--pcDest = '\0'; 
      }
      break;
    }

  }

  return pcTemp;
}

/*
 * Take the element out of a list.
 *
 * This function is intended for scope lists.
 *
 * Will create a copy of a list without the element.  If the element
 * doesn't reside in the list, it will return null. 
 */
EXPORT char * list_remove_element(const char *list, const char *element) 
{
    int			elementLen, newListLen;
    char		tempElem[1024];
    char*		curPtr;
    char*		newList = NULL;
    
    if ( !element || !list || strlen(list) < strlen(element) )
        return NULL;
        
    elementLen = strlen(element);
    
    strcpy( tempElem, "," );
    slp_strcat( tempElem, element );
    slp_strcat( tempElem, "," );
    
    curPtr = strstr( list, tempElem );
    
    if ( curPtr )
    {
        // ok, we found element within the list
        newListLen = strlen(list)-(strlen(tempElem)-1);		// not including null terminator
        newList = (char*)malloc(newListLen+1);
        memcpy( newList, list, curPtr-list );
        
        memcpy( newList+(curPtr-list), curPtr+strlen(tempElem)-1, strlen(list)-((curPtr-list)+strlen(tempElem)-1) );	// whew, that's ugly
        newList[newListLen] = '\0';
    }
    else if ( memcmp( list, &tempElem[1], strlen(tempElem)-1 ) == 0 )
    {
        // the element at the beginning of the list
        newListLen = strlen(list)-(strlen(tempElem)-1);		// not including null terminator
        newList = (char*)malloc(newListLen+1);				// including terminator
        memcpy( newList, list+(strlen(tempElem)-1), newListLen );
        newList[newListLen] = '\0';
    }
    else if ( memcmp( &list[strlen(list)-(strlen(tempElem)-1)], tempElem, strlen(tempElem)-1 ) == 0 )
    {
        // the element at the end of the list
        newListLen = strlen(list)-(strlen(tempElem)-1);		// not including null terminator
        newList = (char*)malloc(newListLen+1);				// include terminator
        memcpy( newList, list, newListLen );
        newList[newListLen] = '\0';
    }
    else if ( strcmp( list, element ) == 0 )		
    {
        // list only contains element
        newList = (char*)malloc(1);
        newList[0] = '\0';
    }
    
    return newList;
}



