I have just written an utility routine for traversing path recently. I think it may be useful to you.
I have compiled and run it on Linux and Windows both, but if you want to use it on Windows, you have to download an extra dirent.h file from http://www.softagalleria.net/dirent.php since there is no dirent.h on Windows.
There is a slight main function in my code, it will demostrate how this code works.
I think there is no memory leak, it's apprecated for any advices, not only for memory leak.
Don't forget to change the include file path.
header
Code:
/**
* ztraversal.h : This file defines the structure used by
* traversal engine.
*/
#if _MSC_VER > 1000
#pragma once
#endif
#ifndef _INC_ZTRAVERSAL
#define _INC_ZTRAVERSAL
#include <stdio.h>
#include <sys/stat.h>
#ifdef WIN32
#include "dirent.h"
#define __S_ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask))
#define S_ISDIR(mode) __S_ISTYPE((mode), S_IFDIR)
#else /* WIN32 */
#include <dirent.h>
#endif /* WIN32 */
#define PATH_BUFF_SIZE FILENAME_MAX * 2
#define TRAVERSAL_SUB 0x01
#define TRAVERSAL_NO_SUB 0x02
#define TRAVERSAL_LOOP 0x04
struct Traversal_tree_node {
struct Traversal_tree_node* parent;
char path_buff[PATH_BUFF_SIZE];
// we will clean the path buffer tail by using clean_up_ptr, the
// length is clean_up_len
char* clean_up_ptr;
int clean_up_len;
DIR* dir;
};
static struct Traversal_tree_node* malloc_node(struct Traversal_tree_node* parent, const char* filename);
static void free_node(struct Traversal_tree_node* pnode);
int init_traversal_engine(const char* passname, int mode);
const char* get_next_file();
void deinit_traversal_engine();
#endif /* _INC_ZTRAVERSAL */
C file
Code:
/**
* *********** Traversal engine ************
* This small routine is for traversing path.
* @usage
* init_traversal_engine(filename, traversal_mode);
* a loop (get_next_file());
* deinit_traversal_engine();
* @constraint The size of the buffer internal to store the path and filename is
* restricted to the FILENAME_MAX * 2
*/
/**
* The tree node the traversal engine currently traversed.
* @constraint Be sure to use malloc_node to allocate new node, and
* use free_node to recovery the memory.
*/
#include <stdarg.h>
#ifdef _ZCOMMON_HEAD
#include "../include/zcommon.h"
#endif /* _ZCOMMON_HEAD */
#include "../include/ztraversal.h"
#ifndef _ZCOMMON_HEAD
#define Z_ERROR -1
#define Z_SUCCESS 0
#endif /* _ZCOMMON_HEAD */
static struct Traversal_tree_node* l_path_current = NULL;
static struct stat l_file_stat;
static int l_mode = TRAVERSAL_NO_SUB;
static int _check_mode(int mode);
#ifndef _ZCOMMON_HEAD
int log_error(int errcode, const char* error_msg, ...) {
va_list args;
va_start(args, error_msg);
vfprintf(stderr, error_msg, args);
va_end(args);
return Z_SUCCESS;
}
#endif /* _ZCOMMON_HEAD */
/**
* Initializing the traversal engine, allocating memory and opening the top dir.
* @constraint filename must be an available path.
* @para filename a directory name with absolute path
* @para mode traversal mode
* TRAVERSAL_NO_SUB: engine will ignore the sub directory.
* TRAVERSAL_SUB : engine will go into the sub directory.
* @return 0 if succeed, otherwise return TE_ERR
*/
int init_traversal_engine(const char* pathname, int mode) {
l_mode = mode;
if (_check_mode(mode) == Z_ERROR) {
return Z_ERROR;
}
l_path_current = malloc_node(NULL, pathname);
if (l_path_current == NULL) {
return Z_ERROR;
}
return Z_SUCCESS;
}
static int _check_mode(int mode) {
if ((mode & TRAVERSAL_SUB) && (mode & TRAVERSAL_NO_SUB)) {
log_error(0x100, "TRAVERSAL_SUB and TRAVERSAL_NO_SUB cannot be set at the same time");
return Z_ERROR;
}
if ((mode & TRAVERSAL_NO_SUB) || (mode& TRAVERSAL_SUB) || (mode & TRAVERSAL_LOOP)) {
return Z_SUCCESS;
}
else {
log_error(0x100, "At least one available mode must be set");
return Z_ERROR;
}
}
/**
* Create a new traversal tree node with the specified path filename.
* @constraint filename must be an available path.
* @para parent parent node of the new created node, if parent is NULL,
* the new one must be the top node.
* @para filename a directory name with absolute path.
* @return NULL if there are errors happened, otherwise return the pointer
* to the new node.
*/
static struct Traversal_tree_node* malloc_node(struct Traversal_tree_node* parent, const char* filename) {
int filename_len = (int)strlen(filename);
struct Traversal_tree_node* node = NULL;
if (filename_len + 2/* file separator / and terminal null */ > PATH_BUFF_SIZE) {
log_error(0x002, "filename too long");
return NULL;
}
else {
node = (struct Traversal_tree_node*)malloc(sizeof(struct Traversal_tree_node));
node->dir = NULL;
node->dir = opendir(filename);
if (node->dir == NULL) {
log_error(1111, "Cannot open directory: %s\n", filename);
free_node(node);
node = NULL;
return NULL;
}
else {
memset(node->path_buff, 0, PATH_BUFF_SIZE);
node->parent = parent;
strcpy(node->path_buff, filename);
#ifdef WIN32
strcat(node->path_buff, "\\");
#endif /* WIN32 */
filename_len = (int)strlen(node->path_buff);
node->clean_up_ptr = node->path_buff + filename_len;
node->clean_up_len = PATH_BUFF_SIZE - filename_len;
return node;
}
}
}
/**
* Get the next file's name with absolute path in current traversal tree.
* @return NULL if there are no files in traversal path.
*/
const char* get_next_file() {
struct dirent* process_entry;
if (l_path_current == NULL || l_path_current->dir == NULL) {
log_error(0x001, "Traversal engine has not been initialized\n");
return NULL;
}
else {
memset(l_path_current->clean_up_ptr, 0, l_path_current->clean_up_len);
}
if ((process_entry = readdir(l_path_current->dir)) != NULL) {
if (strcmp(process_entry->d_name, ".") == 0 ||
strcmp(process_entry->d_name, "..") == 0) {
return get_next_file();
}
strcat(l_path_current->path_buff, process_entry->d_name);
stat(l_path_current->path_buff, &l_file_stat);
//sub directory
if (S_ISDIR(l_file_stat.st_mode)) {
if (l_mode & TRAVERSAL_SUB){
l_path_current = malloc_node(l_path_current, l_path_current->path_buff);
if (l_path_current == NULL) {
return NULL;
}
else {
#ifdef _DEBUG
printf("directory : %s\n", l_path_current->path_buff);
#endif /* _DEBUG */
}
return get_next_file();
}
// ignore sub directory
else {
return get_next_file();
}
}
else { //file
#ifdef _DEBUG
printf("file : %s\n", l_path_current->path_buff);
#endif /* _DEBUG */
return l_path_current->path_buff;
}
}
// no entry in this directory, let's try going upstairs.
else {
struct Traversal_tree_node* node = l_path_current->parent;
char buff[PATH_BUFF_SIZE];
strcpy(buff, l_path_current->path_buff);
free_node(l_path_current);
l_path_current = node;
if (l_path_current == NULL) {
//go back if loop flag has been set
if (l_mode & TRAVERSAL_LOOP) {
deinit_traversal_engine();
init_traversal_engine(buff, l_mode);
}
return NULL;
}
else {
return get_next_file();
}
}
}
/**
* Recovery the memory the traversal_engine allocated, and do some
* cleanups.
*/
void deinit_traversal_engine() {
struct Traversal_tree_node* node = NULL;
while (l_path_current != NULL) {
node = l_path_current->parent;
free_node(l_path_current);
l_path_current = node;
}
}
/**
* Recovery the memory of traversal tree node allocated by malloc_node.
* para pnode a pointer to a node.
*/
static void free_node(struct Traversal_tree_node* pnode) {
if (pnode != NULL) {
if (pnode->dir != NULL) {
closedir(pnode->dir);
pnode->dir = NULL;
}
free(pnode);
}
}
int main(int argc, char* argv[]) {
const char* filename = NULL;
int rc = 0;
if (argc != 2) {
printf("need path name!");
}
else {
rc = init_traversal_engine(argv[1], TRAVERSAL_NO_SUB);
if (rc == Z_SUCCESS) {
while ((filename = get_next_file()) != NULL) {
printf("file: %s\n", filename);
}
deinit_traversal_engine();
}
}
return 0;
}