Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -169,10 +169,34 @@ * \return The substring as a new autoreleased OFString */ - (OFString*)substringFromIndex: (size_t)start toIndex: (size_t)end; +/** + * Creates a new string by appending another string. + * + * \param str The string to append + * \return A new autoreleased OFString with the specified string appended + */ +- (OFString*)stringByAppendingString: (OFString*)str; + +/** + * Checks whether the string has the specified prefix. + * + * \param prefix The prefix to check for + * \return A boolean whether the string has the specified prefix + */ +- (BOOL)hasPrefix: (OFString*)prefix; + +/** + * Checks whether the string has the specified suffix. + * + * \param suffix The suffix to check for + * \return A boolean whether the string has the specified suffix + */ +- (BOOL)hasSuffix: (OFString*)suffix; + /** * Splits an OFString into an OFArray of OFStrings. * * \param delimiter The delimiter for splitting * \return An autoreleased OFArray with the splitted string Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -442,10 +442,36 @@ @throw [OFOutOfRangeException newWithClass: isa]; return [OFString stringWithCString: string + start andLength: end - start]; } + +- (OFString*)stringByAppendingString: (OFString*)str +{ + return [[OFMutableString stringWithString: self] appendString: str]; +} + +- (BOOL)hasPrefix: (OFString*)prefix +{ + size_t len = [prefix length]; + + if (len > length) + return NO; + + return (memcmp(string, [prefix cString], len) ? NO : YES); +} + +- (BOOL)hasSuffix: (OFString*)suffix +{ + size_t len = [suffix length]; + + if (len > length) + return NO; + + return (memcmp(string + (length - len), [suffix cString], len) + ? NO : YES); +} - (OFArray*)splitWithDelimiter: (OFString*)delimiter { OFAutoreleasePool *pool; OFArray *array; Index: tests/OFString/OFString.m ================================================================== --- tests/OFString/OFString.m +++ tests/OFString/OFString.m @@ -22,11 +22,11 @@ #define ZD "%zd" #else #define ZD "%u" #endif -#define NUM_TESTS 62 +#define NUM_TESTS 68 #define SUCCESS \ printf("\r\033[1;%dmTests successful: " ZD "/%d\033[0m", \ (i == NUM_TESTS - 1 ? 32 : 33), i + 1, NUM_TESTS); \ fflush(stdout); #define FAIL \ @@ -142,10 +142,17 @@ CHECK_EXCEPT([@"foo" substringFromIndex: 4 toIndex: 4], OFOutOfRangeException) CHECK_EXCEPT([@"foo" substringFromIndex: 2 toIndex: 0], OFInvalidArgumentException) + /* Misc tests */ + CHECK([[@"foo" stringByAppendingString: @"bar"] isEqual: @"foobar"]) + CHECK([@"foobar" hasPrefix: @"foo"]) + CHECK([@"foobar" hasSuffix: @"bar"]) + CHECK(![@"foobar" hasPrefix: @"foobar0"]) + CHECK(![@"foobar" hasSuffix: @"foobar0"]) + /* Split tests */ a = [@"fooXXbarXXXXbazXXXX" splitWithDelimiter: @"XX"]; CHECK([[a objectAtIndex: j++] isEqual: @"foo"]) CHECK([[a objectAtIndex: j++] isEqual: @"bar"]) CHECK([[a objectAtIndex: j++] isEqual: @""]) @@ -215,13 +222,15 @@ CHECK([[@"€" stringByXMLUnescaping] isEqual: @"€"]); CHECK([[@"𝄞" stringByXMLUnescaping] isEqual: @"𝄞"]); CHECK_EXCEPT([@"&#;" stringByXMLUnescaping], OFInvalidEncodingException) CHECK_EXCEPT([@"&#x;" stringByXMLUnescaping], + OFInvalidEncodingException) + CHECK_EXCEPT([@"&#g;" stringByXMLUnescaping], OFInvalidEncodingException) CHECK_EXCEPT([@"&#xg;" stringByXMLUnescaping], OFInvalidEncodingException) puts(""); return 0; }