config.cpp

changeset 183
f1b8cb53d2a2
parent 182
9374fea8f77f
child 184
fae3bc9ce319
equal deleted inserted replaced
182:9374fea8f77f 183:f1b8cb53d2a2
1 /*
2 * LDForge: LDraw parts authoring CAD
3 * Copyright (C) 2013 Santeri Piippo
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <time.h>
23 #include <QDir>
24 #include "common.h"
25 #include "str.h"
26 #include "config.h"
27
28 std::vector<config*> g_pConfigPointers;
29
30 // =============================================================================
31 const char* g_WeekdayNames[7] = {
32 "Sunday",
33 "Monday",
34 "Tuesday",
35 "Wednesday",
36 "Thursday",
37 "Friday",
38 "Saturday",
39 };
40
41 // =============================================================================
42 static const char* g_MonthNames[12] = {
43 "Januray",
44 "February",
45 "March",
46 "April",
47 "May",
48 "June",
49 "July",
50 "August",
51 "September",
52 "October",
53 "November"
54 "December",
55 };
56
57 static const char* g_ConfigTypeNames[] = {
58 "None",
59 "Integer",
60 "String",
61 "Float",
62 "Boolean",
63 "Key sequence",
64 };
65
66 // =============================================================================
67 // Load the configuration from file
68 bool config::load () {
69 printf ("config::load: loading configuration file.\n");
70
71 // Locale must be disabled for atof
72 setlocale (LC_NUMERIC, "C");
73
74 FILE* fp = fopen (filepath().chars(), "r");
75 char linedata[MAX_INI_LINE];
76 char* line;
77 size_t ln = 0;
78
79 if (!fp)
80 return false; // can't open for reading
81
82 // Read the values.
83 while (fgets (linedata, MAX_INI_LINE, fp)) {
84 ln++;
85 line = linedata;
86
87 while (*line != 0 && (*line <= 32 || *line >= 127))
88 line++; // Skip junk
89
90 if (*line == '\0' || line[0] == '#')
91 continue; // Empty line or comment.
92
93 // Find the equals sign.
94 char* equals = strchr (line, '=');
95 if (!equals) {
96 fprintf (stderr, "couldn't find `=` sign in entry `%s`\n", line);
97 continue;
98 }
99
100 str entry = str (line).substr (0, equals - line);
101
102 // Find the config entry for this.
103 config* cfg = null;
104 for (config* i : g_pConfigPointers)
105 if (entry == i->name)
106 cfg = i;
107
108 if (!cfg) {
109 fprintf (stderr, "unknown config `%s`\n", entry.chars());
110 continue;
111 }
112
113 str valstring = str (line).substr (equals - line + 1, -1);
114
115 // Trim the crap off the end
116 while (~valstring) {
117 char c = valstring[~valstring - 1];
118 if (c <= 32 || c >= 127)
119 valstring -= 1;
120 else
121 break;
122 }
123
124 switch (cfg->getType()) {
125 case CONFIG_int:
126 static_cast<intconfig*> (cfg)->value = atoi (valstring.chars());
127 break;
128
129 case CONFIG_str:
130 static_cast<strconfig*> (cfg)->value = valstring;
131 break;
132
133 case CONFIG_float:
134 static_cast<floatconfig*> (cfg)->value = atof (valstring.chars());
135 break;
136
137 case CONFIG_bool:
138 {
139 bool& val = static_cast<boolconfig*> (cfg)->value;
140
141 if (+valstring == "TRUE" || valstring == "1")
142 val = true;
143 else if (+valstring == "FALSE" || valstring == "0")
144 val = false;
145 break;
146 }
147
148 case CONFIG_keyseq:
149 static_cast<keyseqconfig*> (cfg)->value = keyseq::fromString (valstring.chars ());
150 break;
151
152 default:
153 break;
154 }
155 }
156
157 fclose (fp);
158 return true;
159 }
160
161 // =============================================================================
162 // Write a given formatted string to the given file stream
163 static size_t writef (FILE* fp, const char* fmt, ...) {
164 va_list va;
165
166 va_start (va, fmt);
167 char* buf = vdynformat (fmt, va, 256);
168 va_end (va);
169
170 size_t len = fwrite (buf, 1, strlen (buf), fp);
171 delete[] buf;
172
173 return len;
174 }
175
176 // =============================================================================
177 // Save the configuration to disk
178 bool config::save () {
179 // The function will write floats, disable the locale now so that they
180 // are written properly.
181 setlocale (LC_NUMERIC, "C");
182
183 // If the directory doesn't exist, create it now.
184 if (QDir (dirpath ()).exists () == nope) {
185 fprintf (stderr, "Creating config path %s...\n", dirpath().chars());
186 if (!QDir ().mkpath (dirpath().chars())) {
187 logf (LOG_Warning, "Failed to create the directory. Configuration cannot be saved!\n");
188 return false; // Couldn't create directory
189 }
190 }
191
192 FILE* fp = fopen (filepath().chars(), "w");
193 printf ("writing cfg to %s\n", filepath().chars());
194
195 if (!fp) {
196 printf ("Couldn't open %s for writing\n", filepath().chars());
197 return false;
198 }
199
200 const time_t curtime = time (NULL);
201 const struct tm* timeinfo = localtime (&curtime);
202 const char* daysuffix =
203 (timeinfo->tm_mday % 10 == 1) ? "st" :
204 (timeinfo->tm_mday % 10 == 2) ? "nd" :
205 (timeinfo->tm_mday % 10 == 3) ? "rd" : "th";
206
207 writef (fp, "# Configuration file for " APPNAME "\n");
208 writef (fp, "# Written on %s, %s %d%s %d %.2d:%.2d:%.2d\n",
209 g_WeekdayNames[timeinfo->tm_wday], g_MonthNames[timeinfo->tm_mon],
210 timeinfo->tm_mday, daysuffix, timeinfo->tm_year + 1900,
211 timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
212
213 for (config* cfg : g_pConfigPointers) {
214 str valstring;
215 switch (cfg->getType()) {
216 case CONFIG_int:
217 valstring.format ("%d", static_cast<intconfig*> (cfg)->value);
218 break;
219 case CONFIG_str:
220 valstring = static_cast<strconfig*> (cfg)->value;
221 break;
222 case CONFIG_float:
223 valstring.format ("%f", static_cast<floatconfig*> (cfg)->value);
224
225 // Trim any trailing zeros
226 if (valstring.first (".") != -1) {
227 while (valstring[~valstring - 1] == '0')
228 valstring -= 1;
229
230 // But don't trim the only one out...
231 if (valstring[~valstring - 1] == '.')
232 valstring += '0';
233 }
234
235 break;
236 case CONFIG_bool:
237 valstring = (static_cast<boolconfig*> (cfg)->value) ? "true" : "false";
238 break;
239 case CONFIG_keyseq:
240 valstring = static_cast<keyseqconfig*> (cfg)->value.toString ();
241 break;
242 default:
243 break;
244 }
245
246 const char* sDefault = (cfg->getType() != CONFIG_keyseq) ? cfg->defaultstring :
247 static_cast<keyseqconfig*> (cfg)->defval.toString ().toUtf8 ().constData ();
248
249 // Write the entry now.
250 writef (fp, "\n# [%s] default: %s\n", g_ConfigTypeNames[cfg->getType()], sDefault);
251 writef (fp, "%s=%s\n", cfg->name, valstring.chars());
252 }
253
254 fclose (fp);
255 return true;
256 }
257
258 // =============================================================================
259 void config::reset () {
260 for (size_t i = 0; i < NUM_CONFIG; i++)
261 g_pConfigPointers[i]->resetValue ();
262 }
263
264 // =============================================================================
265 str config::filepath () {
266 str path;
267 path.format ("%s%s.cfg", dirpath ().chars (),
268 str (APPNAME).tolower ().chars ());
269 return path;
270 }
271
272 // =============================================================================
273 str config::dirpath () {
274 return fmt ("%s/.%s/", qchars (QDir::homePath ()),
275 str (APPNAME).tolower ().chars ());
276 }

mercurial