string.c

/* String.c - This module implements the String Structure.
**
** The String structure is a data structure and supporting functions,
** that implement dynamic strings in C.
**
** Mario Giannini
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/************ This section should go into MyString.h ************/
typedef struct _tagSTRING
{
	char * Buffer; /* Our internal buffer */
	int BufferSize, /* The size for our buffer */
		StrLen; /* Length of string inside buffer */
} STRING;

enum ERR_CODES { ERR_OK, ERR_NOMEM, ERR_BADINDEX };
#define S_GROWBY 8

/* Macros, for an easier life */
#define StringStr(x) ((x)->Buffer?(x)->Buffer:"")
#define StringLen(x) ((x)->StringLen)
#define StringGetAt(x,y) ((x)->StrLen>y?(x)->Buffer[y]:'\0')
#define StringCompare(a,b) (strcmp(StringStr(a),StringStr(b))
#define StringAppend(x,s) (StringInsertStr(x,-1,s))
#define StringClear(x) (StringResizeBuffer(x,0))

/* Function prototypes */
STRING * StringConstruct( char * Src );
void StringDestruct( STRING * String );
int StringDelete( STRING * String, int Index, int Count );
int StringInsertCh( STRING * String, int Index, int Ch );
int StringInsertStr( STRING * String, int Index, char * Src );
int StringFromStr( STRING * String, char * Src );

/* The resize function is sort of a 'private' function.  So, we make it static */
static int StringResizeBuffer( STRING * String, int NewSize );
/********************** END OF MyString.h *************************/


/********** This section should go into MyString.c *****************/

/*******************************************************************
** Name:    StringResizeBuffer
** Descr:   Resizes internal buffer to new size
** Params:	String = String structure to be resized
**          NewSize = Newsize for internal buffer, w/o terminator
** Returns: ERR_OK upon success, or error code upon failure
********************************************************************/
static int StringResizeBuffer( STRING * String, int NewSize )
{
	char * Tmp;
	
	NewSize++; /* Always add 1 for null terminator */
	if( NewSize==String->BufferSize ) /* Nothing to do */
		return( ERR_OK );

	if( NewSize==1 ) /* An empty string is stored as a NULL */
	{
		free( String->Buffer );
		String->Buffer = NULL;
		String->BufferSize = String->StrLen = 0;
		return( ERR_OK );
	}
	
	if( String->BufferSize )  /* Realloc, or create, the buffer */
		Tmp = realloc( String->Buffer, NewSize );
	else
		Tmp = calloc( NewSize, sizeof(char) );
	
	if( Tmp==NULL )
		return( ERR_NOMEM );
	
	String->Buffer = Tmp;
	String->BufferSize = NewSize;
	String->Buffer[ NewSize-1 ] = '\0';
	return( ERR_OK );
}

/*******************************************************************
** Name:    StringConstruct
** Descr:   Creates a new StringStructure
** Params:	Src = Initial string value, may be NULL, "", or valid string
** Returns: NULL upon failure, or pointer to new structure upon success
********************************************************************/
STRING * StringConstruct( char * Src )
{
	STRING * String;
	if( (String=calloc(1,sizeof(STRING))) != NULL )
	{
		if( StringFromStr( String, Src ) != ERR_OK )
		{
			free( String );
			return( NULL );
		}
	}
	return( String );
}

/*******************************************************************
** Name:    StringDestruct
** Descr:   Removes a StringStructure from memory
** Params:	String = String structure to be removed
** Returns: Nothing
********************************************************************/
void StringDestruct( STRING * String )
{
	free( String->Buffer );
	free( String );
}

