From 85f1110569e3bb23a34fba0e742ec39ca3331008 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 3 May 2025 10:27:49 +0200 Subject: [PATCH] Codechange: rewrite ini file parsing with the StringConsumer --- src/ini.cpp | 4 +- src/ini_load.cpp | 69 ++++++++++++++------------------- src/ini_type.h | 8 ++-- src/settingsgen/settingsgen.cpp | 4 +- 4 files changed, 37 insertions(+), 48 deletions(-) diff --git a/src/ini.cpp b/src/ini.cpp index 8fb0d591a0..d169833aa1 100644 --- a/src/ini.cpp +++ b/src/ini.cpp @@ -108,7 +108,7 @@ bool IniFile::SaveToDisk(const std::string &filename) return FioFOpenFile(filename, "rb", subdir, size); } -/* virtual */ void IniFile::ReportFileError(std::string_view pre, std::string_view buffer, std::string_view post) +/* virtual */ void IniFile::ReportFileError(std::string_view message) { - ShowInfo("{}{}{}", pre, buffer, post); + ShowInfo("{}", message); } diff --git a/src/ini_load.cpp b/src/ini_load.cpp index 66eb135986..693fb5d276 100644 --- a/src/ini_load.cpp +++ b/src/ini_load.cpp @@ -9,6 +9,7 @@ #include "stdafx.h" #include "core/mem_func.hpp" +#include "core/string_consumer.hpp" #include "ini_type.h" #include "string_func.h" @@ -198,78 +199,68 @@ void IniLoadFile::LoadFromDisk(std::string_view filename, Subdirectory subdir) end += ftell(*in); + size_t line = 0; /* for each line in the file */ while (static_cast(ftell(*in)) < end && fgets(buffer, sizeof(buffer), *in)) { - char c, *s; - /* trim whitespace from the left side */ - for (s = buffer; *s == ' ' || *s == '\t'; s++) {} - - /* trim whitespace from right side. */ - char *e = s + strlen(s); - while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--; - *e = '\0'; + ++line; + StringConsumer consumer{StrTrimView(buffer, StringConsumer::WHITESPACE_OR_NEWLINE)}; /* Skip comments and empty lines outside IGT_SEQUENCE groups. */ - if ((group == nullptr || group->type != IGT_SEQUENCE) && (*s == '#' || *s == ';' || *s == '\0')) { - comment += std::string_view(s, e - s); - comment += '\n'; // comment newline + if ((group == nullptr || group->type != IGT_SEQUENCE) && (!consumer.AnyBytesLeft() || consumer.PeekCharIfIn("#;"))) { + comment += consumer.GetOrigData(); + comment += "\n"; continue; } /* it's a group? */ - if (s[0] == '[') { - if (e[-1] != ']') { - this->ReportFileError("ini: invalid group name '", buffer, "'"); - } else { - e--; + if (consumer.ReadCharIf('[')) { + std::string_view group_name = consumer.ReadUntilChar(']', StringConsumer::KEEP_SEPARATOR); + if (!consumer.ReadCharIf(']') || consumer.AnyBytesLeft()) { + this->ReportFileError(fmt::format("ini [{}]: invalid group name '{}'", line, consumer.GetOrigData())); } - s++; // skip [ - group = &this->CreateGroup(std::string_view(s, e - s)); + group = &this->CreateGroup(group_name); group->comment = std::move(comment); comment.clear(); // std::move leaves comment in a "valid but unspecified state" according to the specification. } else if (group != nullptr) { if (group->type == IGT_SEQUENCE) { /* A sequence group, use the line as item name without further interpretation. */ - IniItem &item = group->CreateItem(std::string_view(buffer, e - buffer)); + IniItem &item = group->CreateItem(consumer.GetOrigData()); item.comment = std::move(comment); comment.clear(); // std::move leaves comment in a "valid but unspecified state" according to the specification. continue; } - char *t; + + static const std::string_view key_parameter_separators = "=\t "; + std::string_view key; /* find end of keyname */ - if (*s == '\"') { - s++; - for (t = s; *t != '\0' && *t != '\"'; t++) {} - if (*t == '\"') *t = ' '; + if (consumer.ReadCharIf('\"')) { + key = consumer.ReadUntilChar('\"', StringConsumer::SKIP_ONE_SEPARATOR); } else { - for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {} + key = consumer.ReadUntilCharIn(key_parameter_separators); } /* it's an item in an existing group */ - IniItem &item = group->CreateItem(std::string_view(s, t - s)); + IniItem &item = group->CreateItem(key); item.comment = std::move(comment); comment.clear(); // std::move leaves comment in a "valid but unspecified state" according to the specification. /* find start of parameter */ - while (*t == '=' || *t == ' ' || *t == '\t') t++; + consumer.SkipUntilCharNotIn(key_parameter_separators); - bool quoted = (*t == '\"'); - /* remove starting quotation marks */ - if (*t == '\"') t++; - /* remove ending quotation marks */ - e = t + strlen(t); - if (e > t && e[-1] == '\"') e--; - *e = '\0'; - - /* If the value was not quoted and empty, it must be nullptr */ - if (!quoted && e == t) { + if (consumer.ReadCharIf('\"')) { + /* There is no escaping in our loader, so we just remove the first and last quote. */ + std::string_view value = consumer.GetLeftData(); + if (value.ends_with("\"")) value.remove_suffix(1); + item.value = StrMakeValid(value); + } else if (!consumer.AnyBytesLeft()) { + /* If the value was not quoted and empty, it must be nullptr */ item.value.reset(); } else { - item.value = StrMakeValid(std::string_view(t)); + item.value = StrMakeValid(consumer.GetLeftData()); } } else { /* it's an orphan item */ - this->ReportFileError("ini: '", buffer, "' outside of group"); + this->ReportFileError(fmt::format("ini [{}]: '{}' is outside of group", line, consumer.GetOrigData())); } } diff --git a/src/ini_type.h b/src/ini_type.h index caf05b32b2..def66ff6d5 100644 --- a/src/ini_type.h +++ b/src/ini_type.h @@ -77,11 +77,9 @@ struct IniLoadFile { /** * Report an error about the file contents. - * @param pre Prefix text of the \a buffer part. - * @param buffer Part of the file with the error. - * @param post Suffix text of the \a buffer part. + * @param message The message to show. */ - virtual void ReportFileError(std::string_view pre, std::string_view buffer, std::string_view post) = 0; + virtual void ReportFileError(std::string_view message) = 0; }; /** Ini file that supports both loading and saving. */ @@ -91,7 +89,7 @@ struct IniFile : IniLoadFile { bool SaveToDisk(const std::string &filename); std::optional OpenFile(std::string_view filename, Subdirectory subdir, size_t *size) override; - void ReportFileError(std::string_view pre, std::string_view buffer, std::string_view post) override; + void ReportFileError(std::string_view message) override; }; #endif /* INI_TYPE_H */ diff --git a/src/settingsgen/settingsgen.cpp b/src/settingsgen/settingsgen.cpp index 987e8e2766..eb313c3885 100644 --- a/src/settingsgen/settingsgen.cpp +++ b/src/settingsgen/settingsgen.cpp @@ -164,9 +164,9 @@ struct SettingsIniFile : IniLoadFile { return in; } - void ReportFileError(std::string_view pre, std::string_view buffer, std::string_view post) override + void ReportFileError(std::string_view message) override { - FatalError("{}{}{}", pre, buffer, post); + FatalError("{}", message); } };