File: cpw_str.h

/***************************************************************************/
/*                                                                         */
/*  cpw_str.h                                                              */
/*                                                                         */
/*    Null safe, Unicode safe string library for Cpw. Feel free to use     */
/*    independantly of Cpw. Relies on opensl.                              */
/*                                                                         */
/*  Copyright 2001-2002 by                                                 */
/*  Jim Mathies,                                                           */
/*                                                                         */
/*  This file is part of the Cpw project, and may only be used,            */
/*  modified, and distributed under the terms of the Cpw project           */
/*  license.  By continuing to use, modify, or distribute this file        */
/*  you indicate that you have read the license and understand and         */
/*  accept it fully.                                                       */
/*                                                                         */
/*  File Platform: cross                                                   */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/*  Example use:                                                           */
/*                                                                         */
/*    pStr s = null;                                                       */
/*    setString( &s, "Hello" );                                            */
/*    freeStr( &s );                                                       */
/*                                                                         */
/***************************************************************************/

#ifndef __cpw_str_h__
#define __cpw_str_h__

#include "cpw_config.h"
#include "cpw_opensl.h"

CPW_BEGIN_HEADER

#define STRBUF 1024 /* used in value conversions */

  /*************************************************************************/
  /*                                                                       */
  /* <Define>                                                              */
  /*    Memory allocation wrappers for this library.                       */
  /*                                                                       */
  #define strMalloc( size )       (char*)cpwmalloc( size )
  #define strRealloc( p, size )   (char*)cpwrealloc( p, size )
  #define strFree( p )            cpwfree( p )
  /*                                                                       */
  /*************************************************************************/

  /*************************************************************************/
  /*                                                                       */
  /* <Define>                                                              */
  /*    String std lib functions safe for whatever string type you are     */
  /*    using.                                                             */
  /*                                                                       */
  #if defined(UNICODE) | defined(_UNICODE)
  #define altstrlen wstrlen
  #else
  #define altstrlen strlen
  #endif
  /*                                                                       */
  /*************************************************************************/

  /*************************************************************************/
  /*                                                                       */
  /* <Struct>                                                              */
  /*    StringContext                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A structure used in storing a string and it's length.              */
  /*                                                                       */
  struct _StringContext {
    char*      str;
    uint_32    len;
  };
  typedef struct _StringContext StringContext;
  typedef StringContext* pStr;
  /*                                                                       */
  /*************************************************************************/

/* internal : string context allocation function */

static pStr newString( void ); 

/* create a new string context */

static pStr 
crtStr( void ) 
{
    pStr sc;
    sc = newString();
    if ( sc == null ) return null;

    sc->len = 0;
    sc->str = 0;

    return sc;
}

/* create an empty string context of length len */

static pStr
crtStrLen( uint_32 len )
{
    pStr sc;
    sc = newString();
    if ( sc == null ) return null;

    sc->len = 0;
    sc->str = 0;

    if ( sc->len == 0 )
	    return sc;

    sc->len = len;
    sc->str = strMalloc(sc->len+1);
    memset( sc->str, 0, sc->len+1 );
    sc->str[sc->len+1] = '\0';

    return sc;
}

/* frees the string and destroys it's context */

static void
freeStr( pStr* psc ) 
{
    if ( psc == null ) return; /* not a valid pointer */
    if ( (*psc) == null ) return; /* no context to free */

    if ( (*psc)->str != null ) {
        strFree( (*psc)->str );
        (*psc)->str = 0;
    }

    strFree( *psc ); /* free the context */
    *psc = 0;
}

/* free's the string resources, but not the string context. */

static void
strClr( pStr sc ) 
{
    if ( sc == null ) return;
    strFree( sc->str );
    sc->len = 0;
    sc->str = 0;
}

/* creates a string context containing the contents of a pStr string passed in */

static pStr
strCpy( pStr str )
{
    pStr sc;

    sc = newString();
    if ( sc == null ) return null;

    sc->len = 0;
    sc->str = 0;

    if ( str == null )      return sc;
    if ( str->str == null ) return sc;
    if ( str->len == 0 )    return sc;

    sc->len = str->len;
    sc->str = strMalloc(str->len+1);
    memcpy(sc->str, str->str, str->len);
    sc->str[sc->len] = '\0';

    return sc;
}

/* creates a string context containing a copy of a char* string passed in */

static pStr
cpyString( char* b )
{
    pStr sc;
    int len;

    sc = newString();
    if ( sc == null ) return null;

    sc->len = 0;
    sc->str = 0;

    if ( b == null ) return sc;

    len = altstrlen( b );
    if ( len == 0 ) return sc;

    sc->len = len;
    sc->str = strMalloc(len+1);
    memcpy(sc->str, b, len);
    sc->str[sc->len] = '\0';

    return sc;
}

/* creates a string context and copies inlen chars of a char* string into it */

static pStr
cpyStrLen( pStr in, uint_32 inlen )
{
    pStr sc;

    sc = newString();
    if ( sc == null ) return null;

    sc->len = 0;
    sc->str = 0;

    if ( in == 0 )      return sc;
    if ( inlen == 0 )   return sc;
    if ( in->len == 0 ) return sc;

    if ( inlen > in->len ) 
        inlen = in->len;        

    sc->len = inlen;
    sc->str = strMalloc(sc->len+1);
    memcpy(sc->str, in->str, sc->len);
    sc->str[sc->len] = '\0';

    return sc;
}

/* creates a string context and copies inlen chars of a char* string into it */

static pStr
cpyStringLen( char* b, uint_32 inlen )
{
    pStr sc;

    sc = newString();
    if ( sc == null ) return null;

    sc->len = 0;
    sc->str = 0;
    /* what if strlen( b ) < inlen ? well, b might be a buffer, so it's up to them to check. */
    if ( inlen == 0 ) return sc;

    sc->len = inlen;
    sc->str = strMalloc(sc->len+1);
    memcpy(sc->str, b, sc->len);
    sc->str[sc->len] = '\0';

    return sc;
}

/* copies a pStr's contents to another pStr */

static void 
setStr( pStr* psc, pStr sc ) 
{
    if ( psc == null ) return;

    if ( *psc != null ) {
        freeStr( psc );
    }

    *psc = strCpy( sc );
}

/* copies a char* string to a pStr */

static void 
setString( pStr* psc, char * s ) 
{
    if ( psc == null ) return;

    if ( *psc != null ) {
        freeStr( psc );
    }

    *psc = cpyString( s );
}

/* increase a string context to size len. pass in a pointer to 
   a pStr so the original can be set. */

static uint_32
sizeStr( pStr* psc, int_32 len )
{
    /* create a context if needed */
    if ( *psc == null ) {
        (*psc) = newString();

        (*psc)->len = 0;
        (*psc)->str = 0;
    }

    if ( len <= 0 ) return 0;

    if ( (*psc)->len == 0 ) {
        (*psc)->len = len;
        (*psc)->str = strMalloc((*psc)->len+1);
        memset( (*psc)->str, 0, (*psc)->len+1 );
    } else {
        /* regardless of down sizing or upsizing (may change sc address) */
        (*psc)->len = len;
        (*psc)->str = strRealloc((*psc)->str, (*psc)->len+1);
        memset( (*psc)->str, 0, (*psc)->len+1 );
    }

    /* return the length */
    return (*psc)->len;
}

/* return the string for pStr */

static char* 
getString( pStr a ) 
{
    if ( a == null ) return null;
    return a->str;
}

/* return the string for pStr */

static char* 
str( pStr a ) 
{
    if ( a == null ) return null;
    return a->str;
}

/* return the length for pStr */

static uint_32
len( pStr a ) 
{
    if ( a == null ) return 0;
    return a->len;
}

/* return the string for pStr */

static uint_32
strLen( pStr a ) 
{
    if ( a == null ) return 0;
    return a->len;
}

/* return the char at index for pStr */

static char 
getChar( pStr sc, uint_32 pos ) 
{
    if ( sc->str == null ) return (char)0x00;

    if ( pos <= sc->len && pos >= 0 ) {
        return sc->str[pos];
    } else {
        return (char)0x00;
    }
}