/*******************************************************************
** Name:    StringDelete
** Descr:   Removes a portion of a string
** Params:	String = String structure to have string portion removed
**          Index = Where portion of string to delete begins
**          Count = How many characters to remove.  -1 means to end of string
** Returns: ERR_OK upon success, or error code upon failure
********************************************************************/
int StringDelete( STRING * String, int Index, int Count )
{
	if( Index<0 || Index >= String->StrLen )
		return( ERR_BADINDEX );
	
	/* Determine how many characters to remove */
	if( Count<0 || Index+Count > String->StrLen )
		Count = String->StrLen - Index;
	
	/* Move those characters, including the null terminator */
	memmove( String->Buffer+Index, String->Buffer+Index+Count,
			String->StrLen - (Index+Count) +1 );
	/* Calculate new string's length */
	String->StrLen -= Count;

	/* If String is empty, then set buffersize to 0 */
	if( String->StrLen==0 )
		StringResizeBuffer( String, 0 );
	return( ERR_OK );
}

/*******************************************************************
** Name:    StringInsertCh
** Descr:   Inserts a new character into a String structure
** Params:	String = String structure to have character inserted
**          Index = Position of new character. -1 means end of string
**          Ch = Character to insert
** Returns: ERR_OK upon success, or error code upon failure
********************************************************************/
int StringInsertCh( STRING * String, int Index, int Ch )
{
	int Err=ERR_OK;

	if( Index==-1 )
		Index = String->StrLen;
	
	if( Index<0 || Index > String->StrLen )
		return( ERR_BADINDEX);

	if( Ch=='\0' ) /* We'll permit them to terminate the string */
	{
		String->Buffer[Index] = '\0';
		String->StrLen = Index;
		return( ERR_OK );
	}
	
	if( String->StrLen+2 > String->BufferSize )
		Err=StringResizeBuffer( String, String->BufferSize+S_GROWBY );
	
	if( Err==ERR_OK )
	{
		String->StrLen++;
		memmove( String->Buffer+Index+1, String->Buffer+Index,
			String->StrLen-Index );
		String->Buffer[Index] = Ch;
	}
	return( Err );
}

/*******************************************************************
** Name:    StringInsertStr
** Descr:   Inserts a C-style string into a String structure
** Params:	String = String structure to have string inserted
**          Index = Where in String to insert. -1 means end of string
** Returns: ERR_OK upon success, or error code upon failure
********************************************************************/
int StringInsertStr( STRING * String, int Index, char *Src )
{
	int Err=ERR_OK;
	if( Index==-1 )
		Index = String->StrLen;
	
	if( Index<0 || Index > String->StrLen )
		return( ERR_BADINDEX );
	if( Src && *Src )
	{
		for( ; *Src && Err==ERR_OK; Src++, Index++ )
			Err=StringInsertCh( String, Index, *Src );
	}
	return( Err );
}

/*******************************************************************
** Name:    StringFromStr
** Descr:   Initializes a String structure with a C-style string
** Params:	String = String structure to be initialized
**          Src = String to use for initialization.  May be NULL.
** Returns: ERR_OK upon success, or error code upon failure
********************************************************************/
int StringFromStr( STRING * String, char * Src )
{
	char * Tmp;
	if( Src==NULL || *Src=='\0' )
	{
		StringResizeBuffer( String, 0 );
		return( ERR_OK );
	}
	if( (Tmp=strdup( Src )) == NULL )
		return( ERR_NOMEM );
	free( String->Buffer );
	String->Buffer = Tmp;
	String->StrLen = strlen(Tmp);
	String->BufferSize = String->StrLen+1;
	return( ERR_OK );
}
/********** End of MyString.c section *****************/


/********** Test for String structure ******************/
void main( void )
{
	STRING * S;

	S = StringConstruct( "Mom" );
	StringInsertCh( S, 0, 'H' );
	StringInsertStr( S, 1, "i " );
	
	printf("%s\n", StringStr(S) ); /* Prints "Hi Mom" */
	StringDelete( S, 4, 100 );
	StringInsertStr( S, -1, "ario" );
	printf("%s\n", StringStr(S) ); /* Prints "Hi Mario" */
	StringFromStr( S, "" );
	StringAppend( S, "This sure is a cool test." );
	printf("%s\n", StringStr(S) ); /* Prints "This sure is a cool test." */
	printf("S[0] is %c\n", StringGetAt(S,0) ); /* Prints 'T' */
	StringDelete(S, 5, 5 );
	printf("%s\n", StringStr(S) ); /* Prints "This is a cool test." */
	StringDestruct( S );
}