Wed, 27 Jan 2021 18:55:14 +0200
fixed an off-by-one error
/* Copyright 2014 - 2016 Teemu Piippo All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <cstring> #include "main.h" #include "mystring.h" BEGIN_ZFC_NAMESPACE /*! * \returns a lower-case version of this string. */ std::string to_lowercase(const std::string& string) { std::string result = string; for (char& ch : result) { if (isupper(ch)) ch += 'a' - 'A'; } return result; } /*! * \brief Joins the elements of this string list into one longer string. * \param delimeter The delimeter to place between the element strings. * \returns the catenated string. */ std::string join_string_list(const std::vector<std::string>& strings, const std::string& delimeter) { std::string result; for (const std::string &item : strings) { if (not result.empty()) result += delimeter; result += item; } return result; } /*! * \brief Modifies the given index so that if it is negative, it is translated into a positive index starting from the * end of the string. For example, an index of -1 will be modified to point to the last character in the string, * -2 to the second last, etc. * \param index Index to translate. */ inline void modifyIndex(const std::string& str, int& index) { if (index < 0) index = str.length() - index; } /*! * \param a Starting index of the range. * \param b Ending index of the range. * \returns a sub-string containing all characters from \c a to \c b, not including the character at \c b. */ std::string mid(const std::string& str, int rangeBegin, int rangeEnd) { modifyIndex(str, rangeBegin); modifyIndex(str, rangeEnd); rangeBegin = max(rangeBegin, 0); rangeEnd = min(rangeEnd, static_cast<signed>(str.length())); if (rangeEnd <= rangeBegin) return ""; else return str.substr(rangeBegin, rangeEnd - rangeBegin); } /*! * \param length Amount of characters to return. * \returns the \c length right-most characters of the string. */ std::string right(const std::string& str, int length) { if (length >= static_cast<signed>(str.length())) return str; else return std::string{str.data() + str.length() - length}; } /*! * \brief Formats this string using \c vsnprintf, using the provided arguments. * \param formatString Template string to use with formatting. * \param args Variadic arguments to use with formatting. */ std::string vsprintf(const char* formatString, va_list args) { std::string result; // Copy the argument list so that we have something to provide to vsnprintf in case we have to call it again. va_list argsCopy; va_copy(argsCopy, args); // First, attempt to format using a fixed-size buffer. static char buffer[1024]; size_t length = vsnprintf(buffer, sizeof buffer, formatString, args); if (length < sizeof buffer) { // vsnprintf succeeded in fitting the formatted string into the buffer, so we're done. result = buffer; } else { // vsnprintf needs more space, so we have to allocate a new buffer and try again. Vector<char> newBuffer(length + 1); vsnprintf(newBuffer.data(), length + 1, formatString, argsCopy); result = newBuffer.data(); } return result; } /*! * \brief Formats this string using \c printf -like syntax. * \param formatString Template string to use with formatting. * \param ... Variadic arguments to use with formatting. */ std::string __cdecl sprintf(const char* formatString, ...) { va_list args; va_start (args, formatString); std::string result = vsprintf(formatString, args); va_end (args); return result; } std::string remove_range(const std::string &string, int start, int end) { std::string result; result.reserve(string.length() - (end - start)); std::copy(string.begin(), string.begin() + start, std::back_inserter(result)); std::copy(string.begin() + end, string.end(), std::back_inserter(result)); return result; } /*! * \param other Sub-string to find from the beginning of this string. * \returns whether or not this string begins with the provided sub-string. */ bool starts_with(const std::string& str, const std::string& other) { if (str.length() < other.length()) return false; else return std::strncmp(str.data(), other.data(), other.length()) == 0; } /*! * \brief Replaces all instances of \c text with \c replacement. * \param text Text to replace away. * \param replacement Text to replace \c text with. */ void replace_all(std::string& str, const char* text, const char* replacement) { int position; while ((position = str.find(text)) != -1) { str.replace(position, std::strlen(text), replacement); } } /*! * \brief Splits this string using the provided delimeter. * \param delimeter Delimeter to use for splitting. * \returns a string list containing the split strings. */ std::vector<std::string> split(const std::string& string, const std::string& delimeter) { std::vector<std::string> result; int a = 0; int b; // Find all separators and store the text left to them. while ((b = string.find(delimeter, a)) != -1) { std::string sub = mid(string, a, b); if (sub.length() > 0) result.push_back(sub); a = b + delimeter.length(); } // Add the string at the right of the last separator if (a < static_cast<int>(string.length())) result.push_back(mid(string, a, string.length())); return result; } /*! * \brief Converts this string to an integer. * \param ok An pointer to a boolean to store whether or not the conversion was successful. * If \c ok is \c NULL, the success state is not stored. * \param base The base to interpret this string with. * \returns the resulting integer. */ std::optional<long> to_int(const char* str, int base) { errno = 0; char* endPointer; long result = strtol(str, &endPointer, base); if (errno == 0 and *endPointer == '\0') { return result; } else { return {}; } } /*! * \brief Removes leading and trailing whitespace from this string. Alternatively a custom filter can be used to strip * something else than whitespace. * \param filter The filtering function to use. */ void normalize(std::string& string, int (*filter)(int)) { int a = 0; int b = string.length() - 1; while ((*filter)(string[a]) and a != b) { ++a; } while ((*filter)(string[b]) and a != b) { --b; } if (a == b) { string = ""; } else if (a != 0 or b != static_cast<signed>(string.length() - 1)) { string = string.substr (a, b - a + 1); } } END_ZFC_NAMESPACE