/* set character at pos equal to c in pStr */ 

static bool 
setChar( pStr sc, uint_32 pos, char c ) 
{
    if ( sc == null ) return false;

    if ( pos <= sc->len && pos >= 0 ) {
        sc->str[pos] = c;
        return true;
    }

    return false;
}

/* delete character at index in pStr */

static bool 
delChar( pStr* psc, uint_32 index ) 
{
    pStr sc;
    uint_32 i;

    /* not a valid pointer to a string context pointer */
    if ( psc == null ) return false;

    sc = *psc;

    /* not a valid string context */
    if ( sc == null ) return false;

    /* index is longer than sc's length */
    if ( index > sc->len ) return false;

    /* shift down contents removing char at index */
    i = 0;
    for( i = index; i <= sc->len; i++ )
        *(sc->str+i) = (char)*(sc->str+i+1); 

    /* set the last char to null */
    *(sc->str + sc->len) = '\0';

    /* we do not deallocate the character's memory at the end, we simply decrease the length by one */
    sc->len--;

    return true;
}

/* drop the ending character off pStr */

static bool
dropChar( pStr* psc )
{
    pStr sc;
    pStr copy;

    if ( psc == null ) return false;

    /* use the original context */
    sc = *psc;

    /* nothing to drop */
    if ( sc->len <= 0 ) return true;

    /* create a copy of the string minus one char (creates a new string stored in copy) */
    copy = cpyStrLen( sc, sc->len-1 );

    /* delete the original context and string */
    freeStr( &sc );
    sc = 0;

    /* set the incoming context pointer to the new context */
    *psc = copy;

    return true;
}

/* returns a new context containing a copy of the sub-string starting 
   at startpos, ending at endpos */

static pStr 
subStr( pStr sc, uint_32 start, int_32 end ) 
{
    pStr sub = null;
    int_32 len;

    if ( sc == null ) return newString();

    if ( start > sc->len ) return newString();

    if ( end == -1 ) end = sc->len - start;

    if ( end > (int_32)sc->len ) end = sc->len - start;

    len = end-start;

    if ( len < 0 ) len = 0;

    sizeStr( &sub, len ); /* adds hidden char on the end! */

    memcpy( sub->str, sc->str+start, len ); 
    
    return sub;
}

/* returns a new context containing a copy of the sub-string 
   starting at start, ending at the end of pStr */

static pStr 
subStrShort( pStr sc, uint_32 start ) 
{
    pStr sub = null;
    int_32 len;

    if ( sc == null ) return newString();

    if ( start > sc->len ) return newString();

    len = sc->len - start;

    if ( len < 0 ) len = 0;

    sizeStr( &sub, len ); /* adds hidden char on the end! */

    memcpy( sub->str, sc->str+start, len ); 
    
    return sub;
}

/* returns a new context containing a copy of the sub-string 
   starting at start for length of len */

static pStr 
subStrLen( pStr sc, uint_32 start, uint_32 len ) 
{
    pStr sub = null;

    //start = 0, len = 10, sc->len = 5  : len = 10
    //start = 5, len = 10, sc->len = 10 : len = 5

    if ( sc == null ) return newString();

    if ( start > sc->len ) return newString();

    if ( (start + len) > sc->len  ) len = sc->len - start;

    sizeStr( &sub, len ); /* adds hidden char on the end! */

    memcpy( sub->str, sc->str+start, len ); 
    
    return sub;
}

static pStr 
strToken( pStr * string, char* key )
{
    int_32 index;
    pStr tmp;
    pStr token;

    if ( string == null ) return false;
    if ( *string == 0 ) return false;
    if ( (*string)->len == 0 ) return false;
    if ( key == null ) return false;

    /* get the last chunk which i assumed to not have a key on the end */

    if ( ( index = slFind( str( *string ), key ) ) <= 0 ) {
        tmp = strCpy( *string );
        freeStr( string );
        return tmp;
    }

    token = subStr( *string, 0, index );
    tmp = subStrShort( *string, index+1 );
    setStr( string, tmp );
    freeStr( &tmp );
    return token;
}

/* concatinate src onto the end of target */

