diff --git a/src/misc/getoptdata.cpp b/src/misc/getoptdata.cpp index fbc4907375..73494bc1dc 100644 --- a/src/misc/getoptdata.cpp +++ b/src/misc/getoptdata.cpp @@ -21,30 +21,30 @@ */ int GetOptData::GetOpt() { - const char *s = this->cont; - if (s == nullptr) { + std::string_view s = this->cont; + if (s.empty()) { if (this->arguments.empty()) return -1; // No arguments left -> finished. s = this->arguments[0]; - if (*s != '-') return -1; // No leading '-' -> not an option -> finished. + if (s[0] != '-') return -1; // No leading '-' -> not an option -> finished. this->arguments = this->arguments.subspan(1); /* Is it a long option? */ for (auto &option : this->options) { - if (option.longname != nullptr && !strcmp(option.longname, s)) { // Long options always use the entire argument. - this->cont = nullptr; + if (option.longname == s) { // Long options always use the entire argument. + this->cont = {}; return this->GetOpt(option); } } - s++; // Skip leading '-'. + s.remove_prefix(1); // Skip leading '-'. } /* Is it a short option? */ for (auto &option : this->options) { - if (option.shortname != '\0' && *s == option.shortname) { - this->cont = (s[1] != '\0') ? s + 1 : nullptr; + if (option.shortname != '\0' && s[0] == option.shortname) { + this->cont = s.substr(1); return this->GetOpt(option); } } @@ -54,16 +54,16 @@ int GetOptData::GetOpt() int GetOptData::GetOpt(const OptionData &option) { - this->opt = nullptr; + this->opt = {}; switch (option.type) { case ODF_NO_VALUE: return option.id; case ODF_HAS_VALUE: case ODF_OPTIONAL_VALUE: - if (this->cont != nullptr) { // Remainder of the argument is the option value. + if (!this->cont.empty()) { // Remainder of the argument is the option value. this->opt = this->cont; - this->cont = nullptr; + this->cont = {}; return option.id; } /* No more arguments, either return an error or a value-less option. */ diff --git a/src/misc/getoptdata.h b/src/misc/getoptdata.h index 244c96358b..a5fe16733d 100644 --- a/src/misc/getoptdata.h +++ b/src/misc/getoptdata.h @@ -22,18 +22,18 @@ struct OptionData { OptionDataType type; ///< The type of option. char id; ///< Unique identification of this option data, often the same as #shortname. char shortname = '\0'; ///< Short option letter if available, else use \c '\0'. - const char *longname = nullptr; ///< Long option name including '-'/'--' prefix, use \c nullptr if not available. + std::string_view longname{}; ///< Long option name including '-'/'--' prefix, leave empty if not available. }; /** Data storage for parsing command line options. */ struct GetOptData { using OptionSpan = std::span; - using ArgumentSpan = std::span; + using ArgumentSpan = std::span; ArgumentSpan arguments; ///< Remaining command line arguments. const OptionSpan options; ///< Command line option descriptions. - const char *opt = nullptr; ///< Option value, if available (else \c nullptr). - const char *cont = nullptr; ///< Next call to #GetOpt should start here (in the middle of an argument). + std::string_view opt; ///< Option value, if available (else empty). + std::string_view cont; ///< Next call to #GetOpt should start here (in the middle of an argument). /** * Constructor of the data store. diff --git a/src/openttd.cpp b/src/openttd.cpp index ab0f807153..7e1e086239 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -497,7 +497,7 @@ static std::vector CreateOptions() * @param arguments The command line arguments passed to the application. * @return 0 when there is no error. */ -int openttd_main(std::span arguments) +int openttd_main(std::span arguments) { _game_session_stats.start_time = std::chrono::steady_clock::now(); _game_session_stats.savegame_size = std::nullopt; @@ -541,7 +541,7 @@ int openttd_main(std::span arguments) blitter = "null"; dedicated = true; SetDebugString("net=4", ShowInfoI); - if (mgo.opt != nullptr) { + if (!mgo.opt.empty()) { scanner->dedicated_host = ParseFullConnectionString(mgo.opt, scanner->dedicated_port); } break; @@ -564,7 +564,7 @@ int openttd_main(std::span arguments) #if defined(_WIN32) CreateConsole(); #endif - if (mgo.opt != nullptr) SetDebugString(mgo.opt, ShowInfoI); + if (!mgo.opt.empty()) SetDebugString(mgo.opt, ShowInfoI); break; } case 'e': @@ -577,7 +577,7 @@ int openttd_main(std::span arguments) } break; case 'g': - if (mgo.opt != nullptr) { + if (!mgo.opt.empty()) { _file_to_saveload.name = mgo.opt; std::string extension = FS2OTTD(std::filesystem::path(OTTD2FS(_file_to_saveload.name)).extension().native()); @@ -609,7 +609,7 @@ int openttd_main(std::span arguments) break; case 'q': { DeterminePaths(arguments[0], only_local_path); - if (StrEmpty(mgo.opt)) { + if (mgo.opt.empty()) { ret = 1; return ret; } diff --git a/src/openttd.h b/src/openttd.h index 6e2edb4b00..340f279d08 100644 --- a/src/openttd.h +++ b/src/openttd.h @@ -83,7 +83,7 @@ extern PauseModes _pause_mode; void AskExitGame(); void AskExitToGameMenu(); -int openttd_main(std::span arguments); +int openttd_main(std::span arguments); void StateGameLoop(); void HandleExitGameRequest(); diff --git a/src/os/macosx/osx_main.cpp b/src/os/macosx/osx_main.cpp index 7d8a7994e9..12385d072d 100644 --- a/src/os/macosx/osx_main.cpp +++ b/src/os/macosx/osx_main.cpp @@ -26,13 +26,16 @@ void CocoaReleaseAutoreleasePool(); int CDECL main(int argc, char *argv[]) { /* Make sure our arguments contain only valid UTF-8 characters. */ - for (int i = 0; i < argc; i++) StrMakeValidInPlace(argv[i]); + std::vector params; + for (int i = 0; i < argc; ++i) { + StrMakeValidInPlace(argv[i]); + params.emplace_back(argv[i]); + } 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; + if (params.size() >= 2 && params[1].starts_with("-psn")) { + params.resize(1); } CrashLog::InitialiseCrashLog(); @@ -41,7 +44,7 @@ int CDECL main(int argc, char *argv[]) signal(SIGPIPE, SIG_IGN); - int ret = openttd_main(std::span(argv, argc)); + int ret = openttd_main(params); CocoaReleaseAutoreleasePool(); diff --git a/src/os/unix/unix_main.cpp b/src/os/unix/unix_main.cpp index 4a71b886b3..046a46d2b5 100644 --- a/src/os/unix/unix_main.cpp +++ b/src/os/unix/unix_main.cpp @@ -21,7 +21,11 @@ int CDECL main(int argc, char *argv[]) { /* Make sure our arguments contain only valid UTF-8 characters. */ - for (int i = 0; i < argc; i++) StrMakeValidInPlace(argv[i]); + std::vector params; + for (int i = 0; i < argc; ++i) { + StrMakeValidInPlace(argv[i]); + params.emplace_back(argv[i]); + } CrashLog::InitialiseCrashLog(); @@ -29,5 +33,5 @@ int CDECL main(int argc, char *argv[]) signal(SIGPIPE, SIG_IGN); - return openttd_main(std::span(argv, argc)); + return openttd_main(params); } diff --git a/src/os/windows/win32_main.cpp b/src/os/windows/win32_main.cpp index 9b5c815e3a..7a1c275fc3 100644 --- a/src/os/windows/win32_main.cpp +++ b/src/os/windows/win32_main.cpp @@ -12,37 +12,31 @@ #include #include "../../openttd.h" #include "../../core/random_func.hpp" +#include "../../core/string_consumer.hpp" #include "../../string_func.h" #include "../../crashlog.h" #include "../../debug.h" #include "../../safeguards.h" -static auto ParseCommandLine(char *line) +static auto ParseCommandLine(std::string_view line) { - std::vector arguments; - for (;;) { - /* skip whitespace */ - while (*line == ' ' || *line == '\t') line++; + std::vector arguments; - /* end? */ - if (*line == '\0') break; + StringConsumer consumer{line}; + while (consumer.AnyBytesLeft()) { + consumer.SkipUntilCharNotIn(StringConsumer::WHITESPACE_NO_NEWLINE); + if (!consumer.AnyBytesLeft()) break; - /* special handling when quoted */ - if (*line == '"') { - arguments.push_back(++line); - while (*line != '"') { - if (*line == '\0') return arguments; - line++; - } + std::string_view argument; + if (consumer.ReadIf("\"")) { + /* special handling when quoted */ + argument = consumer.ReadUntil("\"", StringConsumer::SKIP_ONE_SEPARATOR); } else { - arguments.push_back(line); - while (*line != ' ' && *line != '\t') { - if (*line == '\0') return arguments; - line++; - } + argument = consumer.ReadUntilCharIn(StringConsumer::WHITESPACE_NO_NEWLINE); } - *line++ = '\0'; + + arguments.push_back(argument); }; return arguments; @@ -57,8 +51,8 @@ int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int) CrashLog::InitialiseCrashLog(); - /* Convert the command line to UTF-8. */ - std::string cmdline = FS2OTTD(GetCommandLine()); + /* Convert the command line to valid UTF-8. */ + std::string cmdline = StrMakeValid(FS2OTTD(GetCommandLine())); /* Set the console codepage to UTF-8. */ SetConsoleOutputCP(CP_UTF8); @@ -72,11 +66,7 @@ int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int) /* setup random seed to something quite random */ SetRandomSeed(GetTickCount()); - auto arguments = ParseCommandLine(cmdline.data()); - - /* Make sure our arguments contain only valid UTF-8 characters. */ - for (auto argument : arguments) StrMakeValidInPlace(argument); - + auto arguments = ParseCommandLine(cmdline); int ret = openttd_main(arguments); /* Restore system timer resolution. */ diff --git a/src/settingsgen/settingsgen.cpp b/src/settingsgen/settingsgen.cpp index e80995c08f..987e8e2766 100644 --- a/src/settingsgen/settingsgen.cpp +++ b/src/settingsgen/settingsgen.cpp @@ -403,7 +403,9 @@ int CDECL main(int argc, char *argv[]) std::optional before_file; std::optional after_file; - GetOptData mgo(std::span(argv + 1, argc - 1), _opts); + std::vector params; + for (int i = 1; i < argc; ++i) params.emplace_back(argv[i]); + GetOptData mgo(params, _opts); for (;;) { int i = mgo.GetOpt(); if (i == -1) break; diff --git a/src/strgen/strgen.cpp b/src/strgen/strgen.cpp index 9aa5135777..09840d9cd1 100644 --- a/src/strgen/strgen.cpp +++ b/src/strgen/strgen.cpp @@ -322,7 +322,9 @@ int CDECL main(int argc, char *argv[]) std::filesystem::path src_dir("."); std::filesystem::path dest_dir; - GetOptData mgo(std::span(argv + 1, argc - 1), _opts); + std::vector params; + for (int i = 1; i < argc; ++i) params.emplace_back(argv[i]); + GetOptData mgo(params, _opts); for (;;) { int i = mgo.GetOpt(); if (i == -1) break;