* Load the Ini file's data from the disk. * @param filename the file to load. * @param subdir the sub directory to load the file from. * @pre nothing has been loaded yet. */
| 183 | * @pre nothing has been loaded yet. |
| 184 | */ |
| 185 | void IniLoadFile::LoadFromDisk(std::string_view filename, Subdirectory subdir) |
| 186 | { |
| 187 | assert(this->groups.empty()); |
| 188 | |
| 189 | char buffer[1024]; |
| 190 | IniGroup *group = nullptr; |
| 191 | |
| 192 | std::string comment; |
| 193 | |
| 194 | size_t end; |
| 195 | auto in = this->OpenFile(filename, subdir, &end); |
| 196 | if (!in.has_value()) return; |
| 197 | |
| 198 | end += ftell(*in); |
| 199 | |
| 200 | size_t line = 0; |
| 201 | /* for each line in the file */ |
| 202 | while (static_cast<size_t>(ftell(*in)) < end && fgets(buffer, sizeof(buffer), *in)) { |
| 203 | ++line; |
| 204 | StringConsumer consumer{StrTrimView(buffer, StringConsumer::WHITESPACE_OR_NEWLINE)}; |
| 205 | |
| 206 | /* Skip comments and empty lines outside IGT_SEQUENCE groups. */ |
| 207 | if ((group == nullptr || group->type != IGT_SEQUENCE) && (!consumer.AnyBytesLeft() || consumer.PeekCharIfIn("#;"))) { |
| 208 | comment += consumer.GetOrigData(); |
| 209 | comment += "\n"; |
| 210 | continue; |
| 211 | } |
| 212 | |
| 213 | /* it's a group? */ |
| 214 | if (consumer.ReadCharIf('[')) { |
| 215 | std::string_view group_name = consumer.ReadUntilChar(']', StringConsumer::KEEP_SEPARATOR); |
| 216 | if (!consumer.ReadCharIf(']') || consumer.AnyBytesLeft()) { |
| 217 | this->ReportFileError(fmt::format("ini [{}]: invalid group name '{}'", line, consumer.GetOrigData())); |
| 218 | } |
| 219 | group = &this->CreateGroup(group_name); |
| 220 | group->comment = std::move(comment); |
| 221 | comment.clear(); // std::move leaves comment in a "valid but unspecified state" according to the specification. |
| 222 | } else if (group != nullptr) { |
| 223 | if (group->type == IGT_SEQUENCE) { |
| 224 | /* A sequence group, use the line as item name without further interpretation. */ |
| 225 | IniItem &item = group->CreateItem(consumer.GetOrigData()); |
| 226 | item.comment = std::move(comment); |
| 227 | comment.clear(); // std::move leaves comment in a "valid but unspecified state" according to the specification. |
| 228 | continue; |
| 229 | } |
| 230 | |
| 231 | static const std::string_view key_parameter_separators = "=\t "; |
| 232 | std::string_view key; |
| 233 | /* find end of keyname */ |
| 234 | if (consumer.ReadCharIf('\"')) { |
| 235 | key = consumer.ReadUntilChar('\"', StringConsumer::SKIP_ONE_SEPARATOR); |
| 236 | } else { |
| 237 | key = consumer.ReadUntilCharIn(key_parameter_separators); |
| 238 | } |
| 239 | |
| 240 | /* it's an item in an existing group */ |
| 241 | IniItem &item = group->CreateItem(key); |
| 242 | item.comment = std::move(comment); |
no test coverage detected