2

I have this code which will print to the console all files in a given folder which have a given extension:

int scandir(char dirname[], char const *ext)
/* Scans a directory and retrieves all files of given extension */
{
    DIR *d = NULL;
    struct dirent *dir = NULL;

    d = opendir(dirname);

    if (d)
    {
        while ((dir = readdir(d)) != NULL)
        {
            if (has_extension(dir->d_name, ext))
            {
                printf("%s\n", dir->d_name);
            }
        }
        closedir(d);
    }
    return(0);
}

This function works, but I would like to modify it so that it returns an array of filenames. (I have done a lot of google searches but only come up with functions like mine, which print to console)

I'm fairly new to C and 'low-level' programming, so I am unsure of how to correctly handle the memory here. How do I create and add things to a character array when I do not know how big it is going to be?

I'm using MinGW..

David Ranieri
  • 39,972
  • 7
  • 52
  • 94
jramm
  • 6,415
  • 4
  • 34
  • 73

1 Answers1

2

You can use realloc:

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

extern char *strdup(const char *src);

int scandir(char ***list, char dirname[], char const *ext)
/* Scans a directory and retrieves all files of given extension */
{
    DIR *d = NULL;
    struct dirent *dir = NULL;
    size_t n = 0;

    d = opendir(dirname);
    if (d)
    {
        while ((dir = readdir(d)) != NULL)
        {
            if (has_extension(dir->d_name, ext))
            {
                *list = realloc(*list, sizeof(**list) * (n + 1));
                (*list)[n++] = strdup(dir->d_name);
            }
        }
        closedir(d);
    }
    return n;
}

int main(void)
{
    char **list = NULL;
    size_t i, n = scandir(&list, "/your/path", "jpg");

    for (i = 0; i < n; i++) {
        printf("%s\n", list[i]);
        free(list[i]);
    }
    free(list);
    return 0;
}

Note that strdup() is not a standard function but its available on many implementations.

As an alternative to realloc you can use a singly linked list of strings.

EDIT: As pointed out by @2501, is better to return an allocated array of strings from scandir and pass elems as param:

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

extern char *strdup(const char *src);

char **scandir(char dirname[], char const *ext, size_t *elems)
/* Scans a directory and retrieves all files of given extension */
{
    DIR *d = NULL;
    struct dirent *dir = NULL;
    char **list = NULL;

    d = opendir(dirname);
    if (d)
    {
        while ((dir = readdir(d)) != NULL)
        {
            if (has_extension(dir->d_name, ext))
            {
                list = realloc(list, sizeof(*list) * (*elems + 1));
                list[(*elems)++] = strdup(dir->d_name);
            }
        }
        closedir(d);
    }
    return list;
}

int main(void)
{
    size_t i, n = 0;
    char **list = scandir("/your/path", "jpg", &n);

    for (i = 0; i < n; i++) {
        printf("%s\n", list[i]);
        free(list[i]);
    }
    free(list);
    return 0;
}

Finally, do you really need an array? Consider using a call-back function:

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

void cb_scandir(const char *src)
{
    /* do whatever you want with the passed dir */
    printf("%s\n", src);
}

int scandir(char dirname[], char const *ext, void (*callback)(const char *))
/* Scans a directory and retrieves all files of given extension */
{
    DIR *d = NULL;
    struct dirent *dir = NULL;
    size_t n = 0;

    d = opendir(dirname);
    if (d)
    {
        while ((dir = readdir(d)) != NULL)
        {
            if (has_extension(dir->d_name, ext))
            {
                callback(dir->d_name);
                n++;
            }
        }
        closedir(d);
    }
    return n;
}

int main(void)
{
    scandir("/your/path", "jpg", cb_scandir);
    return 0;
}
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • Thanks. A lot to learn there. In particular the pointers to arrays. – jramm Oct 14 '14 at 10:41
  • @AlterMann I would return a null terminated char**. +1 char*** is always better than using a void** and causing ub . – 2501 Oct 14 '14 at 10:43