From a90bd1c833b3475e434bd2de95ab9dd0347f1780 Mon Sep 17 00:00:00 2001
From: Bert <ber.t@gmx.com>
Date: Fri, 8 Apr 2011 10:23:42 +0200
Subject: [PATCH] Refactored recursive directory util functions

---
 Makefile |   2 +-
 main.c   |  38 ++++++++----
 util.c   | 178 +++++++++++++++++++++++++++++++------------------------
 util.h   |  17 +++++-
 4 files changed, 140 insertions(+), 95 deletions(-)

diff --git a/Makefile b/Makefile
index d814b2a..274ae1e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 all: sxiv
 
-VERSION=git-20110407
+VERSION=git-20110408
 
 CC?=gcc
 PREFIX?=/usr/local
diff --git a/main.c b/main.c
index 76da7fa..51d97d4 100644
--- a/main.c
+++ b/main.c
@@ -92,11 +92,15 @@ int load_image(int new) {
 	return ret;
 }
 
+int fncmp(const void *a, const void *b) {
+	return strcoll(*((char* const*) a), *((char* const*) b));
+}
+
 int main(int argc, char **argv) {
-	int i, j;
+	int i, start;
 	const char *filename;
-	char **fnames;
 	struct stat fstats;
+	r_dir_t dir;
 
 	parse_options(argc, argv);
 
@@ -121,18 +125,26 @@ int main(int argc, char **argv) {
 	} else {
 		for (i = 0; i < options->filecnt; ++i) {
 			filename = options->filenames[i];
-			if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
-				if (options->recursive && (fnames = read_dir_rec(filename))) {
-					for (j = 0; fnames[j]; ++j) {
-						if (!check_append(fnames[j]))
-							free(fnames[j]);
-					}
-					free(fnames);
-				} else {
-					warn("ignoring directory: %s", filename);
-				}
-			} else {
+
+			if (stat(filename, &fstats) || !S_ISDIR(fstats.st_mode)) {
 				check_append(filename);
+			} else {
+				if (!options->recursive) {
+					warn("ignoring directory: %s", filename);
+					continue;
+				}
+				if (r_opendir(&dir, filename)) {
+					warn("could not open directory: %s", filename);
+					continue;
+				}
+				start = fileidx;
+				while ((filename = r_readdir(&dir))) {
+					if (!check_append(filename))
+						free((void*) filename);
+				}
+				r_closedir(&dir);
+				if (fileidx - start > 1)
+					qsort(filenames + start, fileidx - start, sizeof(char*), fncmp);
 			}
 		}
 	}
diff --git a/util.c b/util.c
index a8ebe73..e16de94 100644
--- a/util.c
+++ b/util.c
@@ -18,7 +18,6 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <dirent.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -28,7 +27,6 @@
 #include "util.h"
 
 #define DNAME_CNT 512
-#define FNAME_CNT 1024
 #define FNAME_LEN 1024
 
 void cleanup();
@@ -161,7 +159,105 @@ end:
 	return path;
 }
 
