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