C program to list all files in a directory recursively

Write a C program to list all files in a directory. How to list all files in a directory recursively. How to use readdir() function to list all files in a directory recursively. Logic to list all files and sub-directories of a directory in C programming. How to use opendir(), readdir() and closedir() library functions.

Required knowledge

Basic Input Output, File handling, Recursion

In programming while file manipulation, several times we come across a problem to list files in a directory. List files in a directory and recursively in its sub-directories. However, many C programmers don’t know how to get list of all files and directories within a directory. In this post I will explain how to list files and directories in a directory.

How to print files and directories in tree structure in C programming

The readdir() function

struct dirent *readdir(DIR *dirp);

function is defined in dirent.h header file. It returns pointer to a structure dirent type representing directory entry at the current position in directory stream dirp. On every call to readdir() method, it returns file/directory at current position in directory stream. readdir() returns NULL pointer if reached at the end of directory stream.

But wait, readdir(DIR *dirp) function accepts a parameter of DIR type. How to get reference to DIR type.

The opendir() function

DIR *opendir(const char *dirname);

is also defined in dirent.h header file. Similar to file streams, opendir() opens a directory stream corresponding to the file path pointed by dirname. By default the opened directory stream points to first entry in the directory.

On success, the function returns a pointer to structure of DIR type. Otherwise NULL pointer. The returned value can be further used to perform operations on directory.

Similar to file stream, we open a directory stream, perform some action and finally close the stream. To close a directory stream we use closedir() function.

The closedir() function

int closedir(DIR *dirp);

is also present under same library file. It closes a directory pointed by dirp pointer.

On success the function returns 0, otherwise -1.

How to list all files and directories of a directory

Step by step descriptive logic to list all files and directories in a directory.

  1. Input source path to list all files and sub-directories. Store it in some variable say path.
  2. Open directory stream using opendir() and store its reference to *dir of DIR type.
  3. Initialize another variable of pointer to structure dirent type, say struct dirent *dp.
  4. Read next element from directory stream using dp = readdir(dir).
  5. Print current directory stream item name, using dp->name.
  6. Repeat step 4-5 till dp != NULL.
  7. Finally, close the directory stream pointed by dir variable.

Program to list all files and sub-directories in a directory

/**
 * C program to list all files and sub-directories in a directory.
 */

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>


void listFiles(const char *path);

int main()
{
    // Directory path to list files
    char path[100];

    // Input path from user
    printf("Enter path to list files: ");
    scanf("%s", path);

    listFiles(path);

    return 0;
}


/**
 * Lists all files and sub-directories at given path.
 */
void listFiles(const char *path)
{
    struct dirent *dp;
    DIR *dir = opendir(path);

    // Unable to open directory stream
    if (!dir) 
        return; 

    while ((dp = readdir(dir)) != NULL)
    {
        printf("%s\n", dp->d_name);
    }

    // Close directory stream
    closedir(dir);
}

You can easily convert above function to work for recursive directory listing. Check below program if facing difficulties implementing it recursive way.

Program to list all files and sub-directories of a directory recursively

/**
 * C program to list contents of a directory recursively.
 */

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

void listFilesRecursively(char *path);


int main()
{
    // Directory path to list files
    char path[100];

    // Input path from user
    printf("Enter path to list files: ");
    scanf("%s", path);

    listFilesRecursively(path);

    return 0;
}


/**
 * Lists all files and sub-directories recursively 
 * considering path as base path.
 */
void listFilesRecursively(char *basePath)
{
    char path[1000];
    struct dirent *dp;
    DIR *dir = opendir(basePath);

    // Unable to open directory stream
    if (!dir)
        return;

    while ((dp = readdir(dir)) != NULL)
    {
        if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
        {
            printf("%s\n", dp->d_name);

            // Construct new path from our base path
            strcpy(path, basePath);
            strcat(path, "/");
            strcat(path, dp->d_name);

            listFilesRecursively(path);
        }
    }

    closedir(dir);
}}
Output

Enter path to list files: .
.git
COMMIT_EDITMSG
config
description
FETCH_HEAD
HEAD
hooks
pack
ORIG_HEAD
packed-refs
refs
heads
master
remotes
origin
HEAD
master
tags
.gitignore
.vscode
c_cpp_properties.json
bin
a.exe
c-program-to-find-maximum-and-minimum-range-of-data-types-using-macro.c
c-programming-ws.code-workspace
data
append.txt
blanks.txt
compare1.txt
compare2.txt
copy-file.txt
empty-lines.txt
even-numbers.txt
file1.txt
file2.txt
file3.txt
file4.txt
merged-file.txt
numbers.txt
odd-numbers.txt
prime-numbers.txt
LICENSE
README.md
src
file
append-data.c
check-file-properties.c
compare-file.c
copy-file-contents.c
copy-file-using-function.c
count-characters-words-lines-in-file.c
count-occurrences-of-all-words-in-file.c
create-and-write-contents-to-file.c
delete-all-blank-lines.c
delete-specific-line.c
delete-word-from-file.c
directory-exists.c
file-exists.c
file-programming-example-list.md
find-word-in-file.c
list-files.c
merge-file.c
print-source-of-current-file.c
read-from-file-using-fgetc.c
read-from-file-using-fgets.c
read-numbers-write-even-odd-prime-to-separate-file.c
remove-empty-lines.c
rename-file.c
replace-line-in-file.c
replace-word-in-file.c
replace-word.c
toggle-case-of-file-contents.c
stdlib
atof.c
atoi.c
atol.c
atoll.c
strtol.c
strtoll.c
strtoul.c
strtoull.c

