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 );
}