OpenTTD Source: src/ini_load.cpp Source File

OpenTTD Source 14.0-beta1

ini_load.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
6 */
7
10 #include "stdafx.h"
11 #include "core/alloc_func.hpp"
12 #include "core/mem_func.hpp"
13 #include "ini_type.h"
14 #include "string_func.h"
15
16 #include "safeguards.h"
17
23 IniItem::IniItem(const std::string &name)
24 {
25 this->name = StrMakeValid(name);
26 }
27
32 void IniItem::SetValue(const std::string_view value)
33 {
34 this->value.emplace(value);
35 }
36
42 IniGroup::IniGroup(const std::string &name, IniGroupType type) : type(type), comment("\n")
43 {
44 this->name = StrMakeValid(name);
45 }
46
52 const IniItem *IniGroup::GetItem(const std::string &name) const
53 {
54 for (const IniItem &item : this->items) {
55 if (item.name == name) return &item;
56 }
57
58 return nullptr;
59 }
60
66 IniItem &IniGroup::GetOrCreateItem(const std::string &name)
67 {
68 for (IniItem &item : this->items) {
69 if (item.name == name) return item;
70 }
71
72 /* Item doesn't exist, make a new one. */
73 return this->CreateItem(name);
74 }
75
81 IniItem &IniGroup::CreateItem(const std::string &name)
82 {
83 return this->items.emplace_back(name);
84 }
85
90 void IniGroup::RemoveItem(const std::string &name)
91 {
92 this->items.remove_if([&name](const IniItem &item) { return item.name == name; });
93 }
94
98 void IniGroup::Clear()
99 {
100 this->items.clear();
101 }
102
108 IniLoadFile::IniLoadFile(const IniGroupNameList &list_group_names, const IniGroupNameList &seq_group_names) :
109 list_group_names(list_group_names),
110 seq_group_names(seq_group_names)
111 {
112 }
113
119 const IniGroup *IniLoadFile::GetGroup(const std::string &name) const
120 {
121 for (const IniGroup &group : this->groups) {
122 if (group.name == name) return &group;
123 }
124
125 return nullptr;
126 }
127
133 IniGroup *IniLoadFile::GetGroup(const std::string &name)
134 {
135 for (IniGroup &group : this->groups) {
136 if (group.name == name) return &group;
137 }
138
139 return nullptr;
140 }
141
147 IniGroup &IniLoadFile::GetOrCreateGroup(const std::string &name)
148 {
149 for (IniGroup &group : this->groups) {
150 if (group.name == name) return group;
151 }
152
153 /* Group doesn't exist, make a new one. */
154 return this->CreateGroup(name);
155 }
156
162 IniGroup &IniLoadFile::CreateGroup(const std::string &name)
163 {
164 IniGroupType type = IGT_VARIABLES;
165 if (std::find(this->list_group_names.begin(), this->list_group_names.end(), name) != this->list_group_names.end()) type = IGT_LIST;
166 if (std::find(this->seq_group_names.begin(), this->seq_group_names.end(), name) != this->seq_group_names.end()) type = IGT_SEQUENCE;
167
168 return this->groups.emplace_back(name, type);
169 }
170
175 void IniLoadFile::RemoveGroup(const std::string &name)
176 {
177 size_t len = name.length();
178 this->groups.remove_if([&name, &len](const IniGroup &group) { return group.name.compare(0, len, name) == 0; });
179 }
180
187 void IniLoadFile::LoadFromDisk(const std::string &filename, Subdirectory subdir)
188 {
189 assert(this->groups.empty());
190
191 char buffer[1024];
192 IniGroup *group = nullptr;
193
194 char *comment = nullptr;
195 uint comment_size = 0;
196 uint comment_alloc = 0;
197
198 size_t end;
199 FILE *in = this->OpenFile(filename, subdir, &end);
200 if (in == nullptr) return;
201
202 end += ftell(in);
203
204 /* for each line in the file */
205 while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) {
206 char c, *s;
207 /* trim whitespace from the left side */
208 for (s = buffer; *s == ' ' || *s == '\t'; s++) {}
209
210 /* trim whitespace from right side. */
211 char *e = s + strlen(s);
212 while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
213 *e = '\0';
214
215 /* Skip comments and empty lines outside IGT_SEQUENCE groups. */
216 if ((group == nullptr || group->type != IGT_SEQUENCE) && (*s == '#' || *s == ';' || *s == '\0')) {
217 uint ns = comment_size + (e - s + 1);
218 uint a = comment_alloc;
219 /* add to comment */
220 if (ns > a) {
221 a = std::max(a, 128U);
222 do a *= 2; while (a < ns);
223 comment = ReallocT(comment, comment_alloc = a);
224 }
225 uint pos = comment_size;
226 comment_size += (e - s + 1);
227 comment[pos + e - s] = '\n'; // comment newline
228 memcpy(comment + pos, s, e - s); // copy comment contents
229 continue;
230 }
231
232 /* it's a group? */
233 if (s[0] == '[') {
234 if (e[-1] != ']') {
235 this->ReportFileError("ini: invalid group name '", buffer, "'");
236 } else {
237 e--;
238 }
239 s++; // skip [
240 group = &this->CreateGroup(std::string(s, e - s));
241 if (comment_size != 0) {
242 group->comment.assign(comment, comment_size);
243 comment_size = 0;
244 }
245 } else if (group != nullptr) {
246 if (group->type == IGT_SEQUENCE) {
247 /* A sequence group, use the line as item name without further interpretation. */
248 IniItem &item = group->CreateItem(std::string(buffer, e - buffer));
249 if (comment_size) {
250 item.comment.assign(comment, comment_size);
251 comment_size = 0;
252 }
253 continue;
254 }
255 char *t;
256 /* find end of keyname */
257 if (*s == '\"') {
258 s++;
259 for (t = s; *t != '\0' && *t != '\"'; t++) {}
260 if (*t == '\"') *t = ' ';
261 } else {
262 for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {}
263 }
264
265 /* it's an item in an existing group */
266 IniItem &item = group->CreateItem(std::string(s, t - s));
267 if (comment_size != 0) {
268 item.comment.assign(comment, comment_size);
269 comment_size = 0;
270 }
271
272 /* find start of parameter */
273 while (*t == '=' || *t == ' ' || *t == '\t') t++;
274
275 bool quoted = (*t == '\"');
276 /* remove starting quotation marks */
277 if (*t == '\"') t++;
278 /* remove ending quotation marks */
279 e = t + strlen(t);
280 if (e > t && e[-1] == '\"') e--;
281 *e = '\0';
282
283 /* If the value was not quoted and empty, it must be nullptr */
284 if (!quoted && e == t) {
285 item.value.reset();
286 } else {
287 item.value = StrMakeValid(std::string_view(t));
288 }
289 } else {
290 /* it's an orphan item */
291 this->ReportFileError("ini: '", buffer, "' outside of group");
292 }
293 }
294
295 if (comment_size > 0) {
296 this->comment.assign(comment, comment_size);
297 comment_size = 0;
298 }
299
300 free(comment);
301 fclose(in);
302 }
303
IniItem::SetValue
void SetValue(const std::string_view value)
Replace the current value with another value.
Definition: ini_load.cpp:32
IniLoadFile::comment
std::string comment
last comment in file
Definition: ini_type.h:54
mem_func.hpp
IGT_LIST
@ IGT_LIST
A list of values, separated by and terminated by the next group block.
Definition: ini_type.h:18
IniItem
A single "line" in an ini file.
Definition: ini_type.h:23
IniGroup::GetItem
const IniItem * GetItem(const std::string &name) const
Get the item with the given name.
Definition: ini_load.cpp:52
IniGroup
A group within an ini file.
Definition: ini_type.h:34
StrMakeValid
static void StrMakeValid(T &dst, const char *str, const char *last, StringValidationSettings settings)
Copies the valid (UTF-8) characters from str up to last to the dst.
Definition: string.cpp:114
IniGroup::RemoveItem
void RemoveItem(const std::string &name)
Remove the item with the given name.
Definition: ini_load.cpp:90
IniLoadFile::GetOrCreateGroup
IniGroup & GetOrCreateGroup(const std::string &name)
Get the group with the given name, and if it doesn't exist create a new group.
Definition: ini_load.cpp:147
IniGroup::items
std::list< IniItem > items
all items in the group
Definition: ini_type.h:35
IniLoadFile::seq_group_names
const IniGroupNameList seq_group_names
list of group names that are sequences.
Definition: ini_type.h:56
IniGroup::Clear
void Clear()
Clear all items in the group.
Definition: ini_load.cpp:98
IniLoadFile::list_group_names
const IniGroupNameList list_group_names
list of group names that are lists
Definition: ini_type.h:55
ReallocT
T * ReallocT(T *t_ptr, size_t num_elements)
Simplified reallocation function that allocates the specified number of elements of the given type.
Definition: alloc_func.hpp:111
IniLoadFile::RemoveGroup
void RemoveGroup(const std::string &name)
Remove the group with the given name.
Definition: ini_load.cpp:175
IniItem::IniItem
IniItem(const std::string &name)
Construct a new in-memory item of an Ini file.
Definition: ini_load.cpp:23
IniLoadFile::CreateGroup
IniGroup & CreateGroup(const std::string &name)
Create an group with the given name.
Definition: ini_load.cpp:162
IniLoadFile::OpenFile
virtual FILE * OpenFile(const std::string &filename, Subdirectory subdir, size_t *size)=0
Open the INI file.
IniGroup::GetOrCreateItem
IniItem & GetOrCreateItem(const std::string &name)
Get the item with the given name, and if it doesn't exist create a new item.
Definition: ini_load.cpp:66
free
void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:379
IniItem::value
std::optional< std::string > value
The value of this item.
Definition: ini_type.h:25
safeguards.h
IniLoadFile::GetGroup
const IniGroup * GetGroup(const std::string &name) const
Get the group with the given name.
Definition: ini_load.cpp:119
IniGroupType
IniGroupType
Types of groups.
Definition: ini_type.h:16
IniGroup::type
IniGroupType type
type of group
Definition: ini_type.h:36
IGT_VARIABLES
@ IGT_VARIABLES
Values of the form "landscape = hilly".
Definition: ini_type.h:17
stdafx.h
string_func.h
IniGroup::comment
std::string comment
comment for group
Definition: ini_type.h:38
IniGroup::name
std::string name
name of group
Definition: ini_type.h:37
alloc_func.hpp
IniLoadFile::ReportFileError
virtual void ReportFileError(const char *const pre, const char *const buffer, const char *const post)=0
Report an error about the file contents.
IniLoadFile::IniLoadFile
IniLoadFile(const IniGroupNameList &list_group_names={}, const IniGroupNameList &seq_group_names={})
Construct a new in-memory Ini file representation.
Definition: ini_load.cpp:108
IniGroup::CreateItem
IniItem & CreateItem(const std::string &name)
Create an item with the given name.
Definition: ini_load.cpp:81
IGT_SEQUENCE
@ IGT_SEQUENCE
A list of uninterpreted lines, terminated by the next group block.
Definition: ini_type.h:19
IniGroup::IniGroup
IniGroup(const std::string &name, IniGroupType type)
Construct a new in-memory group of an Ini file.
Definition: ini_load.cpp:42
Subdirectory
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition: fileio_type.h:108
IniLoadFile::groups
std::list< IniGroup > groups
all groups in the ini
Definition: ini_type.h:53
IniItem::name
std::string name
The name of this item.
Definition: ini_type.h:24
IniItem::comment
std::string comment
The comment associated with this item.
Definition: ini_type.h:26
IniLoadFile::LoadFromDisk
void LoadFromDisk(const std::string &filename, Subdirectory subdir)
Load the Ini file's data from the disk.
Definition: ini_load.cpp:187
ini_type.h