diff --git a/common/str.cpp b/common/str.cpp index 56ef39cb824..5e771c8b4d3 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -739,4 +739,84 @@ String tag2string(uint32 tag) { return Common::String(str); } +size_t strlcpy(char *dst, const char *src, size_t size) { + // Our backup of the source's start, we need this + // to calculate the source's length. + const char * const srcStart = src; + + // In case a non-empty size was specified we + // copy over (size - 1) bytes at max. + if (size != 0) { + // Copy over (size - 1) bytes at max. + while (--size != 0) { + if ((*dst++ = *src) == 0) + break; + ++src; + } + + // In case the source string was longer than the + // destination, we need to add a terminating + // zero. + if (size == 0) + *dst = 0; + } + + // Move to the terminating zero of the source + // string, we need this to determin the length + // of the source string. + while (*src) + ++src; + + // Return the source string's length. + return src - srcStart; +} + +size_t strlcat(char *dst, const char *src, size_t size) { + // In case the destination buffer does not contain + // space for at least 1 character, we will just + // return the source string's length. + if (size == 0) + return strlen(src); + + // Our backup of the source's start, we need this + // to calculate the source's length. + const char * const srcStart = src; + + // Our backup of the destination's start, we need + // this to calculate the destination's length. + const char * const dstStart = dst; + + // Search the end of the destination, but do not + // move past the terminating zero. + while (size-- != 0 && *dst != 0) + ++dst; + + // Calculate the destination's length; + const size_t dstLength = dst - dstStart; + + // In case we reached the end of the destination + // buffer before we had a chance to append any + // characters we will just return the destination + // length plus the source string's length. + if (size == 0) + return dstLength + strlen(srcStart); + + // Copy over all of the source that fits + // the destination buffer. We also need + // to take the terminating zero we will + // add into consideration. + while (size-- != 0 && *src != 0) + *dst++ = *src++; + *dst = 0; + + // Move to the terminating zero of the source + // string, we need this to determin the length + // of the source string. + while (*src) + ++src; + + // Return the total length of the result string + return dstLength + (src - srcStart); +} + } // End of namespace Common diff --git a/common/str.h b/common/str.h index 0cdd3e8c107..12e2b0d2d3a 100644 --- a/common/str.h +++ b/common/str.h @@ -325,6 +325,43 @@ bool matchString(const char *str, const char *pat, bool ignoreCase = false, bool */ String tag2string(uint32 tag); +/** + * Copy up to size - 1 characters from src to dst and also zero terminate the + * result. Note that src must be a zero terminated string. + * + * In case size is zero this function just returns the length of the source + * string. + * + * @note This is modeled after OpenBSD's strlcpy. See the manpage here: + * http://www.openbsd.org/cgi-bin/man.cgi?query=strlcpy + * + * @param dst The destination buffer. + * @param src The source string. + * @param size The size of the destination buffer. + * @return The length of the (non-truncated) result, i.e. strlen(src). + */ +size_t strlcpy(char *dst, const char *src, size_t size); + +/** + * Append the string src to the string dst. Note that both src and dst must be + * zero terminated. The result will be zero terminated. At most + * "size - strlen(dst) - 1" bytes will be appended. + * + * In case the dst string does not contain a zero within the first "size" bytes + * the dst string will not be changed and size + strlen(src) is returned. + * + * @note This is modeled after OpenBSD's strlcat. See the manpage here: + * http://www.openbsd.org/cgi-bin/man.cgi?query=strlcat + * + * @param dst The string the source string should be appended to. + * @param src The source string. + * @param size The (total) size of the destination buffer. + * @return The length of the (non-truncated) result. That is + * strlen(dst) + strlen(src). In case strlen(dst) > size + * size + strlen(src) is returned. + */ +size_t strlcat(char *dst, const char *src, size_t size); + /** * Convenience wrapper for tag2string which "returns" a C string. * Note: It is *NOT* safe to do anything with the return value other than directly diff --git a/test/common/str.h b/test/common/str.h index dcef1c1773b..aed2888736c 100644 --- a/test/common/str.h +++ b/test/common/str.h @@ -295,6 +295,58 @@ class StringTestSuite : public CxxTest::TestSuite Common::String s = Common::String::printf("%s%X", "test", 1234); TS_ASSERT(s == "test4D2"); TS_ASSERT(s.size() == 7); + } + void test_strlcpy() { + static const char * const testString = "1234567890"; + + char test1[4]; + TS_ASSERT_EQUALS(Common::strlcpy(test1, testString, 4), strlen(testString)); + TS_ASSERT_EQUALS(strcmp(test1, "123"), 0); + + char test2[12]; + test2[11] = 'X'; + TS_ASSERT_EQUALS(Common::strlcpy(test2, testString, 11), strlen(testString)); + TS_ASSERT_EQUALS(strcmp(test2, testString), 0); + TS_ASSERT_EQUALS(test2[11], 'X'); + + char test3[1] = { 'X' }; + TS_ASSERT_EQUALS(Common::strlcpy(test3, testString, 0), strlen(testString)); + TS_ASSERT_EQUALS(test3[0], 'X'); + + char test4[12]; + TS_ASSERT_EQUALS(Common::strlcpy(test4, testString, 12), strlen(testString)); + TS_ASSERT_EQUALS(strcmp(test4, testString), 0); + } + + void test_strncat() { + static const char * const initialString = "123"; + static const char * const appendString = "4567890"; + static const char * const resultString = "1234567890"; + + char test1[4]; + TS_ASSERT_EQUALS(Common::strlcpy(test1, initialString, 4), strlen(initialString)); + TS_ASSERT_EQUALS(strcmp(test1, initialString), 0); + TS_ASSERT_EQUALS(Common::strlcat(test1, appendString, 4), strlen(resultString)); + TS_ASSERT_EQUALS(strcmp(test1, initialString), 0); + + char test2[12]; + test2[11] = 'X'; + TS_ASSERT_EQUALS(Common::strlcpy(test2, initialString, 11), strlen(initialString)); + TS_ASSERT_EQUALS(strcmp(test2, initialString), 0); + TS_ASSERT_EQUALS(Common::strlcat(test2, appendString, 11), strlen(resultString)); + TS_ASSERT_EQUALS(strcmp(test2, resultString), 0); + TS_ASSERT_EQUALS(test2[11], 'X'); + + char test3[1]; + test3[0] = 'X'; + TS_ASSERT_EQUALS(Common::strlcat(test3, appendString, 0), strlen(appendString)); + TS_ASSERT_EQUALS(test3[0], 'X'); + + char test4[11]; + TS_ASSERT_EQUALS(Common::strlcpy(test4, initialString, 11), strlen(initialString)); + TS_ASSERT_EQUALS(strcmp(test4, initialString), 0); + TS_ASSERT_EQUALS(Common::strlcat(test4, appendString, 11), strlen(resultString)); + TS_ASSERT_EQUALS(strcmp(test4, resultString), 0); } };