@@ -102,25 +102,25 @@ void IConsolePrint(TextColour colour_cod
return;
}
if (_redirect_console_to_admin != INVALID_ADMIN_ID) {
NetworkServerSendAdminRcon(_redirect_console_to_admin, colour_code, string);
/* Create a copy of the string, strip if of colours and invalid
* characters and (when applicable) assign it to the console buffer */
str = stredup(string);
str_strip_colours(str);
str_validate(str, str + strlen(str));
StrMakeValidInPlace(str);
if (_network_dedicated) {
NetworkAdminConsole("console", str);
fprintf(stdout, "%s%s\n", GetLogPrefix(), str);
fflush(stdout);
IConsoleWriteToLogFile(str);
free(str); // free duplicated string since it's not used anymore
IConsoleGUIPrint(colour_code, str);
@@ -333,25 +333,25 @@ bool FiosFileScanner::AddFile(const std:
fios->type = type;
strecpy(fios->name, filename.c_str(), lastof(fios->name));
/* If the file doesn't have a title, use its filename */
const char *t = fios_title;
if (StrEmpty(fios_title)) {
auto ps = filename.rfind(PATHSEPCHAR);
t = filename.c_str() + (ps == std::string::npos ? 0 : ps + 1);
strecpy(fios->title, t, lastof(fios->title));
str_validate(fios->title, lastof(fios->title));
StrMakeValidInPlace(fios->title, lastof(fios->title));
return true;
/**
* Fill the list of the files in a directory, according to some arbitrary rule.
* @param fop Purpose of collecting the list.
* @param callback_proc The function that is called where you need to do the filtering.
* @param subdir The directory from where to start (global) searching.
* @param file_list Destination of the found files.
*/
@@ -385,25 +385,25 @@ static void FiosGetFileList(SaveLoadOper
/* found file must be directory, but not '.' or '..' */
if (FiosIsValidFile(_fios_path->c_str(), dirent, &sb) && S_ISDIR(sb.st_mode) &&
(!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
fios = &file_list.emplace_back();
fios->type = FIOS_TYPE_DIR;
fios->mtime = 0;
strecpy(fios->name, d_name, lastof(fios->name));
std::string dirname = std::string(d_name) + PATHSEP;
SetDParamStr(0, dirname);
GetString(fios->title, STR_SAVELOAD_DIRECTORY, lastof(fios->title));
closedir(dir);
/* Sort the subdirs always by name, ascending, remember user-sorting order */
{
SortingBits order = _savegame_sort_order;
_savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
std::sort(file_list.begin(), file_list.end());
_savegame_sort_order = order;
@@ -437,25 +437,25 @@ static void FiosGetFileList(SaveLoadOper
static void GetFileTitle(const std::string &file, char *title, const char *last, Subdirectory subdir)
std::string buf = file;
buf += ".title";
FILE *f = FioFOpenFile(buf, "r", subdir);
if (f == nullptr) return;
size_t read = fread(title, 1, last - title, f);
assert(title + read <= last);
title[read] = '\0';
str_validate(title, last);
StrMakeValidInPlace(title, last);
FioFCloseFile(f);
* Callback for FiosGetFileList. It tells if a file is a savegame or not.
* @param file Name of the file to check.
* @param ext A pointer to the extension identifier inside file
* @param title Buffer if a callback wants to lookup the title of the file; nullptr to skip the lookup
* @param last Last available byte in buffer (to prevent buffer overflows); not used when title == nullptr
* @return a FIOS_TYPE_* type of the found file, FIOS_TYPE_INVALID if not a savegame
* @see FiosGetFileList
@@ -161,19 +161,19 @@ void LoadFromHighScore()
for (i = 0; i < SP_SAVED_HIGHSCORE_END; i++) {
for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) {
byte length;
if (fread(&length, sizeof(length), 1, fp) != 1 ||
fread(hs->company, std::min<int>(lengthof(hs->company), length), 1, fp) > 1 || // Yes... could be 0 bytes too
fread(&hs->score, sizeof(hs->score), 1, fp) != 1 ||
fseek(fp, 2, SEEK_CUR) == -1) { // XXX - placeholder for hs->title, not saved anymore; compatibility
DEBUG(misc, 1, "Highscore corrupted");
i = SP_SAVED_HIGHSCORE_END;
break;
str_validate(hs->company, lastof(hs->company), SVS_NONE);
StrMakeValidInPlace(hs->company, lastof(hs->company), SVS_NONE);
hs->title = EndGameGetPerformanceTitleFromValue(hs->score);
fclose(fp);
@@ -13,25 +13,25 @@
#include "ini_type.h"
#include "string_func.h"
#include "safeguards.h"
* Construct a new in-memory item of an Ini file.
* @param parent the group we belong to
* @param name the name of the item
IniItem::IniItem(IniGroup *parent, const std::string &name) : next(nullptr)
this->name = str_validate(name);
this->name = StrMakeValid(name);
*parent->last_item = this;
parent->last_item = &this->next;
/** Free everything we loaded. */
IniItem::~IniItem()
delete this->next;
@@ -41,25 +41,25 @@ IniItem::~IniItem()
void IniItem::SetValue(const std::string_view value)
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
IniGroup::IniGroup(IniLoadFile *parent, const std::string &name) : next(nullptr), type(IGT_VARIABLES), item(nullptr)
this->last_item = &this->item;
*parent->last_group = this;
parent->last_group = &this->next;
if (parent->list_group_names != nullptr) {
for (uint i = 0; parent->list_group_names[i] != nullptr; i++) {
if (this->name == parent->list_group_names[i]) {
this->type = IGT_LIST;
@@ -279,25 +279,25 @@ void IniLoadFile::LoadFromDisk(const std
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) {
item->value.reset();
} else {
item->value = str_validate(std::string(t));
item->value = StrMakeValid(std::string(t));
/* it's an orphan item */
this->ReportFileError("ini: '", buffer, "' outside of group");
if (comment_size > 0) {
this->comment.assign(comment, comment_size);
comment_size = 0;
@@ -988,25 +988,25 @@ void QueryString::ClickEditBox(Window *w
/** Class for the string query window. */
struct QueryStringWindow : public Window
QueryString editbox; ///< Editbox.
QueryStringFlags flags; ///< Flags controlling behaviour of the window.
Dimension warning_size; ///< How much space to use for the warning text
QueryStringWindow(StringID str, StringID caption, uint max_bytes, uint max_chars, WindowDesc *desc, Window *parent, CharSetFilter afilter, QueryStringFlags flags) :
Window(desc), editbox(max_bytes, max_chars)
char *last_of = &this->editbox.text.buf[this->editbox.text.max_bytes - 1];
GetString(this->editbox.text.buf, str, last_of);
str_validate(this->editbox.text.buf, last_of, SVS_NONE);
StrMakeValidInPlace(this->editbox.text.buf, last_of, SVS_NONE);
/* Make sure the name isn't too long for the text buffer in the number of
* characters (not bytes). max_chars also counts the '\0' characters. */
while (Utf8StringLength(this->editbox.text.buf) + 1 > this->editbox.text.max_chars) {
*Utf8PrevChar(this->editbox.text.buf + strlen(this->editbox.text.buf)) = '\0';
this->editbox.text.UpdateSize();
if ((flags & QSF_ACCEPT_UNCHANGED) == 0) this->editbox.orig = stredup(this->editbox.text.buf);
this->querystrings[WID_QS_TEXT] = &this->editbox;
@@ -385,25 +385,25 @@ void Packet::Recv_string(char *buffer, s
if (size == 0 || pos == this->Size()) {
*buffer = '\0';
/* If size was sooner to zero then the string in the stream
* skip till the \0, so than packet can be read out correctly for the rest */
while (pos < this->Size() && this->buffer[pos] != '\0') pos++;
pos++;
assert(pos <= std::numeric_limits<PacketSize>::max());
this->pos = static_cast<PacketSize>(pos);
str_validate(bufp, last, settings);
StrMakeValidInPlace(bufp, last, settings);
* Reads characters (bytes) from the packet until it finds a '\0', or reaches a
* maximum of \c length characters.
* When the '\0' has not been reached in the first \c length read characters,
* more characters are read from the packet until '\0' has been reached. However,
* these characters will not end up in the returned string.
* The length of the returned string will be at most \c length - 1 characters.
* @param length The maximum length of the string including '\0'.
* @param settings The string validation settings.
* @return The validated string.
@@ -414,23 +414,23 @@ std::string Packet::Recv_string(size_t l
/* Both loops with Recv_uint8 terminate when reading past the end of the
* packet as Recv_uint8 then closes the connection and returns 0. */
std::string str;
char character;
while (--length > 0 && (character = this->Recv_uint8()) != '\0') str.push_back(character);
if (length == 0) {
/* The string in the packet was longer. Read until the termination. */
while (this->Recv_uint8() != '\0') {}
return str_validate(str, settings);
return StrMakeValid(str, settings);
* Get the amount of bytes that are still available for the Transfer functions.
* @return The number of bytes that still have to be transfered.
size_t Packet::RemainingBytesToTransfer() const
return this->Size() - this->pos;
@@ -2589,25 +2589,25 @@ static ChangeInfoResult LoadTranslationT
* Helper to read a DWord worth of bytes from the reader
* and to return it as a valid string.
* @param reader The source of the DWord.
* @return The read DWord as string.
static std::string ReadDWordAsString(ByteReader *reader)
char output[5];
for (int i = 0; i < 4; i++) output[i] = reader->ReadByte();
output[4] = '\0';
str_validate(output, lastof(output));
StrMakeValidInPlace(output, lastof(output));
return std::string(output);
* Define properties for global variables
* @param gvid ID of the global variable.
* @param numinfo Number of subsequent IDs to change the property for.
* @param prop The property to change.
* @param buf The property value.
* @return ChangeInfoResult.
@@ -165,25 +165,25 @@ void ShowOSErrorBox(const char *buf, boo
rc = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, (const unsigned char *)buf, (const unsigned char *)"OpenTTD", 0, MB_OK | MB_MOVEABLE | MB_ERROR);
/* terminate PM env. */
WinDestroyMsgQueue(hmq);
WinTerminate(hab);
int CDECL main(int argc, char *argv[])
SetRandomSeed(time(nullptr));
/* Make sure our arguments contain only valid UTF-8 characters. */
for (int i = 0; i < argc; i++) ValidateString(argv[i]);
for (int i = 0; i < argc; i++) StrMakeValidInPlace(argv[i]);
return openttd_main(argc, argv);
bool GetClipboardContents(char *buffer, const char *last)
/* XXX -- Currently no clipboard support implemented with GCC */
#ifndef __INNOTEK_LIBC__
HAB hab = 0;
if (WinOpenClipbrd(hab))
@@ -234,25 +234,25 @@ void ShowOSErrorBox(const char *buf, boo
#endif
#ifdef WITH_COCOA
void CocoaSetupAutoreleasePool();
void CocoaReleaseAutoreleasePool();
CocoaSetupAutoreleasePool();
/* This is passed if we are launched by double-clicking */
if (argc >= 2 && strncmp(argv[1], "-psn", 4) == 0) {
argv[1] = nullptr;
argc = 1;
CrashLog::InitialiseCrashLog();
@@ -420,25 +420,25 @@ int APIENTRY WinMain(HINSTANCE hInstance
#if defined(_DEBUG)
CreateConsole();
_set_error_mode(_OUT_TO_MSGBOX); // force assertion output to messagebox
/* setup random seed to something quite random */
SetRandomSeed(GetTickCount());
argc = ParseCommandLine(cmdline, argv, lengthof(argv));
openttd_main(argc, argv);
/* Restore system timer resolution. */
timeEndPeriod(1);
free(cmdline);
return 0;
char *getcwd(char *buf, size_t size)
@@ -225,25 +225,25 @@ static bool VerifyOldNameChecksum(char *
static inline bool CheckOldSavegameType(FILE *f, char *temp, const char *last, uint len)
assert(last - temp + 1 >= (int)len);
if (fread(temp, 1, len, f) != len) {
temp[0] = '\0'; // if reading failed, make the name empty
return false;
bool ret = VerifyOldNameChecksum(temp, len);
temp[len - 2] = '\0'; // name is null-terminated in savegame, but it's better to be sure
str_validate(temp, last);
StrMakeValidInPlace(temp, last);
return ret;
static SavegameType DetermineOldSavegameType(FILE *f, char *title, const char *last)
static_assert(TTD_HEADER_SIZE >= TTO_HEADER_SIZE);
char temp[TTD_HEADER_SIZE] = "Unknown";
SavegameType type = SGT_TTO;
/* Can't fseek to 0 as in tar files that is not correct */
@@ -963,25 +963,25 @@ static void SlString(void *ptr, size_t l
((char *)ptr)[len] = '\0'; // properly terminate the string
StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK;
if ((conv & SLF_ALLOW_CONTROL) != 0) {
settings = settings | SVS_ALLOW_CONTROL_CODE;
if (IsSavegameVersionBefore(SLV_169)) {
str_fix_scc_encoded((char *)ptr, (char *)ptr + len);
if ((conv & SLF_ALLOW_NEWLINE) != 0) {
settings = settings | SVS_ALLOW_NEWLINE;
str_validate((char *)ptr, (char *)ptr + len, settings);
StrMakeValidInPlace((char *)ptr, (char *)ptr + len, settings);
case SLA_PTRS: break;
case SLA_NULL: break;
default: NOT_REACHED();
* Save/Load a \c std::string.
* @param ptr the string being manipulated
* @param conv must be SLE_FILE_STRING
@@ -1007,25 +1007,25 @@ static void SlStdString(void *ptr, VarTy
buf[len] = '\0'; // properly terminate the string
str_fix_scc_encoded(buf, buf + len);
str_validate(buf, buf + len, settings);
StrMakeValidInPlace(buf, buf + len, settings);
// Store sanitized string.
str->assign(buf);
@@ -274,25 +274,25 @@ ScriptObject::ActiveInstance::~ActiveIns
return GetStorage()->event_data;
/* static */ void *&ScriptObject::GetLogPointer()
return GetStorage()->log_data;
/* static */ char *ScriptObject::GetString(StringID string)
char buffer[64];
::GetString(buffer, string, lastof(buffer));
::str_validate(buffer, lastof(buffer), SVS_NONE);
::StrMakeValidInPlace(buffer, lastof(buffer), SVS_NONE);
return ::stredup(buffer);
/* static */ void ScriptObject::SetCallbackVariable(int index, int value)
if ((size_t)index >= GetStorage()->callback_value.size()) GetStorage()->callback_value.resize(index + 1);
GetStorage()->callback_value[index] = value;
/* static */ int ScriptObject::GetCallbackVariable(int index)
return GetStorage()->callback_value[index];
@@ -303,25 +303,25 @@ ScriptObject::ActiveInstance::~ActiveIns
if (!ScriptObject::CanSuspend()) {
throw Script_FatalError("You are not allowed to execute any DoCommand (even indirect) in your constructor, Save(), Load(), and any valuator.");
if (ScriptObject::GetCompany() != OWNER_DEITY && !::Company::IsValidID(ScriptObject::GetCompany())) {
ScriptObject::SetLastError(ScriptError::ERR_PRECONDITION_INVALID_COMPANY);
if (!StrEmpty(text) && (GetCommandFlags(cmd) & CMD_STR_CTRL) == 0) {
/* The string must be valid, i.e. not contain special codes. Since some
* can be made with GSText, make sure the control codes are removed. */
::str_validate(const_cast<char *>(text), text + strlen(text), SVS_NONE);
::StrMakeValidInPlace(text, SVS_NONE);
/* Set the default callback to return a true/false result of the DoCommand */
if (callback == nullptr) callback = &ScriptInstance::DoCommandReturn;
/* Are we only interested in the estimate costs? */
bool estimate_only = GetDoCommandMode() != nullptr && !GetDoCommandMode()();
/* Only set p2 when the command does not come from the network. */
if (GetCommandFlags(cmd) & CMD_CLIENT_ID && p2 == 0) p2 = UINT32_MAX;
/* Store the command for command callback validation. */
@@ -73,25 +73,25 @@ SQInteger ScriptText::_SetParam(int para
if (this->paramt[parameter] != nullptr) this->paramt[parameter]->Release();
this->parami[parameter] = 0;
this->params[parameter] = nullptr;
this->paramt[parameter] = nullptr;
switch (sq_gettype(vm, -1)) {
case OT_STRING: {
const SQChar *value;
sq_getstring(vm, -1, &value);
this->params[parameter] = stredup(value);
ValidateString(this->params[parameter]);
StrMakeValidInPlace(this->params[parameter]);
case OT_INTEGER: {
SQInteger value;
sq_getinteger(vm, -1, &value);
this->parami[parameter] = value;
case OT_INSTANCE: {
@@ -148,25 +148,25 @@ SQInteger ScriptText::AddParam(HSQUIRREL
/* Push our own instance back on top of the stack */
sq_push(vm, 1);
return 1;
SQInteger ScriptText::_set(HSQUIRRELVM vm)
int32 k;
if (sq_gettype(vm, 2) == OT_STRING) {
const SQChar *key_string;
sq_getstring(vm, 2, &key_string);
ValidateString(key_string);
StrMakeValidInPlace(key_string);
if (strncmp(key_string, "param_", 6) != 0 || strlen(key_string) > 8) return SQ_ERROR;
k = atoi(key_string + 6);
} else if (sq_gettype(vm, 2) == OT_INTEGER) {
SQInteger key;
sq_getinteger(vm, 2, &key);
k = (int32)key;
return SQ_ERROR;
if (k > SCRIPT_TEXT_MAX_PARAMETERS) return SQ_ERROR;
@@ -113,44 +113,44 @@ SQInteger ScriptInfo::AddSetting(HSQUIRR
ScriptConfigItem config;
memset(&config, 0, sizeof(config));
config.max_value = 1;
config.step_size = 1;
uint items = 0;
/* Read the table, and find all properties we care about */
sq_pushnull(vm);
while (SQ_SUCCEEDED(sq_next(vm, -2))) {
const SQChar *key;
if (SQ_FAILED(sq_getstring(vm, -2, &key))) return SQ_ERROR;
ValidateString(key);
StrMakeValidInPlace(key);
if (strcmp(key, "name") == 0) {
const SQChar *sqvalue;
if (SQ_FAILED(sq_getstring(vm, -1, &sqvalue))) return SQ_ERROR;
char *name = stredup(sqvalue);
char *s;
ValidateString(name);
StrMakeValidInPlace(name);
/* Don't allow '=' and ',' in configure setting names, as we need those
* 2 chars to nicely store the settings as a string. */
while ((s = strchr(name, '=')) != nullptr) *s = '_';
while ((s = strchr(name, ',')) != nullptr) *s = '_';
config.name = name;
items |= 0x001;
} else if (strcmp(key, "description") == 0) {
const SQChar *sqdescription;
if (SQ_FAILED(sq_getstring(vm, -1, &sqdescription))) return SQ_ERROR;
config.description = stredup(sqdescription);
ValidateString(config.description);
StrMakeValidInPlace(config.description);
items |= 0x002;
} else if (strcmp(key, "min_value") == 0) {
SQInteger res;
if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
config.min_value = res;
items |= 0x004;
} else if (strcmp(key, "max_value") == 0) {
config.max_value = res;
items |= 0x008;
} else if (strcmp(key, "easy_value") == 0) {
@@ -217,25 +217,25 @@ SQInteger ScriptInfo::AddSetting(HSQUIRR
this->engine->ThrowError(error);
this->config_list.push_back(config);
SQInteger ScriptInfo::AddLabels(HSQUIRRELVM vm)
const SQChar *setting_name;
if (SQ_FAILED(sq_getstring(vm, -2, &setting_name))) return SQ_ERROR;
ValidateString(setting_name);
StrMakeValidInPlace(setting_name);
ScriptConfigItem *config = nullptr;
for (auto &item : this->config_list) {
if (strcmp(item.name, setting_name) == 0) config = &item;
if (config == nullptr) {
char error[1024];
seprintf(error, lastof(error), "Trying to add labels for non-defined setting '%s'", setting_name);
@@ -244,25 +244,25 @@ SQInteger ScriptInfo::AddLabels(HSQUIRRE
config->labels = new LabelMapping;
/* Read the table and find all labels */
const SQChar *label;
if (SQ_FAILED(sq_getstring(vm, -2, &key_string))) return SQ_ERROR;
if (SQ_FAILED(sq_getstring(vm, -1, &label))) return SQ_ERROR;
/* Because squirrel doesn't support identifiers starting with a digit,
* we skip the first character. */
int key = atoi(key_string + 1);
ValidateString(label);
StrMakeValidInPlace(label);
/* !Contains() prevents stredup from leaking. */
if (!config->labels->Contains(key)) config->labels->Insert(key, stredup(label));
sq_pop(vm, 2);
sq_pop(vm, 1);
/* Check labels for completeness */
config->complete_labels = true;
for (int value = config->min_value; value <= config->max_value; value++) {
if (!config->labels->Contains(value)) {
@@ -439,25 +439,25 @@ bool Squirrel::CallMethod(HSQOBJECT inst
/* Restore the return-value location. */
this->vm->_suspended_target = last_target;
bool Squirrel::CallStringMethodStrdup(HSQOBJECT instance, const char *method_name, const char **res, int suspend)
HSQOBJECT ret;
if (!this->CallMethod(instance, method_name, &ret, suspend)) return false;
if (ret._type != OT_STRING) return false;
*res = stredup(ObjectToString(&ret));
ValidateString(*res);
StrMakeValidInPlace(*res);
bool Squirrel::CallIntegerMethod(HSQOBJECT instance, const char *method_name, int *res, int suspend)
if (ret._type != OT_INTEGER) return false;
*res = ObjectToInteger(&ret);
@@ -107,25 +107,25 @@ namespace SQConvert {
template <> inline bool GetParam(ForceType<bool> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQBool tmp; sq_getbool (vm, index, &tmp); return tmp != 0; }
template <> inline void *GetParam(ForceType<void *> , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer tmp; sq_getuserpointer(vm, index, &tmp); return tmp; }
template <> inline const char *GetParam(ForceType<const char *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr)
/* Convert what-ever there is as parameter to a string */
sq_tostring(vm, index);
const SQChar *tmp;
sq_getstring(vm, -1, &tmp);
char *tmp_str = stredup(tmp);
sq_poptop(vm);
ptr->push_back((void *)tmp_str);
str_validate(tmp_str, tmp_str + strlen(tmp_str));
StrMakeValidInPlace(tmp_str);
return tmp_str;
template <> inline Array *GetParam(ForceType<Array *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr)
/* Sanity check of the size. */
if (sq_getsize(vm, index) > UINT16_MAX) throw sq_throwerror(vm, "an array used as parameter to a function is too large");
SQObject obj;
sq_getstackobj(vm, index, &obj);
sq_pushobject(vm, obj);
@@ -488,25 +488,25 @@ int32 IntSettingDesc::Read(const void *o
* In the case of string settings this is ensuring the string contains only accepted
* Utf8 characters and is at most the maximum length defined in this setting.
* @param str The string to make valid.
void StringSettingDesc::MakeValueValid(std::string &str) const
if (this->max_length == 0 || str.size() < this->max_length) return;
/* In case a maximum length is imposed by the setting, the length
* includes the '\0' termination for network transfer purposes.
* Also ensure the string is valid after chopping of some bytes. */
std::string stdstr(str, this->max_length - 1);
str.assign(str_validate(stdstr, SVS_NONE));
str.assign(StrMakeValid(stdstr, SVS_NONE));
* Write a string to the actual setting.
* @param object The object the setting is to be saved in.
* @param str The string to save.
void StringSettingDesc::Write(const void *object, const std::string &str) const
reinterpret_cast<std::string *>(GetVariableAddress(object, &this->save))->assign(str);
@@ -177,25 +177,25 @@ void str_fix_scc_encoded(char *str, cons
if (c == '\0') break;
if (c == 0xE028 || c == 0xE02A) {
c = SCC_ENCODED;
str += Utf8Encode(str, c);
*str = '\0';
template <class T>
static void str_validate(T &dst, const char *str, const char *last, StringValidationSettings settings)
static void StrMakeValidInPlace(T &dst, const char *str, const char *last, StringValidationSettings settings)
/* Assume the ABSOLUTE WORST to be in str as it comes from the outside. */
while (str <= last && *str != '\0') {
size_t len = Utf8EncodedCharLen(*str);
WChar c;
/* If the first byte does not look like the first byte of an encoded
* character, i.e. encoded length is 0, then this byte is definitely bad
* and it should be skipped.
* When the first byte looks like the first byte of an encoded character,
* then the remaining bytes in the string are checked whether the whole
* encoded character can be there. If that is not the case, this byte is
@@ -237,68 +237,69 @@ static void str_validate(T &dst, const c
continue;
/* Replace the undesirable character with a question mark */
str += len;
if ((settings & SVS_REPLACE_WITH_QUESTION_MARK) != 0) *dst++ = '?';
/* String termination, if needed, is left to the caller of this function. */
* 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.
* Scans the string for invalid characters and replaces then 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)
void StrMakeValidInPlace(char *str, const char *last, StringValidationSettings settings)
char *dst = str;
str_validate(dst, str, last, settings);
StrMakeValidInPlace(dst, str, last, settings);
*dst = '\0';
* Only use this function when you are sure the string ends with a '\0';
* otherwise use StrMakeValidInPlace(str, last, settings) variant.
* @param str The string (of which you are sure ends with '\0') to validate.
std::string str_validate(const std::string &str, StringValidationSettings settings)
void StrMakeValidInPlace(const char *str, StringValidationSettings settings)
/* We know it is '\0' terminated. */
StrMakeValidInPlace(const_cast<char *>(str), str + strlen(str), settings);
std::string StrMakeValid(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);
StrMakeValidInPlace(dst_iter, buf, last, settings);
return dst.str();
* replaces them with a question mark '?'.
void ValidateString(const char *str)
str_validate(const_cast<char *>(str), str + strlen(str) + 1);
* Checks whether the given string is valid, i.e. contains only
* valid (printable) characters and is properly terminated.
* @param last The last character of the string, i.e. the string
* must be terminated here or earlier.
bool StrValid(const char *str, const char *last)
@@ -30,27 +30,27 @@
#include "core/bitmath_func.hpp"
#include "string_type.h"
char *strecat(char *dst, const char *src, const char *last) NOACCESS(3);
char *strecpy(char *dst, const char *src, const char *last) NOACCESS(3);
char *stredup(const char *src, const char *last = nullptr) NOACCESS(2);
int CDECL seprintf(char *str, const char *last, const char *format, ...) WARN_FORMAT(3, 4) NOACCESS(2);
int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap) WARN_FORMAT(3, 0) NOACCESS(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) NOACCESS(2);
[[nodiscard]] std::string str_validate(const std::string &str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
void ValidateString(const char *str);
void StrMakeValidInPlace(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK) NOACCESS(2);
[[nodiscard]] std::string StrMakeValid(const std::string &str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
void StrMakeValidInPlace(const char *str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
void str_fix_scc_encoded(char *str, const char *last) NOACCESS(2);
void str_strip_colours(char *str);
bool strtolower(char *str);
bool strtolower(std::string &str, std::string::size_type offs = 0);
bool StrValid(const char *str, const char *last) NOACCESS(2);
void StrTrimInPlace(std::string &str);
* Check if a string buffer is empty.
*
@@ -366,34 +366,34 @@ static void Xunzip(byte **bufp, size_t *
#if defined(WITH_LIBLZMA)
/* In-place xunzip */
if (strcmp(suffix, ".xz") == 0) Xunzip((byte**)&this->text, &filesize);
if (!this->text) return;
/* Add space for trailing \0 */
this->text = ReallocT(this->text, filesize + 1);
this->text[filesize] = '\0';
/* Replace tabs and line feeds with a space since str_validate removes those. */
/* Replace tabs and line feeds with a space since StrMakeValidInPlace removes those. */
for (char *p = this->text; *p != '\0'; p++) {
if (*p == '\t' || *p == '\r') *p = ' ';
/* Check for the byte-order-mark, and skip it if needed. */
char *p = this->text + (strncmp(u8"\ufeff", this->text, 3) == 0 ? 3 : 0);
/* Make sure the string is a valid UTF-8 sequence. */
str_validate(p, this->text + filesize, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE);
StrMakeValidInPlace(p, this->text + filesize, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE);
/* Split the string on newlines. */
int row = 0;
this->lines.emplace_back(row, p);
for (; *p != '\0'; p++) {
if (*p == '\n') {
*p = '\0';
this->lines.emplace_back(++row, p + 1);
/* Calculate maximum text line length. */
@@ -220,25 +220,25 @@ static void DedicatedHandleKeyInput()
static_assert(lengthof(_win_console_thread_buffer) <= lengthof(input_line));
strecpy(input_line, _win_console_thread_buffer, lastof(input_line));
SetEvent(_hWaitForInputHandling);
/* Remove trailing \r or \n */
for (char *c = input_line; *c != '\0'; c++) {
if (*c == '\n' || *c == '\r' || c == lastof(input_line)) {
*c = '\0';
str_validate(input_line, lastof(input_line));
StrMakeValidInPlace(input_line, lastof(input_line));
IConsoleCmdExec(input_line); // execute command
void VideoDriver_Dedicated::MainLoop()
/* Signal handlers */
#if defined(UNIX)
signal(SIGTERM, DedicatedSignalHandler);
signal(SIGINT, DedicatedSignalHandler);
signal(SIGQUIT, DedicatedSignalHandler);
Status change: