0

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.

2
  • files[i] has a type char* so this is already wrong: files[i] != '\0'. Didn't look further. Commented Jun 4, 2020 at 21:34
  • you might like the versatile scandir() Commented Jun 4, 2020 at 22:06

1 Answer 1

1

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).

Sign up to request clarification or add additional context in comments.

1 Comment

thanks. that helped. changing the +1 to a +2 solved my issue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.