1
0
Fork 0

Codechange: Use std::string in INI file parsing.

pull/8170/head
Michael Lutz 2020-05-17 23:32:03 +02:00
parent 8aef14386f
commit 715aa67a9c
11 changed files with 179 additions and 168 deletions

View File

@ -21,7 +21,7 @@
*/ */
#define fetch_metadata(name) \ #define fetch_metadata(name) \
item = metadata->GetItem(name, false); \ item = metadata->GetItem(name, false); \
if (item == nullptr || StrEmpty(item->value)) { \ if (item == nullptr || !item->value.has_value() || item->value->empty()) { \
DEBUG(grf, 0, "Base " SET_TYPE "set detail loading: %s field missing.", name); \ DEBUG(grf, 0, "Base " SET_TYPE "set detail loading: %s field missing.", name); \
DEBUG(grf, 0, " Is %s readable for the user running OpenTTD?", full_filename); \ DEBUG(grf, 0, " Is %s readable for the user running OpenTTD?", full_filename); \
return false; \ return false; \
@ -42,28 +42,28 @@ bool BaseSet<T, Tnum_files, Tsearch_in_tars>::FillSetDetails(IniFile *ini, const
IniItem *item; IniItem *item;
fetch_metadata("name"); fetch_metadata("name");
this->name = stredup(item->value); this->name = stredup(item->value->c_str());
fetch_metadata("description"); fetch_metadata("description");
this->description[stredup("")] = stredup(item->value); this->description[stredup("")] = stredup(item->value->c_str());
/* Add the translations of the descriptions too. */ /* Add the translations of the descriptions too. */
for (const IniItem *item = metadata->item; item != nullptr; item = item->next) { for (const IniItem *item = metadata->item; item != nullptr; item = item->next) {
if (strncmp("description.", item->name, 12) != 0) continue; if (item->name.compare(0, 12, "description.") != 0) continue;
this->description[stredup(item->name + 12)] = stredup(item->value); this->description[stredup(item->name.c_str() + 12)] = stredup(item->value.value_or("").c_str());
} }
fetch_metadata("shortname"); fetch_metadata("shortname");
for (uint i = 0; item->value[i] != '\0' && i < 4; i++) { for (uint i = 0; item->value.value()[i] != '\0' && i < 4; i++) {
this->shortname |= ((uint8)item->value[i]) << (i * 8); this->shortname |= ((uint8)item->value.value()[i]) << (i * 8);
} }
fetch_metadata("version"); fetch_metadata("version");
this->version = atoi(item->value); this->version = atoi(item->value->c_str());
item = metadata->GetItem("fallback", false); item = metadata->GetItem("fallback", false);
this->fallback = (item != nullptr && strcmp(item->value, "0") != 0 && strcmp(item->value, "false") != 0); this->fallback = (item != nullptr && item->value && item->value.value() != "0" && item->value.value() != "false");
/* For each of the file types we want to find the file, MD5 checksums and warning messages. */ /* For each of the file types we want to find the file, MD5 checksums and warning messages. */
IniGroup *files = ini->GetGroup("files"); IniGroup *files = ini->GetGroup("files");
@ -73,13 +73,12 @@ bool BaseSet<T, Tnum_files, Tsearch_in_tars>::FillSetDetails(IniFile *ini, const
MD5File *file = &this->files[i]; MD5File *file = &this->files[i];
/* Find the filename first. */ /* Find the filename first. */
item = files->GetItem(BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names[i], false); item = files->GetItem(BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names[i], false);
if (item == nullptr || (item->value == nullptr && !allow_empty_filename)) { if (item == nullptr || (!item->value.has_value() && !allow_empty_filename)) {
DEBUG(grf, 0, "No " SET_TYPE " file for: %s (in %s)", BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names[i], full_filename); DEBUG(grf, 0, "No " SET_TYPE " file for: %s (in %s)", BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names[i], full_filename);
return false; return false;
} }
const char *filename = item->value; if (!item->value.has_value()) {
if (filename == nullptr) {
file->filename = nullptr; file->filename = nullptr;
/* If we list no file, that file must be valid */ /* If we list no file, that file must be valid */
this->valid_files++; this->valid_files++;
@ -87,15 +86,16 @@ bool BaseSet<T, Tnum_files, Tsearch_in_tars>::FillSetDetails(IniFile *ini, const
continue; continue;
} }
const char *filename = item->value->c_str();
file->filename = str_fmt("%s%s", path, filename); file->filename = str_fmt("%s%s", path, filename);
/* Then find the MD5 checksum */ /* Then find the MD5 checksum */
item = md5s->GetItem(filename, false); item = md5s->GetItem(filename, false);
if (item == nullptr || item->value == nullptr) { if (item == nullptr || !item->value.has_value()) {
DEBUG(grf, 0, "No MD5 checksum specified for: %s (in %s)", filename, full_filename); DEBUG(grf, 0, "No MD5 checksum specified for: %s (in %s)", filename, full_filename);
return false; return false;
} }
char *c = item->value; const char *c = item->value->c_str();
for (uint i = 0; i < sizeof(file->hash) * 2; i++, c++) { for (uint i = 0; i < sizeof(file->hash) * 2; i++, c++) {
uint j; uint j;
if ('0' <= *c && *c <= '9') { if ('0' <= *c && *c <= '9') {
@ -118,11 +118,11 @@ bool BaseSet<T, Tnum_files, Tsearch_in_tars>::FillSetDetails(IniFile *ini, const
/* Then find the warning message when the file's missing */ /* Then find the warning message when the file's missing */
item = origin->GetItem(filename, false); item = origin->GetItem(filename, false);
if (item == nullptr) item = origin->GetItem("default", false); if (item == nullptr) item = origin->GetItem("default", false);
if (item == nullptr) { if (item == nullptr || !item->value.has_value()) {
DEBUG(grf, 1, "No origin warning message specified for: %s", filename); DEBUG(grf, 1, "No origin warning message specified for: %s", filename);
file->missing_warning = stredup(""); file->missing_warning = stredup("");
} else { } else {
file->missing_warning = stredup(item->value); file->missing_warning = stredup(item->value->c_str());
} }
file->check_result = T::CheckMD5(file, BASESET_DIR); file->check_result = T::CheckMD5(file, BASESET_DIR);
@ -170,7 +170,7 @@ bool BaseMedia<Tbase_set>::AddFile(const char *filename, size_t basepath_length,
if (set->FillSetDetails(ini, path, filename)) { if (set->FillSetDetails(ini, path, filename)) {
Tbase_set *duplicate = nullptr; Tbase_set *duplicate = nullptr;
for (Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != nullptr; c = c->next) { for (Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != nullptr; c = c->next) {
if (strcmp(c->name, set->name) == 0 || c->shortname == set->shortname) { if (c->name == set->name || c->shortname == set->shortname) {
duplicate = c; duplicate = c;
break; break;
} }

View File

@ -356,11 +356,11 @@ bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *ful
IniItem *item; IniItem *item;
fetch_metadata("palette"); fetch_metadata("palette");
this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS; this->palette = (item->value.value()[0] == 'D' || item->value.value()[0] == 'd') ? PAL_DOS : PAL_WINDOWS;
/* Get optional blitter information. */ /* Get optional blitter information. */
item = metadata->GetItem("blitter", false); item = metadata->GetItem("blitter", false);
this->blitter = (item != nullptr && *item->value == '3') ? BLT_32BPP : BLT_8BPP; this->blitter = (item != nullptr && item->value.value()[0] == '3') ? BLT_32BPP : BLT_8BPP;
} }
return ret; return ret;
} }

View File

@ -290,7 +290,7 @@ void HotkeyList::Load(IniFile *ini)
IniItem *item = group->GetItem(hotkey->name, false); IniItem *item = group->GetItem(hotkey->name, false);
if (item != nullptr) { if (item != nullptr) {
hotkey->keycodes.clear(); hotkey->keycodes.clear();
if (item->value != nullptr) ParseHotkeys(hotkey, item->value); if (item->value.has_value()) ParseHotkeys(hotkey, item->value->c_str());
} }
} }
} }

View File

@ -12,9 +12,11 @@
#include "ini_type.h" #include "ini_type.h"
#include "string_func.h" #include "string_func.h"
#include "fileio_func.h" #include "fileio_func.h"
#include <fstream>
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500)
# include <unistd.h> # include <unistd.h>
# include <fcntl.h>
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
@ -45,31 +47,33 @@ bool IniFile::SaveToDisk(const char *filename)
* that file. This to prevent that when OpenTTD crashes during the save * that file. This to prevent that when OpenTTD crashes during the save
* you end up with a truncated configuration file. * you end up with a truncated configuration file.
*/ */
char file_new[MAX_PATH]; std::string file_new{ filename };
file_new.append(".new");
strecpy(file_new, filename, lastof(file_new)); std::ofstream os(OTTD2FS(file_new.c_str()));
strecat(file_new, ".new", lastof(file_new)); if (os.fail()) return false;
FILE *f = fopen(file_new, "w");
if (f == nullptr) return false;
for (const IniGroup *group = this->group; group != nullptr; group = group->next) { for (const IniGroup *group = this->group; group != nullptr; group = group->next) {
if (group->comment) fputs(group->comment, f); os << group->comment << "[" << group->name << "]\n";
fprintf(f, "[%s]\n", group->name);
for (const IniItem *item = group->item; item != nullptr; item = item->next) { for (const IniItem *item = group->item; item != nullptr; item = item->next) {
if (item->comment != nullptr) fputs(item->comment, f); os << item->comment;
/* protect item->name with quotes if needed */ /* protect item->name with quotes if needed */
if (strchr(item->name, ' ') != nullptr || if (item->name.find(' ') != std::string::npos ||
item->name[0] == '[') { item->name[0] == '[') {
fprintf(f, "\"%s\"", item->name); os << "\"" << item->name << "\"";
} else { } else {
fprintf(f, "%s", item->name); os << item->name;
} }
fprintf(f, " = %s\n", item->value == nullptr ? "" : item->value); os << " = " << item->value.value_or("") << "\n";
} }
} }
if (this->comment) fputs(this->comment, f); os << this->comment;
os.flush();
os.close();
if (os.fail()) return false;
/* /*
* POSIX (and friends) do not guarantee that when a file is closed it is * POSIX (and friends) do not guarantee that when a file is closed it is
@ -78,11 +82,10 @@ bool IniFile::SaveToDisk(const char *filename)
* (modification date etc.) is not important to us; only the real data is. * (modification date etc.) is not important to us; only the real data is.
*/ */
#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 #if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0
int ret = fdatasync(fileno(f)); int f = open(file_new.c_str(), O_RDWR);
fclose(f); int ret = fdatasync(f);
close(f);
if (ret != 0) return false; if (ret != 0) return false;
#else
fclose(f);
#endif #endif
#if defined(_WIN32) #if defined(_WIN32)
@ -91,7 +94,7 @@ bool IniFile::SaveToDisk(const char *filename)
/* Allocate space for one more \0 character. */ /* Allocate space for one more \0 character. */
TCHAR tfilename[MAX_PATH + 1], tfile_new[MAX_PATH + 1]; TCHAR tfilename[MAX_PATH + 1], tfile_new[MAX_PATH + 1];
_tcsncpy(tfilename, OTTD2FS(filename), MAX_PATH); _tcsncpy(tfilename, OTTD2FS(filename), MAX_PATH);
_tcsncpy(tfile_new, OTTD2FS(file_new), MAX_PATH); _tcsncpy(tfile_new, OTTD2FS(file_new.c_str()), MAX_PATH);
/* SHFileOperation wants a double '\0' terminated string. */ /* SHFileOperation wants a double '\0' terminated string. */
tfilename[MAX_PATH - 1] = '\0'; tfilename[MAX_PATH - 1] = '\0';
tfile_new[MAX_PATH - 1] = '\0'; tfile_new[MAX_PATH - 1] = '\0';
@ -107,8 +110,8 @@ bool IniFile::SaveToDisk(const char *filename)
shfopt.pTo = tfilename; shfopt.pTo = tfilename;
SHFileOperation(&shfopt); SHFileOperation(&shfopt);
#else #else
if (rename(file_new, filename) < 0) { if (rename(file_new.c_str(), filename) < 0) {
DEBUG(misc, 0, "Renaming %s to %s failed; configuration not saved", file_new, filename); DEBUG(misc, 0, "Renaming %s to %s failed; configuration not saved", file_new.c_str(), filename);
} }
#endif #endif

View File

@ -19,12 +19,10 @@
* Construct a new in-memory item of an Ini file. * Construct a new in-memory item of an Ini file.
* @param parent the group we belong to * @param parent the group we belong to
* @param name the name of the item * @param name the name of the item
* @param last the last element of the name of the item
*/ */
IniItem::IniItem(IniGroup *parent, const char *name, const char *last) : next(nullptr), value(nullptr), comment(nullptr) IniItem::IniItem(IniGroup *parent, const std::string &name) : next(nullptr)
{ {
this->name = stredup(name, last); this->name = str_validate(name);
str_validate(this->name, this->name + strlen(this->name));
*parent->last_item = this; *parent->last_item = this;
parent->last_item = &this->next; parent->last_item = &this->next;
@ -33,10 +31,6 @@ IniItem::IniItem(IniGroup *parent, const char *name, const char *last) : next(nu
/** Free everything we loaded. */ /** Free everything we loaded. */
IniItem::~IniItem() IniItem::~IniItem()
{ {
free(this->name);
free(this->value);
free(this->comment);
delete this->next; delete this->next;
} }
@ -46,20 +40,21 @@ IniItem::~IniItem()
*/ */
void IniItem::SetValue(const char *value) void IniItem::SetValue(const char *value)
{ {
free(this->value); if (value == nullptr) {
this->value = stredup(value); this->value.reset();
} else {
this->value.emplace(value);
}
} }
/** /**
* Construct a new in-memory group of an Ini file. * Construct a new in-memory group of an Ini file.
* @param parent the file we belong to * @param parent the file we belong to
* @param name the name of the group * @param name the name of the group
* @param last the last element of the name of the group
*/ */
IniGroup::IniGroup(IniLoadFile *parent, const char *name, const char *last) : next(nullptr), type(IGT_VARIABLES), item(nullptr), comment(nullptr) IniGroup::IniGroup(IniLoadFile *parent, const std::string &name) : next(nullptr), type(IGT_VARIABLES), item(nullptr)
{ {
this->name = stredup(name, last); this->name = str_validate(name);
str_validate(this->name, this->name + strlen(this->name));
this->last_item = &this->item; this->last_item = &this->item;
*parent->last_group = this; *parent->last_group = this;
@ -67,7 +62,7 @@ IniGroup::IniGroup(IniLoadFile *parent, const char *name, const char *last) : ne
if (parent->list_group_names != nullptr) { if (parent->list_group_names != nullptr) {
for (uint i = 0; parent->list_group_names[i] != nullptr; i++) { for (uint i = 0; parent->list_group_names[i] != nullptr; i++) {
if (strcmp(this->name, parent->list_group_names[i]) == 0) { if (this->name == parent->list_group_names[i]) {
this->type = IGT_LIST; this->type = IGT_LIST;
return; return;
} }
@ -75,7 +70,7 @@ IniGroup::IniGroup(IniLoadFile *parent, const char *name, const char *last) : ne
} }
if (parent->seq_group_names != nullptr) { if (parent->seq_group_names != nullptr) {
for (uint i = 0; parent->seq_group_names[i] != nullptr; i++) { for (uint i = 0; parent->seq_group_names[i] != nullptr; i++) {
if (strcmp(this->name, parent->seq_group_names[i]) == 0) { if (this->name == parent->seq_group_names[i]) {
this->type = IGT_SEQUENCE; this->type = IGT_SEQUENCE;
return; return;
} }
@ -86,9 +81,6 @@ IniGroup::IniGroup(IniLoadFile *parent, const char *name, const char *last) : ne
/** Free everything we loaded. */ /** Free everything we loaded. */
IniGroup::~IniGroup() IniGroup::~IniGroup()
{ {
free(this->name);
free(this->comment);
delete this->item; delete this->item;
delete this->next; delete this->next;
} }
@ -100,16 +92,16 @@ IniGroup::~IniGroup()
* @param create whether to create an item when not found or not. * @param create whether to create an item when not found or not.
* @return the requested item or nullptr if not found. * @return the requested item or nullptr if not found.
*/ */
IniItem *IniGroup::GetItem(const char *name, bool create) IniItem *IniGroup::GetItem(const std::string &name, bool create)
{ {
for (IniItem *item = this->item; item != nullptr; item = item->next) { for (IniItem *item = this->item; item != nullptr; item = item->next) {
if (strcmp(item->name, name) == 0) return item; if (item->name == name) return item;
} }
if (!create) return nullptr; if (!create) return nullptr;
/* otherwise make a new one */ /* otherwise make a new one */
return new IniItem(this, name, nullptr); return new IniItem(this, name);
} }
/** /**
@ -129,7 +121,6 @@ void IniGroup::Clear()
*/ */
IniLoadFile::IniLoadFile(const char * const *list_group_names, const char * const *seq_group_names) : IniLoadFile::IniLoadFile(const char * const *list_group_names, const char * const *seq_group_names) :
group(nullptr), group(nullptr),
comment(nullptr),
list_group_names(list_group_names), list_group_names(list_group_names),
seq_group_names(seq_group_names) seq_group_names(seq_group_names)
{ {
@ -139,7 +130,6 @@ IniLoadFile::IniLoadFile(const char * const *list_group_names, const char * cons
/** Free everything we loaded. */ /** Free everything we loaded. */
IniLoadFile::~IniLoadFile() IniLoadFile::~IniLoadFile()
{ {
free(this->comment);
delete this->group; delete this->group;
} }
@ -147,26 +137,21 @@ IniLoadFile::~IniLoadFile()
* Get the group with the given name. If it doesn't exist * Get the group with the given name. If it doesn't exist
* and \a create_new is \c true create a new group. * and \a create_new is \c true create a new group.
* @param name name of the group to find. * @param name name of the group to find.
* @param len the maximum length of said name (\c 0 means length of the string).
* @param create_new Allow creation of group if it does not exist. * @param create_new Allow creation of group if it does not exist.
* @return The requested group if it exists or was created, else \c nullptr. * @return The requested group if it exists or was created, else \c nullptr.
*/ */
IniGroup *IniLoadFile::GetGroup(const char *name, size_t len, bool create_new) IniGroup *IniLoadFile::GetGroup(const std::string &name, bool create_new)
{ {
if (len == 0) len = strlen(name);
/* does it exist already? */ /* does it exist already? */
for (IniGroup *group = this->group; group != nullptr; group = group->next) { for (IniGroup *group = this->group; group != nullptr; group = group->next) {
if (!strncmp(group->name, name, len) && group->name[len] == 0) { if (group->name == name) return group;
return group;
}
} }
if (!create_new) return nullptr; if (!create_new) return nullptr;
/* otherwise make a new one */ /* otherwise make a new one */
IniGroup *group = new IniGroup(this, name, name + len - 1); IniGroup *group = new IniGroup(this, name);
group->comment = stredup("\n"); group->comment = "\n";
return group; return group;
} }
@ -182,7 +167,7 @@ void IniLoadFile::RemoveGroup(const char *name)
/* does it exist already? */ /* does it exist already? */
for (group = this->group; group != nullptr; prev = group, group = group->next) { for (group = this->group; group != nullptr; prev = group, group = group->next) {
if (strncmp(group->name, name, len) == 0) { if (group->name.compare(0, len, name) == 0) {
break; break;
} }
} }
@ -260,17 +245,17 @@ void IniLoadFile::LoadFromDisk(const char *filename, Subdirectory subdir)
e--; e--;
} }
s++; // skip [ s++; // skip [
group = new IniGroup(this, s, e - 1); group = new IniGroup(this, std::string(s, e - s));
if (comment_size != 0) { if (comment_size != 0) {
group->comment = stredup(comment, comment + comment_size - 1); group->comment.assign(comment, comment_size);
comment_size = 0; comment_size = 0;
} }
} else if (group != nullptr) { } else if (group != nullptr) {
if (group->type == IGT_SEQUENCE) { if (group->type == IGT_SEQUENCE) {
/* A sequence group, use the line as item name without further interpretation. */ /* A sequence group, use the line as item name without further interpretation. */
IniItem *item = new IniItem(group, buffer, e - 1); IniItem *item = new IniItem(group, std::string(buffer, e - buffer));
if (comment_size) { if (comment_size) {
item->comment = stredup(comment, comment + comment_size - 1); item->comment.assign(comment, comment_size);
comment_size = 0; comment_size = 0;
} }
continue; continue;
@ -286,9 +271,9 @@ void IniLoadFile::LoadFromDisk(const char *filename, Subdirectory subdir)
} }
/* it's an item in an existing group */ /* it's an item in an existing group */
IniItem *item = new IniItem(group, s, t - 1); IniItem *item = new IniItem(group, std::string(s, t - s));
if (comment_size != 0) { if (comment_size != 0) {
item->comment = stredup(comment, comment + comment_size - 1); item->comment.assign(comment, comment_size);
comment_size = 0; comment_size = 0;
} }
@ -304,8 +289,11 @@ void IniLoadFile::LoadFromDisk(const char *filename, Subdirectory subdir)
*e = '\0'; *e = '\0';
/* If the value was not quoted and empty, it must be nullptr */ /* If the value was not quoted and empty, it must be nullptr */
item->value = (!quoted && e == t) ? nullptr : stredup(t); if (!quoted && e == t) {
if (item->value != nullptr) str_validate(item->value, item->value + strlen(item->value)); item->value.reset();
} else {
item->value = str_validate(std::string(t));
}
} else { } else {
/* it's an orphan item */ /* it's an orphan item */
this->ReportFileError("ini: '", buffer, "' outside of group"); this->ReportFileError("ini: '", buffer, "' outside of group");
@ -313,7 +301,7 @@ void IniLoadFile::LoadFromDisk(const char *filename, Subdirectory subdir)
} }
if (comment_size > 0) { if (comment_size > 0) {
this->comment = stredup(comment, comment + comment_size - 1); this->comment.assign(comment, comment_size);
comment_size = 0; comment_size = 0;
} }

View File

@ -11,6 +11,8 @@
#define INI_TYPE_H #define INI_TYPE_H
#include "fileio_type.h" #include "fileio_type.h"
#include <string>
#include "3rdparty/optional/ottd_optional.h"
/** Types of groups */ /** Types of groups */
enum IniGroupType { enum IniGroupType {
@ -21,12 +23,12 @@ enum IniGroupType {
/** A single "line" in an ini file. */ /** A single "line" in an ini file. */
struct IniItem { struct IniItem {
IniItem *next; ///< The next item in this group IniItem *next; ///< The next item in this group
char *name; ///< The name of this item std::string name; ///< The name of this item
char *value; ///< The value of this item opt::optional<std::string> value; ///< The value of this item
char *comment; ///< The comment associated with this item std::string comment; ///< The comment associated with this item
IniItem(struct IniGroup *parent, const char *name, const char *last = nullptr); IniItem(struct IniGroup *parent, const std::string &name);
~IniItem(); ~IniItem();
void SetValue(const char *value); void SetValue(const char *value);
@ -38,13 +40,13 @@ struct IniGroup {
IniGroupType type; ///< type of group IniGroupType type; ///< type of group
IniItem *item; ///< the first item in the group IniItem *item; ///< the first item in the group
IniItem **last_item; ///< the last item in the group IniItem **last_item; ///< the last item in the group
char *name; ///< name of group std::string name; ///< name of group
char *comment; ///< comment for group std::string comment; ///< comment for group
IniGroup(struct IniLoadFile *parent, const char *name, const char *last = nullptr); IniGroup(struct IniLoadFile *parent, const std::string &name);
~IniGroup(); ~IniGroup();
IniItem *GetItem(const char *name, bool create); IniItem *GetItem(const std::string &name, bool create);
void Clear(); void Clear();
}; };
@ -52,14 +54,14 @@ struct IniGroup {
struct IniLoadFile { struct IniLoadFile {
IniGroup *group; ///< the first group in the ini IniGroup *group; ///< the first group in the ini
IniGroup **last_group; ///< the last group in the ini IniGroup **last_group; ///< the last group in the ini
char *comment; ///< last comment in file std::string comment; ///< last comment in file
const char * const *list_group_names; ///< nullptr terminated list with group names that are lists const char * const *list_group_names; ///< nullptr terminated list with group names that are lists
const char * const *seq_group_names; ///< nullptr terminated list with group names that are sequences. const char * const *seq_group_names; ///< nullptr terminated list with group names that are sequences.
IniLoadFile(const char * const *list_group_names = nullptr, const char * const *seq_group_names = nullptr); IniLoadFile(const char * const *list_group_names = nullptr, const char * const *seq_group_names = nullptr);
virtual ~IniLoadFile(); virtual ~IniLoadFile();
IniGroup *GetGroup(const char *name, size_t len = 0, bool create_new = true); IniGroup *GetGroup(const std::string &name, bool create_new = true);
void RemoveGroup(const char *name); void RemoveGroup(const char *name);
void LoadFromDisk(const char *filename, Subdirectory subdir); void LoadFromDisk(const char *filename, Subdirectory subdir);

View File

@ -135,10 +135,10 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
this->songinfo[i].filename = filename; // non-owned pointer this->songinfo[i].filename = filename; // non-owned pointer
IniItem *item = catindex->GetItem(_music_file_names[i], false); IniItem *item = catindex->GetItem(_music_file_names[i], false);
if (item != nullptr && !StrEmpty(item->value)) { if (item != nullptr && item->value.has_value() && !item->value->empty()) {
/* Song has a CAT file index, assume it's MPS MIDI format */ /* Song has a CAT file index, assume it's MPS MIDI format */
this->songinfo[i].filetype = MTT_MPSMIDI; this->songinfo[i].filetype = MTT_MPSMIDI;
this->songinfo[i].cat_index = atoi(item->value); this->songinfo[i].cat_index = atoi(item->value->c_str());
char *songname = GetMusicCatEntryName(filename, this->songinfo[i].cat_index); char *songname = GetMusicCatEntryName(filename, this->songinfo[i].cat_index);
if (songname == nullptr) { if (songname == nullptr) {
DEBUG(grf, 0, "Base music set song missing from CAT file: %s/%d", filename, this->songinfo[i].cat_index); DEBUG(grf, 0, "Base music set song missing from CAT file: %s/%d", filename, this->songinfo[i].cat_index);
@ -161,12 +161,12 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
while (*trimmed_filename == PATHSEPCHAR) trimmed_filename++; while (*trimmed_filename == PATHSEPCHAR) trimmed_filename++;
item = names->GetItem(trimmed_filename, false); item = names->GetItem(trimmed_filename, false);
if (item != nullptr && !StrEmpty(item->value)) break; if (item != nullptr && item->value.has_value() && !item->value->empty()) break;
} }
if (this->songinfo[i].filetype == MTT_STANDARDMIDI) { if (this->songinfo[i].filetype == MTT_STANDARDMIDI) {
if (item != nullptr && !StrEmpty(item->value)) { if (item != nullptr && item->value.has_value() && !item->value->empty()) {
strecpy(this->songinfo[i].songname, item->value, lastof(this->songinfo[i].songname)); strecpy(this->songinfo[i].songname, item->value->c_str(), lastof(this->songinfo[i].songname));
} else { } else {
DEBUG(grf, 0, "Base music set song name missing: %s", filename); DEBUG(grf, 0, "Base music set song name missing: %s", filename);
return false; return false;
@ -181,12 +181,12 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
this->songinfo[i].tracknr = tracknr++; this->songinfo[i].tracknr = tracknr++;
} }
item = timingtrim->GetItem(trimmed_filename, false); item = trimmed_filename != nullptr ? timingtrim->GetItem(trimmed_filename, false) : nullptr;
if (item != nullptr && !StrEmpty(item->value)) { if (item != nullptr && item->value.has_value() && !item->value->empty()) {
const char *endpos = strchr(item->value, ':'); auto endpos = item->value->find(':');
if (endpos != nullptr) { if (endpos != std::string::npos) {
this->songinfo[i].override_start = atoi(item->value); this->songinfo[i].override_start = atoi(item->value->c_str());
this->songinfo[i].override_end = atoi(endpos + 1); this->songinfo[i].override_end = atoi(item->value->c_str() + endpos + 1);
} }
} }
} }

View File

@ -505,10 +505,6 @@ static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grp
{ {
IniGroup *group; IniGroup *group;
IniGroup *group_def = ini->GetGroup(grpname); IniGroup *group_def = ini->GetGroup(grpname);
IniItem *item;
const void *p;
void *ptr;
const char *s;
for (; sd->save.cmd != SL_END; sd++) { for (; sd->save.cmd != SL_END; sd++) {
const SettingDescBase *sdb = &sd->desc; const SettingDescBase *sdb = &sd->desc;
@ -517,30 +513,30 @@ static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grp
if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to)) continue; if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to)) continue;
/* For settings.xx.yy load the settings from [xx] yy = ? */ /* For settings.xx.yy load the settings from [xx] yy = ? */
s = strchr(sdb->name, '.'); std::string s{ sdb->name };
if (s != nullptr) { auto sc = s.find('.');
group = ini->GetGroup(sdb->name, s - sdb->name); if (sc != std::string::npos) {
s++; group = ini->GetGroup(s.substr(0, sc));
s = s.substr(sc + 1);
} else { } else {
s = sdb->name;
group = group_def; group = group_def;
} }
item = group->GetItem(s, false); IniItem *item = group->GetItem(s, false);
if (item == nullptr && group != group_def) { if (item == nullptr && group != group_def) {
/* For settings.xx.yy load the settings from [settingss] yy = ? in case the previous /* For settings.xx.yy load the settings from [settings] yy = ? in case the previous
* did not exist (e.g. loading old config files with a [settings] section */ * did not exist (e.g. loading old config files with a [settings] section */
item = group_def->GetItem(s, false); item = group_def->GetItem(s, false);
} }
if (item == nullptr) { if (item == nullptr) {
/* For settings.xx.zz.yy load the settings from [zz] yy = ? in case the previous /* For settings.xx.zz.yy load the settings from [zz] yy = ? in case the previous
* did not exist (e.g. loading old config files with a [yapf] section */ * did not exist (e.g. loading old config files with a [yapf] section */
const char *sc = strchr(s, '.'); sc = s.find('.');
if (sc != nullptr) item = ini->GetGroup(s, sc - s)->GetItem(sc + 1, false); if (sc != std::string::npos) item = ini->GetGroup(s.substr(0, sc))->GetItem(s.substr(sc + 1), false);
} }
p = (item == nullptr) ? sdb->def : StringToVal(sdb, item->value); const void *p = (item == nullptr) ? sdb->def : StringToVal(sdb, item->value.has_value() ? item->value->c_str() : nullptr);
ptr = GetVariableAddress(object, sld); void *ptr = GetVariableAddress(object, sld);
switch (sdb->cmd) { switch (sdb->cmd) {
case SDT_BOOLX: // All four are various types of (integer) numbers case SDT_BOOLX: // All four are various types of (integer) numbers
@ -604,7 +600,6 @@ static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grp
IniGroup *group_def = nullptr, *group; IniGroup *group_def = nullptr, *group;
IniItem *item; IniItem *item;
char buf[512]; char buf[512];
const char *s;
void *ptr; void *ptr;
for (; sd->save.cmd != SL_END; sd++) { for (; sd->save.cmd != SL_END; sd++) {
@ -617,22 +612,22 @@ static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grp
if (sld->conv & SLF_NOT_IN_CONFIG) continue; if (sld->conv & SLF_NOT_IN_CONFIG) continue;
/* XXX - wtf is this?? (group override?) */ /* XXX - wtf is this?? (group override?) */
s = strchr(sdb->name, '.'); std::string s{ sdb->name };
if (s != nullptr) { auto sc = s.find('.');
group = ini->GetGroup(sdb->name, s - sdb->name); if (sc != std::string::npos) {
s++; group = ini->GetGroup(s.substr(0, sc));
s = s.substr(sc + 1);
} else { } else {
if (group_def == nullptr) group_def = ini->GetGroup(grpname); if (group_def == nullptr) group_def = ini->GetGroup(grpname);
s = sdb->name;
group = group_def; group = group_def;
} }
item = group->GetItem(s, true); item = group->GetItem(s, true);
ptr = GetVariableAddress(object, sld); ptr = GetVariableAddress(object, sld);
if (item->value != nullptr) { if (item->value.has_value()) {
/* check if the value is the same as the old value */ /* check if the value is the same as the old value */
const void *p = StringToVal(sdb, item->value); const void *p = StringToVal(sdb, item->value->c_str());
/* The main type of a variable/setting is in bytes 8-15 /* The main type of a variable/setting is in bytes 8-15
* The subtype (what kind of numbers do we have there) is in 0-7 */ * The subtype (what kind of numbers do we have there) is in 0-7 */
@ -714,8 +709,7 @@ static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grp
} }
/* The value is different, that means we have to write it to the ini */ /* The value is different, that means we have to write it to the ini */
free(item->value); item->value.emplace(buf);
item->value = stredup(buf);
} }
} }
@ -737,7 +731,7 @@ static void IniLoadSettingList(IniFile *ini, const char *grpname, StringList &li
list.clear(); list.clear();
for (const IniItem *item = group->item; item != nullptr; item = item->next) { for (const IniItem *item = group->item; item != nullptr; item = item->next) {
if (item->name != nullptr) list.emplace_back(item->name); if (!item->name.empty()) list.push_back(item->name);
} }
} }
@ -1427,14 +1421,14 @@ static void AILoadConfig(IniFile *ini, const char *grpname)
for (item = group->item; c < MAX_COMPANIES && item != nullptr; c++, item = item->next) { for (item = group->item; c < MAX_COMPANIES && item != nullptr; c++, item = item->next) {
AIConfig *config = AIConfig::GetConfig(c, AIConfig::SSS_FORCE_NEWGAME); AIConfig *config = AIConfig::GetConfig(c, AIConfig::SSS_FORCE_NEWGAME);
config->Change(item->name); config->Change(item->name.c_str());
if (!config->HasScript()) { if (!config->HasScript()) {
if (strcmp(item->name, "none") != 0) { if (item->name != "none") {
DEBUG(script, 0, "The AI by the name '%s' was no longer found, and removed from the list.", item->name); DEBUG(script, 0, "The AI by the name '%s' was no longer found, and removed from the list.", item->name.c_str());
continue; continue;
} }
} }
if (item->value != nullptr) config->StringToSettings(item->value); if (item->value.has_value()) config->StringToSettings(item->value->c_str());
} }
} }
@ -1454,14 +1448,14 @@ static void GameLoadConfig(IniFile *ini, const char *grpname)
GameConfig *config = GameConfig::GetConfig(AIConfig::SSS_FORCE_NEWGAME); GameConfig *config = GameConfig::GetConfig(AIConfig::SSS_FORCE_NEWGAME);
config->Change(item->name); config->Change(item->name.c_str());
if (!config->HasScript()) { if (!config->HasScript()) {
if (strcmp(item->name, "none") != 0) { if (item->name != "none") {
DEBUG(script, 0, "The GameScript by the name '%s' was no longer found, and removed from the list.", item->name); DEBUG(script, 0, "The GameScript by the name '%s' was no longer found, and removed from the list.", item->name.c_str());
return; return;
} }
} }
if (item->value != nullptr) config->StringToSettings(item->value); if (item->value.has_value()) config->StringToSettings(item->value->c_str());
} }
/** /**
@ -1485,7 +1479,7 @@ static int DecodeHexNibble(char c)
* @param dest_size Number of bytes in \a dest. * @param dest_size Number of bytes in \a dest.
* @return Whether reading was successful. * @return Whether reading was successful.
*/ */
static bool DecodeHexText(char *pos, uint8 *dest, size_t dest_size) static bool DecodeHexText(const char *pos, uint8 *dest, size_t dest_size)
{ {
while (dest_size > 0) { while (dest_size > 0) {
int hi = DecodeHexNibble(pos[0]); int hi = DecodeHexNibble(pos[0]);
@ -1517,7 +1511,7 @@ static GRFConfig *GRFLoadConfig(IniFile *ini, const char *grpname, bool is_stati
GRFConfig *c = nullptr; GRFConfig *c = nullptr;
uint8 grfid_buf[4], md5sum[16]; uint8 grfid_buf[4], md5sum[16];
char *filename = item->name; const char *filename = item->name.c_str();
bool has_grfid = false; bool has_grfid = false;
bool has_md5sum = false; bool has_md5sum = false;
@ -1541,8 +1535,8 @@ static GRFConfig *GRFLoadConfig(IniFile *ini, const char *grpname, bool is_stati
if (c == nullptr) c = new GRFConfig(filename); if (c == nullptr) c = new GRFConfig(filename);
/* Parse parameters */ /* Parse parameters */
if (!StrEmpty(item->value)) { if (item->value.has_value() && !item->value->empty()) {
int count = ParseIntList(item->value, c->param, lengthof(c->param)); int count = ParseIntList(item->value->c_str(), c->param, lengthof(c->param));
if (count < 0) { if (count < 0) {
SetDParamStr(0, filename); SetDParamStr(0, filename);
ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY, WL_CRITICAL); ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY, WL_CRITICAL);
@ -1565,7 +1559,7 @@ static GRFConfig *GRFLoadConfig(IniFile *ini, const char *grpname, bool is_stati
SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN); SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN);
} }
SetDParamStr(0, StrEmpty(filename) ? item->name : filename); SetDParamStr(0, StrEmpty(filename) ? item->name.c_str() : filename);
ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_GRF, WL_CRITICAL); ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_GRF, WL_CRITICAL);
delete c; delete c;
continue; continue;
@ -1776,8 +1770,8 @@ StringList GetGRFPresetList()
std::unique_ptr<IniFile> ini(IniLoadConfig()); std::unique_ptr<IniFile> ini(IniLoadConfig());
for (IniGroup *group = ini->group; group != nullptr; group = group->next) { for (IniGroup *group = ini->group; group != nullptr; group = group->next) {
if (strncmp(group->name, "preset-", 7) == 0) { if (group->name.compare(0, 7, "preset-") == 0) {
list.emplace_back(group->name + 7); list.push_back(group->name.substr(7));
} }
} }

View File

@ -214,11 +214,11 @@ static IniLoadFile *LoadIniFile(const char *filename)
*/ */
static void DumpGroup(IniLoadFile *ifile, const char * const group_name) static void DumpGroup(IniLoadFile *ifile, const char * const group_name)
{ {
IniGroup *grp = ifile->GetGroup(group_name, 0, false); IniGroup *grp = ifile->GetGroup(group_name, false);
if (grp != nullptr && grp->type == IGT_SEQUENCE) { if (grp != nullptr && grp->type == IGT_SEQUENCE) {
for (IniItem *item = grp->item; item != nullptr; item = item->next) { for (IniItem *item = grp->item; item != nullptr; item = item->next) {
if (item->name) { if (!item->name.empty()) {
_stored_output.Add(item->name); _stored_output.Add(item->name.c_str());
_stored_output.Add("\n", 1); _stored_output.Add("\n", 1);
} }
} }
@ -236,8 +236,8 @@ static const char *FindItemValue(const char *name, IniGroup *grp, IniGroup *defa
{ {
IniItem *item = grp->GetItem(name, false); IniItem *item = grp->GetItem(name, false);
if (item == nullptr && defaults != nullptr) item = defaults->GetItem(name, false); if (item == nullptr && defaults != nullptr) item = defaults->GetItem(name, false);
if (item == nullptr || item->value == nullptr) return nullptr; if (item == nullptr || !item->value.has_value()) return nullptr;
return item->value; return item->value->c_str();
} }
/** /**
@ -249,19 +249,19 @@ static void DumpSections(IniLoadFile *ifile)
static const int MAX_VAR_LENGTH = 64; static const int MAX_VAR_LENGTH = 64;
static const char * const special_group_names[] = {PREAMBLE_GROUP_NAME, POSTAMBLE_GROUP_NAME, DEFAULTS_GROUP_NAME, TEMPLATES_GROUP_NAME, nullptr}; static const char * const special_group_names[] = {PREAMBLE_GROUP_NAME, POSTAMBLE_GROUP_NAME, DEFAULTS_GROUP_NAME, TEMPLATES_GROUP_NAME, nullptr};
IniGroup *default_grp = ifile->GetGroup(DEFAULTS_GROUP_NAME, 0, false); IniGroup *default_grp = ifile->GetGroup(DEFAULTS_GROUP_NAME, false);
IniGroup *templates_grp = ifile->GetGroup(TEMPLATES_GROUP_NAME, 0, false); IniGroup *templates_grp = ifile->GetGroup(TEMPLATES_GROUP_NAME, false);
if (templates_grp == nullptr) return; if (templates_grp == nullptr) return;
/* Output every group, using its name as template name. */ /* Output every group, using its name as template name. */
for (IniGroup *grp = ifile->group; grp != nullptr; grp = grp->next) { for (IniGroup *grp = ifile->group; grp != nullptr; grp = grp->next) {
const char * const *sgn; const char * const *sgn;
for (sgn = special_group_names; *sgn != nullptr; sgn++) if (strcmp(grp->name, *sgn) == 0) break; for (sgn = special_group_names; *sgn != nullptr; sgn++) if (grp->name == *sgn) break;
if (*sgn != nullptr) continue; if (*sgn != nullptr) continue;
IniItem *template_item = templates_grp->GetItem(grp->name, false); // Find template value. IniItem *template_item = templates_grp->GetItem(grp->name, false); // Find template value.
if (template_item == nullptr || template_item->value == nullptr) { if (template_item == nullptr || !template_item->value.has_value()) {
fprintf(stderr, "settingsgen: Warning: Cannot find template %s\n", grp->name); fprintf(stderr, "settingsgen: Warning: Cannot find template %s\n", grp->name.c_str());
continue; continue;
} }
@ -281,7 +281,7 @@ static void DumpSections(IniLoadFile *ifile)
} }
/* Output text of the template, except template variables of the form '$[_a-z0-9]+' which get replaced by their value. */ /* Output text of the template, except template variables of the form '$[_a-z0-9]+' which get replaced by their value. */
const char *txt = template_item->value; const char *txt = template_item->value->c_str();
while (*txt != '\0') { while (*txt != '\0') {
if (*txt != '$') { if (*txt != '$') {
_stored_output.Add(txt, 1); _stored_output.Add(txt, 1);

View File

@ -185,18 +185,11 @@ void str_fix_scc_encoded(char *str, const char *last)
} }
/** template <class T>
* Scans the string for valid characters and if it finds invalid ones, static void str_validate(T &dst, const char *str, const char *last, StringValidationSettings settings)
* replaces them with a question mark '?' (if not ignored)
* @param str the string to validate
* @param last the last valid character of str
* @param settings the settings for the string validation.
*/
void str_validate(char *str, const char *last, StringValidationSettings settings)
{ {
/* Assume the ABSOLUTE WORST to be in str as it comes from the outside. */ /* Assume the ABSOLUTE WORST to be in str as it comes from the outside. */
char *dst = str;
while (str <= last && *str != '\0') { while (str <= last && *str != '\0') {
size_t len = Utf8EncodedCharLen(*str); size_t len = Utf8EncodedCharLen(*str);
/* If the character is unknown, i.e. encoded length is 0 /* If the character is unknown, i.e. encoded length is 0
@ -220,7 +213,7 @@ void str_validate(char *str, const char *last, StringValidationSettings settings
do { do {
*dst++ = *str++; *dst++ = *str++;
} while (--len != 0); } while (--len != 0);
} else if ((settings & SVS_ALLOW_NEWLINE) != 0 && c == '\n') { } else if ((settings & SVS_ALLOW_NEWLINE) != 0 && c == '\n') {
*dst++ = *str++; *dst++ = *str++;
} else { } else {
if ((settings & SVS_ALLOW_NEWLINE) != 0 && c == '\r' && str[1] == '\n') { if ((settings & SVS_ALLOW_NEWLINE) != 0 && c == '\r' && str[1] == '\n') {
@ -232,10 +225,40 @@ void str_validate(char *str, const char *last, StringValidationSettings settings
if ((settings & SVS_REPLACE_WITH_QUESTION_MARK) != 0) *dst++ = '?'; if ((settings & SVS_REPLACE_WITH_QUESTION_MARK) != 0) *dst++ = '?';
} }
} }
}
/**
* Scans the string for valid characters and if it finds invalid ones,
* replaces them with a question mark '?' (if not ignored)
* @param str the string to validate
* @param last the last valid character of str
* @param settings the settings for the string validation.
*/
void str_validate(char *str, const char *last, StringValidationSettings settings)
{
char *dst = str;
str_validate(dst, str, last, settings);
*dst = '\0'; *dst = '\0';
} }
/**
* Scans the string for valid characters and if it finds invalid ones,
* replaces them with a question mark '?' (if not ignored)
* @param str the string to validate
* @param settings the settings for the string validation.
*/
std::string str_validate(const std::string &str, StringValidationSettings settings)
{
auto buf = str.data();
auto last = buf + str.size();
std::ostringstream dst;
std::ostreambuf_iterator<char> dst_iter(dst);
str_validate(dst_iter, buf, last, settings);
return dst.str();
}
/** /**
* Scans the string for valid characters and if it finds invalid ones, * Scans the string for valid characters and if it finds invalid ones,
* replaces them with a question mark '?'. * replaces them with a question mark '?'.

View File

@ -40,6 +40,7 @@ int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap)
char *CDECL str_fmt(const char *str, ...) WARN_FORMAT(1, 2); char *CDECL str_fmt(const char *str, ...) WARN_FORMAT(1, 2);
void str_validate(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); void str_validate(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
std::string str_validate(const std::string &str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
void ValidateString(const char *str); void ValidateString(const char *str);
void str_fix_scc_encoded(char *str, const char *last); void str_fix_scc_encoded(char *str, const char *last);