Tue, 21 May 2013 19:07:38 +0300
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; }