Reputation: 25
I am writing a C program to get a list of all the files in a given directory. It takes a command-line input representing the file path of the directory from which user wants the files listed. The function returns a string array where each element is one of the files in the directory. Right now, I am having each elements be not just the file name but the entire file path. So here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
char **show_dir_content(char *path)
{
char **files;
int number_of_files = 5;
int i = 0;
files = (char **)calloc(number_of_files, sizeof(char *));
DIR *d = opendir(path);
if (d == NULL)
return files;
struct dirent *dir;
while ((dir = readdir(d)) != NULL)
{
if (dir->d_type != DT_DIR)
{
int path_len = strlen(path);
files[i] = (char *)calloc(strlen(dir->d_name) + path_len + 1, sizeof(char));
sprintf(files[i], "%s/%s", path, dir->d_name);
printf("%d: %s\n", i, files[i]);
i++;
if (i == number_of_files)
{
number_of_files += 5;
files = (char **)realloc(files, number_of_files * sizeof(char *));
}
}
}
return files;
}
int main(int arcv, char *argv[])
{
char **files = show_dir_content(argv[1]);
int i;
printf("\n");
for (i = 0; files[i] != '\0'; i++)
{
printf("%d: %s\n", i, files[i]);
}
for (i = 0; files[i] != '\0'; i++)
{
free(files[i]);
}
return 0;
}
the output is funky to say the least when I enter my current directory as input. So I output 2 different ways. Inside my show_dir_content function, I print each element of the array right after I get the file. And then in the main function, I loop through the array returned from the show_dir_content function, printing each element. If the program was printing properly, both results should be the same but for some reason they are not and I don't know why.
0: ./file2.txt
1: ./file1.txt
2: ./find-file-size.h
3: ./find-file-size.o
4: ./get-files.o
5: ./get-files.h
6: ./Makefile
7: ./get-dir-size.h
8: ./get-dir-size.o
9: ./get-files.c
10: ./find-file-size.c
11: ./get-dir-size.c
12: ./fsz
0: ./file2.txt
1: ./file1.txt
2: ./find-file-size.h
3: ./find-file-size.o
4: ./get-files.o
5: ./get-files.h
6: ./Makefile
7: ./get-dir-size.h./get-dir-size.o./get-files.c
8: ./get-dir-size.o./get-files.c
9: ./get-files.c
10: ./find-file-size.c
11: ./get-dir-size.c
12: ./fsz
As you can see, for the most part, it is correct, except for elements 7 and 8. If anyone can explain why there is a disparity between those 2 elements from within the show_dir_content function to what is printed in the main function, that would be helpful.
Upvotes: 0
Views: 38
Reputation: 35154
Two things: (1) you need to allocate one additional character to cover also the necessary string termination character '\0'
. (2) you should explicitly mark the end of the list with NULL
, as you cannot rely that a realloc
fills the memory block with zeros (with calloc
you can rely on that, but not with realloc
):
while ((dir = readdir(d)) != NULL) {
...
// note the +2, one for the '/' and one for the string termination character
files[i] = (char *)calloc(strlen(dir->d_name) + path_len + 2, sizeof(char));
sprintf(files[i], "%s/%s", path, dir->d_name);
...
}
files[i] = NULL;
The reason for the (undefined) behaviour you observe is probably part (1); the string termination character written by a first sprintf
is out of the memory range allocated; a second calloc
could start right at the string termination character of the first one, and the second's sprintf
overwrites this string termination character. It thereby "concatenates" the two strings (from the viewpoint of the first one).
Upvotes: 1