OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

ini_load.cpp
Go to the documentation of this file.
1 /* $Id: ini_load.cpp 26509 2014-04-25 15:40:32Z rubidium $ */
2
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD 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, version 2.
6 * OpenTTD 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.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
9
12 #include "stdafx.h"
13 #include "core/alloc_func.hpp"
14 #include "core/mem_func.hpp"
15 #include "ini_type.h"
16 #include "string_func.h"
17
18 #include "safeguards.h"
19
26 IniItem::IniItem(IniGroup *parent, const char *name, const char *last) : next(NULL), value(NULL), comment(NULL)
27 {
28 this->name = stredup(name, last);
29 str_validate(this->name, this->name + strlen(this->name));
30
31 *parent->last_item = this;
32 parent->last_item = &this->next;
33 }
34
36 IniItem::~IniItem()
37 {
38 free(this->name);
39 free(this->value);
40 free(this->comment);
41
42 delete this->next;
43 }
44
49 void IniItem::SetValue(const char *value)
50 {
51 free(this->value);
52 this->value = stredup(value);
53 }
54
61 IniGroup::IniGroup(IniLoadFile *parent, const char *name, const char *last) : next(NULL), type(IGT_VARIABLES), item(NULL), comment(NULL)
62 {
63 this->name = stredup(name, last);
64 str_validate(this->name, this->name + strlen(this->name));
65
66 this->last_item = &this->item;
67 *parent->last_group = this;
68 parent->last_group = &this->next;
69
70 if (parent->list_group_names != NULL) {
71 for (uint i = 0; parent->list_group_names[i] != NULL; i++) {
72 if (strcmp(this->name, parent->list_group_names[i]) == 0) {
73 this->type = IGT_LIST;
74 return;
75 }
76 }
77 }
78 if (parent->seq_group_names != NULL) {
79 for (uint i = 0; parent->seq_group_names[i] != NULL; i++) {
80 if (strcmp(this->name, parent->seq_group_names[i]) == 0) {
81 this->type = IGT_SEQUENCE;
82 return;
83 }
84 }
85 }
86 }
87
89 IniGroup::~IniGroup()
90 {
91 free(this->name);
92 free(this->comment);
93
94 delete this->item;
95 delete this->next;
96 }
97
105 IniItem *IniGroup::GetItem(const char *name, bool create)
106 {
107 for (IniItem *item = this->item; item != NULL; item = item->next) {
108 if (strcmp(item->name, name) == 0) return item;
109 }
110
111 if (!create) return NULL;
112
113 /* otherwise make a new one */
114 return new IniItem(this, name, NULL);
115 }
116
120 void IniGroup::Clear()
121 {
122 delete this->item;
123 this->item = NULL;
124 this->last_item = &this->item;
125 }
126
132 IniLoadFile::IniLoadFile(const char * const *list_group_names, const char * const *seq_group_names) :
133 group(NULL),
134 comment(NULL),
135 list_group_names(list_group_names),
136 seq_group_names(seq_group_names)
137 {
138 this->last_group = &this->group;
139 }
140
142 IniLoadFile::~IniLoadFile()
143 {
144 free(this->comment);
145 delete this->group;
146 }
147
156 IniGroup *IniLoadFile::GetGroup(const char *name, size_t len, bool create_new)
157 {
158 if (len == 0) len = strlen(name);
159
160 /* does it exist already? */
161 for (IniGroup *group = this->group; group != NULL; group = group->next) {
162 if (!strncmp(group->name, name, len) && group->name[len] == 0) {
163 return group;
164 }
165 }
166
167 if (!create_new) return NULL;
168
169 /* otherwise make a new one */
170 IniGroup *group = new IniGroup(this, name, name + len - 1);
171 group->comment = stredup("\n");
172 return group;
173 }
174
179 void IniLoadFile::RemoveGroup(const char *name)
180 {
181 size_t len = strlen(name);
182 IniGroup *prev = NULL;
183 IniGroup *group;
184
185 /* does it exist already? */
186 for (group = this->group; group != NULL; prev = group, group = group->next) {
187 if (strncmp(group->name, name, len) == 0) {
188 break;
189 }
190 }
191
192 if (group == NULL) return;
193
194 if (prev != NULL) {
195 prev->next = prev->next->next;
196 if (this->last_group == &group->next) this->last_group = &prev->next;
197 } else {
198 this->group = this->group->next;
199 if (this->last_group == &group->next) this->last_group = &this->group;
200 }
201
202 group->next = NULL;
203 delete group;
204 }
205
212 void IniLoadFile::LoadFromDisk(const char *filename, Subdirectory subdir)
213 {
214 assert(this->last_group == &this->group);
215
216 char buffer[1024];
217 IniGroup *group = NULL;
218
219 char *comment = NULL;
220 uint comment_size = 0;
221 uint comment_alloc = 0;
222
223 size_t end;
224 FILE *in = this->OpenFile(filename, subdir, &end);
225 if (in == NULL) return;
226
227 end += ftell(in);
228
229 /* for each line in the file */
230 while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) {
231 char c, *s;
232 /* trim whitespace from the left side */
233 for (s = buffer; *s == ' ' || *s == '\t'; s++) {}
234
235 /* trim whitespace from right side. */
236 char *e = s + strlen(s);
237 while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
238 *e = '\0';
239
240 /* Skip comments and empty lines outside IGT_SEQUENCE groups. */
241 if ((group == NULL || group->type != IGT_SEQUENCE) && (*s == '#' || *s == ';' || *s == '\0')) {
242 uint ns = comment_size + (e - s + 1);
243 uint a = comment_alloc;
244 /* add to comment */
245 if (ns > a) {
246 a = max(a, 128U);
247 do a *= 2; while (a < ns);
248 comment = ReallocT(comment, comment_alloc = a);
249 }
250 uint pos = comment_size;
251 comment_size += (e - s + 1);
252 comment[pos + e - s] = '\n'; // comment newline
253 memcpy(comment + pos, s, e - s); // copy comment contents
254 continue;
255 }
256
257 /* it's a group? */
258 if (s[0] == '[') {
259 if (e[-1] != ']') {
260 this->ReportFileError("ini: invalid group name '", buffer, "'");
261 } else {
262 e--;
263 }
264 s++; // skip [
265 group = new IniGroup(this, s, e - 1);
266 if (comment_size != 0) {
267 group->comment = stredup(comment, comment + comment_size - 1);
268 comment_size = 0;
269 }
270 } else if (group != NULL) {
271 if (group->type == IGT_SEQUENCE) {
272 /* A sequence group, use the line as item name without further interpretation. */
273 IniItem *item = new IniItem(group, buffer, e - 1);
274 if (comment_size) {
275 item->comment = stredup(comment, comment + comment_size - 1);
276 comment_size = 0;
277 }
278 continue;
279 }
280 char *t;
281 /* find end of keyname */
282 if (*s == '\"') {
283 s++;
284 for (t = s; *t != '\0' && *t != '\"'; t++) {}
285 if (*t == '\"') *t = ' ';
286 } else {
287 for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {}
288 }
289
290 /* it's an item in an existing group */
291 IniItem *item = new IniItem(group, s, t - 1);
292 if (comment_size != 0) {
293 item->comment = stredup(comment, comment + comment_size - 1);
294 comment_size = 0;
295 }
296
297 /* find start of parameter */
298 while (*t == '=' || *t == ' ' || *t == '\t') t++;
299
300 bool quoted = (*t == '\"');
301 /* remove starting quotation marks */
302 if (*t == '\"') t++;
303 /* remove ending quotation marks */
304 e = t + strlen(t);
305 if (e > t && e[-1] == '\"') e--;
306 *e = '\0';
307
308 /* If the value was not quoted and empty, it must be NULL */
309 item->value = (!quoted && e == t) ? NULL : stredup(t);
310 if (item->value != NULL) str_validate(item->value, item->value + strlen(item->value));
311 } else {
312 /* it's an orphan item */
313 this->ReportFileError("ini: '", buffer, "' outside of group");
314 }
315 }
316
317 if (comment_size > 0) {
318 this->comment = stredup(comment, comment + comment_size - 1);
319 comment_size = 0;
320 }
321
322 free(comment);
323 fclose(in);
324 }
325