OpenTTD Source  20241120-master-g6d3adc6169
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(std::string_view name)
24 {
25  this->name = StrMakeValid(name);
26 }
27 
32 void IniItem::SetValue(std::string_view value)
33 {
34  this->value.emplace(value);
35 }
36 
42 IniGroup::IniGroup(std::string_view name, IniGroupType type) : type(type), comment("\n")
43 {
44  this->name = StrMakeValid(name);
45 }
46 
52 const IniItem *IniGroup::GetItem(std::string_view 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(std::string_view 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(std::string_view name)
82 {
83  return this->items.emplace_back(name);
84 }
85 
90 void IniGroup::RemoveItem(std::string_view name)
91 {
92  this->items.remove_if([&name](const IniItem &item) { return item.name == name; });
93 }
94 
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(std::string_view 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(std::string_view name)
134 {
135  for (IniGroup &group : this->groups) {
136  if (group.name == name) return &group;
137  }
138 
139  return nullptr;
140 }
141 
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(std::string_view name)
163 {
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(std::string_view 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  std::string comment;
195 
196  size_t end;
197  auto in = this->OpenFile(filename, subdir, &end);
198  if (!in.has_value()) return;
199 
200  end += ftell(*in);
201 
202  /* for each line in the file */
203  while (static_cast<size_t>(ftell(*in)) < end && fgets(buffer, sizeof(buffer), *in)) {
204  char c, *s;
205  /* trim whitespace from the left side */
206  for (s = buffer; *s == ' ' || *s == '\t'; s++) {}
207 
208  /* trim whitespace from right side. */
209  char *e = s + strlen(s);
210  while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
211  *e = '\0';
212 
213  /* Skip comments and empty lines outside IGT_SEQUENCE groups. */
214  if ((group == nullptr || group->type != IGT_SEQUENCE) && (*s == '#' || *s == ';' || *s == '\0')) {
215  comment += std::string_view(s, e - s);
216  comment += '\n'; // comment newline
217  continue;
218  }
219 
220  /* it's a group? */
221  if (s[0] == '[') {
222  if (e[-1] != ']') {
223  this->ReportFileError("ini: invalid group name '", buffer, "'");
224  } else {
225  e--;
226  }
227  s++; // skip [
228  group = &this->CreateGroup(std::string_view(s, e - s));
229  group->comment = std::move(comment);
230  } else if (group != nullptr) {
231  if (group->type == IGT_SEQUENCE) {
232  /* A sequence group, use the line as item name without further interpretation. */
233  IniItem &item = group->CreateItem(std::string_view(buffer, e - buffer));
234  item.comment = std::move(comment);
235  continue;
236  }
237  char *t;
238  /* find end of keyname */
239  if (*s == '\"') {
240  s++;
241  for (t = s; *t != '\0' && *t != '\"'; t++) {}
242  if (*t == '\"') *t = ' ';
243  } else {
244  for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {}
245  }
246 
247  /* it's an item in an existing group */
248  IniItem &item = group->CreateItem(std::string_view(s, t - s));
249  item.comment = std::move(comment);
250 
251  /* find start of parameter */
252  while (*t == '=' || *t == ' ' || *t == '\t') t++;
253 
254  bool quoted = (*t == '\"');
255  /* remove starting quotation marks */
256  if (*t == '\"') t++;
257  /* remove ending quotation marks */
258  e = t + strlen(t);
259  if (e > t && e[-1] == '\"') e--;
260  *e = '\0';
261 
262  /* If the value was not quoted and empty, it must be nullptr */
263  if (!quoted && e == t) {
264  item.value.reset();
265  } else {
266  item.value = StrMakeValid(std::string_view(t));
267  }
268  } else {
269  /* it's an orphan item */
270  this->ReportFileError("ini: '", buffer, "' outside of group");
271  }
272  }
273 
274  this->comment = std::move(comment);
275 }
276 
Functions related to the allocation of memory.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition: fileio_type.h:115
Types related to reading/writing '*.ini' files.
IniGroupType
Types of groups.
Definition: ini_type.h:16
@ IGT_SEQUENCE
A list of uninterpreted lines, terminated by the next group block.
Definition: ini_type.h:19
@ IGT_VARIABLES
Values of the form "landscape = hilly".
Definition: ini_type.h:17
@ IGT_LIST
A list of values, separated by and terminated by the next group block.
Definition: ini_type.h:18
Functions related to memory operations.
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
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:107
Functions related to low-level strings.
A group within an ini file.
Definition: ini_type.h:34
const IniItem * GetItem(std::string_view name) const
Get the item with the given name.
Definition: ini_load.cpp:52
std::string comment
comment for group
Definition: ini_type.h:38
IniGroup(std::string_view name, IniGroupType type)
Construct a new in-memory group of an Ini file.
Definition: ini_load.cpp:42
IniGroupType type
type of group
Definition: ini_type.h:36
void Clear()
Clear all items in the group.
Definition: ini_load.cpp:98
void RemoveItem(std::string_view name)
Remove the item with the given name.
Definition: ini_load.cpp:90
std::string name
name of group
Definition: ini_type.h:37
IniItem & CreateItem(std::string_view name)
Create an item with the given name.
Definition: ini_load.cpp:81
IniItem & GetOrCreateItem(std::string_view name)
Get the item with the given name, and if it doesn't exist create a new item.
Definition: ini_load.cpp:66
std::list< IniItem > items
all items in the group
Definition: ini_type.h:35
A single "line" in an ini file.
Definition: ini_type.h:23
std::optional< std::string > value
The value of this item.
Definition: ini_type.h:25
std::string name
The name of this item.
Definition: ini_type.h:24
std::string comment
The comment associated with this item.
Definition: ini_type.h:26
IniItem(std::string_view name)
Construct a new in-memory item of an Ini file.
Definition: ini_load.cpp:23
void SetValue(std::string_view value)
Replace the current value with another value.
Definition: ini_load.cpp:32
std::list< IniGroup > groups
all groups in the ini
Definition: ini_type.h:53
void RemoveGroup(std::string_view name)
Remove the group with the given name.
Definition: ini_load.cpp:175
const IniGroupNameList seq_group_names
list of group names that are sequences.
Definition: ini_type.h:56
void LoadFromDisk(const std::string &filename, Subdirectory subdir)
Load the Ini file's data from the disk.
Definition: ini_load.cpp:187
IniLoadFile(const IniGroupNameList &list_group_names={}, const IniGroupNameList &seq_group_names={})
Construct a new in-memory Ini file representation.
Definition: ini_load.cpp:108
const IniGroup * GetGroup(std::string_view name) const
Get the group with the given name.
Definition: ini_load.cpp:119
virtual void ReportFileError(const char *const pre, const char *const buffer, const char *const post)=0
Report an error about the file contents.
std::string comment
last comment in file
Definition: ini_type.h:54
virtual std::optional< FileHandle > OpenFile(const std::string &filename, Subdirectory subdir, size_t *size)=0
Open the INI file.
IniGroup & CreateGroup(std::string_view name)
Create an group with the given name.
Definition: ini_load.cpp:162
const IniGroupNameList list_group_names
list of group names that are lists
Definition: ini_type.h:55
IniGroup & GetOrCreateGroup(std::string_view name)
Get the group with the given name, and if it doesn't exist create a new group.
Definition: ini_load.cpp:147