diff --git a/src/base_media_func.h b/src/base_media_func.h --- a/src/base_media_func.h +++ b/src/base_media_func.h @@ -21,7 +21,7 @@ */ #define fetch_metadata(name) \ 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, " Is %s readable for the user running OpenTTD?", full_filename); \ return false; \ @@ -42,28 +42,28 @@ bool BaseSetname = stredup(item->value); + this->name = stredup(item->value->c_str()); fetch_metadata("description"); - this->description[stredup("")] = stredup(item->value); + this->description[stredup("")] = stredup(item->value->c_str()); /* Add the translations of the descriptions too. */ 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"); - for (uint i = 0; item->value[i] != '\0' && i < 4; i++) { - this->shortname |= ((uint8)item->value[i]) << (i * 8); + for (uint i = 0; item->value.value()[i] != '\0' && i < 4; i++) { + this->shortname |= ((uint8)item->value.value()[i]) << (i * 8); } fetch_metadata("version"); - this->version = atoi(item->value); + this->version = atoi(item->value->c_str()); 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. */ IniGroup *files = ini->GetGroup("files"); @@ -73,13 +73,12 @@ bool BaseSetfiles[i]; /* Find the filename first. */ item = files->GetItem(BaseSet::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::file_names[i], full_filename); return false; } - const char *filename = item->value; - if (filename == nullptr) { + if (!item->value.has_value()) { file->filename = nullptr; /* If we list no file, that file must be valid */ this->valid_files++; @@ -87,15 +86,16 @@ bool BaseSetvalue->c_str(); file->filename = str_fmt("%s%s", path, filename); /* Then find the MD5 checksum */ 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); return false; } - char *c = item->value; + const char *c = item->value->c_str(); for (uint i = 0; i < sizeof(file->hash) * 2; i++, c++) { uint j; if ('0' <= *c && *c <= '9') { @@ -118,11 +118,11 @@ bool BaseSetGetItem(filename, 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); file->missing_warning = stredup(""); } else { - file->missing_warning = stredup(item->value); + file->missing_warning = stredup(item->value->c_str()); } file->check_result = T::CheckMD5(file, BASESET_DIR); @@ -170,7 +170,7 @@ bool BaseMedia::AddFile(const if (set->FillSetDetails(ini, path, filename)) { Tbase_set *duplicate = nullptr; for (Tbase_set *c = BaseMedia::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; break; } diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -356,11 +356,11 @@ bool GraphicsSet::FillSetDetails(IniFile IniItem *item; 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. */ 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; } diff --git a/src/hotkeys.cpp b/src/hotkeys.cpp --- a/src/hotkeys.cpp +++ b/src/hotkeys.cpp @@ -290,7 +290,7 @@ void HotkeyList::Load(IniFile *ini) IniItem *item = group->GetItem(hotkey->name, false); if (item != nullptr) { hotkey->keycodes.clear(); - if (item->value != nullptr) ParseHotkeys(hotkey, item->value); + if (item->value.has_value()) ParseHotkeys(hotkey, item->value->c_str()); } } } diff --git a/src/ini.cpp b/src/ini.cpp --- a/src/ini.cpp +++ b/src/ini.cpp @@ -12,9 +12,11 @@ #include "ini_type.h" #include "string_func.h" #include "fileio_func.h" +#include #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) # include +# include #endif #ifdef _WIN32 @@ -45,31 +47,33 @@ bool IniFile::SaveToDisk(const char *fil * that file. This to prevent that when OpenTTD crashes during the save * 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)); - strecat(file_new, ".new", lastof(file_new)); - FILE *f = fopen(file_new, "w"); - if (f == nullptr) return false; + std::ofstream os(OTTD2FS(file_new.c_str())); + if (os.fail()) return false; for (const IniGroup *group = this->group; group != nullptr; group = group->next) { - if (group->comment) fputs(group->comment, f); - fprintf(f, "[%s]\n", group->name); + os << group->comment << "[" << group->name << "]\n"; 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 */ - if (strchr(item->name, ' ') != nullptr || - item->name[0] == '[') { - fprintf(f, "\"%s\"", item->name); + if (item->name.find(' ') != std::string::npos || + item->name[0] == '[') { + os << "\"" << item->name << "\""; } 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 @@ -78,11 +82,10 @@ bool IniFile::SaveToDisk(const char *fil * (modification date etc.) is not important to us; only the real data is. */ #if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 - int ret = fdatasync(fileno(f)); - fclose(f); + int f = open(file_new.c_str(), O_RDWR); + int ret = fdatasync(f); + close(f); if (ret != 0) return false; -#else - fclose(f); #endif #if defined(_WIN32) @@ -91,7 +94,7 @@ bool IniFile::SaveToDisk(const char *fil /* Allocate space for one more \0 character. */ TCHAR tfilename[MAX_PATH + 1], tfile_new[MAX_PATH + 1]; _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. */ tfilename[MAX_PATH - 1] = '\0'; tfile_new[MAX_PATH - 1] = '\0'; @@ -107,8 +110,8 @@ bool IniFile::SaveToDisk(const char *fil shfopt.pTo = tfilename; SHFileOperation(&shfopt); #else - if (rename(file_new, filename) < 0) { - DEBUG(misc, 0, "Renaming %s to %s failed; configuration not saved", file_new, filename); + if (rename(file_new.c_str(), filename) < 0) { + DEBUG(misc, 0, "Renaming %s to %s failed; configuration not saved", file_new.c_str(), filename); } #endif diff --git a/src/ini_load.cpp b/src/ini_load.cpp --- a/src/ini_load.cpp +++ b/src/ini_load.cpp @@ -19,12 +19,10 @@ * Construct a new in-memory item of an Ini file. * @param parent the group we belong to * @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); - str_validate(this->name, this->name + strlen(this->name)); + this->name = str_validate(name); *parent->last_item = this; parent->last_item = &this->next; @@ -33,10 +31,6 @@ IniItem::IniItem(IniGroup *parent, const /** Free everything we loaded. */ IniItem::~IniItem() { - free(this->name); - free(this->value); - free(this->comment); - delete this->next; } @@ -46,20 +40,21 @@ IniItem::~IniItem() */ void IniItem::SetValue(const char *value) { - free(this->value); - this->value = stredup(value); + if (value == nullptr) { + this->value.reset(); + } else { + this->value.emplace(value); + } } /** * Construct a new in-memory group of an Ini file. * @param parent the file we belong to * @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); - str_validate(this->name, this->name + strlen(this->name)); + this->name = str_validate(name); this->last_item = &this->item; *parent->last_group = this; @@ -67,7 +62,7 @@ IniGroup::IniGroup(IniLoadFile *parent, if (parent->list_group_names != nullptr) { 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; return; } @@ -75,7 +70,7 @@ IniGroup::IniGroup(IniLoadFile *parent, } if (parent->seq_group_names != nullptr) { 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; return; } @@ -86,9 +81,6 @@ IniGroup::IniGroup(IniLoadFile *parent, /** Free everything we loaded. */ IniGroup::~IniGroup() { - free(this->name); - free(this->comment); - delete this->item; delete this->next; } @@ -100,16 +92,16 @@ IniGroup::~IniGroup() * @param create whether to create an item when not found or not. * @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) { - if (strcmp(item->name, name) == 0) return item; + if (item->name == name) return item; } if (!create) return nullptr; /* 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) : group(nullptr), - comment(nullptr), list_group_names(list_group_names), seq_group_names(seq_group_names) { @@ -139,7 +130,6 @@ IniLoadFile::IniLoadFile(const char * co /** Free everything we loaded. */ IniLoadFile::~IniLoadFile() { - free(this->comment); delete this->group; } @@ -147,26 +137,21 @@ IniLoadFile::~IniLoadFile() * Get the group with the given name. If it doesn't exist * and \a create_new is \c true create a new group. * @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. * @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? */ for (IniGroup *group = this->group; group != nullptr; group = group->next) { - if (!strncmp(group->name, name, len) && group->name[len] == 0) { - return group; - } + if (group->name == name) return group; } if (!create_new) return nullptr; /* otherwise make a new one */ - IniGroup *group = new IniGroup(this, name, name + len - 1); - group->comment = stredup("\n"); + IniGroup *group = new IniGroup(this, name); + group->comment = "\n"; return group; } @@ -182,7 +167,7 @@ void IniLoadFile::RemoveGroup(const char /* does it exist already? */ 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; } } @@ -260,17 +245,17 @@ void IniLoadFile::LoadFromDisk(const cha e--; } s++; // skip [ - group = new IniGroup(this, s, e - 1); + group = new IniGroup(this, std::string(s, e - s)); if (comment_size != 0) { - group->comment = stredup(comment, comment + comment_size - 1); + group->comment.assign(comment, comment_size); comment_size = 0; } } else if (group != nullptr) { if (group->type == IGT_SEQUENCE) { /* 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) { - item->comment = stredup(comment, comment + comment_size - 1); + item->comment.assign(comment, comment_size); comment_size = 0; } continue; @@ -286,9 +271,9 @@ void IniLoadFile::LoadFromDisk(const cha } /* 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) { - item->comment = stredup(comment, comment + comment_size - 1); + item->comment.assign(comment, comment_size); comment_size = 0; } @@ -304,8 +289,11 @@ void IniLoadFile::LoadFromDisk(const cha *e = '\0'; /* If the value was not quoted and empty, it must be nullptr */ - item->value = (!quoted && e == t) ? nullptr : stredup(t); - if (item->value != nullptr) str_validate(item->value, item->value + strlen(item->value)); + if (!quoted && e == t) { + item->value.reset(); + } else { + item->value = str_validate(std::string(t)); + } } else { /* it's an orphan item */ this->ReportFileError("ini: '", buffer, "' outside of group"); @@ -313,7 +301,7 @@ void IniLoadFile::LoadFromDisk(const cha } if (comment_size > 0) { - this->comment = stredup(comment, comment + comment_size - 1); + this->comment.assign(comment, comment_size); comment_size = 0; } diff --git a/src/ini_type.h b/src/ini_type.h --- a/src/ini_type.h +++ b/src/ini_type.h @@ -11,6 +11,8 @@ #define INI_TYPE_H #include "fileio_type.h" +#include +#include "3rdparty/optional/ottd_optional.h" /** Types of groups */ enum IniGroupType { @@ -21,12 +23,12 @@ enum IniGroupType { /** A single "line" in an ini file. */ struct IniItem { - IniItem *next; ///< The next item in this group - char *name; ///< The name of this item - char *value; ///< The value of this item - char *comment; ///< The comment associated with this item + IniItem *next; ///< The next item in this group + std::string name; ///< The name of this item + opt::optional value; ///< The value of 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(); void SetValue(const char *value); @@ -38,13 +40,13 @@ struct IniGroup { IniGroupType type; ///< type of group IniItem *item; ///< the first item in the group IniItem **last_item; ///< the last item in the group - char *name; ///< name of group - char *comment; ///< comment for group + std::string name; ///< name of 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(); - IniItem *GetItem(const char *name, bool create); + IniItem *GetItem(const std::string &name, bool create); void Clear(); }; @@ -52,14 +54,14 @@ struct IniGroup { struct IniLoadFile { IniGroup *group; ///< the first 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 *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); 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 LoadFromDisk(const char *filename, Subdirectory subdir); diff --git a/src/music.cpp b/src/music.cpp --- a/src/music.cpp +++ b/src/music.cpp @@ -135,10 +135,10 @@ bool MusicSet::FillSetDetails(IniFile *i this->songinfo[i].filename = filename; // non-owned pointer 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 */ 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); if (songname == nullptr) { 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 *i while (*trimmed_filename == PATHSEPCHAR) trimmed_filename++; 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 (item != nullptr && !StrEmpty(item->value)) { - strecpy(this->songinfo[i].songname, item->value, lastof(this->songinfo[i].songname)); + if (item != nullptr && item->value.has_value() && !item->value->empty()) { + strecpy(this->songinfo[i].songname, item->value->c_str(), lastof(this->songinfo[i].songname)); } else { DEBUG(grf, 0, "Base music set song name missing: %s", filename); return false; @@ -181,12 +181,12 @@ bool MusicSet::FillSetDetails(IniFile *i this->songinfo[i].tracknr = tracknr++; } - item = timingtrim->GetItem(trimmed_filename, false); - if (item != nullptr && !StrEmpty(item->value)) { - const char *endpos = strchr(item->value, ':'); - if (endpos != nullptr) { - this->songinfo[i].override_start = atoi(item->value); - this->songinfo[i].override_end = atoi(endpos + 1); + item = trimmed_filename != nullptr ? timingtrim->GetItem(trimmed_filename, false) : nullptr; + if (item != nullptr && item->value.has_value() && !item->value->empty()) { + auto endpos = item->value->find(':'); + if (endpos != std::string::npos) { + this->songinfo[i].override_start = atoi(item->value->c_str()); + this->songinfo[i].override_end = atoi(item->value->c_str() + endpos + 1); } } } diff --git a/src/settings.cpp b/src/settings.cpp --- a/src/settings.cpp +++ b/src/settings.cpp @@ -505,10 +505,6 @@ static void IniLoadSettings(IniFile *ini { IniGroup *group; IniGroup *group_def = ini->GetGroup(grpname); - IniItem *item; - const void *p; - void *ptr; - const char *s; for (; sd->save.cmd != SL_END; sd++) { const SettingDescBase *sdb = &sd->desc; @@ -517,30 +513,30 @@ static void IniLoadSettings(IniFile *ini if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to)) continue; /* For settings.xx.yy load the settings from [xx] yy = ? */ - s = strchr(sdb->name, '.'); - if (s != nullptr) { - group = ini->GetGroup(sdb->name, s - sdb->name); - s++; + std::string s{ sdb->name }; + auto sc = s.find('.'); + if (sc != std::string::npos) { + group = ini->GetGroup(s.substr(0, sc)); + s = s.substr(sc + 1); } else { - s = sdb->name; group = group_def; } - item = group->GetItem(s, false); + IniItem *item = group->GetItem(s, false); 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 */ item = group_def->GetItem(s, false); } if (item == nullptr) { /* 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 */ - const char *sc = strchr(s, '.'); - if (sc != nullptr) item = ini->GetGroup(s, sc - s)->GetItem(sc + 1, false); + sc = s.find('.'); + 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); - ptr = GetVariableAddress(object, sld); + const void *p = (item == nullptr) ? sdb->def : StringToVal(sdb, item->value.has_value() ? item->value->c_str() : nullptr); + void *ptr = GetVariableAddress(object, sld); switch (sdb->cmd) { case SDT_BOOLX: // All four are various types of (integer) numbers @@ -604,7 +600,6 @@ static void IniSaveSettings(IniFile *ini IniGroup *group_def = nullptr, *group; IniItem *item; char buf[512]; - const char *s; void *ptr; for (; sd->save.cmd != SL_END; sd++) { @@ -617,22 +612,22 @@ static void IniSaveSettings(IniFile *ini if (sld->conv & SLF_NOT_IN_CONFIG) continue; /* XXX - wtf is this?? (group override?) */ - s = strchr(sdb->name, '.'); - if (s != nullptr) { - group = ini->GetGroup(sdb->name, s - sdb->name); - s++; + std::string s{ sdb->name }; + auto sc = s.find('.'); + if (sc != std::string::npos) { + group = ini->GetGroup(s.substr(0, sc)); + s = s.substr(sc + 1); } else { if (group_def == nullptr) group_def = ini->GetGroup(grpname); - s = sdb->name; group = group_def; } item = group->GetItem(s, true); ptr = GetVariableAddress(object, sld); - if (item->value != nullptr) { + if (item->value.has_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 subtype (what kind of numbers do we have there) is in 0-7 */ @@ -714,8 +709,7 @@ static void IniSaveSettings(IniFile *ini } /* The value is different, that means we have to write it to the ini */ - free(item->value); - item->value = stredup(buf); + item->value.emplace(buf); } } @@ -737,7 +731,7 @@ static void IniLoadSettingList(IniFile * list.clear(); 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, c for (item = group->item; c < MAX_COMPANIES && item != nullptr; c++, item = item->next) { AIConfig *config = AIConfig::GetConfig(c, AIConfig::SSS_FORCE_NEWGAME); - config->Change(item->name); + config->Change(item->name.c_str()); if (!config->HasScript()) { - if (strcmp(item->name, "none") != 0) { - DEBUG(script, 0, "The AI by the name '%s' was no longer found, and removed from the list.", item->name); + if (item->name != "none") { + DEBUG(script, 0, "The AI by the name '%s' was no longer found, and removed from the list.", item->name.c_str()); 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, GameConfig *config = GameConfig::GetConfig(AIConfig::SSS_FORCE_NEWGAME); - config->Change(item->name); + config->Change(item->name.c_str()); if (!config->HasScript()) { - if (strcmp(item->name, "none") != 0) { - DEBUG(script, 0, "The GameScript by the name '%s' was no longer found, and removed from the list.", item->name); + if (item->name != "none") { + DEBUG(script, 0, "The GameScript by the name '%s' was no longer found, and removed from the list.", item->name.c_str()); 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. * @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) { int hi = DecodeHexNibble(pos[0]); @@ -1517,7 +1511,7 @@ static GRFConfig *GRFLoadConfig(IniFile GRFConfig *c = nullptr; uint8 grfid_buf[4], md5sum[16]; - char *filename = item->name; + const char *filename = item->name.c_str(); bool has_grfid = false; bool has_md5sum = false; @@ -1541,8 +1535,8 @@ static GRFConfig *GRFLoadConfig(IniFile if (c == nullptr) c = new GRFConfig(filename); /* Parse parameters */ - if (!StrEmpty(item->value)) { - int count = ParseIntList(item->value, c->param, lengthof(c->param)); + if (item->value.has_value() && !item->value->empty()) { + int count = ParseIntList(item->value->c_str(), c->param, lengthof(c->param)); if (count < 0) { SetDParamStr(0, filename); ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY, WL_CRITICAL); @@ -1565,7 +1559,7 @@ static GRFConfig *GRFLoadConfig(IniFile 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); delete c; continue; @@ -1776,8 +1770,8 @@ StringList GetGRFPresetList() std::unique_ptr ini(IniLoadConfig()); for (IniGroup *group = ini->group; group != nullptr; group = group->next) { - if (strncmp(group->name, "preset-", 7) == 0) { - list.emplace_back(group->name + 7); + if (group->name.compare(0, 7, "preset-") == 0) { + list.push_back(group->name.substr(7)); } } diff --git a/src/settingsgen/settingsgen.cpp b/src/settingsgen/settingsgen.cpp --- a/src/settingsgen/settingsgen.cpp +++ b/src/settingsgen/settingsgen.cpp @@ -214,11 +214,11 @@ static IniLoadFile *LoadIniFile(const ch */ 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) { for (IniItem *item = grp->item; item != nullptr; item = item->next) { - if (item->name) { - _stored_output.Add(item->name); + if (!item->name.empty()) { + _stored_output.Add(item->name.c_str()); _stored_output.Add("\n", 1); } } @@ -236,8 +236,8 @@ static const char *FindItemValue(const c { IniItem *item = grp->GetItem(name, false); if (item == nullptr && defaults != nullptr) item = defaults->GetItem(name, false); - if (item == nullptr || item->value == nullptr) return nullptr; - return item->value; + if (item == nullptr || !item->value.has_value()) return nullptr; + return item->value->c_str(); } /** @@ -249,19 +249,19 @@ static void DumpSections(IniLoadFile *if 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}; - IniGroup *default_grp = ifile->GetGroup(DEFAULTS_GROUP_NAME, 0, false); - IniGroup *templates_grp = ifile->GetGroup(TEMPLATES_GROUP_NAME, 0, false); + IniGroup *default_grp = ifile->GetGroup(DEFAULTS_GROUP_NAME, false); + IniGroup *templates_grp = ifile->GetGroup(TEMPLATES_GROUP_NAME, false); if (templates_grp == nullptr) return; /* Output every group, using its name as template name. */ for (IniGroup *grp = ifile->group; grp != nullptr; grp = grp->next) { 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; IniItem *template_item = templates_grp->GetItem(grp->name, false); // Find template value. - if (template_item == nullptr || template_item->value == nullptr) { - fprintf(stderr, "settingsgen: Warning: Cannot find template %s\n", grp->name); + if (template_item == nullptr || !template_item->value.has_value()) { + fprintf(stderr, "settingsgen: Warning: Cannot find template %s\n", grp->name.c_str()); continue; } @@ -281,7 +281,7 @@ static void DumpSections(IniLoadFile *if } /* 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') { if (*txt != '$') { _stored_output.Add(txt, 1); diff --git a/src/string.cpp b/src/string.cpp --- a/src/string.cpp +++ b/src/string.cpp @@ -185,18 +185,11 @@ void str_fix_scc_encoded(char *str, cons } -/** - * 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) +template +static void str_validate(T &dst, const char *str, const char *last, StringValidationSettings settings) { /* Assume the ABSOLUTE WORST to be in str as it comes from the outside. */ - char *dst = str; while (str <= last && *str != '\0') { size_t len = Utf8EncodedCharLen(*str); /* If the character is unknown, i.e. encoded length is 0 @@ -220,7 +213,7 @@ void str_validate(char *str, const char do { *dst++ = *str++; } while (--len != 0); - } else if ((settings & SVS_ALLOW_NEWLINE) != 0 && c == '\n') { + } else if ((settings & SVS_ALLOW_NEWLINE) != 0 && c == '\n') { *dst++ = *str++; } else { if ((settings & SVS_ALLOW_NEWLINE) != 0 && c == '\r' && str[1] == '\n') { @@ -232,12 +225,42 @@ void str_validate(char *str, const char 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'; } /** * 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 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, * replaces them with a question mark '?'. * @param str the string to validate */ diff --git a/src/string_func.h b/src/string_func.h --- a/src/string_func.h +++ b/src/string_func.h @@ -40,6 +40,7 @@ int CDECL vseprintf(char *str, const cha 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); +std::string str_validate(const std::string &str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); void ValidateString(const char *str); void str_fix_scc_encoded(char *str, const char *last);