Line data Source code
1 0 : /* 2 : 3 : NSStringOOExtensions.m 4 : 5 : Oolite 6 : Copyright (C) 2004-2013 Giles C Williams and contributors 7 : 8 : This program is free software; you can redistribute it and/or 9 : modify it under the terms of the GNU General Public License 10 : as published by the Free Software Foundation; either version 2 11 : of the License, or (at your option) any later version. 12 : 13 : This program is distributed in the hope that it will be useful, 14 : but WITHOUT ANY WARRANTY; without even the implied warranty of 15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 : GNU General Public License for more details. 17 : 18 : You should have received a copy of the GNU General Public License 19 : along with this program; if not, write to the Free Software 20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 : MA 02110-1301, USA. 22 : 23 : */ 24 : 25 : #import "NSStringOOExtensions.h" 26 : #import "NSDataOOExtensions.h" 27 : #import "OOCocoa.h" 28 : 29 : 30 : @implementation NSString (OOExtensions) 31 : 32 : + (instancetype) stringWithContentsOfUnicodeFile:(NSString *)path 33 : { 34 : id result = nil; 35 : BOOL OK = YES; 36 : NSData *data = nil; 37 : const uint8_t *bytes = NULL; 38 : size_t length = 0; 39 : const uint8_t *effectiveBytes = NULL; 40 : size_t effectiveLength = 0; 41 : 42 : data = [[NSData oo_dataWithOXZFile:path] retain]; 43 : if (data == nil) OK = NO; 44 : 45 : if (OK) 46 : { 47 : length = [data length]; 48 : bytes = [data bytes]; 49 : } 50 : 51 : if (OK && 2 <= length && (length % sizeof(unichar)) == 0) 52 : { 53 : // Could be UTF-16 54 : unichar firstChar = bytes[0]; 55 : firstChar = (firstChar << 8) | bytes[1]; // Endianism doesn't matter, because we test both orders of BOM. 56 : if (firstChar == 0xFFFE || firstChar == 0xFEFF) 57 : { 58 : // Consider it to be UTF-16. 59 : result = [NSString stringWithCharacters:(unichar *)(bytes + sizeof(unichar)) length:(length / sizeof(unichar)) - 1]; 60 : if (result == nil) OK = NO; 61 : } 62 : } 63 : 64 : if (OK && result == nil) 65 : { 66 : // Not UTF-16. Try UTF-8. 67 : if (3 <= length && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) 68 : { 69 : // File starts with UTF-8 BOM; skip it. 70 : effectiveBytes = bytes + 3; 71 : effectiveLength = length + 3; 72 : } 73 : else 74 : { 75 : effectiveBytes = bytes; 76 : effectiveLength = length; 77 : } 78 : 79 : // Attempt to interpret as UTF-8 80 : result = [[[NSString alloc] initWithBytes:effectiveBytes length:effectiveLength encoding:NSUTF8StringEncoding] autorelease]; 81 : } 82 : 83 : if (OK && result == nil) 84 : { 85 : // Not UTF-16 or UTF-8. Use ISO-Latin-1 (which should work for any byte sequence). 86 : result = [[[NSString alloc] initWithBytes:effectiveBytes length:effectiveLength encoding:NSISOLatin1StringEncoding] autorelease]; 87 : } 88 : 89 : [data release]; 90 : return result; 91 : } 92 : 93 : 94 : + (instancetype) stringWithUTF16String:(const unichar *)chars 95 : { 96 : size_t length; 97 : const unichar *end; 98 : 99 : if (chars == NULL) return nil; 100 : 101 : // Find length of string. 102 : end = chars; 103 : while (*end++) {} 104 : length = end - chars - 1; 105 : 106 : return [NSString stringWithCharacters:chars length:length]; 107 : } 108 : 109 : 110 : - (NSData *) utf16DataWithBOM:(BOOL)includeByteOrderMark 111 : { 112 : size_t lengthInChars; 113 : size_t lengthInBytes; 114 : unichar *buffer = NULL; 115 : unichar *characters = NULL; 116 : 117 : // Calculate sizes 118 : lengthInChars = [self length]; 119 : lengthInBytes = lengthInChars * sizeof(unichar); 120 : if (includeByteOrderMark) lengthInBytes += sizeof(unichar); 121 : 122 : // Allocate buffer 123 : buffer = malloc(lengthInBytes); 124 : if (buffer == NULL) return nil; 125 : 126 : // write BOM (native-endian) if desired 127 : characters = buffer; 128 : if (includeByteOrderMark) 129 : { 130 : *characters++ = 0xFEFF; 131 : } 132 : 133 : // Get the contents 134 : [self getCharacters:characters]; 135 : 136 : // NSData takes ownership of the buffer. 137 : return [NSData dataWithBytesNoCopy:buffer length:lengthInBytes freeWhenDone:YES]; 138 : } 139 : 140 : 141 : - (uint32_t) oo_hash 142 : { 143 : NSUInteger i, length = [self length]; 144 : uint32_t hash = 5381; 145 : for (i = 0; i < length; i++) 146 : { 147 : hash = ((hash << 5) + hash) /* 33 * hash */ ^ [self characterAtIndex:i]; 148 : } 149 : return hash; 150 : } 151 : 152 : 153 : - (NSString *)stringByTrimmingLeadingCharactersInSet:(NSCharacterSet *)characterSet 154 : { 155 : NSRange rangeOfFirstWantedCharacter = [self rangeOfCharacterFromSet:[characterSet invertedSet]]; 156 : if (rangeOfFirstWantedCharacter.location == NSNotFound) return @""; 157 : 158 : return [self substringFromIndex:rangeOfFirstWantedCharacter.location]; 159 : } 160 : 161 : 162 : - (NSString *)stringByTrimmingLeadingWhitespaceAndNewlineCharacters 163 : { 164 : return [self stringByTrimmingLeadingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 165 : } 166 : 167 : 168 : - (NSString *)stringByTrimmingTrailingCharactersInSet:(NSCharacterSet *)characterSet 169 : { 170 : NSRange rangeOfLastWantedCharacter = [self rangeOfCharacterFromSet:[characterSet invertedSet] options:NSBackwardsSearch]; 171 : if (rangeOfLastWantedCharacter.location == NSNotFound) return @""; 172 : 173 : return [self substringToIndex:rangeOfLastWantedCharacter.location+1]; // non-inclusive 174 : } 175 : 176 : 177 : - (NSString *)stringByTrimmingTrailingWhitespaceAndNewlineCharacters 178 : { 179 : return [self stringByTrimmingTrailingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 180 : } 181 : 182 : @end 183 : 184 : 185 : @implementation NSMutableString (OOExtensions) 186 : 187 : - (void) appendLine:(NSString *)line 188 : { 189 : [self appendString:line ? [line stringByAppendingString:@"\n"] : (NSString *)@"\n"]; 190 : } 191 : 192 : 193 : - (void) appendFormatLine:(NSString *)fmt, ... 194 : { 195 : va_list args; 196 : va_start(args, fmt); 197 : [self appendFormatLine:fmt arguments:args]; 198 : va_end(args); 199 : } 200 : 201 : 202 : - (void) appendFormatLine:(NSString *)fmt arguments:(va_list)args 203 : { 204 : NSString *formatted = [[NSString alloc] initWithFormat:fmt arguments:args]; 205 : [self appendLine:formatted]; 206 : [formatted release]; 207 : } 208 : 209 : 210 : - (void) deleteCharacterAtIndex:(unsigned long)index 211 : { 212 : [self deleteCharactersInRange:NSMakeRange(index, 1)]; 213 : } 214 : 215 : @end 216 : 217 : 218 0 : NSString *OOTabString(NSUInteger count) 219 : { 220 : NSString * const staticTabs[] = 221 : { 222 : @"", 223 : @"\t", 224 : @"\t\t", 225 : @"\t\t\t", 226 : @"\t\t\t\t", 227 : @"\t\t\t\t\t", // 5 228 : @"\t\t\t\t\t\t", 229 : @"\t\t\t\t\t\t\t", 230 : @"\t\t\t\t\t\t\t\t", 231 : @"\t\t\t\t\t\t\t\t\t", 232 : @"\t\t\t\t\t\t\t\t\t\t" // 10 233 : }; 234 : enum { kStaticTabCount = sizeof staticTabs / sizeof *staticTabs }; 235 : 236 : if (count < kStaticTabCount) 237 : { 238 : return staticTabs[count]; 239 : } 240 : else 241 : { 242 : return [staticTabs[kStaticTabCount - 1] stringByAppendingString:OOTabString(count - (kStaticTabCount - 1))]; 243 : } 244 : }