-int create_dir_rec(const char *path) {
+int r_opendir(r_dir_t *rdir, const char *dirname) {
+	if (!rdir || !dirname || !*dirname)
+		return -1;
+
+	if (!(rdir->dir = opendir(dirname))) {
+		rdir->name = NULL;
+		rdir->stack = NULL;
+		return -1;
+	}
+
+	rdir->stcap = DNAME_CNT;
+	rdir->stack = (char**) s_malloc(rdir->stcap * sizeof(char*));
+	rdir->stlen = 0;
+
+	rdir->name = (char*) dirname;
+	rdir->d = 0;
+
+	return 0;
+}
+
+int r_closedir(r_dir_t *rdir) {
+	int ret = 0;
+
+	if (!rdir)
+		return -1;
+	
+	if (rdir->stack) {
+		while (rdir->stlen > 0)
+			free(rdir->stack[--rdir->stlen]);
+		free(rdir->stack);
+		rdir->stack = NULL;
+	}
+
+	if (rdir->dir) {
+		if (!(ret = closedir(rdir->dir)))
+			rdir->dir = NULL;
+	}
+
+	if (rdir->d && rdir->name) {
+		free(rdir->name);
+		rdir->name = NULL;
+	}
+
+	return ret;
+}
+
+char* r_readdir(r_dir_t *rdir) {
+	size_t len;
+	char *filename;
+	struct dirent *dentry;
+	struct stat fstats;
+
+	if (!rdir || !rdir->dir || !rdir->name)
+		return NULL;
+
+	while (1) {
+		if (rdir->dir && (dentry = readdir(rdir->dir))) {
+			if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
+				continue;
+
+			len = strlen(rdir->name) + strlen(dentry->d_name) + 2;
+			filename = (char*) s_malloc(len);
+			snprintf(filename, len, "%s%s%s", rdir->name,
+			         rdir->name[strlen(rdir->name)-1] == '/' ? "" : "/",
+			         dentry->d_name);
+
+			if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
+				/* put subdirectory on the stack */
+				if (rdir->stlen == rdir->stcap) {
+					rdir->stcap *= 2;
+					rdir->stack = (char**) s_realloc(rdir->stack,
+					                                 rdir->stcap * sizeof(char*));
+				}
+				rdir->stack[rdir->stlen++] = filename;
+				continue;
+			}
+			return filename;
+		}
+		
+		if (rdir->stlen > 0) {
+			/* open next subdirectory */
+			closedir(rdir->dir);
+			if (rdir->d)
+				free(rdir->name);
+			rdir->name = rdir->stack[--rdir->stlen];
+			rdir->d = 1;
+			if (!(rdir->dir = opendir(rdir->name)))
+				warn("could not open directory: %s", rdir->name);
+			continue;
+		}
+
+		/* no more entries */
+		break;
+	}
+
+	return NULL;
+}
+
+int r_mkdir(const char *path) {
 	char *dir, *d;
 	struct stat stats;
 	int err = 0;
@@ -202,82 +298,6 @@ int create_dir_rec(const char *path) {
 	return err;
 }
 
-int fncmp(const void *a, const void *b) {
-	return strcoll(*((char* const*) a), *((char* const*) b));
-}
-
-char** read_dir_rec(const char *dirname) {
-	int dcnt, didx, fcnt, fidx;
-	char **dnames, **fnames, *filename;
-	unsigned char first;
-	size_t len;
-	DIR *dir;
-	struct dirent *dentry;
-	struct stat fstats;
-
-	if (!dirname)
-		return NULL;
-
-	dcnt = DNAME_CNT;
-	didx = first = 1;
-	dnames = (char**) s_malloc(dcnt * sizeof(char*));
-	dnames[0] = (char*) dirname;
-
-	fcnt = FNAME_CNT;
-	fidx = 0;
-	fnames = (char**) s_malloc(fcnt * sizeof(char*));
-
-	while (didx > 0) {
-		dirname = dnames[--didx];
-		if (!(dir = opendir(dirname))) {
-			warn("could not open directory: %s", dirname);
-		} else {
-			while ((dentry = readdir(dir))) {
-				if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
-					continue;
-
-				len = strlen(dirname) + strlen(dentry->d_name) + 2;
-				filename = (char*) s_malloc(len * sizeof(char));
-				snprintf(filename, len, "%s%s%s", dirname,
-				         dirname[strlen(dirname)-1] == '/' ? "" : "/", dentry->d_name);
-
-				if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
-					if (didx == dcnt) {
-						dcnt *= 2;
-						dnames = (char**) s_realloc(dnames, dcnt * sizeof(char*));
-					}
-					dnames[didx++] = filename;
-				} else {
-					if (fidx + 1 == fcnt) {
-						fcnt *= 2;
-						fnames = (char**) s_realloc(fnames, fcnt * sizeof(char*));
-					}
-					fnames[fidx++] = filename;
-				}
-			}
-			closedir(dir);
-		}
-
-		if (!first)
-			free((void*) dirname);
-		else
-			first = 0;
-	}
-
-	if (!fidx) {
-		free(fnames);
-		fnames = NULL;
-	} else {
-		if (fidx > 1)
-			qsort(fnames, fidx, sizeof(char*), fncmp);
-		fnames[fidx] = NULL;
-	}
-
-	free(dnames);
-
-	return fnames;
-}
-
 char* readline(FILE *stream) {
 	size_t len;
 	char *buf, *s, *end;
diff --git a/util.h b/util.h
index 34e9b18..8e8a20d 100644
--- a/util.h
+++ b/util.h
@@ -21,6 +21,7 @@
 
 #include <stdio.h>
 #include <stdarg.h>
+#include <dirent.h>
 
 #define ABS(a)   ((a) < 0 ? (-(a)) : (a))
 #define MIN(a,b) ((a) < (b) ? (a) : (b))
@@ -35,6 +36,16 @@
 		(tv)->tv_usec = (ts)->tv_nsec / 1000;  \
 }
 
+typedef struct {
+	DIR *dir;
+	char *name;
+	int d;
+
+	char **stack;
+	int stcap;
+	int stlen;
+} r_dir_t;
+
 void* s_malloc(size_t);
 void* s_realloc(void*, size_t);
 
@@ -45,8 +56,10 @@ void size_readable(float*, const char**);
 
 char* absolute_path(const char*);
 
-int create_dir_rec(const char*);
-char** read_dir_rec(const char*);
+int r_opendir(r_dir_t*, const char*);
+int r_closedir(r_dir_t*);
+char* r_readdir(r_dir_t*);
+int r_mkdir(const char *);
 
 char* readline(FILE*);