depthFirstTraversal.c
/*************************************************************************/
/*    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
/*                                                                       */
/*  depthFirstTraversal                                                  */
/*                                                                       */
/*  A non recursive depth first traversal of a directory tree useful in  */
/*  the event that the stack size is limited relative to the heap size.  */
/*                                                                       */
/*************************************************************************/
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
 
#include "stringQueue.h"
 
#ifndef S_ISDIR
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#endif
 
/*************************************************************************/
/*    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    */
/*************************************************************************/
/* Concatenates two filesystem paths a and b together.                    */
/*************************************************************************/
char *pathCombine(char *a, char *b) {
    size_t sa = strlen(a);
    size_t sb = strlen(b);
    char *rt = (char*)calloc(sa + sb + 1, sizeof(char *));
    memcpy(rt, a, sa);
    switch (a[sa - 1]) {
        case '/':
            break;
        default:
            rt = (char*)realloc(rt, (strlen(rt) + 1) * sizeof(char *));
            rt[sa] = '/';
        break;
    }
    memcpy(rt + strlen(rt), b, sb + 1);
    return rt;
}
 
int main(int argc, char **argv) {
    DIR *dir;
    struct dirent *de;
    struct stat st;
    stringQueue *dirQueue = stringQueueCreate(1);
    char *path;
    char *file;
 
    if (argc != 2) {
        printf("Syntax: %s [DIRECTORY]\n", argv[0]);
        return 1;
    }
 
    /* Enqueue the current working directory path. */
    stringQueueEnqueue(dirQueue, strdup(argv[1]));
 
    /* While there are still directories to be processed. */
    while ((path = stringQueueDequeue(dirQueue)) != NULL) {
        /* If we cannot open a directory, warn and continue. */
        if ((dir = opendir(path)) == NULL) {
            printf("Unable to open directory: %s\n", path);
            continue;
        }
 
        /* Read the contents of the current directory. */
        while ((de = readdir(dir))) {
            /* Combine path with file. */
            file = pathCombine(path, de->d_name);
 
            /* If we fail to get the stats for the file, skip it. */
            if(stat(file, &st) != 0) {
                free(file);
                continue;
            }
 
            /* If the file is not a directory, skip it. */
            if(!S_ISDIR(st.st_mode)) {
                free(file);
                continue;
            }
 
            /* If the directory starts with "." or "..", then skip it. */
            if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) {
                free(file);
                continue;
            }
 
            /* Print the directory. */
            printf("Directory: %s\n", file);
 
            /* Enqueue the directory. */
            stringQueueEnqueue(dirQueue, file);
 
            /* And release the file. */
            free(file);
        }
 
        /* And close the directory. */
        closedir(dir);
    }
 
    return 0;
}