fsort.c

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


/** This program attempts to read an entire file's contents into memory.  The file is stored on a
*** line-by-line basis.  Each line is stored as a char pointer in an array of char pointers.  The
*** lines and the array that contains the char pointers for the lines are all dynamically
*** allocated.  The ReadInLines function creates this array and performs all the memory management
*** except for FreeLines which releases all memory.
***
*** The lines are stored in an array of char pointers where the terminating pointer is a NULL
*** pointer (not the null or '\0' character)
***
*** For the real purpose of this code, just take a look at how easy main()
*** was to write.
**/

/** Prototypes **/
unsigned int ReadInLines( char *Filename, char ***Lines );
void FreeLines( char **Lines );
void PrintLines( char **Lines );
void SortLines( char **Lines );

#define GROWARRAYBY 8
#define GROWBUFFERBY 128

/** FreeLines - Frees lines created by ReadInLines **/
void FreeLines( char **Lines )
{
	int i;
	if( Lines )
	{
		for( i=0; Lines[i]; i++ )
			free(Lines[i]);
		free(Lines);
	}
}

/** ReadInLines - Reads entire contents of 'Filename', loading into Lines
*** Returns: 0 if OK, 1 if out of memory, 2 if can't open file
**/
unsigned int ReadInLines( char *Filename, char ***Lines )
{
	FILE *Input;
	char *Buffer, *BufTmp, **Tmp;
	unsigned int Error=0, Max=0, Cur=0;
	unsigned int BufMax=GROWBUFFERBY, BufCur=0, PrevSize=0;
	
	if( (Input = fopen( Filename, "r" )) == NULL )	/* Attempt to open file */
		return( 2 );
	if( (Buffer=malloc( BufMax )) == NULL )	/* Attempt to create our initial buffer */
			Error = 1;
	else 
	{
		while( Error == 0 && fgets( Buffer+PrevSize, (BufMax-PrevSize), Input ) != NULL ) /* while no errors, and we read a line... */
		{
			if( Buffer[0] && Buffer[ strlen(Buffer)-1 ]!='\n' && !feof(Input) )
			{	/* No '\n' means we didn't read entire line, so grow string buffer */
				PrevSize=BufMax-1;
				BufMax += GROWBUFFERBY;
				if( (BufTmp=realloc( Buffer, BufMax*sizeof(char) )) == NULL )
					Error = 1;
				else
					Buffer = BufTmp;
				continue;
			}
			PrevSize = 0;

			/* Just in the last line from the file had no carriage return */
			if( feof(Input) && Buffer[0] && Buffer[strlen(Buffer)]!='\n' && strlen(Buffer)<BufMax+1 )
				strcat( Buffer, "\n" );

			if( Cur+1 >= Max )  /* Do we need to 'grow' the array of pointers? */
			{
				Max += GROWARRAYBY; /* If so, use realloc() which changes the size of a block of memory */
				if( (Tmp = realloc( *Lines, sizeof( char * ) * Max )) == NULL )
				{
					Error = 1;
					break; /* In case of error, get us out of the loop */
				}
				*Lines = Tmp; /* Make sure we put the new address of Lines where it should go */
			}
			
			/* Note that Tmp is == *Lines, so we can use Tmp[] rather than Lines, easier to read */
			if( (Tmp[Cur++]=strdup( Buffer )) == NULL )  /* Store a duplicate of our buffer in the array */
				Error = 1;
		}
		Tmp[Cur]=NULL; /* make sure terminator is there */
	}
	
	fclose( Input );
	free( Buffer );

	if( Error )
		FreeLines( *Lines );  /* Remove any memory if we had a problem */
	else
		*Lines = realloc( *Lines, sizeof(char *) * (Cur+1) );  /* Adjust size */
		
	return( Error );
}

/** PrintLines - Prints 'Lines', as loaded from ReadInLines **/
void PrintLines( char **Lines )
{
	while( *Lines )
		printf("%s", *Lines++ );   /* Note: No index needed! */
}

/** SortLines - Sorts 'Lines' as loaded by ReadInLines **/
void SortLines( char **Lines )
{            
	char *Tmp;
	int i, o;
	for( o=0; Lines[o]; o++ )  /* Sort by brute force (not an intelligent sort) */
		for( i = o + 1; Lines[i]; i++ )
			if( stricmp( Lines[i], Lines[o] ) < 0 )  /* Compare every member of array */
			{
				Tmp = Lines[i];	/* Swap any out-of-place items we find */
				Lines[i] = Lines[o];
				Lines[o] = Tmp;	
			}
}

int main( int argc, char *argv[] )
{
	char **Lines = NULL;
	int Err;
	
	/* Check command line arguments */
	if( argc!=2 )
		printf("usage: fsort filename\n");
	else
	{
		if( (Err = ReadInLines( argv[1], &Lines )) != 0 )
			printf("Error: %s %s\n", Err==1?"out of memory on":"can't open", argv[1] );
		else
		{
			SortLines( Lines );
			PrintLines( Lines );
			FreeLines( Lines );
		}
	}
	return( 0 );
}