Jens said:
Nearly - but since it's supposed to return an array of strings
it has to be
char ** getDir( char * pathName);
Thanks, Jens, I won't forget to add the const, but I did tonight. I
think I've got a pretty good template to move forward. I see the
analogy between reading a text file with standard c and reading a
directory with posix extensions. I think both tasks are suited to
resizable 2-d arrays.
So this is what I have (thanks, Richard):
dan@dan-desktop:~/source/unleashed/ch11$ gcc -Wall -Wextra -c -o
string2.o strarr2.c
dan@dan-desktop:~/source/unleashed/ch11$ gcc -std=c99 -Wall -Wextra
string2.o t2.c -o out
dan@dan-desktop:~/source/unleashed/ch11$ ./out strarr2.c
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "strarr.h"
void FreeStrArray(char **Array, size_t NumFiles)
{
size_t index;
if(Array != NULL)
{
for(index = 0; index < NumFiles; index++)
{
if(Array[index] != NULL)
{
free(Array[index]);
}
}
free(Array);
}
}
char **AllocStrArray(size_t NumFiles, size_t Width)
{
char **Array = NULL;
size_t index;
int Success = 1;
/* allocating 0 bytes is not a great idea, and
* represents a logic error.
*/
assert(NumFiles > 0);
assert(Width > 0);
/* Just in case the zero allocation is NOT caught
* in testing, we'll check for it here.
*/
if(NumFiles > 0 && Width > 0)
{
Array = malloc(NumFiles * sizeof *Array);
if(Array != NULL)
{
for(index = 0; index < NumFiles; index++)
{
Array[index] = malloc(Width * sizeof *Array[index]);
if(NULL == Array[index])
{
Success = 0;
}
else
{
/* Making this into an empty string is a quick
* op which will almost invariably be The Right
* Thing and can never be The Wrong Thing, so
* we might as well do it.
*/
Array[index][0] = '\0';
}
}
/* If any inner allocation failed,
* we should clean up.
*/
if(1 != Success)
{
FreeStrArray(Array, NumFiles);
Array = NULL;
}
}
}
return Array;
}
int ResizeOneString(char **Array,
size_t index,
size_t NewSize)
{
char *p;
int Success = 1;
assert(Array != NULL);
p = realloc(Array[index], NewSize);
if(p != NULL)
{
Array[index] = p;
}
else
{
Success = 0;
}
return Success;
}
int AddFilesToStrArray(char ***ArrayPtr,
size_t OldNumFiles,
int NumFilesToAdd,
size_t InitWidth)
{
char **p;
int Success = 1;
int index;
int OldFiles;
OldFiles = (int)OldNumFiles;
if(NumFilesToAdd < 0)
{
for(index = OldFiles - 1;
index >= OldFiles + NumFilesToAdd;
index--)
{
free((*ArrayPtr)[index]);
}
}
p = realloc(*ArrayPtr,
(OldFiles + NumFilesToAdd) *
sizeof(**ArrayPtr));
if(p != NULL)
{
*ArrayPtr = p;
for(index = OldFiles;
Success && index < OldFiles + NumFilesToAdd;
index++)
{
(*ArrayPtr)[index] = malloc(InitWidth);
if((*ArrayPtr)[index] != NULL)
{
(*ArrayPtr)[index][0] = '\0';
}
else
{
Success = 0;
}
}
}
else
{
Success = 0;
}
return Success;
}
int ConsolidateStrArray(char **ArrayPtr,
size_t NumFiles)
{
size_t index;
size_t Len;
int NumFailures = 0;
for(index = 0; index < NumFiles; index++)
{
/* If the library has been correctly used, no
* index pointer will ever be NULL, so we should
* assert that this is the case.
*/
assert(ArrayPtr[index] != NULL);
Len = 1 + strlen(ArrayPtr[index]);
if(0 == ResizeOneString(ArrayPtr, index, Len))
{
++NumFailures;
}
}
return NumFailures;
}
/* end of strarr.c */
/*
gcc -Wall -Wextra -c -o string2.o strarr2.c */
dan@dan-desktop:~/source/unleashed/ch11$ ./out t2.c
#include <stdio.h>
#include <string.h>
#include "strarr2.h"
#define DEFAULT_LINE_LEN 64
#define LINES_PER_ALLOC 16
#define ERR_FILES_NOT_ADDED 1
#define ERR_STRING_NOT_RESIZED 2
#define ERR_PATH_OPEN_FAILED 3
#define ERR_ALLOC_FAILED 4
int ReadFile(char *Filename,
char ***Array,
int *NumFiles)
{
char Buffer[DEFAULT_LINE_LEN] = {0};
char *NewLine = NULL;
FILE *fp;
int Error = 0;
int index = 0;
size_t NumBlocks;
*NumFiles = 0;
*Array = AllocStrArray(LINES_PER_ALLOC,
DEFAULT_LINE_LEN);
if(NULL != *Array)
{
fp = fopen(Filename, "r");
if(fp != NULL)
{
*NumFiles = LINES_PER_ALLOC;
NumBlocks = 1;
/* fgets will give us no more than sizeof Buffer
* bytes, including zero terminator and newline
* if one is present within that number of bytes.
* Therefore we need to cater for longer lines.
* To do this, we call fgets again (and again
* and again) until we encounter a newline.
*/
while(0 == Error &&
NULL != fgets(Buffer, sizeof Buffer, fp))
{
NewLine = strchr(Buffer, '\n');
if(NewLine != NULL)
{
*NewLine = '\0';
}
/* This strcat relies on the AllocStrArray()
* function initialising indexs to empty strings.
*/
strcat((*Array)[index], Buffer);
if(NewLine != NULL)
{
/* There was a newline, so the
* next line is a new one.
*/
NumBlocks = 1;
++index;
if(index >= *NumFiles)
{
/* Add another LINES_PER_ALLOC lines.
* If it didn't work, give up.
*/
if(0 == AddFilesToStrArray(Array,
*NumFiles,
LINES_PER_ALLOC,
DEFAULT_LINE_LEN))
{
Error = ERR_FILES_NOT_ADDED;
}
else
{
*NumFiles += LINES_PER_ALLOC;
}
}
}
else
{
++NumBlocks;
/* Make room for some more data on this line */
if(0 ==
ResizeOneString(*Array,
index,
NumBlocks * DEFAULT_LINE_LEN))
{
Error = ERR_STRING_NOT_RESIZED;
}
}
}
fclose(fp);
if(0 == Error && *NumFiles > index)
{
if(0 == AddFilesToStrArray(Array,
*NumFiles,
index - *NumFiles,
0))
{
Error = ERR_ALLOC_FAILED;
}
*NumFiles = index;
}
}
else
{
Error = ERR_PATH_OPEN_FAILED; /* Can't open file */
}
}
else
{
Error = ERR_ALLOC_FAILED; /* Can't allocate memory */
}
if(Error != 0)
{
/* If the original allocation failed,
* *Array will be NULL. FreeStrArray()
* correctly handles this possibility.
*/
FreeStrArray(*Array, *NumFiles);
*NumFiles = 0;
}
else
{
ConsolidateStrArray(*Array, *NumFiles);
}
return Error;
}
int main(int argc, char **argv)
{
char **array = NULL;
int numFiles;
int index;
int error;
if(argc > 1)
{
error = ReadFile(argv[1], &array, &numFiles);
switch(error)
{
case 0:
for(index = 0; index < numFiles; index++)
{
printf("%s\n", array[index]);
}
FreeStrArray(array, numFiles);
break;
case ERR_STRING_NOT_RESIZED:
case ERR_ALLOC_FAILED:
case ERR_FILES_NOT_ADDED:
puts("Insufficient memory.");
break;
case ERR_PATH_OPEN_FAILED:
printf("Couldn't open %s for reading\n", argv[1]);
break;
default:
printf("Unknown error! Code %d.\n", error);
break;
}
}
else
{
puts("Please specify the text file name.");
}
return 0;
}
/* end of c11_018.c */
/*
gcc -std=c99 -Wall -Wextra string2.o t2.c -o out */
dan@dan-desktop:~/source/unleashed/ch11$
My questions now focus on main. Somehow, I've got to take this line:
error = ReadFile(argv[1], &array, &numFiles);
, and turn it into:
char ** getDir( const char * pathName);
Any constructive comments gladly received. Cheers,