static bool
concatStr( pStr* target, pStr src )
{
    uint_32 tlen;
    uint_32 slen;
    char * p;

    if ( target == null || src == null ) return false;

    if ( *target == null ) *target = newString();

    tlen = (*target)->len;
    slen = src->len;

    if ( slen <= 0 ) return true;

    if ( tlen == 0 ) {
        *target = strCpy( src );
    } else {
        p = slAppend( (*target)->str, src->str );
        freeStr( target );
        setString( target, p );
        slFree( p );  
    }

    return true;
}

/* concatinate src onto the end of target */

static bool
concatString( pStr* target, char * src )
{
    uint_32 tlen;
    uint_32 slen;
    char * p;

    if ( target == null || src == null ) return false;

    if ( *target == null ) *target = newString();

    tlen = (*target)->len;
    slen = slLength( src );

    if ( slen == 0 ) return true;

    if ( tlen == 0 ) {
        freeStr( target );
        *target = cpyString( src );
    } else {
        p = slAppend( (*target)->str, src ); /* creates a new string in p */
        freeStr( target );
        setString( target, p ); /* creates a new string in target */
        slFree( p );  
    }

    return true;
}

/* concatinate src(s) onto the end of target. You must
   have the last string = "\0" */

static bool
concatStrings( pStr* target, char * src, ... )
{
    va_list marker;
    uint_32 tlen;
    uint_32 slen;
    char * p;

    if ( target == null || src == null ) return false;

    if ( *target == null ) *target = newString();

    va_start( marker, src );     /* Initialize variable arguments. */
    while( src != null )
    {
        slen = slLength( src );
        tlen = (*target)->len;

        if ( slen == 0 ) break;

        if ( (*target)->len == 0 ) {
            freeStr( target );
            *target = cpyString( src );
        } else {
            //sizeStr( target, tlen+slen ); /* adds hidden char on the end. */
            //memcpy( (*target)->str + tlen, src, slen );
            p = slAppend( (*target)->str, src );
            freeStr( target );
            setString( target, p );
            slFree( p );  
        }
        src = va_arg( marker, char* );
    }
    va_end( marker );              /* Reset variable arguments.      */

    return true;
}

/* concatinate src onto the end of target for a total len */

static bool
concatStringLen( pStr* target, char * src, uint_32 len )
{
    uint_32 tlen;
    uint_32 slen;

    if ( target == null || src == null ) return false;

    if ( *target == null ) *target = newString();

    tlen = (*target)->len;
    slen = slLength( src );

    if ( slen <= 0 ) return true;

    sizeStr( target, tlen+slen ); /* adds hidden char on the end. */
    
    memcpy( (*target)->str + tlen, src, slen );

    return true;
}

/* concatinate a charcter onto the end of target */

static bool 
concatChar( pStr* target, char c )
{
    char buf[1];

    buf[0] = c;
    buf[1] = 0;

    return concatString( target, buf );
}

#define appendStr concatStr
#define appendString concatString

/* memcmp wrapper with a little bit o fudge - case sensitive */

static int_32 
compareStrCs( pStr a, pStr b )
{
    if ( a == null && b == null ) return 0;

    if ( a == null || b == null ) return -1;

    if ( a->str == null && b->str == null ) return 0;

    if ( a->str == null || b->str == null ) return -1;

    return memcmp( a->str, b->str, a->len );
}

/* strncmp wrapper - case sensitive */

static int_32 
compareStrLenCs( pStr a, pStr b, uint_32 len )
{
    if ( a == null && b == null ) return 0;

    if ( a == null || b == null ) return -1;

    if ( a->str == null && b->str == null ) return 0;

    if ( a->str == null || b->str == null ) return -1;

    return memcmp( a->str, b->str, len );
}

/* strncmp wrapper - case sensitive */

static int_32 
compareStringLenCs( pStr a, char* b, uint_32 len )
{
    if ( a == null && b == null ) return 0;

    if ( a == null || b == null ) return -1;

    if ( a->str == null && b == null ) return 0;

    if ( a->str == null || b == null ) return -1;

    return memcmp( a->str, b, len );
}

