src/config.cpp

Tue, 21 May 2013 19:07:38 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Tue, 21 May 2013 19:07:38 +0300
changeset 239
ea09eeba1c2b
parent 238
3b35f502a6c7
child 286
7a562bf3d829
permissions
-rw-r--r--

Use a C-array for config too for the same reasons as with actions

/*
 *  LDForge: LDraw parts authoring CAD
 *  Copyright (C) 2013 Santeri Piippo
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <errno.h>
#include <time.h>
#include <QDir>
#include "common.h"
#include "config.h"
#include "misc.h"
#include "gui.h"

config* g_configPointers[MAX_CONFIG];
static ushort g_cfgPointerCursor = 0;

// =============================================================================
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",
	"Key sequence",
};

// =============================================================================
// Load the configuration from file
bool config::load () {
	printf ("config::load: Loading configuration file...\n");
	printf ("config::load: Path to configuration is %s\n", filepath ().chars ());
	
	// Locale must be disabled for atof
	setlocale (LC_NUMERIC, "C");
	
	FILE* fp = fopen (filepath ().chars(), "r");
	char linedata[MAX_INI_LINE];
	char* line;
	size_t ln = 0;
	
	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.
		
		// 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);
		
		// Find the config entry for this.
		config* cfg = null;
		for (config* i : g_configPointers) {
			if (!i)
				break;
			
			if (entry == i->name)
				cfg = i;
		}
		
		if (!cfg) {
			fprintf (stderr, "unknown config `%s`\n", entry.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 (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_keyseq:
			static_cast<keyseqconfig*> (cfg)->value = keyseq::fromString (valstring.chars ());
			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 = dynafmt (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 () {
	// The function will write floats, disable the locale now so that they
	// are written properly.
	setlocale (LC_NUMERIC, "C");
	
	// If the directory doesn't exist, create it now.
	if (QDir (dirpath ()).exists () == false) {
		fprintf (stderr, "Creating config path %s...\n", dirpath().chars());
		if (!QDir ().mkpath (dirpath ().chars ())) {
			critical ("Failed to create the directory. Configuration cannot be saved!\n");
			return false; // Couldn't create directory
		}
	}
	
	FILE* fp = fopen (filepath ().chars (), "w");
	printf ("writing cfg to %s\n", filepath().chars());
	
	if (!fp) {
		critical (fmt ("Cannot save configuration, cannot 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);
	
	for (config* cfg : g_configPointers) {
		if (!cfg)
			break;
		
		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 ("%s", ftoa (static_cast<floatconfig*> (cfg)->value).c ());
			break;
		
		case CONFIG_bool:
			valstring = (static_cast<boolconfig*> (cfg)->value) ? "true" : "false";
			break;
		
		case CONFIG_keyseq:
			valstring = static_cast<keyseqconfig*> (cfg)->value.toString ();
			break;
		
		default:
			break;
		}
		
		const char* defstr = (cfg->getType() != CONFIG_keyseq) ? cfg->defaultstring :
			qchars (static_cast<keyseqconfig*> (cfg)->defval.toString ());
		
		// Write the entry now.
		writef (fp, "\n# [%s] default: %s\n", g_ConfigTypeNames[cfg->getType()], defstr);
		writef (fp, "%s=%s\n", cfg->name, valstring.chars());
	}
	
	fclose (fp);
	return true;
}

// =============================================================================
void config::reset () {
	for (config* cfg : g_configPointers) {
		if (!cfg)
			break;
		
		cfg->resetValue ();
	}
}

// =============================================================================
str config::filepath () {
	str path;
	path.format ("%s%s.cfg", dirpath ().chars (),
		str (APPNAME).lower ().chars ());
	return path;
}

// =============================================================================
str config::dirpath () {
#ifndef _WIN32
	return fmt ("%s" DIRSLASH ".%s" DIRSLASH,
		qchars (QDir::homePath ()),
		str (APPNAME).lower ().chars ());
#else
	return fmt ("%s" DIRSLASH APPNAME DIRSLASH, qchars (QDir::homePath ()));
#endif // _WIN32
}

void addConfig (config* ptr) {
	if (g_cfgPointerCursor == 0)
		memset (g_configPointers, 0, sizeof g_configPointers);
	
	assert (g_cfgPointerCursor < MAX_CONFIG);
	g_configPointers[g_cfgPointerCursor++] = ptr;
}

mercurial