├── .gitignore ├── AMR_ANSIEscapeHelper.h ├── AMR_ANSIEscapeHelper.m ├── AnsiColorsTest.xcodeproj ├── TemplateIcon.icns ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── alir.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── alir.xcuserdatad │ └── xcschemes │ ├── AnsiColorsTest.xcscheme │ └── xcschememanagement.plist ├── AnsiColorsTest_Prefix.pch ├── English.lproj ├── InfoPlist.strings └── MainMenu.xib ├── Info.plist ├── Makefile ├── MyController.h ├── MyController.m ├── a.out ├── deployment-files └── readme.txt ├── main.m ├── readme.md ├── test-onechar.pl ├── test.c └── test.pl /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | 4 | build 5 | deployment 6 | headerdoc 7 | 8 | deploymentScpTarget 9 | pathToSparklePrivateKey 10 | 11 | *.nib.orig 12 | *.pbxuser 13 | *.perspective 14 | *.perspectivev3 15 | -------------------------------------------------------------------------------- /AMR_ANSIEscapeHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // ANSIEscapeHelper.h 3 | // AnsiColorsTest 4 | // 5 | // Created by Ali Rantakari on 18.3.09. 6 | // 7 | // Version 0.9.6 8 | // 9 | /* 10 | The MIT License 11 | 12 | Copyright (c) 2008-2009,2013 Ali Rantakari 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in 22 | all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | THE SOFTWARE. 31 | */ 32 | 33 | #import 34 | 35 | 36 | #if !__has_feature(objc_arc) 37 | #warning "This code requires ARC to be enabled." 38 | #endif 39 | 40 | 41 | // dictionary keys for the SGR code dictionaries that the array 42 | // escapeCodesForString:cleanString: returns contains 43 | #define kAMRCodeDictKey_code @"code" 44 | #define kAMRCodeDictKey_location @"location" 45 | 46 | // dictionary keys for the string formatting attribute 47 | // dictionaries that the array attributesForString:cleanString: 48 | // returns contains 49 | #define kAMRAttrDictKey_range @"range" 50 | #define kAMRAttrDictKey_attrName @"attributeName" 51 | #define kAMRAttrDictKey_attrValue @"attributeValue" 52 | 53 | 54 | /*! 55 | @enum AMR_SGRCode 56 | 57 | @abstract SGR (Select Graphic Rendition) ANSI control codes. 58 | */ 59 | typedef enum 60 | { 61 | AMR_SGRCodeNoneOrInvalid = -1, 62 | 63 | AMR_SGRCodeAllReset = 0, 64 | 65 | AMR_SGRCodeIntensityBold = 1, 66 | AMR_SGRCodeIntensityFaint = 2, 67 | AMR_SGRCodeIntensityNormal = 22, 68 | 69 | AMR_SGRCodeItalicOn = 3, 70 | 71 | AMR_SGRCodeUnderlineSingle = 4, 72 | AMR_SGRCodeUnderlineDouble = 21, 73 | AMR_SGRCodeUnderlineNone = 24, 74 | 75 | AMR_SGRCodeFgBlack = 30, 76 | AMR_SGRCodeFgRed = 31, 77 | AMR_SGRCodeFgGreen = 32, 78 | AMR_SGRCodeFgYellow = 33, 79 | AMR_SGRCodeFgBlue = 34, 80 | AMR_SGRCodeFgMagenta = 35, 81 | AMR_SGRCodeFgCyan = 36, 82 | AMR_SGRCodeFgWhite = 37, 83 | AMR_SGRCodeFgReset = 39, 84 | 85 | AMR_SGRCodeBgBlack = 40, 86 | AMR_SGRCodeBgRed = 41, 87 | AMR_SGRCodeBgGreen = 42, 88 | AMR_SGRCodeBgYellow = 43, 89 | AMR_SGRCodeBgBlue = 44, 90 | AMR_SGRCodeBgMagenta = 45, 91 | AMR_SGRCodeBgCyan = 46, 92 | AMR_SGRCodeBgWhite = 47, 93 | AMR_SGRCodeBgReset = 49, 94 | 95 | AMR_SGRCodeFgBrightBlack = 90, 96 | AMR_SGRCodeFgBrightRed = 91, 97 | AMR_SGRCodeFgBrightGreen = 92, 98 | AMR_SGRCodeFgBrightYellow = 93, 99 | AMR_SGRCodeFgBrightBlue = 94, 100 | AMR_SGRCodeFgBrightMagenta = 95, 101 | AMR_SGRCodeFgBrightCyan = 96, 102 | AMR_SGRCodeFgBrightWhite = 97, 103 | 104 | AMR_SGRCodeBgBrightBlack = 100, 105 | AMR_SGRCodeBgBrightRed = 101, 106 | AMR_SGRCodeBgBrightGreen = 102, 107 | AMR_SGRCodeBgBrightYellow = 103, 108 | AMR_SGRCodeBgBrightBlue = 104, 109 | AMR_SGRCodeBgBrightMagenta = 105, 110 | AMR_SGRCodeBgBrightCyan = 106, 111 | AMR_SGRCodeBgBrightWhite = 107 112 | } AMR_SGRCode; 113 | 114 | 115 | 116 | 117 | 118 | 119 | /*! 120 | @class AMR_ANSIEscapeHelper 121 | 122 | @abstract Contains helper methods for dealing with strings 123 | that contain ANSI escape sequences for formatting (colors, 124 | underlining, bold etc.) 125 | */ 126 | @interface AMR_ANSIEscapeHelper : NSObject 127 | 128 | /*! 129 | @property defaultStringColor 130 | 131 | @abstract The default color used when creating an attributed string (default is black). 132 | */ 133 | @property(copy) NSColor *defaultStringColor; 134 | 135 | 136 | /*! 137 | @property font 138 | 139 | @abstract The font to use when creating string formatting attribute values. 140 | */ 141 | @property(copy) NSFont *font; 142 | 143 | /*! 144 | @property ansiColors 145 | 146 | @abstract The colors to use for displaying ANSI colors. 147 | 148 | @discussion Keys in this dictionary should be NSNumber objects containing SGR code 149 | values from the AMR_SGRCode enum. The corresponding values for these keys 150 | should be NSColor objects. If this property is nil or if it doesn't 151 | contain a key for a specific SGR code, the default color will be used 152 | instead. 153 | */ 154 | @property(retain) NSMutableDictionary *ansiColors; 155 | 156 | 157 | /*! 158 | @method attributedStringWithANSIEscapedString: 159 | 160 | @abstract Returns an attributed string that corresponds both in contents 161 | and formatting to a given string that contains ANSI escape 162 | sequences. 163 | 164 | @param aString A String containing ANSI escape sequences 165 | 166 | @result An attributed string that mimics as closely as possible 167 | the formatting of the given ANSI-escaped string. 168 | */ 169 | - (NSAttributedString*) attributedStringWithANSIEscapedString:(NSString*)aString; 170 | 171 | 172 | /*! 173 | @method ansiEscapedStringWithAttributedString: 174 | 175 | @abstract Returns a string containing ANSI escape sequences that corresponds 176 | both in contents and formatting to a given attributed string. 177 | 178 | @param aAttributedString An attributed string 179 | 180 | @result A string that mimics as closely as possible 181 | the formatting of the given attributed string with 182 | ANSI escape sequences. 183 | */ 184 | - (NSString*) ansiEscapedStringWithAttributedString:(NSAttributedString*)aAttributedString; 185 | 186 | 187 | /*! 188 | @method escapeCodesForString:cleanString: 189 | 190 | @abstract Returns an array of SGR codes and their locations from a 191 | string containing ANSI escape sequences as well as a "clean" 192 | version of the string (i.e. one without the ANSI escape 193 | sequences.) 194 | 195 | @param aString A String containing ANSI escape sequences 196 | @param aCleanString Upon return, contains a "clean" version of aString (i.e. aString 197 | without the ANSI escape sequences) 198 | 199 | @result An array of NSDictionary objects, each of which has 200 | an NSNumber value for the key "code" (specifying an SGR code) and 201 | another NSNumber value for the key "location" (specifying the 202 | location of the code within aCleanString.) 203 | */ 204 | - (NSArray*) escapeCodesForString:(NSString*)aString cleanString:(NSString**)aCleanString; 205 | 206 | 207 | /*! 208 | @method ansiEscapedStringWithCodesAndLocations:cleanString: 209 | 210 | @abstract Returns a string containing ANSI escape codes for formatting based 211 | on a string and an array of SGR codes and their locations within 212 | the given string. 213 | 214 | @param aCodesArray An array of NSDictionary objects, each of which should have 215 | an NSNumber value for the key "code" (specifying an SGR 216 | code) and another NSNumber value for the key "location" 217 | (specifying the location of this SGR code in aCleanString.) 218 | @param aCleanString The string to which to insert the ANSI escape codes 219 | described in aCodesArray. 220 | 221 | @result A string containing ANSI escape sequences. 222 | */ 223 | - (NSString*) ansiEscapedStringWithCodesAndLocations:(NSArray*)aCodesArray cleanString:(NSString*)aCleanString; 224 | 225 | 226 | /*! 227 | @method attributesForString:cleanString: 228 | 229 | @abstract Convert ANSI escape sequences in a string to string formatting attributes. 230 | 231 | @discussion Given a string with some ANSI escape sequences in it, this method returns 232 | attributes for formatting the specified string according to those ANSI 233 | escape sequences as well as a "clean" (i.e. free of the escape sequences) 234 | version of this string. 235 | 236 | @param aString A String containing ANSI escape sequences 237 | @param aCleanString Upon return, contains a "clean" version of aString (i.e. aString 238 | without the ANSI escape sequences.) Pass in NULL if you're not 239 | interested in this. 240 | 241 | @result An array containing NSDictionary objects, each of which has keys "range" 242 | (an NSValue containing an NSRange, specifying the range for the 243 | attribute within the "clean" version of aString), "attributeName" (an 244 | NSString) and "attributeValue" (an NSObject). You may use these as 245 | arguments for NSMutableAttributedString's methods for setting the 246 | visual formatting. 247 | */ 248 | - (NSArray*) attributesForString:(NSString*)aString cleanString:(NSString**)aCleanString; 249 | 250 | 251 | /*! 252 | @method AMR_SGRCode:endsFormattingIntroducedByCode: 253 | 254 | @abstract Whether the occurrence of a given SGR code would end the formatting run 255 | introduced by another SGR code. 256 | 257 | @discussion For example, AMR_SGRCodeFgReset, AMR_SGRCodeAllReset or any SGR code 258 | specifying a foreground color would end the formatting run 259 | introduced by a foreground color -specifying SGR code. 260 | 261 | @param endCode The SGR code to test as a candidate for ending the formatting run 262 | introduced by startCode 263 | @param startCode The SGR code that has introduced a formatting run 264 | 265 | @result YES if the occurrence of endCode would end the formatting run 266 | introduced by startCode, NO otherwise. 267 | */ 268 | - (BOOL) AMR_SGRCode:(AMR_SGRCode)endCode endsFormattingIntroducedByCode:(AMR_SGRCode)startCode; 269 | 270 | 271 | /*! 272 | @method colorForSGRCode: 273 | 274 | @abstract Returns the color to use for displaying a specific ANSI color. 275 | 276 | @discussion This method first considers the values set in the ansiColors 277 | property and only then the standard basic colors (NSColor's 278 | redColor, blueColor etc.) 279 | 280 | @param code An SGR code that specifies an ANSI color. 281 | 282 | @result The color to use for displaying the ANSI color specified by code. 283 | */ 284 | - (NSColor*) colorForSGRCode:(AMR_SGRCode)code; 285 | 286 | 287 | /*! 288 | @method AMR_SGRCodeForColor:isForegroundColor: 289 | 290 | @abstract Returns a color SGR code that corresponds to a given color. 291 | 292 | @discussion This method matches colors to their equivalent SGR codes 293 | by going through the colors specified in the ansiColors 294 | dictionary, and if ansiColors is null or if a match is 295 | not found there, by comparing the given color to the 296 | standard basic colors (NSColor's redColor, blueColor 297 | etc.) The comparison is done simply by checking for 298 | equality. 299 | 300 | @param aColor The color to get a corresponding SGR code for 301 | @param aForeground Whether you want a foreground or background color code 302 | 303 | @result SGR code that corresponds with aColor. 304 | */ 305 | - (AMR_SGRCode) AMR_SGRCodeForColor:(NSColor*)aColor isForegroundColor:(BOOL)aForeground; 306 | 307 | 308 | /*! 309 | @method closestSGRCodeForColor:isForegroundColor: 310 | 311 | @abstract Returns a color SGR code that represents the closest ANSI 312 | color to a given color. 313 | 314 | @discussion This method attempts to find the closest ANSI color to 315 | aColor and return its SGR code. 316 | 317 | @param aColor The color to get a closest color SGR code match for 318 | @param aForeground Whether you want a foreground or background color code 319 | 320 | @result SGR code for the ANSI color that is closest to aColor. 321 | */ 322 | - (AMR_SGRCode) closestSGRCodeForColor:(NSColor *)color isForegroundColor:(BOOL)foreground; 323 | 324 | 325 | 326 | @end 327 | -------------------------------------------------------------------------------- /AMR_ANSIEscapeHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // ANSIEscapeHelper.m 3 | // 4 | // Created by Ali Rantakari on 18.3.09. 5 | 6 | /* 7 | The MIT License 8 | 9 | Copyright (c) 2008-2009,2013 Ali Rantakari 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in 19 | all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | THE SOFTWARE. 28 | */ 29 | 30 | /* 31 | todo: 32 | 33 | - don't add useless "reset" escape codes to the string in 34 | -ansiEscapedStringWithAttributedString: 35 | 36 | */ 37 | 38 | 39 | 40 | #import "AMR_ANSIEscapeHelper.h" 41 | 42 | 43 | // the CSI (Control Sequence Initiator) -- i.e. "escape sequence prefix". 44 | // (add your own CSI:Miami joke here) 45 | #define kANSIEscapeCSI @"\033[" 46 | 47 | // the end byte of an SGR (Select Graphic Rendition) 48 | // ANSI Escape Sequence 49 | #define kANSIEscapeSGREnd @"m" 50 | 51 | 52 | // color definition helper macros 53 | #define kBrightColorBrightness 1.0 54 | #define kBrightColorSaturation 0.4 55 | #define kBrightColorAlpha 1.0 56 | #define kBrightColorWithHue(h) [NSColor colorWithCalibratedHue:(h) saturation:kBrightColorSaturation brightness:kBrightColorBrightness alpha:kBrightColorAlpha] 57 | 58 | // default colors 59 | #define kDefaultANSIColorFgBlack NSColor.blackColor 60 | #define kDefaultANSIColorFgRed NSColor.redColor 61 | #define kDefaultANSIColorFgGreen NSColor.greenColor 62 | #define kDefaultANSIColorFgYellow NSColor.yellowColor 63 | #define kDefaultANSIColorFgBlue NSColor.blueColor 64 | #define kDefaultANSIColorFgMagenta NSColor.magentaColor 65 | #define kDefaultANSIColorFgCyan NSColor.cyanColor 66 | #define kDefaultANSIColorFgWhite NSColor.whiteColor 67 | 68 | #define kDefaultANSIColorFgBrightBlack [NSColor colorWithCalibratedWhite:0.337 alpha:1.0] 69 | #define kDefaultANSIColorFgBrightRed kBrightColorWithHue(1.0) 70 | #define kDefaultANSIColorFgBrightGreen kBrightColorWithHue(1.0/3.0) 71 | #define kDefaultANSIColorFgBrightYellow kBrightColorWithHue(1.0/6.0) 72 | #define kDefaultANSIColorFgBrightBlue kBrightColorWithHue(2.0/3.0) 73 | #define kDefaultANSIColorFgBrightMagenta kBrightColorWithHue(5.0/6.0) 74 | #define kDefaultANSIColorFgBrightCyan kBrightColorWithHue(0.5) 75 | #define kDefaultANSIColorFgBrightWhite NSColor.whiteColor 76 | 77 | #define kDefaultANSIColorBgBlack NSColor.blackColor 78 | #define kDefaultANSIColorBgRed NSColor.redColor 79 | #define kDefaultANSIColorBgGreen NSColor.greenColor 80 | #define kDefaultANSIColorBgYellow NSColor.yellowColor 81 | #define kDefaultANSIColorBgBlue NSColor.blueColor 82 | #define kDefaultANSIColorBgMagenta NSColor.magentaColor 83 | #define kDefaultANSIColorBgCyan NSColor.cyanColor 84 | #define kDefaultANSIColorBgWhite NSColor.whiteColor 85 | 86 | #define kDefaultANSIColorBgBrightBlack kDefaultANSIColorFgBrightBlack 87 | #define kDefaultANSIColorBgBrightRed kDefaultANSIColorFgBrightRed 88 | #define kDefaultANSIColorBgBrightGreen kDefaultANSIColorFgBrightGreen 89 | #define kDefaultANSIColorBgBrightYellow kDefaultANSIColorFgBrightYellow 90 | #define kDefaultANSIColorBgBrightBlue kDefaultANSIColorFgBrightBlue 91 | #define kDefaultANSIColorBgBrightMagenta kDefaultANSIColorFgBrightMagenta 92 | #define kDefaultANSIColorBgBrightCyan kDefaultANSIColorFgBrightCyan 93 | #define kDefaultANSIColorBgBrightWhite kDefaultANSIColorFgBrightWhite 94 | 95 | #define kDefaultFontSize [NSFont systemFontOfSize:NSFont.systemFontSize] 96 | #define kDefaultForegroundColor NSColor.blackColor 97 | 98 | // minimum weight for an NSFont for it to be considered bold 99 | #define kBoldFontMinWeight 9 100 | 101 | 102 | @implementation AMR_ANSIEscapeHelper 103 | 104 | - (id) init 105 | { 106 | if (!(self = [super init])) 107 | return nil; 108 | 109 | self.ansiColors = [NSMutableDictionary dictionary]; 110 | 111 | return self; 112 | } 113 | 114 | 115 | 116 | - (NSAttributedString*) attributedStringWithANSIEscapedString:(NSString*)aString 117 | { 118 | if (aString == nil) 119 | return nil; 120 | 121 | NSString *cleanString; 122 | NSArray *attributesAndRanges = [self attributesForString:aString cleanString:&cleanString]; 123 | NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] 124 | initWithString:cleanString 125 | attributes:@{ 126 | NSFontAttributeName: self.font ?: kDefaultFontSize, 127 | NSForegroundColorAttributeName: self.defaultStringColor ?: kDefaultForegroundColor 128 | }]; 129 | 130 | for (NSDictionary *thisAttributeDict in attributesAndRanges) 131 | { 132 | [attributedString 133 | addAttribute:thisAttributeDict[kAMRAttrDictKey_attrName] 134 | value:thisAttributeDict[kAMRAttrDictKey_attrValue] 135 | range:[thisAttributeDict[kAMRAttrDictKey_range] rangeValue] 136 | ]; 137 | } 138 | 139 | return attributedString; 140 | } 141 | 142 | 143 | 144 | - (NSString*) ansiEscapedStringWithAttributedString:(NSAttributedString*)aAttributedString 145 | { 146 | NSMutableArray *codesAndLocations = [NSMutableArray array]; 147 | 148 | NSArray *attrNames = @[ 149 | NSFontAttributeName, NSForegroundColorAttributeName, 150 | NSBackgroundColorAttributeName, NSUnderlineStyleAttributeName, 151 | ]; 152 | 153 | for (NSString *thisAttrName in attrNames) 154 | { 155 | NSRange limitRange = NSMakeRange(0, aAttributedString.length); 156 | id attributeValue; 157 | NSRange effectiveRange; 158 | 159 | while (limitRange.length > 0) 160 | { 161 | attributeValue = [aAttributedString 162 | attribute:thisAttrName 163 | atIndex:limitRange.location 164 | longestEffectiveRange:&effectiveRange 165 | inRange:limitRange 166 | ]; 167 | 168 | AMR_SGRCode thisSGRCode = AMR_SGRCodeNoneOrInvalid; 169 | 170 | if ([thisAttrName isEqualToString:NSForegroundColorAttributeName]) 171 | { 172 | if (attributeValue != nil) 173 | thisSGRCode = [self closestSGRCodeForColor:attributeValue isForegroundColor:YES]; 174 | else 175 | thisSGRCode = AMR_SGRCodeFgReset; 176 | } 177 | else if ([thisAttrName isEqualToString:NSBackgroundColorAttributeName]) 178 | { 179 | if (attributeValue != nil) 180 | thisSGRCode = [self closestSGRCodeForColor:attributeValue isForegroundColor:NO]; 181 | else 182 | thisSGRCode = AMR_SGRCodeBgReset; 183 | } 184 | else if ([thisAttrName isEqualToString:NSFontAttributeName]) 185 | { 186 | // we currently only use NSFontAttributeName for bolding so 187 | // here we assume that the formatting "type" in ANSI SGR 188 | // terms is indeed intensity 189 | if (attributeValue != nil) 190 | thisSGRCode = ([NSFontManager.sharedFontManager weightOfFont:attributeValue] >= kBoldFontMinWeight) 191 | ? AMR_SGRCodeIntensityBold : AMR_SGRCodeIntensityNormal; 192 | else 193 | thisSGRCode = AMR_SGRCodeIntensityNormal; 194 | } 195 | else if ([thisAttrName isEqualToString:NSUnderlineStyleAttributeName]) 196 | { 197 | if (attributeValue != nil) 198 | { 199 | if ([attributeValue intValue] == NSUnderlineStyleSingle) 200 | thisSGRCode = AMR_SGRCodeUnderlineSingle; 201 | else if ([attributeValue intValue] == NSUnderlineStyleDouble) 202 | thisSGRCode = AMR_SGRCodeUnderlineDouble; 203 | else 204 | thisSGRCode = AMR_SGRCodeUnderlineNone; 205 | } 206 | else 207 | thisSGRCode = AMR_SGRCodeUnderlineNone; 208 | } 209 | 210 | if (thisSGRCode != AMR_SGRCodeNoneOrInvalid) 211 | { 212 | [codesAndLocations addObject: @{ 213 | kAMRCodeDictKey_code: @(thisSGRCode), 214 | kAMRCodeDictKey_location: @(effectiveRange.location), 215 | }]; 216 | } 217 | 218 | limitRange = NSMakeRange(NSMaxRange(effectiveRange), 219 | NSMaxRange(limitRange) - NSMaxRange(effectiveRange)); 220 | } 221 | } 222 | 223 | return [self ansiEscapedStringWithCodesAndLocations:codesAndLocations cleanString:aAttributedString.string]; 224 | } 225 | 226 | 227 | - (NSArray*) escapeCodesForString:(NSString*)aString cleanString:(NSString**)aCleanString 228 | { 229 | if (aString == nil) 230 | return nil; 231 | if (aString.length <= kANSIEscapeCSI.length) 232 | { 233 | if (aCleanString) 234 | *aCleanString = aString.copy; 235 | return @[]; 236 | } 237 | 238 | NSString *cleanString = @""; 239 | 240 | // find all escape sequence codes from aString and put them in this array 241 | // along with their start locations within the "clean" version of aString 242 | NSMutableArray *formatCodes = [NSMutableArray array]; 243 | 244 | NSUInteger aStringLength = aString.length; 245 | NSUInteger coveredLength = 0; 246 | NSRange searchRange = NSMakeRange(0,aStringLength); 247 | NSRange thisEscapeSequenceRange; 248 | do 249 | { 250 | thisEscapeSequenceRange = [aString rangeOfString:kANSIEscapeCSI options:NSLiteralSearch range:searchRange]; 251 | if (thisEscapeSequenceRange.location != NSNotFound) 252 | { 253 | // adjust range's length so that it encompasses the whole ANSI escape sequence 254 | // and not just the Control Sequence Initiator (the "prefix") by finding the 255 | // final byte of the control sequence (one that has an ASCII decimal value 256 | // between 64 and 126.) at the same time, read all formatting codes from inside 257 | // this escape sequence (there may be several, separated by semicolons.) 258 | NSMutableArray *codes = [NSMutableArray array]; 259 | unsigned int code = 0; 260 | unsigned int lengthAddition = 1; 261 | NSUInteger thisIndex; 262 | for (;;) 263 | { 264 | thisIndex = (NSMaxRange(thisEscapeSequenceRange)+lengthAddition-1); 265 | if (thisIndex >= aStringLength) 266 | break; 267 | 268 | unichar c = [aString characterAtIndex:thisIndex]; 269 | 270 | if (('0' <= c) && (c <= '9')) 271 | { 272 | int digit = c - '0'; 273 | code = (code == 0) ? digit : code*10+digit; 274 | } 275 | 276 | // ASCII decimal 109 is the SGR (Select Graphic Rendition) final byte 277 | // ("m"). this means that the code value we've just read specifies formatting 278 | // for the output; exactly what we're interested in. 279 | if (c == 'm') 280 | { 281 | [codes addObject:@(code)]; 282 | break; 283 | } 284 | else if ((64 <= c) && (c <= 126)) // any other valid final byte 285 | { 286 | [codes removeAllObjects]; 287 | break; 288 | } 289 | else if (c == ';') // separates codes within the same sequence 290 | { 291 | [codes addObject:@(code)]; 292 | code = 0; 293 | } 294 | 295 | lengthAddition++; 296 | } 297 | thisEscapeSequenceRange.length += lengthAddition; 298 | 299 | NSUInteger locationInCleanString = coveredLength+thisEscapeSequenceRange.location-searchRange.location; 300 | 301 | for (NSNumber *codeToAdd in codes) 302 | { 303 | [formatCodes addObject: @{ 304 | kAMRCodeDictKey_code: codeToAdd, 305 | kAMRCodeDictKey_location: @(locationInCleanString) 306 | }]; 307 | } 308 | 309 | NSUInteger thisCoveredLength = thisEscapeSequenceRange.location-searchRange.location; 310 | if (thisCoveredLength > 0) 311 | cleanString = [cleanString stringByAppendingString:[aString substringWithRange:NSMakeRange(searchRange.location, thisCoveredLength)]]; 312 | 313 | coveredLength += thisCoveredLength; 314 | searchRange.location = NSMaxRange(thisEscapeSequenceRange); 315 | searchRange.length = aStringLength-searchRange.location; 316 | } 317 | } 318 | while(thisEscapeSequenceRange.location != NSNotFound); 319 | 320 | if (searchRange.length > 0) 321 | cleanString = [cleanString stringByAppendingString:[aString substringWithRange:searchRange]]; 322 | 323 | if (aCleanString) 324 | *aCleanString = cleanString; 325 | return formatCodes; 326 | } 327 | 328 | 329 | 330 | 331 | - (NSString*) ansiEscapedStringWithCodesAndLocations:(NSArray*)aCodesArray cleanString:(NSString*)aCleanString 332 | { 333 | NSMutableString* retStr = [NSMutableString stringWithCapacity:aCleanString.length]; 334 | 335 | NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:kAMRCodeDictKey_location ascending:YES]; 336 | NSArray *codesArray = [aCodesArray sortedArrayUsingDescriptors:@[sortDescriptor]]; 337 | 338 | NSUInteger aCleanStringIndex = 0; 339 | NSUInteger aCleanStringLength = aCleanString.length; 340 | for (NSDictionary *thisCodeDict in codesArray) 341 | { 342 | if (!( thisCodeDict[kAMRCodeDictKey_code] && 343 | thisCodeDict[kAMRCodeDictKey_location] 344 | )) 345 | continue; 346 | 347 | AMR_SGRCode thisCode = [thisCodeDict[kAMRCodeDictKey_code] unsignedIntValue]; 348 | NSUInteger formattingRunStartLocation = [thisCodeDict[kAMRCodeDictKey_location] unsignedIntegerValue]; 349 | 350 | if (formattingRunStartLocation > aCleanStringLength) 351 | continue; 352 | 353 | if (aCleanStringIndex < formattingRunStartLocation) 354 | [retStr appendString:[aCleanString substringWithRange:NSMakeRange(aCleanStringIndex, formattingRunStartLocation-aCleanStringIndex)]]; 355 | [retStr appendFormat:@"%@%d%@", kANSIEscapeCSI, thisCode, kANSIEscapeSGREnd]; 356 | 357 | aCleanStringIndex = formattingRunStartLocation; 358 | } 359 | 360 | if (aCleanStringIndex < aCleanStringLength) 361 | [retStr appendString:[aCleanString substringFromIndex:aCleanStringIndex]]; 362 | 363 | [retStr appendFormat:@"%@%d%@", kANSIEscapeCSI, AMR_SGRCodeAllReset, kANSIEscapeSGREnd]; 364 | 365 | return retStr; 366 | } 367 | 368 | 369 | 370 | 371 | 372 | - (NSArray*) attributesForString:(NSString*)aString cleanString:(NSString**)aCleanString 373 | { 374 | if (aString == nil) 375 | return nil; 376 | if (aString.length <= kANSIEscapeCSI.length) 377 | { 378 | if (aCleanString) 379 | *aCleanString = aString.copy; 380 | return @[]; 381 | } 382 | 383 | NSMutableArray *attrsAndRanges = [NSMutableArray array]; 384 | 385 | NSString *cleanString; 386 | NSArray *formatCodes = [self escapeCodesForString:aString cleanString:&cleanString]; 387 | 388 | // go through all the found escape sequence codes and for each one, create 389 | // the string formatting attribute name and value, find the next escape 390 | // sequence that specifies the end of the formatting run started by 391 | // the currently handled code, and generate a range from the difference 392 | // in those codes' locations within the clean aString. 393 | for (NSUInteger iCode = 0; iCode < formatCodes.count; iCode++) 394 | { 395 | NSDictionary *thisCodeDict = formatCodes[iCode]; 396 | AMR_SGRCode thisCode = [thisCodeDict[kAMRCodeDictKey_code] unsignedIntValue]; 397 | NSUInteger formattingRunStartLocation = [thisCodeDict[kAMRCodeDictKey_location] unsignedIntegerValue]; 398 | 399 | // the attributed string attribute name for the formatting run introduced 400 | // by this code 401 | NSString *thisAttributeName = nil; 402 | 403 | // the attributed string attribute value for this formatting run introduced 404 | // by this code 405 | NSObject *thisAttributeValue = nil; 406 | 407 | // set attribute name 408 | switch(thisCode) 409 | { 410 | case AMR_SGRCodeFgBlack: 411 | case AMR_SGRCodeFgRed: 412 | case AMR_SGRCodeFgGreen: 413 | case AMR_SGRCodeFgYellow: 414 | case AMR_SGRCodeFgBlue: 415 | case AMR_SGRCodeFgMagenta: 416 | case AMR_SGRCodeFgCyan: 417 | case AMR_SGRCodeFgWhite: 418 | case AMR_SGRCodeFgBrightBlack: 419 | case AMR_SGRCodeFgBrightRed: 420 | case AMR_SGRCodeFgBrightGreen: 421 | case AMR_SGRCodeFgBrightYellow: 422 | case AMR_SGRCodeFgBrightBlue: 423 | case AMR_SGRCodeFgBrightMagenta: 424 | case AMR_SGRCodeFgBrightCyan: 425 | case AMR_SGRCodeFgBrightWhite: 426 | thisAttributeName = NSForegroundColorAttributeName; 427 | break; 428 | case AMR_SGRCodeBgBlack: 429 | case AMR_SGRCodeBgRed: 430 | case AMR_SGRCodeBgGreen: 431 | case AMR_SGRCodeBgYellow: 432 | case AMR_SGRCodeBgBlue: 433 | case AMR_SGRCodeBgMagenta: 434 | case AMR_SGRCodeBgCyan: 435 | case AMR_SGRCodeBgWhite: 436 | case AMR_SGRCodeBgBrightBlack: 437 | case AMR_SGRCodeBgBrightRed: 438 | case AMR_SGRCodeBgBrightGreen: 439 | case AMR_SGRCodeBgBrightYellow: 440 | case AMR_SGRCodeBgBrightBlue: 441 | case AMR_SGRCodeBgBrightMagenta: 442 | case AMR_SGRCodeBgBrightCyan: 443 | case AMR_SGRCodeBgBrightWhite: 444 | thisAttributeName = NSBackgroundColorAttributeName; 445 | break; 446 | case AMR_SGRCodeIntensityBold: 447 | case AMR_SGRCodeIntensityNormal: 448 | case AMR_SGRCodeIntensityFaint: 449 | thisAttributeName = NSFontAttributeName; 450 | break; 451 | case AMR_SGRCodeUnderlineSingle: 452 | case AMR_SGRCodeUnderlineDouble: 453 | case AMR_SGRCodeUnderlineNone: 454 | thisAttributeName = NSUnderlineStyleAttributeName; 455 | break; 456 | case AMR_SGRCodeAllReset: 457 | case AMR_SGRCodeFgReset: 458 | case AMR_SGRCodeBgReset: 459 | case AMR_SGRCodeNoneOrInvalid: 460 | case AMR_SGRCodeItalicOn: 461 | continue; 462 | } 463 | 464 | // set attribute value 465 | switch(thisCode) 466 | { 467 | case AMR_SGRCodeBgBlack: 468 | case AMR_SGRCodeFgBlack: 469 | case AMR_SGRCodeBgRed: 470 | case AMR_SGRCodeFgRed: 471 | case AMR_SGRCodeBgGreen: 472 | case AMR_SGRCodeFgGreen: 473 | case AMR_SGRCodeBgYellow: 474 | case AMR_SGRCodeFgYellow: 475 | case AMR_SGRCodeBgBlue: 476 | case AMR_SGRCodeFgBlue: 477 | case AMR_SGRCodeBgMagenta: 478 | case AMR_SGRCodeFgMagenta: 479 | case AMR_SGRCodeBgCyan: 480 | case AMR_SGRCodeFgCyan: 481 | case AMR_SGRCodeBgWhite: 482 | case AMR_SGRCodeFgWhite: 483 | case AMR_SGRCodeBgBrightBlack: 484 | case AMR_SGRCodeFgBrightBlack: 485 | case AMR_SGRCodeBgBrightRed: 486 | case AMR_SGRCodeFgBrightRed: 487 | case AMR_SGRCodeBgBrightGreen: 488 | case AMR_SGRCodeFgBrightGreen: 489 | case AMR_SGRCodeBgBrightYellow: 490 | case AMR_SGRCodeFgBrightYellow: 491 | case AMR_SGRCodeBgBrightBlue: 492 | case AMR_SGRCodeFgBrightBlue: 493 | case AMR_SGRCodeBgBrightMagenta: 494 | case AMR_SGRCodeFgBrightMagenta: 495 | case AMR_SGRCodeBgBrightCyan: 496 | case AMR_SGRCodeFgBrightCyan: 497 | case AMR_SGRCodeBgBrightWhite: 498 | case AMR_SGRCodeFgBrightWhite: 499 | thisAttributeValue = [self colorForSGRCode:thisCode]; 500 | break; 501 | case AMR_SGRCodeIntensityBold: 502 | { 503 | NSFont *boldFont = [NSFontManager.sharedFontManager convertFont:self.font toHaveTrait:NSBoldFontMask]; 504 | thisAttributeValue = boldFont; 505 | } 506 | break; 507 | case AMR_SGRCodeIntensityNormal: 508 | case AMR_SGRCodeIntensityFaint: 509 | { 510 | NSFont *unboldFont = [NSFontManager.sharedFontManager convertFont:self.font toHaveTrait:NSUnboldFontMask]; 511 | thisAttributeValue = unboldFont; 512 | } 513 | break; 514 | case AMR_SGRCodeUnderlineSingle: 515 | thisAttributeValue = @(NSUnderlineStyleSingle); 516 | break; 517 | case AMR_SGRCodeUnderlineDouble: 518 | thisAttributeValue = @(NSUnderlineStyleDouble); 519 | break; 520 | case AMR_SGRCodeUnderlineNone: 521 | thisAttributeValue = @(NSUnderlineStyleNone); 522 | break; 523 | case AMR_SGRCodeAllReset: 524 | case AMR_SGRCodeFgReset: 525 | case AMR_SGRCodeBgReset: 526 | case AMR_SGRCodeNoneOrInvalid: 527 | case AMR_SGRCodeItalicOn: 528 | break; 529 | } 530 | 531 | 532 | // find the next sequence that specifies the end of this formatting run 533 | NSInteger formattingRunEndLocation = -1; 534 | if (iCode < (formatCodes.count - 1)) 535 | { 536 | NSDictionary *thisEndCodeCandidateDict; 537 | unichar thisEndCodeCandidate; 538 | for (NSUInteger iEndCode = iCode+1; iEndCode < formatCodes.count; iEndCode++) 539 | { 540 | thisEndCodeCandidateDict = formatCodes[iEndCode]; 541 | thisEndCodeCandidate = [thisEndCodeCandidateDict[kAMRCodeDictKey_code] unsignedIntValue]; 542 | 543 | if ([self AMR_SGRCode:thisEndCodeCandidate endsFormattingIntroducedByCode:thisCode]) 544 | { 545 | formattingRunEndLocation = [thisEndCodeCandidateDict[kAMRCodeDictKey_location] unsignedIntegerValue]; 546 | break; 547 | } 548 | } 549 | } 550 | if (formattingRunEndLocation == -1) 551 | formattingRunEndLocation = cleanString.length; 552 | 553 | if (thisAttributeName && thisAttributeValue) 554 | { 555 | [attrsAndRanges addObject:@{ 556 | kAMRAttrDictKey_range: [NSValue valueWithRange:NSMakeRange(formattingRunStartLocation, (formattingRunEndLocation-formattingRunStartLocation))], 557 | kAMRAttrDictKey_attrName: thisAttributeName, 558 | kAMRAttrDictKey_attrValue: thisAttributeValue, 559 | }]; 560 | } 561 | } 562 | 563 | if (aCleanString) 564 | *aCleanString = cleanString; 565 | return attrsAndRanges; 566 | } 567 | 568 | 569 | 570 | 571 | 572 | - (BOOL) AMR_SGRCode:(AMR_SGRCode)endCode endsFormattingIntroducedByCode:(AMR_SGRCode)startCode 573 | { 574 | switch(startCode) 575 | { 576 | case AMR_SGRCodeFgBlack: 577 | case AMR_SGRCodeFgRed: 578 | case AMR_SGRCodeFgGreen: 579 | case AMR_SGRCodeFgYellow: 580 | case AMR_SGRCodeFgBlue: 581 | case AMR_SGRCodeFgMagenta: 582 | case AMR_SGRCodeFgCyan: 583 | case AMR_SGRCodeFgWhite: 584 | case AMR_SGRCodeFgBrightBlack: 585 | case AMR_SGRCodeFgBrightRed: 586 | case AMR_SGRCodeFgBrightGreen: 587 | case AMR_SGRCodeFgBrightYellow: 588 | case AMR_SGRCodeFgBrightBlue: 589 | case AMR_SGRCodeFgBrightMagenta: 590 | case AMR_SGRCodeFgBrightCyan: 591 | case AMR_SGRCodeFgBrightWhite: 592 | return (endCode == AMR_SGRCodeAllReset || endCode == AMR_SGRCodeFgReset || 593 | endCode == AMR_SGRCodeFgBlack || endCode == AMR_SGRCodeFgRed || 594 | endCode == AMR_SGRCodeFgGreen || endCode == AMR_SGRCodeFgYellow || 595 | endCode == AMR_SGRCodeFgBlue || endCode == AMR_SGRCodeFgMagenta || 596 | endCode == AMR_SGRCodeFgCyan || endCode == AMR_SGRCodeFgWhite || 597 | endCode == AMR_SGRCodeFgBrightBlack || endCode == AMR_SGRCodeFgBrightRed || 598 | endCode == AMR_SGRCodeFgBrightGreen || endCode == AMR_SGRCodeFgBrightYellow || 599 | endCode == AMR_SGRCodeFgBrightBlue || endCode == AMR_SGRCodeFgBrightMagenta || 600 | endCode == AMR_SGRCodeFgBrightCyan || endCode == AMR_SGRCodeFgBrightWhite); 601 | case AMR_SGRCodeBgBlack: 602 | case AMR_SGRCodeBgRed: 603 | case AMR_SGRCodeBgGreen: 604 | case AMR_SGRCodeBgYellow: 605 | case AMR_SGRCodeBgBlue: 606 | case AMR_SGRCodeBgMagenta: 607 | case AMR_SGRCodeBgCyan: 608 | case AMR_SGRCodeBgWhite: 609 | case AMR_SGRCodeBgBrightBlack: 610 | case AMR_SGRCodeBgBrightRed: 611 | case AMR_SGRCodeBgBrightGreen: 612 | case AMR_SGRCodeBgBrightYellow: 613 | case AMR_SGRCodeBgBrightBlue: 614 | case AMR_SGRCodeBgBrightMagenta: 615 | case AMR_SGRCodeBgBrightCyan: 616 | case AMR_SGRCodeBgBrightWhite: 617 | return (endCode == AMR_SGRCodeAllReset || endCode == AMR_SGRCodeBgReset || 618 | endCode == AMR_SGRCodeBgBlack || endCode == AMR_SGRCodeBgRed || 619 | endCode == AMR_SGRCodeBgGreen || endCode == AMR_SGRCodeBgYellow || 620 | endCode == AMR_SGRCodeBgBlue || endCode == AMR_SGRCodeBgMagenta || 621 | endCode == AMR_SGRCodeBgCyan || endCode == AMR_SGRCodeBgWhite || 622 | endCode == AMR_SGRCodeBgBrightBlack || endCode == AMR_SGRCodeBgBrightRed || 623 | endCode == AMR_SGRCodeBgBrightGreen || endCode == AMR_SGRCodeBgBrightYellow || 624 | endCode == AMR_SGRCodeBgBrightBlue || endCode == AMR_SGRCodeBgBrightMagenta || 625 | endCode == AMR_SGRCodeBgBrightCyan || endCode == AMR_SGRCodeBgBrightWhite); 626 | case AMR_SGRCodeIntensityBold: 627 | case AMR_SGRCodeIntensityNormal: 628 | return (endCode == AMR_SGRCodeAllReset || endCode == AMR_SGRCodeIntensityNormal || 629 | endCode == AMR_SGRCodeIntensityBold || endCode == AMR_SGRCodeIntensityFaint); 630 | case AMR_SGRCodeUnderlineSingle: 631 | case AMR_SGRCodeUnderlineDouble: 632 | return (endCode == AMR_SGRCodeAllReset || endCode == AMR_SGRCodeUnderlineNone || 633 | endCode == AMR_SGRCodeUnderlineSingle || endCode == AMR_SGRCodeUnderlineDouble); 634 | case AMR_SGRCodeNoneOrInvalid: 635 | case AMR_SGRCodeItalicOn: 636 | case AMR_SGRCodeUnderlineNone: 637 | case AMR_SGRCodeIntensityFaint: 638 | case AMR_SGRCodeAllReset: 639 | case AMR_SGRCodeBgReset: 640 | case AMR_SGRCodeFgReset: 641 | return NO; 642 | } 643 | 644 | return NO; 645 | } 646 | 647 | 648 | 649 | 650 | - (NSColor*) colorForSGRCode:(AMR_SGRCode)code 651 | { 652 | if (self.ansiColors) 653 | { 654 | NSColor *preferredColor = self.ansiColors[@(code)]; 655 | if (preferredColor) 656 | return preferredColor; 657 | } 658 | 659 | switch(code) 660 | { 661 | case AMR_SGRCodeFgBlack: 662 | return kDefaultANSIColorFgBlack; 663 | case AMR_SGRCodeFgRed: 664 | return kDefaultANSIColorFgRed; 665 | case AMR_SGRCodeFgGreen: 666 | return kDefaultANSIColorFgGreen; 667 | case AMR_SGRCodeFgYellow: 668 | return kDefaultANSIColorFgYellow; 669 | case AMR_SGRCodeFgBlue: 670 | return kDefaultANSIColorFgBlue; 671 | case AMR_SGRCodeFgMagenta: 672 | return kDefaultANSIColorFgMagenta; 673 | case AMR_SGRCodeFgCyan: 674 | return kDefaultANSIColorFgCyan; 675 | case AMR_SGRCodeFgWhite: 676 | return kDefaultANSIColorFgWhite; 677 | case AMR_SGRCodeFgBrightBlack: 678 | return kDefaultANSIColorFgBrightBlack; 679 | case AMR_SGRCodeFgBrightRed: 680 | return kDefaultANSIColorFgBrightRed; 681 | case AMR_SGRCodeFgBrightGreen: 682 | return kDefaultANSIColorFgBrightGreen; 683 | case AMR_SGRCodeFgBrightYellow: 684 | return kDefaultANSIColorFgBrightYellow; 685 | case AMR_SGRCodeFgBrightBlue: 686 | return kDefaultANSIColorFgBrightBlue; 687 | case AMR_SGRCodeFgBrightMagenta: 688 | return kDefaultANSIColorFgBrightMagenta; 689 | case AMR_SGRCodeFgBrightCyan: 690 | return kDefaultANSIColorFgBrightCyan; 691 | case AMR_SGRCodeFgBrightWhite: 692 | return kDefaultANSIColorFgBrightWhite; 693 | case AMR_SGRCodeBgBlack: 694 | return kDefaultANSIColorBgBlack; 695 | case AMR_SGRCodeBgRed: 696 | return kDefaultANSIColorBgRed; 697 | case AMR_SGRCodeBgGreen: 698 | return kDefaultANSIColorBgGreen; 699 | case AMR_SGRCodeBgYellow: 700 | return kDefaultANSIColorBgYellow; 701 | case AMR_SGRCodeBgBlue: 702 | return kDefaultANSIColorBgBlue; 703 | case AMR_SGRCodeBgMagenta: 704 | return kDefaultANSIColorBgMagenta; 705 | case AMR_SGRCodeBgCyan: 706 | return kDefaultANSIColorBgCyan; 707 | case AMR_SGRCodeBgWhite: 708 | return kDefaultANSIColorBgWhite; 709 | case AMR_SGRCodeBgBrightBlack: 710 | return kDefaultANSIColorBgBrightBlack; 711 | case AMR_SGRCodeBgBrightRed: 712 | return kDefaultANSIColorBgBrightRed; 713 | case AMR_SGRCodeBgBrightGreen: 714 | return kDefaultANSIColorBgBrightGreen; 715 | case AMR_SGRCodeBgBrightYellow: 716 | return kDefaultANSIColorBgBrightYellow; 717 | case AMR_SGRCodeBgBrightBlue: 718 | return kDefaultANSIColorBgBrightBlue; 719 | case AMR_SGRCodeBgBrightMagenta: 720 | return kDefaultANSIColorBgBrightMagenta; 721 | case AMR_SGRCodeBgBrightCyan: 722 | return kDefaultANSIColorBgBrightCyan; 723 | case AMR_SGRCodeBgBrightWhite: 724 | return kDefaultANSIColorBgBrightWhite; 725 | case AMR_SGRCodeNoneOrInvalid: 726 | case AMR_SGRCodeItalicOn: 727 | case AMR_SGRCodeUnderlineNone: 728 | case AMR_SGRCodeIntensityFaint: 729 | case AMR_SGRCodeAllReset: 730 | case AMR_SGRCodeBgReset: 731 | case AMR_SGRCodeFgReset: 732 | case AMR_SGRCodeIntensityBold: 733 | case AMR_SGRCodeIntensityNormal: 734 | case AMR_SGRCodeUnderlineSingle: 735 | case AMR_SGRCodeUnderlineDouble: 736 | break; 737 | } 738 | 739 | return kDefaultANSIColorFgBlack; 740 | } 741 | 742 | 743 | - (AMR_SGRCode) AMR_SGRCodeForColor:(NSColor*)aColor isForegroundColor:(BOOL)aForeground 744 | { 745 | if (self.ansiColors) 746 | { 747 | NSArray *codesForGivenColor = [self.ansiColors allKeysForObject:aColor]; 748 | 749 | if (codesForGivenColor != nil && 0 < codesForGivenColor.count) 750 | { 751 | for (NSNumber *thisCode in codesForGivenColor) 752 | { 753 | BOOL thisIsForegroundColor = (thisCode.intValue < 40); 754 | if (aForeground == thisIsForegroundColor) 755 | return thisCode.intValue; 756 | } 757 | } 758 | } 759 | 760 | if (aForeground) 761 | { 762 | if ([aColor isEqual:kDefaultANSIColorFgBlack]) 763 | return AMR_SGRCodeFgBlack; 764 | else if ([aColor isEqual:kDefaultANSIColorFgRed]) 765 | return AMR_SGRCodeFgRed; 766 | else if ([aColor isEqual:kDefaultANSIColorFgGreen]) 767 | return AMR_SGRCodeFgGreen; 768 | else if ([aColor isEqual:kDefaultANSIColorFgYellow]) 769 | return AMR_SGRCodeFgYellow; 770 | else if ([aColor isEqual:kDefaultANSIColorFgBlue]) 771 | return AMR_SGRCodeFgBlue; 772 | else if ([aColor isEqual:kDefaultANSIColorFgMagenta]) 773 | return AMR_SGRCodeFgMagenta; 774 | else if ([aColor isEqual:kDefaultANSIColorFgCyan]) 775 | return AMR_SGRCodeFgCyan; 776 | else if ([aColor isEqual:kDefaultANSIColorFgWhite]) 777 | return AMR_SGRCodeFgWhite; 778 | else if ([aColor isEqual:kDefaultANSIColorFgBrightBlack]) 779 | return AMR_SGRCodeFgBrightBlack; 780 | else if ([aColor isEqual:kDefaultANSIColorFgBrightRed]) 781 | return AMR_SGRCodeFgBrightRed; 782 | else if ([aColor isEqual:kDefaultANSIColorFgBrightGreen]) 783 | return AMR_SGRCodeFgBrightGreen; 784 | else if ([aColor isEqual:kDefaultANSIColorFgBrightYellow]) 785 | return AMR_SGRCodeFgBrightYellow; 786 | else if ([aColor isEqual:kDefaultANSIColorFgBrightBlue]) 787 | return AMR_SGRCodeFgBrightBlue; 788 | else if ([aColor isEqual:kDefaultANSIColorFgBrightMagenta]) 789 | return AMR_SGRCodeFgBrightMagenta; 790 | else if ([aColor isEqual:kDefaultANSIColorFgBrightCyan]) 791 | return AMR_SGRCodeFgBrightCyan; 792 | else if ([aColor isEqual:kDefaultANSIColorFgBrightWhite]) 793 | return AMR_SGRCodeFgBrightWhite; 794 | } 795 | else 796 | { 797 | if ([aColor isEqual:kDefaultANSIColorBgBlack]) 798 | return AMR_SGRCodeBgBlack; 799 | else if ([aColor isEqual:kDefaultANSIColorBgRed]) 800 | return AMR_SGRCodeBgRed; 801 | else if ([aColor isEqual:kDefaultANSIColorBgGreen]) 802 | return AMR_SGRCodeBgGreen; 803 | else if ([aColor isEqual:kDefaultANSIColorBgYellow]) 804 | return AMR_SGRCodeBgYellow; 805 | else if ([aColor isEqual:kDefaultANSIColorBgBlue]) 806 | return AMR_SGRCodeBgBlue; 807 | else if ([aColor isEqual:kDefaultANSIColorBgMagenta]) 808 | return AMR_SGRCodeBgMagenta; 809 | else if ([aColor isEqual:kDefaultANSIColorBgCyan]) 810 | return AMR_SGRCodeBgCyan; 811 | else if ([aColor isEqual:kDefaultANSIColorBgWhite]) 812 | return AMR_SGRCodeBgWhite; 813 | else if ([aColor isEqual:kDefaultANSIColorBgBrightBlack]) 814 | return AMR_SGRCodeBgBrightBlack; 815 | else if ([aColor isEqual:kDefaultANSIColorBgBrightRed]) 816 | return AMR_SGRCodeBgBrightRed; 817 | else if ([aColor isEqual:kDefaultANSIColorBgBrightGreen]) 818 | return AMR_SGRCodeBgBrightGreen; 819 | else if ([aColor isEqual:kDefaultANSIColorBgBrightYellow]) 820 | return AMR_SGRCodeBgBrightYellow; 821 | else if ([aColor isEqual:kDefaultANSIColorBgBrightBlue]) 822 | return AMR_SGRCodeBgBrightBlue; 823 | else if ([aColor isEqual:kDefaultANSIColorBgBrightMagenta]) 824 | return AMR_SGRCodeBgBrightMagenta; 825 | else if ([aColor isEqual:kDefaultANSIColorBgBrightCyan]) 826 | return AMR_SGRCodeBgBrightCyan; 827 | else if ([aColor isEqual:kDefaultANSIColorBgBrightWhite]) 828 | return AMR_SGRCodeBgBrightWhite; 829 | } 830 | 831 | return AMR_SGRCodeNoneOrInvalid; 832 | } 833 | 834 | 835 | 836 | // helper struct typedef and a few functions for 837 | // -closestSGRCodeForColor:isForegroundColor: 838 | 839 | typedef struct { 840 | CGFloat hue; 841 | CGFloat saturation; 842 | CGFloat brightness; 843 | } AMR_HSB; 844 | 845 | AMR_HSB makeHSB(CGFloat hue, CGFloat saturation, CGFloat brightness) 846 | { 847 | AMR_HSB outHSB; 848 | outHSB.hue = hue; 849 | outHSB.saturation = saturation; 850 | outHSB.brightness = brightness; 851 | return outHSB; 852 | } 853 | 854 | AMR_HSB getHSBFromColor(NSColor *color) 855 | { 856 | CGFloat hue = 0.0; 857 | CGFloat saturation = 0.0; 858 | CGFloat brightness = 0.0; 859 | [[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace] 860 | getHue:&hue 861 | saturation:&saturation 862 | brightness:&brightness 863 | alpha:NULL 864 | ]; 865 | return makeHSB(hue, saturation, brightness); 866 | } 867 | 868 | BOOL floatsEqual(CGFloat first, CGFloat second, CGFloat maxAbsError) 869 | { 870 | return (fabs(first-second)) < maxAbsError; 871 | } 872 | 873 | #define MAX_HUE_FLOAT_EQUALITY_ABS_ERROR 0.000001 874 | 875 | - (AMR_SGRCode) closestSGRCodeForColor:(NSColor *)color isForegroundColor:(BOOL)foreground 876 | { 877 | if (color == nil) 878 | return AMR_SGRCodeNoneOrInvalid; 879 | 880 | AMR_SGRCode closestColorSGRCode = [self AMR_SGRCodeForColor:color isForegroundColor:foreground]; 881 | if (closestColorSGRCode != AMR_SGRCodeNoneOrInvalid) 882 | return closestColorSGRCode; 883 | 884 | AMR_HSB givenColorHSB = getHSBFromColor(color); 885 | 886 | CGFloat closestColorHueDiff = FLT_MAX; 887 | CGFloat closestColorSaturationDiff = FLT_MAX; 888 | CGFloat closestColorBrightnessDiff = FLT_MAX; 889 | 890 | // (background SGR codes are +10 from foreground ones:) 891 | NSUInteger AMR_SGRCodeShift = (foreground)?0:10; 892 | NSArray *ansiFgColorCodes = @[ 893 | @(AMR_SGRCodeFgBlack+AMR_SGRCodeShift), 894 | @(AMR_SGRCodeFgRed+AMR_SGRCodeShift), 895 | @(AMR_SGRCodeFgGreen+AMR_SGRCodeShift), 896 | @(AMR_SGRCodeFgYellow+AMR_SGRCodeShift), 897 | @(AMR_SGRCodeFgBlue+AMR_SGRCodeShift), 898 | @(AMR_SGRCodeFgMagenta+AMR_SGRCodeShift), 899 | @(AMR_SGRCodeFgCyan+AMR_SGRCodeShift), 900 | @(AMR_SGRCodeFgWhite+AMR_SGRCodeShift), 901 | @(AMR_SGRCodeFgBrightBlack+AMR_SGRCodeShift), 902 | @(AMR_SGRCodeFgBrightRed+AMR_SGRCodeShift), 903 | @(AMR_SGRCodeFgBrightGreen+AMR_SGRCodeShift), 904 | @(AMR_SGRCodeFgBrightYellow+AMR_SGRCodeShift), 905 | @(AMR_SGRCodeFgBrightBlue+AMR_SGRCodeShift), 906 | @(AMR_SGRCodeFgBrightMagenta+AMR_SGRCodeShift), 907 | @(AMR_SGRCodeFgBrightCyan+AMR_SGRCodeShift), 908 | @(AMR_SGRCodeFgBrightWhite+AMR_SGRCodeShift), 909 | ]; 910 | for (NSNumber *thisSGRCodeNumber in ansiFgColorCodes) 911 | { 912 | AMR_SGRCode thisSGRCode = thisSGRCodeNumber.intValue; 913 | NSColor *thisColor = [self colorForSGRCode:thisSGRCode]; 914 | 915 | AMR_HSB thisColorHSB = getHSBFromColor(thisColor); 916 | 917 | CGFloat hueDiff = fabs(givenColorHSB.hue - thisColorHSB.hue); 918 | CGFloat saturationDiff = fabs(givenColorHSB.saturation - thisColorHSB.saturation); 919 | CGFloat brightnessDiff = fabs(givenColorHSB.brightness - thisColorHSB.brightness); 920 | 921 | // comparison depends on hue, saturation and brightness 922 | // (strictly in that order): 923 | 924 | if (!floatsEqual(hueDiff, closestColorHueDiff, MAX_HUE_FLOAT_EQUALITY_ABS_ERROR)) 925 | { 926 | if (hueDiff > closestColorHueDiff) 927 | continue; 928 | closestColorSGRCode = thisSGRCode; 929 | closestColorHueDiff = hueDiff; 930 | closestColorSaturationDiff = saturationDiff; 931 | closestColorBrightnessDiff = brightnessDiff; 932 | continue; 933 | } 934 | 935 | if (!floatsEqual(saturationDiff, closestColorSaturationDiff, MAX_HUE_FLOAT_EQUALITY_ABS_ERROR)) 936 | { 937 | if (saturationDiff > closestColorSaturationDiff) 938 | continue; 939 | closestColorSGRCode = thisSGRCode; 940 | closestColorHueDiff = hueDiff; 941 | closestColorSaturationDiff = saturationDiff; 942 | closestColorBrightnessDiff = brightnessDiff; 943 | continue; 944 | } 945 | 946 | if (!floatsEqual(brightnessDiff, closestColorBrightnessDiff, MAX_HUE_FLOAT_EQUALITY_ABS_ERROR)) 947 | { 948 | if (brightnessDiff > closestColorBrightnessDiff) 949 | continue; 950 | closestColorSGRCode = thisSGRCode; 951 | closestColorHueDiff = hueDiff; 952 | closestColorSaturationDiff = saturationDiff; 953 | closestColorBrightnessDiff = brightnessDiff; 954 | continue; 955 | } 956 | 957 | // If hue (especially hue!), saturation and brightness diffs all 958 | // are equal to some other color, we need to prefer one or the 959 | // other so we'll select the more 'distinctive' color of the 960 | // two (this is *very* subjective, obviously). I basically just 961 | // looked at the hue chart, went through all the points between 962 | // our main ANSI colors and decided which side the middle point 963 | // would lean on. (e.g. the purple color that is exactly between 964 | // the blue and magenta ANSI colors looks more magenta than 965 | // blue to me so I put magenta higher than blue in the list 966 | // below.) 967 | // 968 | // subjective ordering of colors from most to least 'distinctive': 969 | int colorDistinctivenessOrder[6] = { 970 | AMR_SGRCodeFgRed+AMR_SGRCodeShift, 971 | AMR_SGRCodeFgMagenta+AMR_SGRCodeShift, 972 | AMR_SGRCodeFgBlue+AMR_SGRCodeShift, 973 | AMR_SGRCodeFgGreen+AMR_SGRCodeShift, 974 | AMR_SGRCodeFgCyan+AMR_SGRCodeShift, 975 | AMR_SGRCodeFgYellow+AMR_SGRCodeShift 976 | }; 977 | for (int i = 0; i < 6; i++) 978 | { 979 | if (colorDistinctivenessOrder[i] == closestColorSGRCode) 980 | break; 981 | else if (colorDistinctivenessOrder[i] == thisSGRCode) 982 | { 983 | closestColorSGRCode = thisSGRCode; 984 | closestColorHueDiff = hueDiff; 985 | closestColorSaturationDiff = saturationDiff; 986 | closestColorBrightnessDiff = brightnessDiff; 987 | } 988 | } 989 | } 990 | 991 | return closestColorSGRCode; 992 | } 993 | 994 | 995 | 996 | @end 997 | -------------------------------------------------------------------------------- /AnsiColorsTest.xcodeproj/TemplateIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ali-rantakari/ANSIEscapeHelper/2931e9e3d162ad76613231358f3d6a77eebd760f/AnsiColorsTest.xcodeproj/TemplateIcon.icns -------------------------------------------------------------------------------- /AnsiColorsTest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; }; 11 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 12 | 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 13 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; 14 | C91DC0170F72938E006C7A67 /* test-onechar.pl in Resources */ = {isa = PBXBuildFile; fileRef = C91DC0160F72938E006C7A67 /* test-onechar.pl */; }; 15 | C926C7960F7136220057A11E /* MyController.m in Sources */ = {isa = PBXBuildFile; fileRef = C926C7940F7136220057A11E /* MyController.m */; }; 16 | C926C8850F715CFB0057A11E /* a.out in Resources */ = {isa = PBXBuildFile; fileRef = C926C8840F715CFB0057A11E /* a.out */; }; 17 | C9DA2CFF16B4247000572FD1 /* AMR_ANSIEscapeHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = C9DA2CFE16B4247000572FD1 /* AMR_ANSIEscapeHelper.m */; }; 18 | C9FB76BD0F71AC3A009BA734 /* test.pl in Resources */ = {isa = PBXBuildFile; fileRef = C9FB76BC0F71AC3A009BA734 /* test.pl */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 23 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 24 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; 25 | 1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; }; 26 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 27 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 28 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 29 | 32CA4F630368D1EE00C91783 /* AnsiColorsTest_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnsiColorsTest_Prefix.pch; sourceTree = ""; }; 30 | 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | 8D1107320486CEB800E47090 /* AnsiColorsTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AnsiColorsTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | C91DC0160F72938E006C7A67 /* test-onechar.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = "test-onechar.pl"; sourceTree = ""; }; 33 | C926C7940F7136220057A11E /* MyController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyController.m; sourceTree = ""; }; 34 | C926C7950F7136220057A11E /* MyController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyController.h; sourceTree = ""; }; 35 | C926C8840F715CFB0057A11E /* a.out */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = a.out; sourceTree = ""; }; 36 | C9DA2CFD16B4247000572FD1 /* AMR_ANSIEscapeHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AMR_ANSIEscapeHelper.h; sourceTree = ""; }; 37 | C9DA2CFE16B4247000572FD1 /* AMR_ANSIEscapeHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AMR_ANSIEscapeHelper.m; sourceTree = ""; }; 38 | C9FB76BC0F71AC3A009BA734 /* test.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = test.pl; sourceTree = ""; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | 8D11072E0486CEB800E47090 /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXFrameworksBuildPhase section */ 51 | 52 | /* Begin PBXGroup section */ 53 | 080E96DDFE201D6D7F000001 /* Classes */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | C9DA2CFD16B4247000572FD1 /* AMR_ANSIEscapeHelper.h */, 57 | C9DA2CFE16B4247000572FD1 /* AMR_ANSIEscapeHelper.m */, 58 | C926C7950F7136220057A11E /* MyController.h */, 59 | C926C7940F7136220057A11E /* MyController.m */, 60 | ); 61 | name = Classes; 62 | sourceTree = ""; 63 | }; 64 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, 68 | ); 69 | name = "Linked Frameworks"; 70 | sourceTree = ""; 71 | }; 72 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 29B97324FDCFA39411CA2CEA /* AppKit.framework */, 76 | 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */, 77 | 29B97325FDCFA39411CA2CEA /* Foundation.framework */, 78 | ); 79 | name = "Other Frameworks"; 80 | sourceTree = ""; 81 | }; 82 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 8D1107320486CEB800E47090 /* AnsiColorsTest.app */, 86 | ); 87 | name = Products; 88 | sourceTree = ""; 89 | }; 90 | 29B97314FDCFA39411CA2CEA /* AnsiColorsTest */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 080E96DDFE201D6D7F000001 /* Classes */, 94 | 29B97315FDCFA39411CA2CEA /* Other Sources */, 95 | 29B97317FDCFA39411CA2CEA /* Resources */, 96 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 97 | 19C28FACFE9D520D11CA2CBB /* Products */, 98 | ); 99 | name = AnsiColorsTest; 100 | sourceTree = ""; 101 | }; 102 | 29B97315FDCFA39411CA2CEA /* Other Sources */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 32CA4F630368D1EE00C91783 /* AnsiColorsTest_Prefix.pch */, 106 | 29B97316FDCFA39411CA2CEA /* main.m */, 107 | ); 108 | name = "Other Sources"; 109 | sourceTree = ""; 110 | }; 111 | 29B97317FDCFA39411CA2CEA /* Resources */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | C91DC0160F72938E006C7A67 /* test-onechar.pl */, 115 | C9FB76BC0F71AC3A009BA734 /* test.pl */, 116 | C926C8840F715CFB0057A11E /* a.out */, 117 | 8D1107310486CEB800E47090 /* Info.plist */, 118 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, 119 | 1DDD58140DA1D0A300B32029 /* MainMenu.xib */, 120 | ); 121 | name = Resources; 122 | sourceTree = ""; 123 | }; 124 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 128 | 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, 129 | ); 130 | name = Frameworks; 131 | sourceTree = ""; 132 | }; 133 | /* End PBXGroup section */ 134 | 135 | /* Begin PBXNativeTarget section */ 136 | 8D1107260486CEB800E47090 /* AnsiColorsTest */ = { 137 | isa = PBXNativeTarget; 138 | buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "AnsiColorsTest" */; 139 | buildPhases = ( 140 | 8D1107290486CEB800E47090 /* Resources */, 141 | 8D11072C0486CEB800E47090 /* Sources */, 142 | 8D11072E0486CEB800E47090 /* Frameworks */, 143 | ); 144 | buildRules = ( 145 | ); 146 | dependencies = ( 147 | ); 148 | name = AnsiColorsTest; 149 | productInstallPath = "$(HOME)/Applications"; 150 | productName = AnsiColorsTest; 151 | productReference = 8D1107320486CEB800E47090 /* AnsiColorsTest.app */; 152 | productType = "com.apple.product-type.application"; 153 | }; 154 | /* End PBXNativeTarget section */ 155 | 156 | /* Begin PBXProject section */ 157 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 158 | isa = PBXProject; 159 | attributes = { 160 | LastUpgradeCheck = 0450; 161 | }; 162 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "AnsiColorsTest" */; 163 | compatibilityVersion = "Xcode 3.2"; 164 | developmentRegion = English; 165 | hasScannedForEncodings = 1; 166 | knownRegions = ( 167 | en, 168 | ); 169 | mainGroup = 29B97314FDCFA39411CA2CEA /* AnsiColorsTest */; 170 | projectDirPath = ""; 171 | projectRoot = ""; 172 | targets = ( 173 | 8D1107260486CEB800E47090 /* AnsiColorsTest */, 174 | ); 175 | }; 176 | /* End PBXProject section */ 177 | 178 | /* Begin PBXResourcesBuildPhase section */ 179 | 8D1107290486CEB800E47090 /* Resources */ = { 180 | isa = PBXResourcesBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, 184 | 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */, 185 | C926C8850F715CFB0057A11E /* a.out in Resources */, 186 | C9FB76BD0F71AC3A009BA734 /* test.pl in Resources */, 187 | C91DC0170F72938E006C7A67 /* test-onechar.pl in Resources */, 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | /* End PBXResourcesBuildPhase section */ 192 | 193 | /* Begin PBXSourcesBuildPhase section */ 194 | 8D11072C0486CEB800E47090 /* Sources */ = { 195 | isa = PBXSourcesBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | 8D11072D0486CEB800E47090 /* main.m in Sources */, 199 | C926C7960F7136220057A11E /* MyController.m in Sources */, 200 | C9DA2CFF16B4247000572FD1 /* AMR_ANSIEscapeHelper.m in Sources */, 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | /* End PBXSourcesBuildPhase section */ 205 | 206 | /* Begin PBXVariantGroup section */ 207 | 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { 208 | isa = PBXVariantGroup; 209 | children = ( 210 | 089C165DFE840E0CC02AAC07 /* English */, 211 | ); 212 | name = InfoPlist.strings; 213 | sourceTree = ""; 214 | }; 215 | 1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 1DDD58150DA1D0A300B32029 /* English */, 219 | ); 220 | name = MainMenu.xib; 221 | sourceTree = ""; 222 | }; 223 | /* End PBXVariantGroup section */ 224 | 225 | /* Begin XCBuildConfiguration section */ 226 | C01FCF4B08A954540054247B /* Debug */ = { 227 | isa = XCBuildConfiguration; 228 | buildSettings = { 229 | ALWAYS_SEARCH_USER_PATHS = NO; 230 | COMBINE_HIDPI_IMAGES = YES; 231 | COPY_PHASE_STRIP = NO; 232 | GCC_DYNAMIC_NO_PIC = NO; 233 | GCC_MODEL_TUNING = G5; 234 | GCC_OPTIMIZATION_LEVEL = 0; 235 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 236 | GCC_PREFIX_HEADER = AnsiColorsTest_Prefix.pch; 237 | INFOPLIST_FILE = Info.plist; 238 | INSTALL_PATH = "$(HOME)/Applications"; 239 | PRODUCT_NAME = AnsiColorsTest; 240 | SDKROOT = ""; 241 | }; 242 | name = Debug; 243 | }; 244 | C01FCF4C08A954540054247B /* Release */ = { 245 | isa = XCBuildConfiguration; 246 | buildSettings = { 247 | ALWAYS_SEARCH_USER_PATHS = NO; 248 | COMBINE_HIDPI_IMAGES = YES; 249 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 250 | GCC_MODEL_TUNING = G5; 251 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 252 | GCC_PREFIX_HEADER = AnsiColorsTest_Prefix.pch; 253 | INFOPLIST_FILE = Info.plist; 254 | INSTALL_PATH = "$(HOME)/Applications"; 255 | PRODUCT_NAME = AnsiColorsTest; 256 | SDKROOT = ""; 257 | }; 258 | name = Release; 259 | }; 260 | C01FCF4F08A954540054247B /* Debug */ = { 261 | isa = XCBuildConfiguration; 262 | buildSettings = { 263 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 264 | CLANG_ENABLE_OBJC_ARC = YES; 265 | GCC_C_LANGUAGE_STANDARD = c99; 266 | GCC_OPTIMIZATION_LEVEL = 0; 267 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 268 | GCC_WARN_UNUSED_VARIABLE = YES; 269 | MACOSX_DEPLOYMENT_TARGET = 10.6; 270 | ONLY_ACTIVE_ARCH = YES; 271 | SDKROOT = macosx; 272 | VALID_ARCHS = x86_64; 273 | }; 274 | name = Debug; 275 | }; 276 | C01FCF5008A954540054247B /* Release */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 280 | CLANG_ENABLE_OBJC_ARC = YES; 281 | GCC_C_LANGUAGE_STANDARD = c99; 282 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 283 | GCC_WARN_UNUSED_VARIABLE = YES; 284 | MACOSX_DEPLOYMENT_TARGET = 10.6; 285 | SDKROOT = macosx; 286 | VALID_ARCHS = x86_64; 287 | }; 288 | name = Release; 289 | }; 290 | /* End XCBuildConfiguration section */ 291 | 292 | /* Begin XCConfigurationList section */ 293 | C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "AnsiColorsTest" */ = { 294 | isa = XCConfigurationList; 295 | buildConfigurations = ( 296 | C01FCF4B08A954540054247B /* Debug */, 297 | C01FCF4C08A954540054247B /* Release */, 298 | ); 299 | defaultConfigurationIsVisible = 0; 300 | defaultConfigurationName = Release; 301 | }; 302 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "AnsiColorsTest" */ = { 303 | isa = XCConfigurationList; 304 | buildConfigurations = ( 305 | C01FCF4F08A954540054247B /* Debug */, 306 | C01FCF5008A954540054247B /* Release */, 307 | ); 308 | defaultConfigurationIsVisible = 0; 309 | defaultConfigurationName = Release; 310 | }; 311 | /* End XCConfigurationList section */ 312 | }; 313 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 314 | } 315 | -------------------------------------------------------------------------------- /AnsiColorsTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AnsiColorsTest.xcodeproj/project.xcworkspace/xcuserdata/alir.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ali-rantakari/ANSIEscapeHelper/2931e9e3d162ad76613231358f3d6a77eebd760f/AnsiColorsTest.xcodeproj/project.xcworkspace/xcuserdata/alir.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AnsiColorsTest.xcodeproj/xcuserdata/alir.xcuserdatad/xcschemes/AnsiColorsTest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /AnsiColorsTest.xcodeproj/xcuserdata/alir.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AnsiColorsTest.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 8D1107260486CEB800E47090 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /AnsiColorsTest_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'AnsiColorsTest' target in the 'AnsiColorsTest' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ali-rantakari/ANSIEscapeHelper/2931e9e3d162ad76613231358f3d6a77eebd760f/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | org.hasseg.AnsiColorsTest 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | NSMainNibFile 24 | MainMenu 25 | NSPrincipalClass 26 | NSApplication 27 | 28 | 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # ANSIEscapeHelper makefile 2 | # 3 | # Created by Ali Rantakari on 4 May, 2009 4 | # 5 | 6 | SHELL=/bin/bash 7 | 8 | CURRDATE=$(shell date +"%Y-%m-%d") 9 | APP_VERSION=$(shell /bin/bash -c "cat ANSIEscapeHelper.h | grep '^// Version .*' | cut -d ' ' -f 4") 10 | VERSION_ON_SERVER=$(shell curl -Ss http://hasseg.org/ansiEscapeHelper/?versioncheck=y) 11 | TEMP_DEPLOYMENT_DIR=deployment/$(APP_VERSION) 12 | TEMP_DEPLOYMENT_ZIPFILE=$(TEMP_DEPLOYMENT_DIR)/ansiEscapeHelper-v$(APP_VERSION).zip 13 | VERSIONCHANGELOGFILELOC="$(TEMP_DEPLOYMENT_DIR)/changelog.html" 14 | GENERALCHANGELOGFILELOC="changelog.html" 15 | SCP_TARGET=$(shell cat ./deploymentScpTarget) 16 | DEPLOYMENT_INCLUDES_DIR="./deployment-files" 17 | DOCS_DIR=./headerdoc 18 | 19 | 20 | 21 | 22 | 23 | testversionnumber: 24 | @echo 25 | @echo Version is: $(APP_VERSION) 26 | @echo 27 | 28 | 29 | #------------------------------------------------------------------------- 30 | #------------------------------------------------------------------------- 31 | # generate documentation 32 | #------------------------------------------------------------------------- 33 | docs: 34 | @echo 35 | @echo ---- Generating HTML docs from header file: 36 | @echo ====================================== 37 | /Developer/usr/bin/headerdoc2html -o $(DOCS_DIR) ./ANSIEscapeHelper.h 38 | 39 | 40 | 41 | #------------------------------------------------------------------------- 42 | #------------------------------------------------------------------------- 43 | # make release package (prepare for deployment) 44 | #------------------------------------------------------------------------- 45 | package: docs 46 | @echo 47 | @echo ---- Preparing for deployment: 48 | @echo ====================================== 49 | 50 | # create zip archive 51 | mkdir -p "$(TEMP_DEPLOYMENT_DIR)" 52 | echo "-D -j $(TEMP_DEPLOYMENT_ZIPFILE) ANSIEscapeHelper.h ANSIEscapeHelper.m" | xargs zip 53 | cd $(DEPLOYMENT_INCLUDES_DIR) && echo "-g -R ../$(TEMP_DEPLOYMENT_ZIPFILE) *" | xargs zip 54 | echo "-g -r $(TEMP_DEPLOYMENT_ZIPFILE) $(DOCS_DIR)" | xargs zip 55 | 56 | # if changelog doesn't already exist in the deployment dir 57 | # for this version, get 'general' changelog file from root if 58 | # one exists, and if not, create an empty changelog file 59 | @( if [ ! -e $(VERSIONCHANGELOGFILELOC) ];then\ 60 | if [ -e $(GENERALCHANGELOGFILELOC) ];then\ 61 | cp $(GENERALCHANGELOGFILELOC) $(VERSIONCHANGELOGFILELOC);\ 62 | echo "Copied existing changelog.html from project root into deployment dir - opening it for editing";\ 63 | else\ 64 | echo -e "
    \n
  • \n
