From febe3948063a14fc636b2feb506e4064608e559c Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 9 May 2023 21:35:50 +0200 Subject: [PATCH] Codechange: replace C-style strings with C++-style strings in textfile (#10772) --- src/string.cpp | 11 ++++++--- src/string_func.h | 2 +- src/string_type.h | 8 ++++++- src/strings.cpp | 12 ++++++---- src/strings_func.h | 4 ++-- src/textfile_gui.cpp | 55 ++++++++++++++++++++------------------------ src/textfile_gui.h | 24 +++++++++---------- 7 files changed, 63 insertions(+), 53 deletions(-) diff --git a/src/string.cpp b/src/string.cpp index 35b3dc5b3d..89ee7bad9d 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -221,9 +221,14 @@ static void StrMakeValidInPlace(T &dst, const char *str, const char *last, Strin str += len; continue; } - /* Replace the undesirable character with a question mark */ str += len; - if ((settings & SVS_REPLACE_WITH_QUESTION_MARK) != 0) *dst++ = '?'; + if ((settings & SVS_REPLACE_TAB_CR_NL_WITH_SPACE) != 0 && (c == '\r' || c == '\n' || c == '\t')) { + /* Replace the tab, carriage return or newline with a space. */ + *dst++ = ' '; + } else if ((settings & SVS_REPLACE_WITH_QUESTION_MARK) != 0) { + /* Replace the undesirable character with a question mark */ + *dst++ = '?'; + } } } @@ -263,7 +268,7 @@ void StrMakeValidInPlace(char *str, StringValidationSettings settings) * @param str The string to validate. * @param settings The settings for the string validation. */ -std::string StrMakeValid(const std::string &str, StringValidationSettings settings) +std::string StrMakeValid(std::string_view str, StringValidationSettings settings) { auto buf = str.data(); auto last = buf + str.size(); diff --git a/src/string_func.h b/src/string_func.h index c03101c213..192ed6a8fb 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -40,7 +40,7 @@ int CDECL seprintf(char *str, const char *last, const char *format, ...) WARN_FO std::string FormatArrayAsHex(span data); 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); +[[nodiscard]] std::string StrMakeValid(std::string_view str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); void StrMakeValidInPlace(char *str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); void str_fix_scc_encoded(char *str, const char *last) NOACCESS(2); diff --git a/src/string_type.h b/src/string_type.h index 4483b7474c..841097516e 100644 --- a/src/string_type.h +++ b/src/string_type.h @@ -47,8 +47,14 @@ static const WChar CHAR_TD_PDF = 0x202C; ///< Restore the text-direction state t enum StringValidationSettings { SVS_NONE = 0, ///< Allow nothing and replace nothing. SVS_REPLACE_WITH_QUESTION_MARK = 1 << 0, ///< Replace the unknown/bad bits with question marks. - SVS_ALLOW_NEWLINE = 1 << 1, ///< Allow newlines. + SVS_ALLOW_NEWLINE = 1 << 1, ///< Allow newlines; replaces '\r\n' with '\n' during processing. SVS_ALLOW_CONTROL_CODE = 1 << 2, ///< Allow the special control codes. + /** + * Replace tabs ('\t'), carriage returns ('\r') and newlines ('\n') with spaces. + * When #SVS_ALLOW_NEWLINE is set, a '\n' or '\r\n' combination are not replaced with a space. A lone '\r' is replaced with a space. + * When #SVS_REPLACE_WITH_QUESTION_MARK is set, this replacement runs first. + */ + SVS_REPLACE_TAB_CR_NL_WITH_SPACE = 1 << 3, }; DECLARE_ENUM_AS_BIT_SET(StringValidationSettings) diff --git a/src/strings.cpp b/src/strings.cpp index 840b064e5a..32e34ad5f1 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -2103,9 +2103,13 @@ bool MissingGlyphSearcher::FindMissingGlyphs() } this->Reset(); - for (const char *text = this->NextString(); text != nullptr; text = this->NextString()) { + for (auto text = this->NextString(); text.has_value(); text = this->NextString()) { + auto src = text->cbegin(); + FontSize size = this->DefaultSize(); - for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) { + while (src != text->cend()) { + WChar c = Utf8Consume(src); + if (c >= SCC_FIRST_FONT && c <= SCC_LAST_FONT) { size = (FontSize)(c - SCC_FIRST_FONT); } else if (!IsInsideMM(c, SCC_SPRITE_START, SCC_SPRITE_END) && IsPrintable(c) && !IsTextDirectionChar(c) && c != '?' && GetGlyph(size, c) == question_mark[size]) { @@ -2144,9 +2148,9 @@ class LanguagePackGlyphSearcher : public MissingGlyphSearcher { return FS_NORMAL; } - const char *NextString() override + std::optional NextString() override { - if (this->i >= TEXT_TAB_END) return nullptr; + if (this->i >= TEXT_TAB_END) return std::nullopt; const char *ret = _langpack.offsets[_langpack.langtab_start[this->i] + this->j]; diff --git a/src/strings_func.h b/src/strings_func.h index 760931d1a9..12f414961a 100644 --- a/src/strings_func.h +++ b/src/strings_func.h @@ -261,9 +261,9 @@ public: /** * Get the next string to search through. - * @return The next string or nullptr if there is none. + * @return The next string or nullopt if there is none. */ - virtual const char *NextString() = 0; + virtual std::optional NextString() = 0; /** * Get the default (font) size of the string. diff --git a/src/textfile_gui.cpp b/src/textfile_gui.cpp index 89a5747bb4..ba067daead 100644 --- a/src/textfile_gui.cpp +++ b/src/textfile_gui.cpp @@ -70,11 +70,6 @@ TextfileWindow::TextfileWindow(TextfileType file_type) : Window(&_textfile_desc) this->hscroll->SetStepSize(10); // Speed up horizontal scrollbar } -/* virtual */ TextfileWindow::~TextfileWindow() -{ - free(this->text); -} - /** * Get the total height of the content displayed in this window, if wrapping is disabled. * @return the height in pixels @@ -199,9 +194,9 @@ void TextfileWindow::SetupScrollbars(bool force_reflow) return FS_MONO; } -/* virtual */ const char *TextfileWindow::NextString() +/* virtual */ std::optional TextfileWindow::NextString() { - if (this->search_iterator >= this->lines.size()) return nullptr; + if (this->search_iterator >= this->lines.size()) return std::nullopt; return this->lines[this->search_iterator++].text; } @@ -344,48 +339,48 @@ static void Xunzip(byte **bufp, size_t *sizep) FILE *handle = FioFOpenFile(textfile, "rb", dir, &filesize); if (handle == nullptr) return; - this->text = ReallocT(this->text, filesize); - size_t read = fread(this->text, 1, filesize, handle); + char *buf = MallocT(filesize); + size_t read = fread(buf, 1, filesize, handle); fclose(handle); - if (read != filesize) return; + if (read != filesize) { + free(buf); + return; + } #if defined(WITH_ZLIB) /* In-place gunzip */ - if (StrEndsWith(textfile, ".gz")) Gunzip((byte**)&this->text, &filesize); + if (StrEndsWith(textfile, ".gz")) Gunzip((byte**)&buf, &filesize); #endif #if defined(WITH_LIBLZMA) /* In-place xunzip */ - if (StrEndsWith(textfile, ".xz")) Xunzip((byte**)&this->text, &filesize); + if (StrEndsWith(textfile, ".xz")) Xunzip((byte**)&buf, &filesize); #endif - if (!this->text) return; + if (buf == nullptr) 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 StrMakeValidInPlace removes those. */ - for (char *p = this->text; *p != '\0'; p++) { - if (*p == '\t' || *p == '\r') *p = ' '; - } + std::string_view sv_buf(buf, filesize); /* Check for the byte-order-mark, and skip it if needed. */ - char *p = this->text + (strncmp(u8"\ufeff", this->text, 3) == 0 ? 3 : 0); + if (StrStartsWith(sv_buf, u8"\ufeff")) sv_buf.remove_prefix(3); - /* Make sure the string is a valid UTF-8 sequence. */ - StrMakeValidInPlace(p, this->text + filesize, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE); + /* Replace any invalid characters with a question-mark. This copies the buf in the process. */ + this->text = StrMakeValid(sv_buf, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE | SVS_REPLACE_TAB_CR_NL_WITH_SPACE); + free(buf); /* Split the string on newlines. */ + std::string_view p(this->text); 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); - } + auto next = p.find_first_of('\n'); + while (next != std::string_view::npos) { + this->lines.emplace_back(row, p.substr(0, next)); + p.remove_prefix(next + 1); + + row++; + next = p.find_first_of('\n'); } + this->lines.emplace_back(row, p); /* Calculate maximum text line length. */ uint max_length = 0; diff --git a/src/textfile_gui.h b/src/textfile_gui.h index 5e0a7180f7..c233aba638 100644 --- a/src/textfile_gui.h +++ b/src/textfile_gui.h @@ -19,25 +19,14 @@ std::optional GetTextfile(TextfileType type, Subdirectory dir, cons /** Window for displaying a textfile */ struct TextfileWindow : public Window, MissingGlyphSearcher { - struct Line { - int top; ///< Top scroll position. - int bottom; ///< Bottom scroll position. - const char *text; ///< Pointer to text buffer. - - Line(int top, const char *text) : top(top), bottom(top + 1), text(text) {} - }; - TextfileType file_type; ///< Type of textfile to view. Scrollbar *vscroll; ///< Vertical scrollbar. Scrollbar *hscroll; ///< Horizontal scrollbar. - char *text; ///< Lines of text from the NewGRF's textfile. - std::vector lines; ///< #text, split into lines in a table with lines. uint search_iterator; ///< Iterator for the font check search. uint max_length; ///< Maximum length of unwrapped text line. TextfileWindow(TextfileType file_type); - ~TextfileWindow(); void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override; void OnClick(Point pt, int widget, int click_count) override; @@ -47,13 +36,24 @@ struct TextfileWindow : public Window, MissingGlyphSearcher { void Reset() override; FontSize DefaultSize() override; - const char *NextString() override; + std::optional NextString() override; bool Monospace() override; void SetFontNames(FontCacheSettings *settings, const char *font_name, const void *os_data) override; virtual void LoadTextfile(const std::string &textfile, Subdirectory dir); private: + struct Line { + int top; ///< Top scroll position. + int bottom; ///< Bottom scroll position. + std::string_view text; ///< Pointer to text buffer. + + Line(int top, std::string_view text) : top(top), bottom(top + 1), text(text) {} + }; + + std::string text; ///< Lines of text from the NewGRF's textfile. + std::vector lines; ///< #text, split into lines in a table with lines. + uint ReflowContent(); uint GetContentHeight(); void SetupScrollbars(bool force_reflow);