tl.c

/* tl.c - TEXTLINES implementation file
** MG 9.16.97
*/

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

#include "tl.h"

/******************************************************************
** Name:	TLConstruct
** Descr:	Constructs an empty TEXTLINES structure
** Author:	MG 9/16/97
*******************************************************************/
TEXTLINES * TLConstruct( void )
{
	return( calloc( 1, sizeof( TEXTLINES )));
}

/******************************************************************
** Name:	TLDestruct
** Descr:	Removes a TEXTLINES structure from memory
** Params:	Dest = Struct to be removed
** Author:	MG 9/16/97
*******************************************************************/
void TLDestruct( TEXTLINES * Dest )
{
	if( Dest )
	{
		TLClear( Dest );
		free( Dest );
	}
}

/******************************************************************
** Name:	TLResize
** Descr:	Resizes a TXTLINES struct to be able to contain a certain number of lines.
** Params:	Dest = Struct to be resized
**          Count = New # of lines to be held
** Returns:	ERR_OK upon success, or error code upon failure
** Author:	MG 9/16/97
*******************************************************************/
int TLResize( TEXTLINES * Dest, int Count )
{
	char ** Tmp;
	
	while( Dest->LineCount > Count )
		free( Dest->Lines[ --Dest->LineCount ] );

	if( (Tmp = realloc( Dest->Lines, Count*sizeof(char*))) == NULL )
		return( ERR_NOMEM );
	Dest->Lines = Tmp;
	Dest->Max = Count;
	
	return( ERR_OK );
}

/******************************************************************
** Name:	TLInsert
** Descr:	Inserts a copy of a string into a TEXTLINES structure
** Params:	Dest = Struct to be added to
**			Index = Position to place new string. -1 means at 'end'
**          Src = new string to be copied
** Returns:	ERR_OK upon success, or an error code upon failure
** Author:	MG 9/16/97
*******************************************************************/
int TLInsert( TEXTLINES * Dest, int Index, char *Src )
{
	int Err, i;
	char *New;
	if( Index==-1 )
		Index=Dest->LineCount;

	if( Index<0 || Index > Dest->LineCount )
		return( ERR_BADINDEX );

	if( (New=strdup( Src )) == NULL )
		return( ERR_NOMEM );

	if( Dest->LineCount+1 >= Dest->Max )
		if( (Err=TLResize( Dest, Dest->Max+TLGROWBY )) != ERR_OK )
		{
			free( New );
			return( Err );
		}	
	for( i=Dest->LineCount; i>Index; i-- )
		Dest->Lines[i]=Dest->Lines[i-1];
	Dest->Lines[Index]=New;
	Dest->LineCount++;
	return( ERR_OK );
}

/******************************************************************
** Name:	TLRemove
** Descr:	Removes a line from a TEXTLINES structure
** Params:	Dest = Struct to have line removed from
**          Index = Position of line to be removed
** Returns:	ERR_OK upon success, or error code upon failure
** Author:	MG 9/16/97
*******************************************************************/
int TLRemove( TEXTLINES * Dest, int Index )
{
	int i;
	if( Dest->LineCount == 0 )
		return( ERR_BADINDEX );
	if( Index==-1 )
		Index=Dest->LineCount-1;
	for( i=Index; i < Dest->LineCount; i++ )
		Dest->Lines[i]=Dest->Lines[i+1];
	Dest->LineCount--;
#ifdef TLSHRINK
	if( Dest->Max - Dest->LineCount > TLGROWBY )
		return( TLResize( Dest, Dest->Max - TLGROWBY ) );
#endif
	return( ERR_OK );
}

/******************************************************************
** Name:	TLClear
** Descr:	Removes all lines from a TEXTLINES structure
** Params:	Dest = Struct to be cleared
** Author:	MG 9/16/97
*******************************************************************/
void TLClear( TEXTLINES * Dest )
{
	if( Dest->LineCount )
	{
		while( Dest->LineCount-- )
			free( Dest->Lines[ Dest->LineCount ] );
		free( Dest->Lines );
		Dest->Lines = NULL;
		Dest->Max = 0;
	}
}

/******************************************************************
** Name:	TLSwap
** Descr:	Swaps to lines from a TEXTLINES structure
** Params:	Dest = Struct to have elements swapped
**          A = Location of first element to swap
**          B = Location of second element to swap
** Returns:	ERR_OK upon success, or error code upon failure
** Author:	MG 9/16/97
*******************************************************************/
int TLSwap( TEXTLINES * Dest, int A, int B )
{
	char * Tmp;
	if( A<0 || A >=Dest->LineCount || B < 0 || B >=Dest->LineCount )
		return( ERR_BADINDEX );
	Tmp = Dest->Lines[A];
	Dest->Lines[A] = Dest->Lines[B];
	Dest->Lines[B] = Tmp;
	return( ERR_OK );
}

