config.cpp

changeset 0
c51cce84a9ac
child 29
55406ce7446e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/config.cpp	Fri Mar 15 20:11:18 2013 +0200
@@ -0,0 +1,351 @@
+#include <stdio.h>
+// #include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include "str.h"
+#include "config.h"
+
+#ifdef CONFIG_WITH_QT
+#include <QDir>
+#endif // CONFIG_WITH_QT
+
+// =============================================================================
+// Define the configs
+#define CFG(TYPE, SECT, NAME, DESCR, DEFAULT) \
+	TYPE##config SECT##_##NAME (CFGSECTNAME (SECT), DESCR, \
+		DEFAULT, #NAME, #SECT "_" #NAME, #TYPE, #DEFAULT);
+
+#define SECT(...)
+ #include "cfgdef.h"
+#undef CFG
+#undef SECT
+
+// =============================================================================
+config* config::pointers[] = {
+#define CFG(TYPE, SECT, NAME, DESCR, DEFAULT) &SECT##_##NAME,
+#define SECT(...)
+ #include "cfgdef.h"
+#undef CFG
+#undef SECT
+};
+
+// =============================================================================
+const char* config::sections[] = {
+#define CFG(...)
+#define SECT(A,B) #A,
+ #include "cfgdef.h"
+#undef CFG
+#undef SECT
+};
+
+// =============================================================================
+const char* config::sectionNames[] = {
+#define CFG(...)
+#define SECT(A,B) #B,
+ #include "cfgdef.h"
+#undef CFG
+#undef SECT
+};
+
+// =============================================================================
+const char* g_WeekdayNames[7] = {
+	"Sunday",
+	"Monday",
+	"Tuesday",
+	"Wednesday",
+	"Thursday",
+	"Friday",
+	"Saturday",
+};
+
+// =============================================================================
+static const char* g_MonthNames[12] = {
+	"Januray",
+	"February",
+	"March",
+	"April",
+	"May",
+	"June",
+	"July",
+	"August",
+	"September",
+	"October",
+	"November"
+	"December",
+};
+
+static const char* g_ConfigTypeNames[] = {
+	"None",
+	"Integer",
+	"String",
+	"Float",
+	"Boolean",
+	"Color",
+};
+
+// =============================================================================
+// Load the configuration from file
+bool config::load () {
+	FILE* fp = fopen (filepath().chars(), "r");
+	char linedata[MAX_INI_LINE];
+	char* line;
+	size_t ln = 0;
+	configsection_e section = NO_CONFIG_SECTION;
+	
+	if (!fp)
+		return false; // can't open for reading
+	
+	// Read the values.
+	while (fgets (linedata, MAX_INI_LINE, fp)) {
+		ln++;
+		line = linedata;
+		
+		while (*line != 0 && (*line <= 32 || *line >= 127))
+			line++; // Skip junk
+		
+		if (*line == '\0' || line[0] == '#')
+			continue; // Empty line or comment.
+		
+		if (line[0] == '[') {
+			// Section
+			char* endbracket = strchr (line, ']');
+			
+			if (!endbracket) {
+				fprintf (stderr, "badly formed section: %s", line);
+				continue;
+			}
+			
+			str sectionName = str (line).substr (1, endbracket - line);
+			const configsection_e oldsection = section;
+			section = NO_CONFIG_SECTION;
+			
+			// Find the section
+			for (unsigned i = 0; i < NUM_ConfigSections && section == NO_CONFIG_SECTION; i++)
+				if (sectionName.compare (sectionNames[i]) == 0)
+					section = (configsection_e)i;
+			
+			if (section == NO_CONFIG_SECTION) {
+				fprintf (stderr, "unknown config section `%s`\n", sectionName.chars());
+				section = oldsection;
+			}
+			
+			continue;
+		}
+		
+		// Find the equals sign.
+		char* equals = strchr (line, '=');
+		if (!equals) {
+			fprintf (stderr, "couldn't find `=` sign in entry `%s`\n", line);
+			continue;
+		}
+		
+		str entry = str (line).substr (0, equals - line);
+		
+		str configname;
+		configname.format ("%s_%s", sections[section], entry.chars ());
+		
+		// Find the config entry for this.
+		config* cfg = NULL;
+		for (size_t i = 0; i < NUM_CONFIG && !cfg; i++)
+			if (configname.compare (pointers[i]->fullname) == 0)
+				cfg = pointers[i];
+		
+		if (!cfg) {
+			fprintf (stderr, "unknown config `%s`\n", configname.chars());
+			continue;
+		}
+		
+		str valstring = str (line).substr (equals - line + 1, -1);
+		
+		// Trim the crap off the end
+		while (~valstring) {
+			char c = valstring[~valstring - 1];
+			if (c <= 32 || c >= 127)
+				valstring -= 1;
+			else
+				break;
+		}
+		
+		switch (const_cast<config*> (cfg)->getType()) {
+		case CONFIG_int:
+			static_cast<intconfig*> (cfg)->value = atoi (valstring.chars());
+			break;
+		case CONFIG_str:
+			static_cast<strconfig*> (cfg)->value = valstring;
+			break;
+		case CONFIG_float:
+			static_cast<floatconfig*> (cfg)->value = atof (valstring.chars());
+			break;
+		case CONFIG_bool:
+		{
+			bool& val = static_cast<boolconfig*> (cfg)->value;
+			
+			if (+valstring == "TRUE" || valstring == "1")
+				val = true;
+			else if (+valstring == "FALSE" || valstring == "0")
+				val = false;
+			break;
+		}
+		case CONFIG_color:
+			static_cast<colorconfig*> (cfg)->value.parseFromString (valstring);
+			break;
+		default:
+			break;
+		}
+	}
+	
+	fclose (fp);
+	return true;
+}
+
+// =============================================================================
+// Write a given formatted string to the given file stream
+static size_t writef (FILE* fp, const char* fmt, ...) {
+	va_list va;
+	
+	va_start (va, fmt);
+	char* buf = vdynformat (fmt, va, 256);
+	va_end (va);
+	
+	size_t len = fwrite (buf, 1, strlen (buf), fp);
+	delete[] buf;
+	
+	return len;
+}
+
+// =============================================================================
+// Save the configuration to disk
+bool config::save () {
+#ifdef APPNAME
+	#ifdef CONFIG_WITH_QT
+		// If the directory doesn't exist, create it now.
+		if (!QDir (dirpath().chars()).exists ()) {
+			fprintf (stderr, "Creating config path %s...\n", dirpath().chars());
+			if (!QDir ().mkpath (dirpath().chars())) {
+				fprintf (stderr, "Failed to create the directory. Configuration cannot be saved!\n");
+				return false; // Couldn't create directory
+			}
+		}
+	#else
+		#warning Need QT to check for directories. Config will not be able
+		#warning to save properly if ~/.APPNAME/ does not exist.
+	#endif // CONFIG_WITH_QT
+#endif // CONFIG_DIRECTORY
+	
+	FILE* fp = fopen (filepath().chars(), "w");
+	printf ("writing cfg to %s\n", filepath().chars());
+	
+	if (!fp) {
+		printf ("Couldn't open %s for writing\n", filepath().chars());
+		return false;
+	}
+	
+	const time_t curtime = time (NULL);
+	const struct tm* timeinfo = localtime (&curtime);
+	const char* daysuffix =
+		(timeinfo->tm_mday % 10 == 1) ? "st" :
+		(timeinfo->tm_mday % 10 == 2) ? "nd" :
+		(timeinfo->tm_mday % 10 == 3) ? "rd" : "th";
+	
+	writef (fp, "# Configuration file for " APPNAME "\n");
+	writef (fp, "# Written on %s, %s %d%s %d %.2d:%.2d:%.2d\n",
+		g_WeekdayNames[timeinfo->tm_wday], g_MonthNames[timeinfo->tm_mon],
+		timeinfo->tm_mday, daysuffix, timeinfo->tm_year + 1900,
+		timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
+	writef (fp, "\n");
+	
+	for (int i = 0; i < NUM_ConfigSections; i++) {
+		if (i > 0)
+			writef (fp, "\n");
+		
+		writef (fp, "[%s]\n", sectionNames[i]);
+		bool first = true;
+		
+		for (size_t j = 0; j < NUM_CONFIG; j++) {
+			config* cfg = pointers[j];
+			
+			if (cfg->sect != i)
+				continue;
+			
+			if (!first)
+				writef (fp, "\n");
+			
+			str valstring;
+			switch (cfg->getType()) {
+			case CONFIG_int:
+				valstring.format ("%d", static_cast<intconfig*> (cfg)->value);
+				break;
+			case CONFIG_str:
+				valstring = static_cast<strconfig*> (cfg)->value;
+				break;
+			case CONFIG_float:
+				valstring.format ("%f", static_cast<floatconfig*> (cfg)->value);
+				
+				// Trim any trailing zeros
+				if (valstring.first (".") != -1) {
+					while (valstring[~valstring - 1] == '0')
+						valstring -= 1;
+					
+					// But don't trim the only one out...
+					if (valstring[~valstring - 1] == '.')
+						valstring += '0';
+				}
+				
+				break;
+			case CONFIG_bool:
+				valstring = (static_cast<boolconfig*> (cfg)->value) ? "true" : "false";
+				break;
+			case CONFIG_color:
+				valstring = (str)(static_cast<colorconfig*> (cfg)->value);
+				break;
+			default:
+				break;
+			}
+			
+			// Write the entry now.
+			writef (fp, "# %s, default: %s\n", g_ConfigTypeNames[cfg->getType()], cfg->defaultstring);
+			writef (fp, "# %s: %s\n", cfg->fullname, cfg->description);
+			writef (fp, "%s=%s\n", cfg->name, valstring.chars());
+			first = false;
+		}
+	}
+	
+	fclose (fp);
+	return true;
+}
+
+// =============================================================================
+void config::reset () {
+	for (size_t i = 0; i < NUM_CONFIG; i++)
+		pointers[i]->resetValue ();
+}
+
+// =============================================================================
+str config::filepath () {
+#ifdef APPNAME
+	str path;
+	path.format ("%s" APPNAME ".ini", dirpath().chars());
+	return path;
+#else // APPNAME
+	return "config.ini";
+#endif // APPNAME
+}
+
+// =============================================================================
+str config::dirpath () {
+#ifdef APPNAME
+	str path;
+	
+	#ifdef CONFIG_WITH_QT
+		path = (QDir::homePath ().toStdString().c_str());
+	#else // CONFIG_WITH_QT
+		path = "~";
+	#endif // CONFIG_WITH_QT
+	
+	path += "/." APPNAME "/";
+#else
+	path = "./";
+#endif // APPNAME
+	
+	return path;
+}
\ No newline at end of file

mercurial