diff options
Diffstat (limited to 'Source/SPSQLParser.m')
-rw-r--r-- | Source/SPSQLParser.m | 655 |
1 files changed, 655 insertions, 0 deletions
diff --git a/Source/SPSQLParser.m b/Source/SPSQLParser.m new file mode 100644 index 00000000..9828f529 --- /dev/null +++ b/Source/SPSQLParser.m @@ -0,0 +1,655 @@ +// +// SPSQLParsing.m +// sequel-pro +// +// Created by Rowan Beentje on 18/01/2009. +// Copyright 2009 Rowan Beentje. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPSQLParser.h" + +/* + * Please see the header files for a general description of the purpose of this class, + * and increased overview detail for the functions below. + */ +@implementation SPSQLParser : NSMutableString + + + +/* + * Removes comments within the current string, trimming "#", "--[/s]", and "/* * /" style strings. + */ +- (void) deleteComments +{ + long currentStringIndex, commentEndIndex, quotedStringEndIndex; + unichar currentCharacter; + long stringLength = [string length]; + + // Walk along the string, processing characters. + for (currentStringIndex = 0; currentStringIndex < stringLength; currentStringIndex++) { + currentCharacter = [string characterAtIndex:currentStringIndex]; + switch (currentCharacter) { + + // When quote characters are encountered walk to the end of the quoted string. + case '\'': + case '"': + case '`': + quotedStringEndIndex = [self endIndexOfStringQuotedByCharacter:currentCharacter startingAtIndex:currentStringIndex+1]; + if (quotedStringEndIndex == NSNotFound) { + return; + } + currentStringIndex = quotedStringEndIndex; + break; + + // For comments starting "--[\s]", ensure the start syntax is valid before proceeding. + case '-': + if (stringLength < currentStringIndex + 2) break; + if ([string characterAtIndex:currentStringIndex+1] != '-') break; + if (![[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[string characterAtIndex:currentStringIndex+2]]) break; + commentEndIndex = [self endIndexOfCommentOfType:SPDoubleDashComment startingAtIndex:currentStringIndex]; + + // Remove the comment + [string deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; + stringLength -= commentEndIndex - currentStringIndex + 1; + currentStringIndex--; + break; + + case '#': + commentEndIndex = [self endIndexOfCommentOfType:SPHashComment startingAtIndex:currentStringIndex]; + + // Remove the comment + [string deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; + stringLength -= commentEndIndex - currentStringIndex + 1; + currentStringIndex--; + break; + + // For comments starting "/*", ensure the start syntax is valid before proceeding. + case '/': + if (stringLength < currentStringIndex + 1) break; + if ([string characterAtIndex:currentStringIndex+1] != '*') break; + commentEndIndex = [self endIndexOfCommentOfType:SPCStyleComment startingAtIndex:currentStringIndex]; + + // Remove the comment + [string deleteCharactersInRange:NSMakeRange(currentStringIndex, commentEndIndex - currentStringIndex + 1)]; + stringLength -= commentEndIndex - currentStringIndex + 1; + currentStringIndex--; + break; + } + } +} + + +/* + * Removes quotes surrounding the string if present, and un-escapes internal occurrences of the quote character before returning. + */ +- (NSString *) unquotedString +{ + NSMutableString *returnString; + long stringEndIndex; + unichar quoteCharacter; + + if (![string length]) return nil; + + // If the first character is not a quote character, return the entire string. + quoteCharacter = [string characterAtIndex:0]; + if (quoteCharacter != '`' && quoteCharacter != '"' && quoteCharacter != '\'') { + return [NSString stringWithString:string]; + } + + // Get the end of the string + stringEndIndex = [self endIndexOfStringQuotedByCharacter:quoteCharacter startingAtIndex:1]; + if (stringEndIndex == NSNotFound) { + return [NSString stringWithString:string]; + } + + // Trim the string appropriately + returnString = [NSMutableString stringWithString:[string substringWithRange:NSMakeRange(1, stringEndIndex-1)]]; + + // Remove escaped characters and escaped strings as appropriate + if (quoteCharacter == '`' || quoteCharacter == '"' || quoteCharacter == '\'') { + [returnString replaceOccurrencesOfString:[NSString stringWithFormat:@"%C%C", quoteCharacter, quoteCharacter] withString:[NSString stringWithFormat:@"%C", quoteCharacter] options:0 range:NSMakeRange(0, [returnString length])]; + } + if (quoteCharacter == '"') { + [returnString replaceOccurrencesOfString:@"\\\"" withString:@"\"" options:0 range:NSMakeRange(0, [returnString length])]; + [returnString replaceOccurrencesOfString:@"\\\\" withString:@"\\" options:0 range:NSMakeRange(0, [returnString length])]; + } else if (quoteCharacter == '\'') { + [returnString replaceOccurrencesOfString:@"\\'" withString:@"'" options:0 range:NSMakeRange(0, [returnString length])]; + [returnString replaceOccurrencesOfString:@"\\\\" withString:@"\\" options:0 range:NSMakeRange(0, [returnString length])]; + } + + return returnString; +} + + +/* + * Removes characters from the string up to the first occurrence of the supplied character. + */ +- (BOOL) trimToCharacter:(unichar)character inclusively:(BOOL)inclusive +{ + return [self trimToCharacter:character inclusively:inclusive ignoringQuotedStrings:YES]; +} + + +/* + * As trimToCharacter: ..., but allows control over whether characters within quoted + * strings are ignored. + */ +- (BOOL) trimToCharacter:(unichar)character inclusively:(BOOL)inclusive ignoringQuotedStrings:(BOOL)ignoreQuotedStrings +{ + long stringIndex; + + // Get the first occurrence of the specified character, returning NO if not found + stringIndex = [self firstOccurrenceOfCharacter:character ignoringQuotedStrings:ignoreQuotedStrings]; + if (stringIndex == NSNotFound) return NO; + + // If it has been found, trim the string appropriately and return YES + [string deleteCharactersInRange:NSMakeRange(0, stringIndex + (inclusive?1:0))]; + return YES; +} + + +/* + * Returns an NSString containing characters from the string up to the first occurrence of the supplied character. + */ +- (NSString *) stringToCharacter:(unichar)character inclusively:(BOOL)inclusive +{ + return [self stringToCharacter:character inclusively:inclusive ignoringQuotedStrings:YES]; +} + + +/* + * As stringToCharacter: ..., but allows control over whether characters within quoted strings + * are ignored. + */ +- (NSString *) stringToCharacter:(unichar)character inclusively:(BOOL)inclusive ignoringQuotedStrings:(BOOL)ignoreQuotedStrings { + long stringIndex; + + // Get the first occurrence of the specified character, returning nil if not found + stringIndex = [self firstOccurrenceOfCharacter:character ignoringQuotedStrings:ignoreQuotedStrings]; + if (stringIndex == NSNotFound) return nil; + + // If it has been found, return the appropriate string range + return [string substringWithRange:NSMakeRange(0, stringIndex + (inclusive?1:0))]; +} + + +/* + * Returns an NSString containing characters from the string up to the first occurrence of the supplied + * character, also removing them from the string. + */ +- (NSString *) trimAndReturnStringToCharacter:(unichar)character trimmingInclusively:(BOOL)inclusiveTrim returningInclusively:(BOOL)inclusiveReturn +{ + return [self trimAndReturnStringToCharacter:character trimmingInclusively:inclusiveTrim returningInclusively:inclusiveReturn ignoringQuotedStrings:YES]; +} + + +/* + * As trimAndReturnStringToCharacter: ..., but allows control over whether characters within quoted + * strings are ignored. + */ +- (NSString *) trimAndReturnStringToCharacter:(unichar)character trimmingInclusively:(BOOL)inclusiveTrim returningInclusively:(BOOL)inclusiveReturn ignoringQuotedStrings:(BOOL)ignoreQuotedStrings +{ + long stringIndex; + NSString *resultString; + + // Get the first occurrence of the specified character, returning nil if it could not be found + stringIndex = [self firstOccurrenceOfCharacter:character ignoringQuotedStrings:ignoreQuotedStrings]; + if (stringIndex == NSNotFound) return nil; + + // Select the appropriate string range, truncate the current string, and return the selected string + resultString = [NSString stringWithString:[string substringWithRange:NSMakeRange(0, stringIndex + (inclusiveReturn?1:0))]]; + [string deleteCharactersInRange:NSMakeRange(0, stringIndex + (inclusiveTrim?1:0))]; + return resultString; +} + + +/* + * Returns characters from the string up to and from the first occurrence of the supplied opening character + * to the appropriate occurrence of the supplied closing character. "inclusively" controls whether the supplied + * characters should also be returned. + */ +- (NSString *) stringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter inclusively:(BOOL)inclusive +{ + return [self stringFromCharacter:fromCharacter toCharacter:toCharacter inclusively:inclusive skippingBrackets:NO ignoringQuotedStrings:YES]; +} + + +/* + * As stringFromCharacter: toCharacter: ..., but allows control over whether to skip + * over bracket-enclosed characters, as in subqueries, enums, definitions or groups + */ +- (NSString *) stringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter inclusively:(BOOL)inclusive skippingBrackets:(BOOL)skipBrackets +{ + return [self stringFromCharacter:fromCharacter toCharacter:toCharacter inclusively:inclusive skippingBrackets:skipBrackets ignoringQuotedStrings:YES]; +} + + +/* + * As stringFromCharacter: toCharacter: ..., but allows control over whether characters within quoted + * strings are ignored. + */ +- (NSString *) stringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter inclusively:(BOOL)inclusive ignoringQuotedStrings:(BOOL)ignoreQuotedStrings +{ + return [self stringFromCharacter:fromCharacter toCharacter:toCharacter inclusively:inclusive skippingBrackets:NO ignoringQuotedStrings:ignoreQuotedStrings]; +} + + +/* + * As stringFromCharacter: toCharacter: ..., but allows control over both bracketing and quoting. + */ +- (NSString *) stringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter inclusively:(BOOL)inclusive skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings +{ + long fromCharacterIndex, toCharacterIndex; + + // Look for the first occurrence of the from: character + fromCharacterIndex = [self firstOccurrenceOfCharacter:fromCharacter afterIndex:-1 skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings]; + if (fromCharacterIndex == NSNotFound) return nil; + + // Look for the first/balancing occurrence of the to: character + toCharacterIndex = [self firstOccurrenceOfCharacter:toCharacter afterIndex:fromCharacterIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings]; + if (toCharacterIndex == NSNotFound) return nil; + + // Return the correct part of the string. + return [string substringWithRange:NSMakeRange(fromCharacterIndex + (inclusive?0:1), toCharacterIndex + (inclusive?1:-1) - fromCharacterIndex)]; +} + + +/* + * As stringFromCharacter: toCharacter: ..., but also trims the string up to the "to" character and + * up to or including the "from" character, depending on whether "trimmingInclusively" is set. + */ +- (NSString *) trimAndReturnStringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter trimmingInclusively:(BOOL)inclusiveTrim returningInclusively:(BOOL)inclusiveReturn +{ + return [self trimAndReturnStringFromCharacter:fromCharacter toCharacter:toCharacter trimmingInclusively:inclusiveTrim returningInclusively:inclusiveReturn skippingBrackets:NO ignoringQuotedStrings:YES]; +} + + +/* + * As trimAndReturnStringFromCharacter: toCharacter: ..., but allows control over whether to + * skip over bracket-enclosed characters, as in subqueries, enums, definitions or groups. + */ +- (NSString *) trimAndReturnStringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter trimmingInclusively:(BOOL)inclusiveTrim returningInclusively:(BOOL)inclusiveReturn skippingBrackets:(BOOL)skipBrackets +{ + return [self trimAndReturnStringFromCharacter:fromCharacter toCharacter:toCharacter trimmingInclusively:inclusiveTrim returningInclusively:inclusiveReturn skippingBrackets:skipBrackets ignoringQuotedStrings:YES]; +} + + +/* + * As trimAndReturnStringFromCharacter: toCharacter: ..., but allows control over whether characters + * within quoted strings are ignored. + */ +- (NSString *) trimAndReturnStringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter trimmingInclusively:(BOOL)inclusiveTrim returningInclusively:(BOOL)inclusiveReturn ignoringQuotedStrings:(BOOL)ignoreQuotedStrings +{ + return [self trimAndReturnStringFromCharacter:fromCharacter toCharacter:toCharacter trimmingInclusively:inclusiveTrim returningInclusively:inclusiveReturn skippingBrackets:NO ignoringQuotedStrings:ignoreQuotedStrings]; +} + + +/* + * As trimAndReturnStringFromCharacter: toCharacter: ..., but allows control over both bracketing + * and quoting. + */ +- (NSString *) trimAndReturnStringFromCharacter:(unichar)fromCharacter toCharacter:(unichar)toCharacter trimmingInclusively:(BOOL)inclusiveTrim returningInclusively:(BOOL)inclusiveReturn skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings +{ + long fromCharacterIndex, toCharacterIndex; + NSString *resultString; + + // Look for the first occurrence of the from: character + fromCharacterIndex = [self firstOccurrenceOfCharacter:fromCharacter afterIndex:-1 skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings]; + if (fromCharacterIndex == NSNotFound) return nil; + + // Look for the first/balancing occurrence of the to: character + toCharacterIndex = [self firstOccurrenceOfCharacter:toCharacter afterIndex:fromCharacterIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings]; + if (toCharacterIndex == NSNotFound) return nil; + + // Select the correct part of the string, truncate the current string, and return the selected string. + resultString = [string substringWithRange:NSMakeRange(fromCharacterIndex + (inclusiveReturn?0:1), toCharacterIndex + (inclusiveReturn?1:-1) - fromCharacterIndex)]; + [string deleteCharactersInRange:NSMakeRange(fromCharacterIndex + (inclusiveTrim?0:1), toCharacterIndex + (inclusiveTrim?1:-1) - fromCharacterIndex)]; + return resultString; +} + +/* + * Split a string on the boundaries formed by the supplied character, returning an array of strings. + */ +- (NSArray *) splitStringByCharacter:(unichar)character +{ + return [self splitStringByCharacter:character skippingBrackets:NO ignoringQuotedStrings:YES]; +} + +/* + * As splitStringByCharacter: ..., but allows control over whether to skip over bracket-enclosed + * characters, as in subqueries, enums, definitions or groups. + */ +- (NSArray *) splitStringByCharacter:(unichar)character skippingBrackets:(BOOL)skipBrackets +{ + return [self splitStringByCharacter:character skippingBrackets:skipBrackets ignoringQuotedStrings:YES]; +} + + +/* + * As splitStringByCharacter:, but allows control over whether characters + * within quoted strings are ignored. + */ +- (NSArray *) splitStringByCharacter:(unichar)character ignoringQuotedStrings:(BOOL)ignoreQuotedStrings +{ + return [self splitStringByCharacter:character skippingBrackets:NO ignoringQuotedStrings:ignoreQuotedStrings]; +} + + +/* + * As splitStringByCharacter: ..., but allows control over both bracketing and quoting. + */ +- (NSArray *) splitStringByCharacter:(unichar)character skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings +{ + NSMutableArray *resultsArray = [NSMutableArray array]; + long stringIndex = -1, nextIndex = 0; + + // Walk through the string finding the character to split by, and add non-zero length strings. + while (1) { + nextIndex = [self firstOccurrenceOfCharacter:character afterIndex:stringIndex skippingBrackets:skipBrackets ignoringQuotedStrings:ignoreQuotedStrings]; + if (nextIndex == NSNotFound) { + break; + } + + if (nextIndex - stringIndex - 1 > 0) { + [resultsArray addObject:[string substringWithRange:NSMakeRange(stringIndex+1, nextIndex - stringIndex - 1)]]; + } + stringIndex = nextIndex; + } + + // Add the end of the string after the previously matched character where appropriate. + if (stringIndex + 1 < [string length]) { + [resultsArray addObject:[string substringWithRange:NSMakeRange(stringIndex+1, [string length] - stringIndex - 1)]]; + } + + return resultsArray; +} + + +/* + * A method intended for use by the functions above. + */ +- (long) firstOccurrenceOfCharacter:(unichar)character ignoringQuotedStrings:(BOOL)ignoreQuotedStrings +{ + return [self firstOccurrenceOfCharacter:character afterIndex:-1 skippingBrackets:NO ignoringQuotedStrings:ignoreQuotedStrings]; +} + + +/* + * A method intended for use by the functions above. + */ +- (long) firstOccurrenceOfCharacter:(unichar)character afterIndex:(long)startIndex ignoringQuotedStrings:(BOOL)ignoreQuotedStrings +{ + return [self firstOccurrenceOfCharacter:character afterIndex:startIndex skippingBrackets:NO ignoringQuotedStrings:ignoreQuotedStrings]; +} + + +/* + * A method intended for use by the functions above. + */ +- (long) firstOccurrenceOfCharacter:(unichar)character afterIndex:(long)startIndex skippingBrackets:(BOOL)skipBrackets ignoringQuotedStrings:(BOOL)ignoreQuotedStrings +{ + long currentStringIndex, quotedStringEndIndex; + unichar currentCharacter; + long stringLength = [string length]; + int bracketingLevel = 0; + + // Sanity check inputs + if (startIndex < -1) startIndex = -1; + + // Walk along the string, processing characters + for (currentStringIndex = startIndex + 1; currentStringIndex < stringLength; currentStringIndex++) { + currentCharacter = [string characterAtIndex:currentStringIndex]; + + // Check for the ending character, and if it has been found and quoting/brackets is valid, return. + if (currentCharacter == character) { + if (!skipBrackets || bracketingLevel <= 0) { + return currentStringIndex; + } + } + + // Process strings and comments as appropriate + switch (currentCharacter) { + + // When quote characters are encountered and strings are not being ignored, walk to the end of the quoted string. + case '\'': + case '"': + case '`': + if (!ignoreQuotedStrings) break; + quotedStringEndIndex = [self endIndexOfStringQuotedByCharacter:currentCharacter startingAtIndex:currentStringIndex+1]; + if (quotedStringEndIndex == NSNotFound) { + return NSNotFound; + } + currentStringIndex = quotedStringEndIndex; + break; + + // For opening brackets increment the bracket count + case '(': + bracketingLevel++; + break; + + // For closing brackets decrement the bracket count + case ')': + bracketingLevel--; + + // For comments starting "--[\s]", ensure the start syntax is valid before proceeding. + case '-': + if (stringLength < currentStringIndex + 2) break; + if ([string characterAtIndex:currentStringIndex+1] != '-') break; + if (![[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:[string characterAtIndex:currentStringIndex+2]]) break; + currentStringIndex = [self endIndexOfCommentOfType:SPDoubleDashComment startingAtIndex:currentStringIndex]; + break; + + case '#': + currentStringIndex = [self endIndexOfCommentOfType:SPHashComment startingAtIndex:currentStringIndex]; + break; + + // For comments starting "/*", ensure the start syntax is valid before proceeding. + case '/': + if (stringLength < currentStringIndex + 1) break; + if ([string characterAtIndex:currentStringIndex+1] != '*') break; + currentStringIndex = [self endIndexOfCommentOfType:SPCStyleComment startingAtIndex:currentStringIndex]; + break; + } + } + + // If no matches have been made in this string, return NSNotFound. + return NSNotFound; +} + +/* + * A method intended for use by the functions above. + */ +- (long) endIndexOfStringQuotedByCharacter:(unichar)quoteCharacter startingAtIndex:(long)index +{ + long currentStringIndex, stringLength, i, quotedStringLength; + BOOL characterIsEscaped; + unichar currentCharacter; + + stringLength = [string length]; + + // Walk the string looking for the string end + for ( currentStringIndex = index; currentStringIndex < stringLength; currentStringIndex++) { + currentCharacter = [string characterAtIndex:currentStringIndex]; + + // If the string end is a backtick and one has been encountered, treat it as end of string + if (quoteCharacter == '`' && currentCharacter == '`') { + + // ...as long as the next character isn't also a backtick, in which case it's being quoted. Skip both. + if ((currentStringIndex + 1) < stringLength && [string characterAtIndex:currentStringIndex+1] == '`') { + currentStringIndex++; + continue; + } + + return currentStringIndex; + + // Otherwise, prepare to treat the string as ended when meeting the correct boundary character.... + } else if (currentCharacter == quoteCharacter) { + + // ...but only if the string end isn't escaped with an *odd* number of escaping characters... + characterIsEscaped = NO; + i = 1; + quotedStringLength = currentStringIndex - 1; + while ((quotedStringLength - i) > 0 && [string characterAtIndex:currentStringIndex - i] == '\\') { + characterIsEscaped = !characterIsEscaped; + i++; + } + + // If an even number have been found, it may be the end of the string - as long as the subsequent character + // isn't also the same character, in which case it's another form of escaping. + if (!characterIsEscaped) { + if ((currentStringIndex + 1) < stringLength && [string characterAtIndex:currentStringIndex+1] == quoteCharacter) { + currentStringIndex++; + continue; + } + + // Really is the end of the string. + return currentStringIndex; + } + } + } + + return NSNotFound; +} + +/* + * A method intended for use by the functions above. + */ +- (long) endIndexOfCommentOfType:(SPCommentType)commentType startingAtIndex:(long)index +{ + long stringLength = [string length]; + unichar currentCharacter; + + switch (commentType) { + + // For comments of type "--[\s]", start the comment processing two characters in to match the start syntax, + // then flow into the Hash comment handling (looking for first newline). + case SPDoubleDashComment: + index = index+2; + + // For comments starting "--[\s]" and "#", continue until the first newline. + case SPHashComment: + index++; + for ( ; index < stringLength; index++ ) { + currentCharacter = [string characterAtIndex:index]; + if (currentCharacter == '\r' || currentCharacter == '\n') { + return index-1; + } + } + break; + + // For comments starting "/*", start the comment processing one character in to match the start syntax, then + // continue until the first matching "*/". + case SPCStyleComment: + index = index+2; + for ( ; index < stringLength; index++ ) { + if ([string characterAtIndex:index] == '*') { + if ((stringLength > index + 1) && [string characterAtIndex:index+1] == '/') { + return (index+1); + } + } + } + } + + // If no match has been found, the comment must continue until the very end of the string. + return (stringLength-1); +} + + +/* Required and primitive methods to allow subclassing class cluster */ +#pragma mark - +- (id) init { + if (self = [super init]) { + string = [[NSMutableString string] retain]; + } + return self; +} +- (id) initWithBytes:(const void *)bytes length:(unsigned int)length encoding:(NSStringEncoding)encoding { + if (self = [super init]) { + string = [[NSMutableString alloc] initWithBytes:bytes length:length encoding:encoding]; + } + return self; +} +- (id) initWithBytesNoCopy:(void *)bytes length:(unsigned int)length encoding:(NSStringEncoding)encoding freeWhenDone:(BOOL)flag { + if (self = [super init]) { + string = [[NSMutableString alloc] initWithBytesNoCopy:bytes length:length encoding:encoding freeWhenDone:flag]; + } + return self; +} +- (id) initWithCapacity:(unsigned int)capacity { + if (self = [super init]) { + string = [[NSMutableString stringWithCapacity:capacity] retain]; + } + return self; +} +- (id) initWithCharactersNoCopy:(unichar *)characters length:(unsigned int)length freeWhenDone:(BOOL)flag { + if (self = [super init]) { + string = [[NSMutableString alloc] initWithCharactersNoCopy:characters length:length freeWhenDone:flag]; + } + return self; +} +- (id) initWithContentsOfFile:(id)path { + return [self initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; +} +- (id) initWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)encoding error:(NSError **)error { + if (self = [super init]) { + string = [[NSMutableString alloc] initWithContentsOfFile:path encoding:encoding error:error]; + } + return self; +} +- (id) initWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding { + if (self = [super init]) { + string = [[NSMutableString alloc] initWithCString:nullTerminatedCString encoding:encoding]; + } + return self; +} +- (id) initWithFormat:(NSString *)format, ... { + va_list argList; + va_start(argList, format); + id str = [self initWithFormat:format arguments:argList]; + va_end(argList); + return str; +} +- (id) initWithFormat:(NSString *)format arguments:(va_list)argList { + if (self = [super init]) { + string = [[NSMutableString alloc] initWithFormat:format arguments:argList]; + } + return self; +} +- (unsigned int) length { + return [string length]; +} +- (unichar) characterAtIndex:(unsigned int)index { + return [string characterAtIndex:index]; +} +- (id) description { + return [string description]; +} +- (unsigned int) replaceOccurrencesOfString:(NSString *)target withString:(NSString *)replacement options:(unsigned)options range:(NSRange)searchRange { + return [string replaceOccurrencesOfString:target withString:replacement options:options range:searchRange]; +} +- (void) setString:(NSString *)aString { + [string setString:aString]; +} +- (void) replaceCharactersInRange:(NSRange)range withString:(NSString *)aString { + [string replaceCharactersInRange:range withString:aString]; +} +- (void) dealloc { + [string release]; + [super dealloc]; +} +@end
\ No newline at end of file |