From f6adcb2403ce87b3f761fdad82fab4061fa5be0d Mon Sep 17 00:00:00 2001
From: Nicolas Pope <nwpope@utu.fi>
Date: Sun, 14 Apr 2019 09:03:13 +0300
Subject: [PATCH] Improve config file search process and generalise

---
 common/cpp/include/ftl/configuration.hpp |  7 ++
 common/cpp/src/configuration.cpp         | 91 ++++++++++++++++++++++--
 2 files changed, 94 insertions(+), 4 deletions(-)

diff --git a/common/cpp/include/ftl/configuration.hpp b/common/cpp/include/ftl/configuration.hpp
index df32e2294..b4b0e6eaa 100644
--- a/common/cpp/include/ftl/configuration.hpp
+++ b/common/cpp/include/ftl/configuration.hpp
@@ -4,11 +4,18 @@
 #include <nlohmann/json.hpp>
 #include <string>
 #include <vector>
+#include <optional>
 
 namespace ftl {
 
 extern nlohmann::json config;
 
+bool is_directory(const std::string &path);
+bool is_file(const std::string &path);
+bool create_directory(const std::string &path);
+
+std::optional<std::string> locateFile(const std::string &name, const std::vector<std::string> &paths);
+
 std::vector<std::string> configure(int argc, char **argv, const std::string &app);
 
 };
diff --git a/common/cpp/src/configuration.cpp b/common/cpp/src/configuration.cpp
index ccae3c756..4055e48a5 100644
--- a/common/cpp/src/configuration.cpp
+++ b/common/cpp/src/configuration.cpp
@@ -1,6 +1,14 @@
 #include <glog/logging.h>
 #include <ftl/config.h>
 
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
 #include <nlohmann/json.hpp>
 #include <ftl/configuration.hpp>
 
@@ -14,6 +22,9 @@ using std::ifstream;
 using std::string;
 using std::map;
 using std::vector;
+using std::optional;
+using ftl::is_file;
+using ftl::is_directory;
 
 // Store loaded configuration
 namespace ftl {
@@ -22,6 +33,67 @@ json config;
 
 using ftl::config;
 
+bool ftl::is_directory(const std::string &path) {
+#ifdef WIN32
+	DWORD attrib = GetFileAttributesA(path.c_str());
+	if (attrib == INVALID_FILE_ATTRIBUTES) return false;
+	else return (attrib & FILE_ATTRIBUTE_DIRECTORY);
+#else
+	struct stat s;
+	if (::stat(path.c_str(), &s) == 0) {
+		return S_ISDIR(s.st_mode);
+	} else {
+		return false;
+	}
+#endif
+}
+
+bool ftl::is_file(const std::string &path) {
+#ifdef WIN32
+	DWORD attrib = GetFileAttributesA(path.c_str());
+	if (attrib == INVALID_FILE_ATTRIBUTES) return false;
+	else return !(attrib & FILE_ATTRIBUTE_DIRECTORY);
+#else
+	struct stat s;
+	if (::stat(path.c_str(), &s) == 0) {
+		return S_ISREG(s.st_mode);
+	} else {
+		return false;
+	}
+#endif
+}
+
+bool ftl::create_directory(const std::string &path) {
+#ifdef WIN32
+	// TODO(nick)
+#else
+	if (!is_directory(path)) {
+		int err = ::mkdir(path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+		return err != -1;
+	}
+	return true;
+#endif
+}
+
+optional<string> locateFile(const string &name, const vector<string> &paths) {	
+	for (auto p : paths) {
+		if (is_directory(p)) {
+			if (is_file(p+"/"+name)) {
+				return p+"/"+name;
+			}
+		} else if (p.size() >= name.size() && 
+				p.compare(p.size() - name.size(), name.size(), name) == 0 &&
+				is_file(p)) {
+			return p;
+		}
+	}
+	
+	if (is_file("./"+name)) return "./"+name;
+	if (is_file(string(FTL_LOCAL_CONFIG_ROOT) +"/"+ name)) return string(FTL_LOCAL_CONFIG_ROOT) +"/"+ name;
+	if (is_file(string(FTL_GLOBAL_CONFIG_ROOT) +"/"+ name)) return string(FTL_GLOBAL_CONFIG_ROOT) +"/"+ name;
+	return {};
+}
+
 /**
  * Find and load a JSON configuration file
  */
@@ -30,10 +102,15 @@ static bool findConfiguration(const string &file, const vector<string> &paths,
 	ifstream i;
 	
 	if (file != "") i.open(file);
-	if (!i.is_open()) i.open("./config.json");
-	if (!i.is_open()) i.open(FTL_LOCAL_CONFIG_ROOT "/config.json");
-	if (!i.is_open()) i.open(FTL_GLOBAL_CONFIG_ROOT "/config.json");
+
+	if (!i.is_open()) {
+		auto f = locateFile("config.json", paths);
+		if (!f) return false;
+		i.open(*f);
+	}
+	
 	if (!i.is_open()) return false;
+	
 	i >> config;
 	config = config[app];
 	return true;
@@ -53,7 +130,13 @@ static map<string, string> read_options(char ***argv, int *argc) {
 		if ((p = cmd.find("=")) == string::npos) {
 			opts[cmd.substr(2)] = "true";
 		} else {
-			opts[cmd.substr(2, p-2)] = cmd.substr(p+1);
+			auto val = cmd.substr(p+1);
+			if (std::isdigit(val[0]) || val == "true" || val == "false" || val == "null") {
+				opts[cmd.substr(2, p-2)] = val;
+			} else {
+				if (val[0] == '\\') opts[cmd.substr(2, p-2)] = val;
+				else opts[cmd.substr(2, p-2)] = "\""+val+"\"";
+			}
 		}
 
 		(*argc)--;
-- 
GitLab