\n" > $(VERSIONCHANGELOGFILELOC);\ 65 | echo "Created new empty changelog.html into deployment dir - opening it for editing";\ 66 | fi; \ 67 | else\ 68 | echo "changelog.html exists for $(APP_VERSION) - opening it for editing";\ 69 | fi ) 70 | @open -a Smultron $(VERSIONCHANGELOGFILELOC) 71 | 72 | 73 | 74 | 75 | #------------------------------------------------------------------------- 76 | #------------------------------------------------------------------------- 77 | # deploy to server 78 | #------------------------------------------------------------------------- 79 | deploy: package 80 | @echo 81 | @echo ---- Deploying to server: 82 | @echo ====================================== 83 | 84 | @echo "Checking latest version number vs. current version number..." 85 | @( if [ "$(VERSION_ON_SERVER)" != "$(APP_VERSION)" ];then\ 86 | echo "Latest version on server is $(VERSION_ON_SERVER). Uploading $(APP_VERSION).";\ 87 | else\ 88 | echo "NOTE: Current version exists on server: ($(APP_VERSION)).";\ 89 | fi;\ 90 | echo "Press enter to continue uploading to server or Ctrl-C to cancel.";\ 91 | read INPUTSTR;\ 92 | scp -r $(TEMP_DEPLOYMENT_DIR) $(DOCS_DIR) $(SCP_TARGET); ) 93 | 94 | 95 | 96 | 97 | #------------------------------------------------------------------------- 98 | #------------------------------------------------------------------------- 99 | # deploy headerdocs to server 100 | #------------------------------------------------------------------------- 101 | deploy-docs: docs 102 | @echo 103 | @echo ---- Deploying headerdoc HTML files to server: 104 | @echo ====================================== 105 | 106 | @( echo "Press enter to continue uploading to server or Ctrl-C to cancel.";\ 107 | read INPUTSTR;\ 108 | scp -r $(DOCS_DIR) $(SCP_TARGET); ) 109 | 110 | 111 | 112 | #------------------------------------------------------------------------- 113 | #------------------------------------------------------------------------- 114 | clean: 115 | @echo 116 | @echo ---- Cleaning up: 117 | @echo ====================================== 118 | -rm -Rf deployment/* 119 | -rm -Rf $(DOCS_DIR)/* 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /MyController.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "AMR_ANSIEscapeHelper.h" 3 | 4 | @interface MyController : NSObject 5 | { 6 | IBOutlet NSButton *button; 7 | IBOutlet NSTextView *textView; 8 | 9 | AMR_ANSIEscapeHelper *ansiEscapeHelper; 10 | } 11 | 12 | - (IBAction) cProgramButtonPress:(id)sender; 13 | - (IBAction) icalBuddyButtonPress:(id)sender; 14 | - (IBAction) perlScriptButtonPress:(id)sender; 15 | - (IBAction) oneCharPerlScriptButtonPress:(id)sender; 16 | 17 | - (void) showString:(NSString*)string; 18 | - (NSString *) runTaskWithPath:(NSString *)path withArgs:(NSArray *)args; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /MyController.m: -------------------------------------------------------------------------------- 1 | #import "MyController.h" 2 | 3 | 4 | #define kANSIColorPrefKey_FgBlack @"ansiColorsFgBlack" 5 | #define kANSIColorPrefKey_FgWhite @"ansiColorsFgWhite" 6 | #define kANSIColorPrefKey_FgRed @"ansiColorsFgRed" 7 | #define kANSIColorPrefKey_FgGreen @"ansiColorsFgGreen" 8 | #define kANSIColorPrefKey_FgYellow @"ansiColorsFgYellow" 9 | #define kANSIColorPrefKey_FgBlue @"ansiColorsFgBlue" 10 | #define kANSIColorPrefKey_FgMagenta @"ansiColorsFgMagenta" 11 | #define kANSIColorPrefKey_FgCyan @"ansiColorsFgCyan" 12 | #define kANSIColorPrefKey_BgBlack @"ansiColorsBgBlack" 13 | #define kANSIColorPrefKey_BgWhite @"ansiColorsBgWhite" 14 | #define kANSIColorPrefKey_BgRed @"ansiColorsBgRed" 15 | #define kANSIColorPrefKey_BgGreen @"ansiColorsBgGreen" 16 | #define kANSIColorPrefKey_BgYellow @"ansiColorsBgYellow" 17 | #define kANSIColorPrefKey_BgBlue @"ansiColorsBgBlue" 18 | #define kANSIColorPrefKey_BgMagenta @"ansiColorsBgMagenta" 19 | #define kANSIColorPrefKey_BgCyan @"ansiColorsBgCyan" 20 | 21 | 22 | @implementation MyController 23 | 24 | 25 | - (IBAction) cProgramButtonPress:(id)sender 26 | { 27 | NSLog(@"resource = %@", [[NSBundle mainBundle] pathForResource:@"a" ofType:@"out"]); 28 | NSString *newLinesString = [self runTaskWithPath:[[NSBundle mainBundle] pathForResource:@"a" ofType:@"out"] withArgs:[NSArray array]]; 29 | NSLog(@"newLinesString = %@", newLinesString); 30 | [self showString:newLinesString]; 31 | } 32 | 33 | - (IBAction) icalBuddyButtonPress:(id)sender 34 | { 35 | NSString *newLinesString = [self 36 | runTaskWithPath:@"/usr/local/bin/icalBuddy" 37 | withArgs:[NSArray arrayWithObjects:@"-f",@"-sc",@"eventsToday+10",nil] 38 | ]; 39 | [self showString:newLinesString]; 40 | } 41 | 42 | - (IBAction) perlScriptButtonPress:(id)sender 43 | { 44 | NSString *newLinesString = [self runTaskWithPath:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"pl"] withArgs:[NSArray array]]; 45 | [self showString:newLinesString]; 46 | } 47 | 48 | - (IBAction) oneCharPerlScriptButtonPress:(id)sender 49 | { 50 | NSString *newLinesString = [self runTaskWithPath:[[NSBundle mainBundle] pathForResource:@"test-onechar" ofType:@"pl"] withArgs:[NSArray array]]; 51 | [self showString:newLinesString]; 52 | } 53 | 54 | 55 | - (void) showString:(NSString*)string 56 | { 57 | [textView setBaseWritingDirection:NSWritingDirectionLeftToRight]; 58 | 59 | // clean all attributes 60 | NSArray *attrs = @[ 61 | NSFontAttributeName, 62 | NSParagraphStyleAttributeName, 63 | NSForegroundColorAttributeName, 64 | NSUnderlineStyleAttributeName, 65 | NSSuperscriptAttributeName, 66 | NSBackgroundColorAttributeName, 67 | NSAttachmentAttributeName, 68 | NSLigatureAttributeName, 69 | NSBaselineOffsetAttributeName, 70 | NSKernAttributeName, 71 | NSLinkAttributeName, 72 | NSStrokeWidthAttributeName, 73 | NSStrokeColorAttributeName, 74 | NSUnderlineColorAttributeName, 75 | NSStrikethroughStyleAttributeName, 76 | NSStrikethroughColorAttributeName, 77 | NSShadowAttributeName, 78 | NSObliquenessAttributeName, 79 | NSExpansionAttributeName, 80 | NSCursorAttributeName, 81 | NSToolTipAttributeName, 82 | NSMarkedClauseSegmentAttributeName, 83 | ]; 84 | NSString *attr; 85 | NSRange fullRange = NSMakeRange(0, [[textView string] length]); 86 | for (attr in attrs) 87 | { 88 | [[textView textStorage] removeAttribute:attr range:fullRange]; 89 | } 90 | 91 | 92 | ansiEscapeHelper = [[AMR_ANSIEscapeHelper alloc] init]; 93 | 94 | // set colors & font to use to ansiEscapeHelper 95 | NSDictionary *colorPrefDefaults = [NSDictionary dictionaryWithObjectsAndKeys: 96 | [NSNumber numberWithInt:AMR_SGRCodeFgBlack], kANSIColorPrefKey_FgBlack, 97 | [NSNumber numberWithInt:AMR_SGRCodeFgWhite], kANSIColorPrefKey_FgWhite, 98 | [NSNumber numberWithInt:AMR_SGRCodeFgRed], kANSIColorPrefKey_FgRed, 99 | [NSNumber numberWithInt:AMR_SGRCodeFgGreen], kANSIColorPrefKey_FgGreen, 100 | [NSNumber numberWithInt:AMR_SGRCodeFgYellow], kANSIColorPrefKey_FgYellow, 101 | [NSNumber numberWithInt:AMR_SGRCodeFgBlue], kANSIColorPrefKey_FgBlue, 102 | [NSNumber numberWithInt:AMR_SGRCodeFgMagenta], kANSIColorPrefKey_FgMagenta, 103 | [NSNumber numberWithInt:AMR_SGRCodeFgCyan], kANSIColorPrefKey_FgCyan, 104 | [NSNumber numberWithInt:AMR_SGRCodeBgBlack], kANSIColorPrefKey_BgBlack, 105 | [NSNumber numberWithInt:AMR_SGRCodeBgWhite], kANSIColorPrefKey_BgWhite, 106 | [NSNumber numberWithInt:AMR_SGRCodeBgRed], kANSIColorPrefKey_BgRed, 107 | [NSNumber numberWithInt:AMR_SGRCodeBgGreen], kANSIColorPrefKey_BgGreen, 108 | [NSNumber numberWithInt:AMR_SGRCodeBgYellow], kANSIColorPrefKey_BgYellow, 109 | [NSNumber numberWithInt:AMR_SGRCodeBgBlue], kANSIColorPrefKey_BgBlue, 110 | [NSNumber numberWithInt:AMR_SGRCodeBgMagenta], kANSIColorPrefKey_BgMagenta, 111 | [NSNumber numberWithInt:AMR_SGRCodeBgCyan], kANSIColorPrefKey_BgCyan, 112 | nil]; 113 | NSUInteger iColorPrefDefaultsKey; 114 | NSData *colorData; 115 | NSString *thisPrefName; 116 | for (iColorPrefDefaultsKey = 0; iColorPrefDefaultsKey < [[colorPrefDefaults allKeys] count]; iColorPrefDefaultsKey++) 117 | { 118 | thisPrefName = [[colorPrefDefaults allKeys] objectAtIndex:iColorPrefDefaultsKey]; 119 | colorData = [[NSUserDefaults standardUserDefaults] dataForKey:thisPrefName]; 120 | if (colorData != nil) 121 | { 122 | NSColor *thisColor = (NSColor *)[NSUnarchiver unarchiveObjectWithData:colorData]; 123 | [[ansiEscapeHelper ansiColors] setObject:thisColor forKey:[colorPrefDefaults objectForKey:thisPrefName]]; 124 | } 125 | } 126 | [ansiEscapeHelper setFont:[textView font]]; 127 | 128 | if (string == nil) 129 | return; 130 | 131 | // get attributed string and display it 132 | NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] init]; 133 | [attrStr setAttributedString:[ansiEscapeHelper attributedStringWithANSIEscapedString:string]]; 134 | 135 | /* 136 | // test changing the attributed string programmatically after generating it 137 | // but before changing it back to an ANSI-escaped string 138 | [attrStr 139 | addAttributes:[NSDictionary 140 | dictionaryWithObject:(NSColor *)[NSUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] dataForKey:kANSIColorPrefKey_FgMagenta]] 141 | forKey:NSForegroundColorAttributeName 142 | ] 143 | range:NSMakeRange(16, 4) 144 | ]; 145 | */ 146 | 147 | [[textView textStorage] setAttributedString:attrStr]; 148 | 149 | NSString *ansiStr = [ansiEscapeHelper ansiEscapedStringWithAttributedString:attrStr]; 150 | NSLog(@"\n%@", ansiStr); 151 | } 152 | 153 | 154 | 155 | 156 | 157 | - (NSString *) runTaskWithPath:(NSString *)path withArgs:(NSArray *)args 158 | { 159 | NSPipe *pipe; 160 | pipe = [NSPipe pipe]; 161 | 162 | NSTask *task; 163 | task = [[NSTask alloc] init]; 164 | [task setLaunchPath: path]; 165 | [task setArguments: args]; 166 | [task setStandardOutput: pipe]; 167 | 168 | NSFileHandle *file; 169 | file = [pipe fileHandleForReading]; 170 | 171 | [task launch]; 172 | 173 | NSData *data; 174 | data = [file readDataToEndOfFile]; 175 | 176 | NSString *string; 177 | string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]; 178 | 179 | return string; 180 | } 181 | 182 | 183 | 184 | 185 | @end 186 | -------------------------------------------------------------------------------- /a.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ali-rantakari/ANSIEscapeHelper/2931e9e3d162ad76613231358f3d6a77eebd760f/a.out -------------------------------------------------------------------------------- /deployment-files/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | DESCRIPTION: 3 | -------------------------------- 4 | 5 | ANSIEscapeHelper is an Objective-C class for dealing with ANSI escape 6 | sequences. Its main purpose is to translate between NSStrings 7 | that contain ANSI escape sequences and similarly formatted 8 | NSAttributedStrings. 9 | 10 | The headerdoc directory contains the API documentation in HTML format. 11 | 12 | If you fix something in this code or use it in your software, please 13 | let me know -- I'm curious ;). You can contact me at http://hasseg.org. 14 | Thanks. 15 | 16 | 17 | Copyright 2009 Ali Rantakari 18 | http://hasseg.org/ansiEscapeHelper 19 | 20 | 21 | 22 | 23 | 24 | LICENSE: 25 | -------------------------------- 26 | 27 | The MIT License 28 | 29 | Copyright (c) 2009 Ali Rantakari 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining a copy 32 | of this software and associated documentation files (the "Software"), to deal 33 | in the Software without restriction, including without limitation the rights 34 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 35 | copies of the Software, and to permit persons to whom the Software is 36 | furnished to do so, subject to the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be included in 39 | all copies or substantial portions of the Software. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 44 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 46 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 47 | THE SOFTWARE. 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AnsiColorsTest 4 | // 5 | // Created by Ali Rantakari on 18.3.09. 6 | // 7 | 8 | #import 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | return NSApplicationMain(argc, (const char **) argv); 13 | } 14 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ANSIEscapeHelper 2 | 3 | This is an Objective-C class for dealing with ANSI escape sequences. Its main purpose is to translate between `NSString`s that contain ANSI escape sequences and similarly formatted `NSAttributedString`s. 4 | 5 | Here's a quick and simple example of how you'd most likely want to use this class: 6 | 7 | AMR_ANSIEscapeHelper *ansiEscapeHelper = 8 | [[[AMR_ANSIEscapeHelper alloc] init] autorelease]; 9 | 10 | // display an ANSI-escaped string in a text view: 11 | NSString *ansiEscapedStr = 12 | @"Let's pretend this string contains ANSI escape sequences"; 13 | NSAttributedString *attrStr = [ansiEscapeHelper 14 | attributedStringWithANSIEscapedString:ansiEscapedStr]; 15 | [[nsTextViewInstance textStorage] setAttributedString:attrStr]; 16 | 17 | // get an ANSI-escaped string from a text view: 18 | NSAttributedString *attrStr = [nsTextViewInstance textStorage]; 19 | NSString *ansiEscapedStr = [ansiEscapeHelper 20 | ansiEscapedStringWithAttributedString:attrStr]; 21 | 22 | 23 | ## The MIT License 24 | 25 | Copyright (c) Ali Rantakari 26 | 27 | Permission is hereby granted, free of charge, to any person obtaining a copy 28 | of this software and associated documentation files (the "Software"), to deal 29 | in the Software without restriction, including without limitation the rights 30 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 31 | copies of the Software, and to permit persons to whom the Software is 32 | furnished to do so, subject to the following conditions: 33 | 34 | The above copyright notice and this permission notice shall be included in 35 | all copies or substantial portions of the Software. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 43 | THE SOFTWARE. -------------------------------------------------------------------------------- /test-onechar.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use Term::ANSIColor qw(:constants); 4 | 5 | # CLEAR, RESET, BOLD, DARK, UNDERLINE, UNDERSCORE, BLINK, REVERSE, CONCEALED, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, ON_BLACK, ON_RED, ON_GREEN, ON_YELLOW, ON_BLUE, ON_MAGENTA, ON_CYAN, and ON_WHITE 6 | 7 | print MAGENTA, "9" 8 | 9 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define kANSIEscapeReset "\033[0m" 4 | #define kANSIEscapeResetAlt "\033[m" 5 | 6 | #define kANSIEscapeItalic "\033[3m" 7 | 8 | #define kANSIEscapeUnderlineSingle "\033[4m" 9 | #define kANSIEscapeUnderlineDouble "\033[21m" 10 | #define kANSIEscapeUnderlineNone "\033[24m" 11 | 12 | #define kANSIEscapeBold "\033[1m" 13 | #define kANSIEscapeBoldOff "\033[22m" 14 | 15 | #define kANSIEscapeRed "\033[31m" 16 | #define kANSIEscapeGreen "\033[32m" 17 | #define kANSIEscapeYellow "\033[33m" 18 | #define kANSIEscapeBlue "\033[34m" 19 | #define kANSIEscapeMagenta "\033[35m" 20 | #define kANSIEscapeCyan "\033[36m" 21 | #define kANSIEscapeWhite "\033[37m" 22 | #define kANSIEscapeFgReset "\033[39m" 23 | 24 | #define kANSIEscapeBgRed "\033[41m" 25 | #define kANSIEscapeBgGreen "\033[42m" 26 | #define kANSIEscapeBgYellow "\033[43m" 27 | #define kANSIEscapeBgBlue "\033[44m" 28 | #define kANSIEscapeBgMagenta "\033[45m" 29 | #define kANSIEscapeBgCyan "\033[46m" 30 | #define kANSIEscapeBgWhite "\033[47m" 31 | #define kANSIEscapeBgReset "\033[49m" 32 | 33 | #define kANSIEscapeBrightBlack "\033[90m" 34 | #define kANSIEscapeBrightRed "\033[91m" 35 | #define kANSIEscapeBrightGreen "\033[92m" 36 | #define kANSIEscapeBrightYellow "\033[93m" 37 | #define kANSIEscapeBrightBlue "\033[94m" 38 | #define kANSIEscapeBrightMagenta "\033[95m" 39 | #define kANSIEscapeBrightCyan "\033[96m" 40 | #define kANSIEscapeBrightWhite "\033[97m" 41 | 42 | #define kANSIEscapeBgBrightBlack "\033[100m" 43 | #define kANSIEscapeBgBrightRed "\033[101m" 44 | #define kANSIEscapeBgBrightGreen "\033[102m" 45 | #define kANSIEscapeBgBrightYellow "\033[103m" 46 | #define kANSIEscapeBgBrightBlue "\033[104m" 47 | #define kANSIEscapeBgBrightMagenta "\033[105m" 48 | #define kANSIEscapeBgBrightCyan "\033[106m" 49 | #define kANSIEscapeBgBrightWhite "\033[107m" 50 | 51 | 52 | int main(int argc, char *argv[]) 53 | { 54 | /* 55 | printf("alternative CSI:\n"); 56 | printf("\23344mhello\233m\n"); 57 | printf("\n"); 58 | */ 59 | 60 | printf("foreground colors:\n"); 61 | printf("%sred %sgreen%s %syellow%s and %sblue.\n", kANSIEscapeRed, kANSIEscapeGreen, kANSIEscapeReset, kANSIEscapeYellow, kANSIEscapeReset, kANSIEscapeBlue); 62 | printf("%smagenta %scyan%s and %swhite%s.\n", kANSIEscapeMagenta, kANSIEscapeCyan, kANSIEscapeReset, kANSIEscapeWhite, kANSIEscapeReset); 63 | printf("\n"); 64 | 65 | printf("bright foreground colors:\n"); 66 | printf("%sred %sgreen%s %syellow%s and %sblue.\n", kANSIEscapeBrightRed, kANSIEscapeBrightGreen, kANSIEscapeReset, kANSIEscapeBrightYellow, kANSIEscapeReset, kANSIEscapeBrightBlue); 67 | printf("%smagenta %scyan%s and %swhite%s. also %sblack%s.\n", kANSIEscapeBrightMagenta, kANSIEscapeBrightCyan, kANSIEscapeReset, kANSIEscapeBrightWhite, kANSIEscapeReset, kANSIEscapeBrightBlack, kANSIEscapeReset); 68 | printf("\n"); 69 | 70 | printf("background colors:\n"); 71 | printf("%sred %sgreen%s %syellow%s and %sblue.\n", kANSIEscapeBgRed, kANSIEscapeBgGreen, kANSIEscapeReset, kANSIEscapeBgYellow, kANSIEscapeReset, kANSIEscapeBgBlue); 72 | printf("%smagenta %scyan%s and %swhite%s.\n", kANSIEscapeBgMagenta, kANSIEscapeBgCyan, kANSIEscapeReset, kANSIEscapeBgWhite, kANSIEscapeReset); 73 | printf("\n"); 74 | 75 | printf("bright background colors:\n"); 76 | printf("%sred %sgreen%s %syellow%s and %sblue.\n", kANSIEscapeBgBrightRed, kANSIEscapeBgBrightGreen, kANSIEscapeReset, kANSIEscapeBgBrightYellow, kANSIEscapeReset, kANSIEscapeBgBrightBlue); 77 | printf("%smagenta %scyan%s and %swhite%s. also %sblack%s.\n", kANSIEscapeBgBrightMagenta, kANSIEscapeBgBrightCyan, kANSIEscapeReset, kANSIEscapeBgBrightWhite, kANSIEscapeReset, kANSIEscapeBgBrightBlack, kANSIEscapeReset); 78 | printf("\n"); 79 | 80 | printf("bright color reset test:\n"); 81 | printf("%sgreen%s reset %sblue bg%s reset.\n", kANSIEscapeBrightGreen, kANSIEscapeFgReset, kANSIEscapeBgBrightBlue, kANSIEscapeBgReset); 82 | printf("%sbright %sand normal%s. %sbright bg %sand normal.%s\n", kANSIEscapeBrightCyan, kANSIEscapeCyan, kANSIEscapeFgReset, kANSIEscapeBgBrightRed, kANSIEscapeBgRed, kANSIEscapeBgReset); 83 | printf("\n"); 84 | 85 | printf("overlapping foreground and background colors:\n"); 86 | printf("%sgreen bg with %syellow fg%s still green bg%s bg reset\n", kANSIEscapeBgGreen, kANSIEscapeYellow, kANSIEscapeFgReset, kANSIEscapeBgReset); 87 | printf("\n"); 88 | 89 | printf("test italic & alternative reset:\n"); 90 | printf("hello %si am italic!%s ..still? %sred and now...%sreset!\n", kANSIEscapeItalic, kANSIEscapeItalic, kANSIEscapeRed, kANSIEscapeResetAlt); 91 | printf("\n"); 92 | 93 | printf("bold and underline:\n"); 94 | printf("start underline:%shello i am %sdoubly%s underlined %sand bold%s, %soccasionally.%s sometimes not underlined anymore.%s\n", kANSIEscapeUnderlineSingle, kANSIEscapeUnderlineDouble, kANSIEscapeUnderlineSingle, kANSIEscapeBold, kANSIEscapeBoldOff, kANSIEscapeBlue, kANSIEscapeUnderlineNone, kANSIEscapeReset); 95 | printf("\n"); 96 | 97 | printf("several codes in one sequence:\n"); 98 | printf("\033[46;31;4mlots of formats at once%s\n", kANSIEscapeResetAlt); 99 | printf("\n"); 100 | 101 | printf("non-SGR control sequences (after 'd', delete 2 chars backwards):\n"); 102 | printf("abcd\033[2Defg\n"); 103 | printf("\n"); 104 | 105 | printf("And some normal text here."); 106 | printf("\n"); 107 | 108 | return 0; 109 | } -------------------------------------------------------------------------------- /test.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use Term::ANSIColor qw(:constants); 4 | 5 | # CLEAR, RESET, BOLD, DARK, UNDERLINE, UNDERSCORE, BLINK, REVERSE, CONCEALED, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, ON_BLACK, ON_RED, ON_GREEN, ON_YELLOW, ON_BLUE, ON_MAGENTA, ON_CYAN, and ON_WHITE 6 | 7 | print "some", BLUE, " blue", RESET, " and some ", RED, "red\n"; 8 | print YELLOW, "yellow", RESET, " and ", CYAN, "cyan and ", MAGENTA, "magenta", RESET, "\n"; 9 | 10 | print "\n"; 11 | 12 | print ON_YELLOW, BLUE, "blue on yellow go sweden :)", ON_BLUE, WHITE, " okay lets get serious now\n", CLEAR; 13 | 14 | print "\n"; 15 | 16 | print RED, "red."; --------------------------------------------------------------------------------