/******************************************************************
** Name:	TLLoadFromFile
** Descr:	Loads the lines of text from a file into a TEXTLINES structure
** Params:	Dest = Struct to be loaded with file data
**          Filename = name of file to be loaded
**          ClearFirst = if true, then previous contents of Dest are cleared, 
**				otherwise they aren't
** Returns:	ERR_OK upon success, or error code upon failure
** Author:	MG 9/16/97
*******************************************************************/
int TLLoadFromFile( TEXTLINES * Dest, char * Filename, int ClearFirst )
{
	FILE * Input;
	char * Buffer, * Tmp;
	int BufferSize=512, Extra=0, Err=ERR_OK;
	if( (Input=fopen( Filename, "r" )) == NULL )
		return( ERR_NOOPEN );
	if( (Buffer=malloc( BufferSize )) == NULL )
	{
		fclose( Input );
		return( ERR_NOMEM );
	}
	if( ClearFirst )
		TLClear( Dest );
	while( Err==ERR_OK && fgets( Buffer+Extra, BufferSize-Extra, Input ) )
	{
		if( Buffer[ strlen(Buffer)-1 ] != '\n' ) /* Didn't read whole line */
		{
			BufferSize += 512;
			if( (Tmp=realloc( Buffer, BufferSize )) == NULL )
			{
				free( Buffer );
				fclose( Input );
				return( ERR_NOMEM );
			}
			Buffer = Tmp;
			Extra += 512;
			continue; /* get more of the line */
		}
		Extra = 0;
		Trim(Buffer);
		Err = TLAppend( Dest, Buffer );
	}
	
	fclose( Input );
	free( Buffer );
	if( Err )
		TLClear( Dest );
	return( Err );
}

/******************************************************************
** Name:	TLSaveToFile
** Descr:	Save a TEXTLINES structure's lines to a file
** Params:	Dest = Struct to be saved
**          Filename = name of file to be saved to
** Returns:	ERR_OK upon success, or error code upon failure
** Author:	MG 9/16/97
*******************************************************************/
int TLSaveToFile( TEXTLINES * Src, char * Filename )
{
	FILE * Output;
	int i;

	if( (Output=fopen(Filename,"w+"))==NULL )
		return( ERR_NOOPEN );
	for( i=0; i < Src->LineCount; i++ )
		fprintf( Output, "%s\n", Src->Lines[i] );
	fclose( Output );
	return( ERR_OK );
}

/******************************************************************
** Name:	TLEzSortICmp
** Descr:	A callback function for TLEZSort to compare elements insensitive
** Params:	A = Address of first element to compare
**          B = Address of second element to compare
** Returns:	0 if items are identical, <0 if A < B, or >0 if A > B
** Author:	MG 9/16/97
*******************************************************************/
int TLEzSortICmp( const void* A, const void* B )
{
	return( stricmp( *(const char**)A, *(const char**)B ) );
}
/******************************************************************
** Name:	TLEzSortCmp
** Descr:	A callback function for TLEZSort to compare elements sensitive
** Params:	A = Address of first element to compare
**          B = Address of second element to compare
** Returns:	0 if items are identical, <0 if A < B, or >0 if A > B
** Author:	MG 9/16/97
*******************************************************************/
int TLEzSortCmp( const void* A, const void* B )
{
	return( strcmp( *(const char**)A, *(const char**)B ) );
}

/******************************************************************
** Name:	TLEzSort
** Descr:	A simple sorting routine to sort lines in a TEXTLINES structure
** Params:	Dest = Struct to be sorted
**          Sensitive = True to perform case sensitive sort, false for insensitive
** Author:	MG 9/16/97
*******************************************************************/
void TLEzSort( TEXTLINES * Dest, int Sensitive )
{
	if(Sensitive)
		qsort( Dest->Lines, Dest->LineCount, sizeof(char*), TLEzSortCmp );
	else
		qsort( Dest->Lines, Dest->LineCount, sizeof(char*), TLEzSortICmp );
}

/******************************************************************
** Name:	Trim
** Descr:	Removes trailing white space from a string
** Params:	Src = String to be modified
** Returns:	True if string is not blank, false if it is blank
** Author:	MG 9/16/97
*******************************************************************/
int Trim( char *Src )
{
	char *End = Src+strlen(Src)-1;
	for( ; End>=Src && isspace(*End); End-- )
		*End='\0';
	return( *Src );
}

/******************************************************************
** Name:	ErrMsg
** Descr:	Prints an error message, if applicable, and optionally terminates 
**				program
** Params:	ErrCode = Error code to be tested. ERR_OK will result in no action
**          Exit = If True, then function will terminate program, otherwise it doesn't
** Author:	MG 9/16/97
*******************************************************************/
void ErrMsg( int ErrCode, int Exit )
{
	switch( ErrCode )
	{
	case ERR_OK:
		return;
	case ERR_BADINDEX:
		perror( "Bad index" );
		break;
	case ERR_NOMEM:
		perror( "Out of memory" );
		break;
	case ERR_NOOPEN:
		perror( "Can't open file" );
		break;
	default:
		perror( "Unknown error" );
	}
	if( Exit )
		exit( ErrCode );
}