From 864fa87522c3c0a062c11adfc0421cba92ac6efd Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Wed, 4 Mar 2020 17:38:03 -0600
Subject: [PATCH v6 3/4] Convert data structure to use a list

---
 src/backend/utils/adt/genfile.c | 78 +++++++++++++++++----------------
 1 file changed, 41 insertions(+), 37 deletions(-)

diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index c5148f547b..bc473e79ef 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -37,8 +37,9 @@
 
 typedef struct
 {
-	char	   *location;
-	DIR		   *dirdesc;
+	/* List of opened dirs */
+	List		*location;
+	List		*dirdesc;
 	bool		include_dot_dirs;
 
 	/* Used in ls_dir_files: */
@@ -474,10 +475,9 @@ pg_ls_dir(PG_FUNCTION_ARGS)
 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
 		fctx = palloc(sizeof(directory_fctx));
-		fctx->location = convert_and_check_filename(PG_GETARG_TEXT_PP(0));
-
+		fctx->location = lappend(NIL, convert_and_check_filename(PG_GETARG_TEXT_PP(0)));
+		fctx->dirdesc = lappend(NIL, AllocateDir(linitial(fctx->location)));
 		fctx->include_dot_dirs = include_dot_dirs;
-		fctx->dirdesc = AllocateDir(fctx->location);
 
 		if (!fctx->dirdesc)
 		{
@@ -490,7 +490,7 @@ pg_ls_dir(PG_FUNCTION_ARGS)
 				ereport(ERROR,
 						(errcode_for_file_access(),
 						 errmsg("could not open directory \"%s\": %m",
-								fctx->location)));
+								(char*)linitial(fctx->location))));
 		}
 		funcctx->user_fctx = fctx;
 		MemoryContextSwitchTo(oldcontext);
@@ -499,7 +499,7 @@ pg_ls_dir(PG_FUNCTION_ARGS)
 	funcctx = SRF_PERCALL_SETUP();
 	fctx = (directory_fctx *) funcctx->user_fctx;
 
-	while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
+	while ((de = ReadDir(linitial(fctx->dirdesc), linitial(fctx->location))) != NULL)
 	{
 		if (!fctx->include_dot_dirs &&
 			(strcmp(de->d_name, ".") == 0 ||
@@ -509,7 +509,9 @@ pg_ls_dir(PG_FUNCTION_ARGS)
 		SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(de->d_name));
 	}
 
-	FreeDir(fctx->dirdesc);
+	FreeDir(linitial(fctx->dirdesc));
+	list_free(fctx->dirdesc);
+	list_free_deep(fctx->location);
 
 	SRF_RETURN_DONE(funcctx);
 }
@@ -528,20 +530,25 @@ pg_ls_dir_1arg(PG_FUNCTION_ARGS)
 }
 
 /*
- * Populate fctx with list of pathnames and stat structues.  Generate a full
- * list of the stat structures all at once - the alternative is to re-stat
- * everything later on, which then requires somehow handling cases like the 2nd
- * stat fails, or the 2nd stat returns a dir which didn't exist at the time we
- * originally looked, or existed but wasn't a dir.  stat()ing everything all at
- * once gets as close as we can to a consistent view of the filesystem, from
- * which files might be removed or renamed or reordered.
+ * Populate fctx with list of pathnames and stat structues.
+ *
+ * Generate a full list of the stat structures all at once.  That gets as close
+ * as we can to a consistent view of the filesystem, from which files might be
+ * removed or renamed or reordered.
+ *
+ * recurse is the recursion level into a stack of dirs, or -1 to not decend
+ * into directories.
  */
 static void
-populate_paths(directory_fctx *fctx, bool recurse)
+populate_paths(directory_fctx *fctx, int recurse)
 {
 	struct dirent *de;
 	struct stat	attrib;
-	while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
+
+	DIR *dirdesc = llast(fctx->dirdesc);
+	char *location = llast(fctx->location);
+
+	while ((de = ReadDir(dirdesc, location)) != NULL)
 	{
 		char		path[MAXPGPATH];
 
@@ -550,28 +557,23 @@ populate_paths(directory_fctx *fctx, bool recurse)
 			continue;
 
 		/* Get the file info */
-		snprintf(path, sizeof(path), "%s/%s", fctx->location, de->d_name);
+		snprintf(path, sizeof(path), "%s/%s", location, de->d_name);
 		if (stat(path, &attrib) < 0)
 			ereport(ERROR,
 					(errcode_for_file_access(),
 					 errmsg("could not stat file \"%s\": %m", path)));
 
 		/* Ignore anything but regular files, or dirs, if requested */
-		if (S_ISDIR(attrib.st_mode))
+		if (recurse == -1)
+			; /* Do nothing, fall through */
+		else if (S_ISDIR(attrib.st_mode))
 		{
-			/* Save current dir while recursing */
-			directory_fctx oldfctx = *fctx;
-
-			fctx->location = path;
-			fctx->dirdesc = AllocateDir(path);
-
-			if (recurse)
-				populate_paths(fctx, recurse);
-
-			/* Restore previous dir */
-			FreeDir(fctx->dirdesc);
-			fctx->dirdesc = oldfctx.dirdesc;
-			fctx->location = oldfctx.location;
+			/* Reallocate location and dirdesc whenever recursing */
+			fctx->location = lappend(fctx->location, path);
+			fctx->dirdesc = lappend(fctx->dirdesc, AllocateDir(path));
+			populate_paths(fctx, 1+recurse);
+			FreeDir(llast(fctx->dirdesc));
+			list_delete_last(fctx->dirdesc);
 		} else if (!S_ISREG(attrib.st_mode))
 			continue;
 
@@ -619,8 +621,8 @@ pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok, bool
 
 		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
 
-		fctx->location = pstrdup(dir);
-		fctx->dirdesc = AllocateDir(fctx->location);
+		fctx->location = lappend(NIL, pstrdup(dir));
+		fctx->dirdesc = lappend(NIL, AllocateDir(dir));
 		fctx->npaths = 0;
 		fctx->path = palloc(sizeof(*fctx->path));
 		fctx->stat = palloc(sizeof(*fctx->stat));
@@ -636,10 +638,10 @@ pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok, bool
 				ereport(ERROR,
 						(errcode_for_file_access(),
 						 errmsg("could not open directory \"%s\": %m",
-								fctx->location)));
+								dir)));
 		}
 
-		populate_paths(fctx, dir_ok);
+		populate_paths(fctx, dir_ok ? 0 : -1);
 		funcctx->max_calls = fctx->npaths;
 		funcctx->user_fctx = fctx;
 		MemoryContextSwitchTo(oldcontext);
@@ -674,7 +676,9 @@ pg_ls_dir_files(FunctionCallInfo fcinfo, const char *dir, bool missing_ok, bool
 
 	pfree(fctx->path);
 	pfree(fctx->stat);
-	FreeDir(fctx->dirdesc);
+	FreeDir(linitial(fctx->dirdesc));
+	list_free(fctx->dirdesc);
+	list_free(fctx->location);
 	SRF_RETURN_DONE(funcctx);
 }
 
-- 
2.17.0