/* strcmp wrapper with a little bit o fudge - case sensitive */

static int_32 
compareStrNc( pStr a, pStr b )
{
    if ( a == null && b == null ) return 0;

    if ( a == null || b == null ) return -1;

    if ( a->str == null && b->str == null ) return 0;

    if ( a->str == null || b->str == null ) return -1;

    return _memicmp( a->str, b->str, a->len );
}

/* strncmp wrapper - case insensitive */

static int_32 
compareStringNc( pStr a, char* b )
{
    if ( a == null && b == null ) return 0;

    if ( a == null || b == null ) return -1;

    if ( a->str == null && b == null ) return 0;

    if ( a->str == null || b == null ) return -1;

    return _memicmp( a->str, b, a->len );
}

/* strncmp wrapper - case insensitive */

static int_32 
compareStrLenNc( pStr a, pStr b, uint_32 len )
{
    if ( a == null && b == null ) return 0;

    if ( a == null || b == null ) return -1;

    if ( a->str == null && b->str == null ) return 0;

    if ( a->str == null || b->str == null ) return -1;

    return _memicmp( a->str, b->str, len );
}

/* strncmp wrapper - case insensitive */

static int_32 
compareStringLenNc( pStr a, char* b, uint_32 len )
{
    if ( a == null && b == null ) return 0;

    if ( a == null || b == null ) return -1;

    if ( a->str == null && b == null ) return 0;

    if ( a->str == null || b == null ) return -1;

    return _memicmp( a->str, b, len );
}

/* replace all instances of token with replace in target */

static int_32 
replaceString( pStr* target, char* token, char* replace )
{
    char* newstr;
    
    if ( token == null || replace == null ) 
        return -1;

    if ( target == null || (*target)->len <= 0 ) 
        return 0;

    if ( memcmp( token, replace, (slLength(token) > slLength(replace) ? slLength(token):slLength(replace)) ) == 0 ) {
        return -1;
    } 

    newstr = slReplace( (*target)->str, token, replace ); 
    setString( target, newstr );
    strFree( newstr );

    return 1;
}

/* Simple, straight forward sub string search.  
   returns -1 if not found, 'starting character 
   position' for sub string is returned. */

static int_32
containsStr( pStr a, pStr b )		
{
    if ( a == null && b == null ) return 0;

    if ( a == null || b == null ) return 0;

    if ( a->str == null || b->str == null ) return 0;

    return slFind( str( a ), str( b ) );
}

/* Simple, straight forward sub string search.  
   returns -1 if not found, 'starting character 
   position' for sub string is returned. */

static int_32
containsString( pStr a, char* b )		
{
    if ( a == null && b == null ) return 0;

    if ( a == null || b == null ) return 0;

    if ( a->str == null ) return 0;

    return slFind( str( a ), b );
}

/* convert pStr to int32 value */

static int_32 
strToInt32( pStr a )		
{
    if ( a == null ) return 0;

    if ( a->str == null ) return 0;

    return atoi( str( a ) );
}

/* convert pStr to double value */

static float_64 
strToFloat64( pStr a )		
{
    if ( a == null ) return 0;

    if ( a->str == null ) return 0;

    return atof( str( a ) );
}

/* convert int32 value to pStr string */

static pStr 
int32ToStr( int_32 value )		
{
    pStr str;
    char buf[STRBUF];

    str = newString();
    sprintf( buf, "%i", value );

    setString( &str, buf ); 
    return str;
}

/* convert double value to pStr string */

static pStr 
float64ToStr( float_64 value )		
{
    pStr str;
    char buf[STRBUF];

    str = newString();
    sprintf( buf, "%i", value );

    setString( &str, buf ); 
    return str;
}

/* internal : string context allocation function */

static pStr 
newString( void )
{
    pStr p;
    p = (pStr)strMalloc( sizeof( StringContext ) );
    if ( p == null ) return null;
    return (pStr)memset( (void*)p, 0, sizeof( StringContext ) );
}

/* don't clutter or conflict with anything - string lib use only */
#undef strMalloc
#undef strRealloc
#undef strFree

CPW_END_HEADER
#endif