Let’s raise the level. The above recursive method prints all files and sub-directories in same indentation. Its very difficult to know which files are in which directory. So lets print all files and sub-directories in tree structure.

Program to print all files and sub-directories in tree structure

/**
 * C program to list file and sub-directories of a directory 
 * recursively in tree structure.
 */

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>


void tree(char *basePath, const int root);

int main()
{
    // Directory path to list files
    char path[100];

    // Input path from user
    printf("Enter path to list files: ");
    scanf("%s", path);

    tree(path, 0);

    return 0;
}


/**
 * Tree, prints all files and sub-directories of a given 
 * directory in tree structure.
 * 
 * @param basePath Base path to traverse directory
 * @param root     Integer representing indention for current directory
 */
void tree(char *basePath, const int root)
{
    int i;
    char path[1000];
    struct dirent *dp;
    DIR *dir = opendir(basePath);

    if (!dir)
        return;

    while ((dp = readdir(dir)) != NULL)
    {
        if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
        {
            for (i=0; i<root; i++) 
            {
                if (i%2 == 0 || i == 0)
                    printf("%c", 179);
                else
                    printf(" ");

            }

            printf("%c%c%s\n", 195, 196, dp->d_name);

            strcpy(path, basePath);
            strcat(path, "/");
            strcat(path, dp->d_name);
            tree(path, root + 2);
        }
    }

    closedir(dir);
}
Output

Enter path to list files: .
├─.git
│ ├─COMMIT_EDITMSG
│ ├─config
│ ├─description
│ ├─FETCH_HEAD
│ ├─HEAD
│ ├─ORIG_HEAD
│ ├─packed-refs
│ ├─refs
│ │ ├─heads
│ │ │ ├─master
│ │ ├─remotes
│ │ │ ├─origin
│ │ │ │ ├─HEAD
│ │ │ │ ├─master
│ │ ├─tags
├─.gitignore
├─.vscode
│ ├─c_cpp_properties.json
├─bin
│ ├─a.exe
├─c-program-to-find-maximum-and-minimum-range-of-data-types-using-macro.c
├─c-programming-ws.code-workspace
├─data
│ ├─append.txt
│ ├─blanks.txt
│ ├─compare1.txt
│ ├─compare2.txt
│ ├─copy-file.txt
│ ├─empty-lines.txt
│ ├─even-numbers.txt
│ ├─file1.txt
│ ├─file2.txt
│ ├─file3.txt
│ ├─file4.txt
│ ├─merged-file.txt
│ ├─numbers.txt
│ ├─odd-numbers.txt
│ ├─prime-numbers.txt
├─LICENSE
├─README.md
├─src
│ ├─file
│ │ ├─append-data.c
│ │ ├─check-file-properties.c
│ │ ├─compare-file.c
│ │ ├─copy-file-contents.c
│ │ ├─copy-file-using-function.c
│ │ ├─count-characters-words-lines-in-file.c
│ │ ├─count-occurrences-of-all-words-in-file.c
│ │ ├─create-and-write-contents-to-file.c
│ │ ├─delete-all-blank-lines.c
│ │ ├─delete-specific-line.c
│ │ ├─delete-word-from-file.c
│ │ ├─directory-exists.c
│ │ ├─file-exists.c
│ │ ├─file-programming-example-list.md
│ │ ├─find-word-in-file.c
│ │ ├─list-files.c
│ │ ├─merge-file.c
│ │ ├─print-source-of-current-file.c
│ │ ├─read-from-file-using-fgetc.c
│ │ ├─read-from-file-using-fgets.c
│ │ ├─read-numbers-write-even-odd-prime-to-separate-file.c
│ │ ├─remove-empty-lines.c
│ │ ├─rename-file.c
│ │ ├─replace-line-in-file.c
│ │ ├─replace-word-in-file.c
│ │ ├─replace-word.c
│ │ ├─toggle-case-of-file-contents.c
│ ├─stdlib
│ │ ├─atof.c
│ │ ├─atoi.c
│ │ ├─atol.c
│ │ ├─atoll.c
│ │ ├─strtol.c
│ │ ├─strtoll.c
│ │ ├─strtoul.c
│ │ ├─strtoull.c

Happy coding 😉