Subversion Repositories Mobile Apps.GyroMouse

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. #import "DDTTYLogger.h"
  2.  
  3. #import <unistd.h>
  4. #import <sys/uio.h>
  5.  
  6. /**
  7.  * Welcome to Cocoa Lumberjack!
  8.  *
  9.  * The project page has a wealth of documentation if you have any questions.
  10.  * https://github.com/CocoaLumberjack/CocoaLumberjack
  11.  *
  12.  * If you're new to the project you may wish to read the "Getting Started" wiki.
  13.  * https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
  14. **/
  15.  
  16. #if ! __has_feature(objc_arc)
  17. #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  18. #endif
  19.  
  20. // We probably shouldn't be using DDLog() statements within the DDLog implementation.
  21. // But we still want to leave our log statements for any future debugging,
  22. // and to allow other developers to trace the implementation (which is a great learning tool).
  23. //
  24. // So we use primitive logging macros around NSLog.
  25. // We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog.
  26.  
  27. #define LOG_LEVEL 2
  28.  
  29. #define NSLogError(frmt, ...)    do{ if(LOG_LEVEL >= 1) NSLog((frmt), ##__VA_ARGS__); } while(0)
  30. #define NSLogWarn(frmt, ...)     do{ if(LOG_LEVEL >= 2) NSLog((frmt), ##__VA_ARGS__); } while(0)
  31. #define NSLogInfo(frmt, ...)     do{ if(LOG_LEVEL >= 3) NSLog((frmt), ##__VA_ARGS__); } while(0)
  32. #define NSLogDebug(frmt, ...)    do{ if(LOG_LEVEL >= 4) NSLog((frmt), ##__VA_ARGS__); } while(0)
  33. #define NSLogVerbose(frmt, ...)  do{ if(LOG_LEVEL >= 5) NSLog((frmt), ##__VA_ARGS__); } while(0)
  34.  
  35. // Xcode does NOT natively support colors in the Xcode debugging console.
  36. // You'll need to install the XcodeColors plugin to see colors in the Xcode console.
  37. // https://github.com/robbiehanson/XcodeColors
  38. //
  39. // The following is documentation from the XcodeColors project:
  40. //
  41. //
  42. // How to apply color formatting to your log statements:
  43. //
  44. // To set the foreground color:
  45. // Insert the ESCAPE_SEQ into your string, followed by "fg124,12,255;" where r=124, g=12, b=255.
  46. //
  47. // To set the background color:
  48. // Insert the ESCAPE_SEQ into your string, followed by "bg12,24,36;" where r=12, g=24, b=36.
  49. //
  50. // To reset the foreground color (to default value):
  51. // Insert the ESCAPE_SEQ into your string, followed by "fg;"
  52. //
  53. // To reset the background color (to default value):
  54. // Insert the ESCAPE_SEQ into your string, followed by "bg;"
  55. //
  56. // To reset the foreground and background color (to default values) in one operation:
  57. // Insert the ESCAPE_SEQ into your string, followed by ";"
  58.  
  59. #define XCODE_COLORS_ESCAPE_SEQ "\033["
  60.  
  61. #define XCODE_COLORS_RESET_FG  XCODE_COLORS_ESCAPE_SEQ "fg;" // Clear any foreground color
  62. #define XCODE_COLORS_RESET_BG  XCODE_COLORS_ESCAPE_SEQ "bg;" // Clear any background color
  63. #define XCODE_COLORS_RESET     XCODE_COLORS_ESCAPE_SEQ ";"   // Clear any foreground or background color
  64.  
  65. // Some simple defines to make life easier on ourself
  66.  
  67. #if TARGET_OS_IPHONE
  68.   #define MakeColor(r, g, b) [UIColor colorWithRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f]
  69. #else
  70.   #define MakeColor(r, g, b) [NSColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f]
  71. #endif
  72.  
  73. #if TARGET_OS_IPHONE
  74.   #define OSColor UIColor
  75. #else
  76.   #define OSColor NSColor
  77. #endif
  78.  
  79. // If running in a shell, not all RGB colors will be supported.
  80. // In this case we automatically map to the closest available color.
  81. // In order to provide this mapping, we have a hard-coded set of the standard RGB values available in the shell.
  82. // However, not every shell is the same, and Apple likes to think different even when it comes to shell colors.
  83. //
  84. // Map to standard Terminal.app colors (1), or
  85. // map to standard xterm colors (0).
  86.  
  87. #define MAP_TO_TERMINAL_APP_COLORS 1
  88.  
  89.  
  90. @interface DDTTYLoggerColorProfile : NSObject {
  91. @public
  92.     int mask;
  93.     int context;
  94.    
  95.     uint8_t fg_r;
  96.     uint8_t fg_g;
  97.     uint8_t fg_b;
  98.    
  99.     uint8_t bg_r;
  100.     uint8_t bg_g;
  101.     uint8_t bg_b;
  102.    
  103.     NSUInteger fgCodeIndex;
  104.     NSString *fgCodeRaw;
  105.    
  106.     NSUInteger bgCodeIndex;
  107.     NSString *bgCodeRaw;
  108.    
  109.     char fgCode[24];
  110.     size_t fgCodeLen;
  111.    
  112.     char bgCode[24];
  113.     size_t bgCodeLen;
  114.    
  115.     char resetCode[8];
  116.     size_t resetCodeLen;
  117. }
  118.  
  119. - (instancetype)initWithForegroundColor:(OSColor *)fgColor backgroundColor:(OSColor *)bgColor flag:(int)mask context:(int)ctxt;
  120.  
  121. @end
  122.  
  123. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  124. #pragma mark -
  125. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  126.  
  127. @implementation DDTTYLogger
  128.  
  129. static BOOL isaColorTTY;
  130. static BOOL isaColor256TTY;
  131. static BOOL isaXcodeColorTTY;
  132.  
  133. static NSArray *codes_fg = nil;
  134. static NSArray *codes_bg = nil;
  135. static NSArray *colors   = nil;
  136.  
  137. static DDTTYLogger *sharedInstance;
  138.  
  139. /**
  140.  * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 16 color mode.
  141.  *
  142.  * This method is used when the application is running from within a shell that only supports 16 color mode.
  143.  * This method is not invoked if the application is running within Xcode, or via normal UI app launch.
  144. **/
  145. + (void)initialize_colors_16
  146. {
  147.     if (codes_fg || codes_bg || colors) return;
  148.    
  149.     NSMutableArray *m_codes_fg = [NSMutableArray arrayWithCapacity:16];
  150.     NSMutableArray *m_codes_bg = [NSMutableArray arrayWithCapacity:16];
  151.     NSMutableArray *m_colors   = [NSMutableArray arrayWithCapacity:16];
  152.    
  153.     // In a standard shell only 16 colors are supported.
  154.     //
  155.     // More information about ansi escape codes can be found online.
  156.     // http://en.wikipedia.org/wiki/ANSI_escape_code
  157.    
  158.     [m_codes_fg addObject:@"30m"];   // normal - black
  159.     [m_codes_fg addObject:@"31m"];   // normal - red
  160.     [m_codes_fg addObject:@"32m"];   // normal - green
  161.     [m_codes_fg addObject:@"33m"];   // normal - yellow
  162.     [m_codes_fg addObject:@"34m"];   // normal - blue
  163.     [m_codes_fg addObject:@"35m"];   // normal - magenta
  164.     [m_codes_fg addObject:@"36m"];   // normal - cyan
  165.     [m_codes_fg addObject:@"37m"];   // normal - gray
  166.     [m_codes_fg addObject:@"1;30m"]; // bright - darkgray
  167.     [m_codes_fg addObject:@"1;31m"]; // bright - red
  168.     [m_codes_fg addObject:@"1;32m"]; // bright - green
  169.     [m_codes_fg addObject:@"1;33m"]; // bright - yellow
  170.     [m_codes_fg addObject:@"1;34m"]; // bright - blue
  171.     [m_codes_fg addObject:@"1;35m"]; // bright - magenta
  172.     [m_codes_fg addObject:@"1;36m"]; // bright - cyan
  173.     [m_codes_fg addObject:@"1;37m"]; // bright - white
  174.    
  175.     [m_codes_bg addObject:@"40m"];   // normal - black
  176.     [m_codes_bg addObject:@"41m"];   // normal - red
  177.     [m_codes_bg addObject:@"42m"];   // normal - green
  178.     [m_codes_bg addObject:@"43m"];   // normal - yellow
  179.     [m_codes_bg addObject:@"44m"];   // normal - blue
  180.     [m_codes_bg addObject:@"45m"];   // normal - magenta
  181.     [m_codes_bg addObject:@"46m"];   // normal - cyan
  182.     [m_codes_bg addObject:@"47m"];   // normal - gray
  183.     [m_codes_bg addObject:@"1;40m"]; // bright - darkgray
  184.     [m_codes_bg addObject:@"1;41m"]; // bright - red
  185.     [m_codes_bg addObject:@"1;42m"]; // bright - green
  186.     [m_codes_bg addObject:@"1;43m"]; // bright - yellow
  187.     [m_codes_bg addObject:@"1;44m"]; // bright - blue
  188.     [m_codes_bg addObject:@"1;45m"]; // bright - magenta
  189.     [m_codes_bg addObject:@"1;46m"]; // bright - cyan
  190.     [m_codes_bg addObject:@"1;47m"]; // bright - white
  191.    
  192. #if MAP_TO_TERMINAL_APP_COLORS
  193.    
  194.     // Standard Terminal.app colors:
  195.     //
  196.     // These are the default colors used by Apple's Terminal.app.
  197.    
  198.     [m_colors addObject:MakeColor(  0,   0,   0)]; // normal - black
  199.     [m_colors addObject:MakeColor(194,  54,  33)]; // normal - red
  200.     [m_colors addObject:MakeColor( 37, 188,  36)]; // normal - green
  201.     [m_colors addObject:MakeColor(173, 173,  39)]; // normal - yellow
  202.     [m_colors addObject:MakeColor( 73,  46, 225)]; // normal - blue
  203.     [m_colors addObject:MakeColor(211,  56, 211)]; // normal - magenta
  204.     [m_colors addObject:MakeColor( 51, 187, 200)]; // normal - cyan
  205.     [m_colors addObject:MakeColor(203, 204, 205)]; // normal - gray
  206.     [m_colors addObject:MakeColor(129, 131, 131)]; // bright - darkgray
  207.     [m_colors addObject:MakeColor(252,  57,  31)]; // bright - red
  208.     [m_colors addObject:MakeColor( 49, 231,  34)]; // bright - green
  209.     [m_colors addObject:MakeColor(234, 236,  35)]; // bright - yellow
  210.     [m_colors addObject:MakeColor( 88,  51, 255)]; // bright - blue
  211.     [m_colors addObject:MakeColor(249,  53, 248)]; // bright - magenta
  212.     [m_colors addObject:MakeColor( 20, 240, 240)]; // bright - cyan
  213.     [m_colors addObject:MakeColor(233, 235, 235)]; // bright - white
  214.    
  215. #else
  216.    
  217.     // Standard xterm colors:
  218.     //
  219.     // These are the default colors used by most xterm shells.
  220.    
  221.     [m_colors addObject:MakeColor(  0,   0,   0)]; // normal - black
  222.     [m_colors addObject:MakeColor(205,   0,   0)]; // normal - red
  223.     [m_colors addObject:MakeColor(  0, 205,   0)]; // normal - green
  224.     [m_colors addObject:MakeColor(205, 205,   0)]; // normal - yellow
  225.     [m_colors addObject:MakeColor(  0,   0, 238)]; // normal - blue
  226.     [m_colors addObject:MakeColor(205,   0, 205)]; // normal - magenta
  227.     [m_colors addObject:MakeColor(  0, 205, 205)]; // normal - cyan
  228.     [m_colors addObject:MakeColor(229, 229, 229)]; // normal - gray
  229.     [m_colors addObject:MakeColor(127, 127, 127)]; // bright - darkgray
  230.     [m_colors addObject:MakeColor(255,   0,   0)]; // bright - red
  231.     [m_colors addObject:MakeColor(  0, 255,   0)]; // bright - green
  232.     [m_colors addObject:MakeColor(255, 255,   0)]; // bright - yellow
  233.     [m_colors addObject:MakeColor( 92,  92, 255)]; // bright - blue
  234.     [m_colors addObject:MakeColor(255,   0, 255)]; // bright - magenta
  235.     [m_colors addObject:MakeColor(  0, 255, 255)]; // bright - cyan
  236.     [m_colors addObject:MakeColor(255, 255, 255)]; // bright - white
  237.    
  238. #endif
  239.    
  240.     codes_fg = [m_codes_fg copy];
  241.     codes_bg = [m_codes_bg copy];
  242.     colors   = [m_colors   copy];
  243.    
  244.     NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)");
  245.     NSAssert([codes_fg count] == [colors count],   @"Invalid colors/codes array(s)");
  246. }
  247.  
  248. /**
  249.  * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 256 color mode.
  250.  *
  251.  * This method is used when the application is running from within a shell that supports 256 color mode.
  252.  * This method is not invoked if the application is running within Xcode, or via normal UI app launch.
  253. **/
  254. + (void)initialize_colors_256
  255. {
  256.     if (codes_fg || codes_bg || colors) return;
  257.    
  258.     NSMutableArray *m_codes_fg = [NSMutableArray arrayWithCapacity:(256-16)];
  259.     NSMutableArray *m_codes_bg = [NSMutableArray arrayWithCapacity:(256-16)];
  260.     NSMutableArray *m_colors   = [NSMutableArray arrayWithCapacity:(256-16)];
  261.    
  262.     #if MAP_TO_TERMINAL_APP_COLORS
  263.    
  264.     // Standard Terminal.app colors:
  265.     //
  266.     // These are the colors the Terminal.app uses in xterm-256color mode.
  267.     // In this mode, the terminal supports 256 different colors, specified by 256 color codes.
  268.     //
  269.     // The first 16 color codes map to the original 16 color codes supported by the earlier xterm-color mode.
  270.     // These are actually configurable, and thus we ignore them for the purposes of mapping,
  271.     // as we can't rely on them being constant. They are largely duplicated anyway.
  272.     //
  273.     // The next 216 color codes are designed to run the spectrum, with several shades of every color.
  274.     // While the color codes are standardized, the actual RGB values for each color code is not.
  275.     // Apple's Terminal.app uses different RGB values from that of a standard xterm.
  276.     // Apple's choices in colors are designed to be a little nicer on the eyes.
  277.     //
  278.     // The last 24 color codes represent a grayscale.
  279.     //
  280.     // Unfortunately, unlike the standard xterm color chart,
  281.     // Apple's RGB values cannot be calculated using a simple formula (at least not that I know of).
  282.     // Also, I don't know of any ways to programmatically query the shell for the RGB values.
  283.     // So this big giant color chart had to be made by hand.
  284.     //
  285.     // More information about ansi escape codes can be found online.
  286.     // http://en.wikipedia.org/wiki/ANSI_escape_code
  287.    
  288.     // Colors
  289.    
  290.     [m_colors addObject:MakeColor( 47,  49,  49)];
  291.     [m_colors addObject:MakeColor( 60,  42, 144)];
  292.     [m_colors addObject:MakeColor( 66,  44, 183)];
  293.     [m_colors addObject:MakeColor( 73,  46, 222)];
  294.     [m_colors addObject:MakeColor( 81,  50, 253)];
  295.     [m_colors addObject:MakeColor( 88,  51, 255)];
  296.    
  297.     [m_colors addObject:MakeColor( 42, 128,  37)];
  298.     [m_colors addObject:MakeColor( 42, 127, 128)];
  299.     [m_colors addObject:MakeColor( 44, 126, 169)];
  300.     [m_colors addObject:MakeColor( 56, 125, 209)];
  301.     [m_colors addObject:MakeColor( 59, 124, 245)];
  302.     [m_colors addObject:MakeColor( 66, 123, 255)];
  303.    
  304.     [m_colors addObject:MakeColor( 51, 163,  41)];
  305.     [m_colors addObject:MakeColor( 39, 162, 121)];
  306.     [m_colors addObject:MakeColor( 42, 161, 162)];
  307.     [m_colors addObject:MakeColor( 53, 160, 202)];
  308.     [m_colors addObject:MakeColor( 45, 159, 240)];
  309.     [m_colors addObject:MakeColor( 58, 158, 255)];
  310.    
  311.     [m_colors addObject:MakeColor( 31, 196,  37)];
  312.     [m_colors addObject:MakeColor( 48, 196, 115)];
  313.     [m_colors addObject:MakeColor( 39, 195, 155)];
  314.     [m_colors addObject:MakeColor( 49, 195, 195)];
  315.     [m_colors addObject:MakeColor( 32, 194, 235)];
  316.     [m_colors addObject:MakeColor( 53, 193, 255)];
  317.    
  318.     [m_colors addObject:MakeColor( 50, 229,  35)];
  319.     [m_colors addObject:MakeColor( 40, 229, 109)];
  320.     [m_colors addObject:MakeColor( 27, 229, 149)];
  321.     [m_colors addObject:MakeColor( 49, 228, 189)];
  322.     [m_colors addObject:MakeColor( 33, 228, 228)];
  323.     [m_colors addObject:MakeColor( 53, 227, 255)];
  324.    
  325.     [m_colors addObject:MakeColor( 27, 254,  30)];
  326.     [m_colors addObject:MakeColor( 30, 254, 103)];
  327.     [m_colors addObject:MakeColor( 45, 254, 143)];
  328.     [m_colors addObject:MakeColor( 38, 253, 182)];
  329.     [m_colors addObject:MakeColor( 38, 253, 222)];
  330.     [m_colors addObject:MakeColor( 42, 253, 252)];
  331.    
  332.     [m_colors addObject:MakeColor(140,  48,  40)];
  333.     [m_colors addObject:MakeColor(136,  51, 136)];
  334.     [m_colors addObject:MakeColor(135,  52, 177)];
  335.     [m_colors addObject:MakeColor(134,  52, 217)];
  336.     [m_colors addObject:MakeColor(135,  56, 248)];
  337.     [m_colors addObject:MakeColor(134,  53, 255)];
  338.    
  339.     [m_colors addObject:MakeColor(125, 125,  38)];
  340.     [m_colors addObject:MakeColor(124, 125, 125)];
  341.     [m_colors addObject:MakeColor(122, 124, 166)];
  342.     [m_colors addObject:MakeColor(123, 124, 207)];
  343.     [m_colors addObject:MakeColor(123, 122, 247)];
  344.     [m_colors addObject:MakeColor(124, 121, 255)];
  345.    
  346.     [m_colors addObject:MakeColor(119, 160,  35)];
  347.     [m_colors addObject:MakeColor(117, 160, 120)];
  348.     [m_colors addObject:MakeColor(117, 160, 160)];
  349.     [m_colors addObject:MakeColor(115, 159, 201)];
  350.     [m_colors addObject:MakeColor(116, 158, 240)];
  351.     [m_colors addObject:MakeColor(117, 157, 255)];
  352.    
  353.     [m_colors addObject:MakeColor(113, 195,  39)];
  354.     [m_colors addObject:MakeColor(110, 194, 114)];
  355.     [m_colors addObject:MakeColor(111, 194, 154)];
  356.     [m_colors addObject:MakeColor(108, 194, 194)];
  357.     [m_colors addObject:MakeColor(109, 193, 234)];
  358.     [m_colors addObject:MakeColor(108, 192, 255)];
  359.    
  360.     [m_colors addObject:MakeColor(105, 228,  30)];
  361.     [m_colors addObject:MakeColor(103, 228, 109)];
  362.     [m_colors addObject:MakeColor(105, 228, 148)];
  363.     [m_colors addObject:MakeColor(100, 227, 188)];
  364.     [m_colors addObject:MakeColor( 99, 227, 227)];
  365.     [m_colors addObject:MakeColor( 99, 226, 253)];
  366.    
  367.     [m_colors addObject:MakeColor( 92, 253,  34)];
  368.     [m_colors addObject:MakeColor( 96, 253, 103)];
  369.     [m_colors addObject:MakeColor( 97, 253, 142)];
  370.     [m_colors addObject:MakeColor( 88, 253, 182)];
  371.     [m_colors addObject:MakeColor( 93, 253, 221)];
  372.     [m_colors addObject:MakeColor( 88, 254, 251)];
  373.    
  374.     [m_colors addObject:MakeColor(177,  53,  34)];
  375.     [m_colors addObject:MakeColor(174,  54, 131)];
  376.     [m_colors addObject:MakeColor(172,  55, 172)];
  377.     [m_colors addObject:MakeColor(171,  57, 213)];
  378.     [m_colors addObject:MakeColor(170,  55, 249)];
  379.     [m_colors addObject:MakeColor(170,  57, 255)];
  380.    
  381.     [m_colors addObject:MakeColor(165, 123,  37)];
  382.     [m_colors addObject:MakeColor(163, 123, 123)];
  383.     [m_colors addObject:MakeColor(162, 123, 164)];
  384.     [m_colors addObject:MakeColor(161, 122, 205)];
  385.     [m_colors addObject:MakeColor(161, 121, 241)];
  386.     [m_colors addObject:MakeColor(161, 121, 255)];
  387.    
  388.     [m_colors addObject:MakeColor(158, 159,  33)];
  389.     [m_colors addObject:MakeColor(157, 158, 118)];
  390.     [m_colors addObject:MakeColor(157, 158, 159)];
  391.     [m_colors addObject:MakeColor(155, 157, 199)];
  392.     [m_colors addObject:MakeColor(155, 157, 239)];
  393.     [m_colors addObject:MakeColor(154, 156, 255)];
  394.    
  395.     [m_colors addObject:MakeColor(152, 193,  40)];
  396.     [m_colors addObject:MakeColor(151, 193, 113)];
  397.     [m_colors addObject:MakeColor(150, 193, 153)];
  398.     [m_colors addObject:MakeColor(150, 192, 193)];
  399.     [m_colors addObject:MakeColor(148, 192, 232)];
  400.     [m_colors addObject:MakeColor(149, 191, 253)];
  401.    
  402.     [m_colors addObject:MakeColor(146, 227,  28)];
  403.     [m_colors addObject:MakeColor(144, 227, 108)];
  404.     [m_colors addObject:MakeColor(144, 227, 147)];
  405.     [m_colors addObject:MakeColor(144, 227, 187)];
  406.     [m_colors addObject:MakeColor(142, 226, 227)];
  407.     [m_colors addObject:MakeColor(142, 225, 252)];
  408.    
  409.     [m_colors addObject:MakeColor(138, 253,  36)];
  410.     [m_colors addObject:MakeColor(137, 253, 102)];
  411.     [m_colors addObject:MakeColor(136, 253, 141)];
  412.     [m_colors addObject:MakeColor(138, 254, 181)];
  413.     [m_colors addObject:MakeColor(135, 255, 220)];
  414.     [m_colors addObject:MakeColor(133, 255, 250)];
  415.    
  416.     [m_colors addObject:MakeColor(214,  57,  30)];
  417.     [m_colors addObject:MakeColor(211,  59, 126)];
  418.     [m_colors addObject:MakeColor(209,  57, 168)];
  419.     [m_colors addObject:MakeColor(208,  55, 208)];
  420.     [m_colors addObject:MakeColor(207,  58, 247)];
  421.     [m_colors addObject:MakeColor(206,  61, 255)];
  422.    
  423.     [m_colors addObject:MakeColor(204, 121,  32)];
  424.     [m_colors addObject:MakeColor(202, 121, 121)];
  425.     [m_colors addObject:MakeColor(201, 121, 161)];
  426.     [m_colors addObject:MakeColor(200, 120, 202)];
  427.     [m_colors addObject:MakeColor(200, 120, 241)];
  428.     [m_colors addObject:MakeColor(198, 119, 255)];
  429.    
  430.     [m_colors addObject:MakeColor(198, 157,  37)];
  431.     [m_colors addObject:MakeColor(196, 157, 116)];
  432.     [m_colors addObject:MakeColor(195, 156, 157)];
  433.     [m_colors addObject:MakeColor(195, 156, 197)];
  434.     [m_colors addObject:MakeColor(194, 155, 236)];
  435.     [m_colors addObject:MakeColor(193, 155, 255)];
  436.    
  437.     [m_colors addObject:MakeColor(191, 192,  36)];
  438.     [m_colors addObject:MakeColor(190, 191, 112)];
  439.     [m_colors addObject:MakeColor(189, 191, 152)];
  440.     [m_colors addObject:MakeColor(189, 191, 191)];
  441.     [m_colors addObject:MakeColor(188, 190, 230)];
  442.     [m_colors addObject:MakeColor(187, 190, 253)];
  443.    
  444.     [m_colors addObject:MakeColor(185, 226,  28)];
  445.     [m_colors addObject:MakeColor(184, 226, 106)];
  446.     [m_colors addObject:MakeColor(183, 225, 146)];
  447.     [m_colors addObject:MakeColor(183, 225, 186)];
  448.     [m_colors addObject:MakeColor(182, 225, 225)];
  449.     [m_colors addObject:MakeColor(181, 224, 252)];
  450.    
  451.     [m_colors addObject:MakeColor(178, 255,  35)];
  452.     [m_colors addObject:MakeColor(178, 255, 101)];
  453.     [m_colors addObject:MakeColor(177, 254, 141)];
  454.     [m_colors addObject:MakeColor(176, 254, 180)];
  455.     [m_colors addObject:MakeColor(176, 254, 220)];
  456.     [m_colors addObject:MakeColor(175, 253, 249)];
  457.    
  458.     [m_colors addObject:MakeColor(247,  56,  30)];
  459.     [m_colors addObject:MakeColor(245,  57, 122)];
  460.     [m_colors addObject:MakeColor(243,  59, 163)];
  461.     [m_colors addObject:MakeColor(244,  60, 204)];
  462.     [m_colors addObject:MakeColor(242,  59, 241)];
  463.     [m_colors addObject:MakeColor(240,  55, 255)];
  464.    
  465.     [m_colors addObject:MakeColor(241, 119,  36)];
  466.     [m_colors addObject:MakeColor(240, 120, 118)];
  467.     [m_colors addObject:MakeColor(238, 119, 158)];
  468.     [m_colors addObject:MakeColor(237, 119, 199)];
  469.     [m_colors addObject:MakeColor(237, 118, 238)];
  470.     [m_colors addObject:MakeColor(236, 118, 255)];
  471.    
  472.     [m_colors addObject:MakeColor(235, 154,  36)];
  473.     [m_colors addObject:MakeColor(235, 154, 114)];
  474.     [m_colors addObject:MakeColor(234, 154, 154)];
  475.     [m_colors addObject:MakeColor(232, 154, 194)];
  476.     [m_colors addObject:MakeColor(232, 153, 234)];
  477.     [m_colors addObject:MakeColor(232, 153, 255)];
  478.    
  479.     [m_colors addObject:MakeColor(230, 190,  30)];
  480.     [m_colors addObject:MakeColor(229, 189, 110)];
  481.     [m_colors addObject:MakeColor(228, 189, 150)];
  482.     [m_colors addObject:MakeColor(227, 189, 190)];
  483.     [m_colors addObject:MakeColor(227, 189, 229)];
  484.     [m_colors addObject:MakeColor(226, 188, 255)];
  485.    
  486.     [m_colors addObject:MakeColor(224, 224,  35)];
  487.     [m_colors addObject:MakeColor(223, 224, 105)];
  488.     [m_colors addObject:MakeColor(222, 224, 144)];
  489.     [m_colors addObject:MakeColor(222, 223, 184)];
  490.     [m_colors addObject:MakeColor(222, 223, 224)];
  491.     [m_colors addObject:MakeColor(220, 223, 253)];
  492.    
  493.     [m_colors addObject:MakeColor(217, 253,  28)];
  494.     [m_colors addObject:MakeColor(217, 253,  99)];
  495.     [m_colors addObject:MakeColor(216, 252, 139)];
  496.     [m_colors addObject:MakeColor(216, 252, 179)];
  497.     [m_colors addObject:MakeColor(215, 252, 218)];
  498.     [m_colors addObject:MakeColor(215, 251, 250)];
  499.    
  500.     [m_colors addObject:MakeColor(255,  61,  30)];
  501.     [m_colors addObject:MakeColor(255,  60, 118)];
  502.     [m_colors addObject:MakeColor(255,  58, 159)];
  503.     [m_colors addObject:MakeColor(255,  56, 199)];
  504.     [m_colors addObject:MakeColor(255,  55, 238)];
  505.     [m_colors addObject:MakeColor(255,  59, 255)];
  506.    
  507.     [m_colors addObject:MakeColor(255, 117,  29)];
  508.     [m_colors addObject:MakeColor(255, 117, 115)];
  509.     [m_colors addObject:MakeColor(255, 117, 155)];
  510.     [m_colors addObject:MakeColor(255, 117, 195)];
  511.     [m_colors addObject:MakeColor(255, 116, 235)];
  512.     [m_colors addObject:MakeColor(254, 116, 255)];
  513.    
  514.     [m_colors addObject:MakeColor(255, 152,  27)];
  515.     [m_colors addObject:MakeColor(255, 152, 111)];
  516.     [m_colors addObject:MakeColor(254, 152, 152)];
  517.     [m_colors addObject:MakeColor(255, 152, 192)];
  518.     [m_colors addObject:MakeColor(254, 151, 231)];
  519.     [m_colors addObject:MakeColor(253, 151, 253)];
  520.    
  521.     [m_colors addObject:MakeColor(255, 187,  33)];
  522.     [m_colors addObject:MakeColor(253, 187, 107)];
  523.     [m_colors addObject:MakeColor(252, 187, 148)];
  524.     [m_colors addObject:MakeColor(253, 187, 187)];
  525.     [m_colors addObject:MakeColor(254, 187, 227)];
  526.     [m_colors addObject:MakeColor(252, 186, 252)];
  527.    
  528.     [m_colors addObject:MakeColor(252, 222,  34)];
  529.     [m_colors addObject:MakeColor(251, 222, 103)];
  530.     [m_colors addObject:MakeColor(251, 222, 143)];
  531.     [m_colors addObject:MakeColor(250, 222, 182)];
  532.     [m_colors addObject:MakeColor(251, 221, 222)];
  533.     [m_colors addObject:MakeColor(252, 221, 252)];
  534.    
  535.     [m_colors addObject:MakeColor(251, 252,  15)];
  536.     [m_colors addObject:MakeColor(251, 252,  97)];
  537.     [m_colors addObject:MakeColor(249, 252, 137)];
  538.     [m_colors addObject:MakeColor(247, 252, 177)];
  539.     [m_colors addObject:MakeColor(247, 253, 217)];
  540.     [m_colors addObject:MakeColor(254, 255, 255)];
  541.    
  542.     // Grayscale
  543.    
  544.     [m_colors addObject:MakeColor( 52,  53,  53)];
  545.     [m_colors addObject:MakeColor( 57,  58,  59)];
  546.     [m_colors addObject:MakeColor( 66,  67,  67)];
  547.     [m_colors addObject:MakeColor( 75,  76,  76)];
  548.     [m_colors addObject:MakeColor( 83,  85,  85)];
  549.     [m_colors addObject:MakeColor( 92,  93,  94)];
  550.    
  551.     [m_colors addObject:MakeColor(101, 102, 102)];
  552.     [m_colors addObject:MakeColor(109, 111, 111)];
  553.     [m_colors addObject:MakeColor(118, 119, 119)];
  554.     [m_colors addObject:MakeColor(126, 127, 128)];
  555.     [m_colors addObject:MakeColor(134, 136, 136)];
  556.     [m_colors addObject:MakeColor(143, 144, 145)];
  557.    
  558.     [m_colors addObject:MakeColor(151, 152, 153)];
  559.     [m_colors addObject:MakeColor(159, 161, 161)];
  560.     [m_colors addObject:MakeColor(167, 169, 169)];
  561.     [m_colors addObject:MakeColor(176, 177, 177)];
  562.     [m_colors addObject:MakeColor(184, 185, 186)];
  563.     [m_colors addObject:MakeColor(192, 193, 194)];
  564.    
  565.     [m_colors addObject:MakeColor(200, 201, 202)];
  566.     [m_colors addObject:MakeColor(208, 209, 210)];
  567.     [m_colors addObject:MakeColor(216, 218, 218)];
  568.     [m_colors addObject:MakeColor(224, 226, 226)];
  569.     [m_colors addObject:MakeColor(232, 234, 234)];
  570.     [m_colors addObject:MakeColor(240, 242, 242)];
  571.    
  572.     // Color codes
  573.    
  574.     int index = 16;
  575.    
  576.     while (index < 256)
  577.     {
  578.         [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
  579.         [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
  580.        
  581.         index++;
  582.     }
  583.    
  584.     #else
  585.    
  586.     // Standard xterm colors:
  587.     //
  588.     // These are the colors xterm shells use in xterm-256color mode.
  589.     // In this mode, the shell supports 256 different colors, specified by 256 color codes.
  590.     //
  591.     // The first 16 color codes map to the original 16 color codes supported by the earlier xterm-color mode.
  592.     // These are generally configurable, and thus we ignore them for the purposes of mapping,
  593.     // as we can't rely on them being constant. They are largely duplicated anyway.
  594.     //
  595.     // The next 216 color codes are designed to run the spectrum, with several shades of every color.
  596.     // The last 24 color codes represent a grayscale.
  597.     //
  598.     // While the color codes are standardized, the actual RGB values for each color code is not.
  599.     // However most standard xterms follow a well known color chart,
  600.     // which can easily be calculated using the simple formula below.
  601.     //
  602.     // More information about ansi escape codes can be found online.
  603.     // http://en.wikipedia.org/wiki/ANSI_escape_code
  604.    
  605.     int index = 16;
  606.    
  607.     int r; // red
  608.     int g; // green
  609.     int b; // blue
  610.    
  611.     int ri; // r increment
  612.     int gi; // g increment
  613.     int bi; // b increment
  614.    
  615.     // Calculate xterm colors (using standard algorithm)
  616.    
  617.     int r = 0;
  618.     int g = 0;
  619.     int b = 0;
  620.    
  621.     for (ri = 0; ri < 6; ri++)
  622.     {
  623.         r = (ri == 0) ? 0 : 95 + (40 * (ri - 1));
  624.        
  625.         for (gi = 0; gi < 6; gi++)
  626.         {
  627.             g = (gi == 0) ? 0 : 95 + (40 * (gi - 1));
  628.            
  629.             for (bi = 0; bi < 6; bi++)
  630.             {
  631.                 b = (bi == 0) ? 0 : 95 + (40 * (bi - 1));
  632.                
  633.                 [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
  634.                 [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
  635.                 [m_colors addObject:MakeColor(r, g, b)];
  636.                
  637.                 index++;
  638.             }
  639.         }
  640.     }
  641.    
  642.     // Calculate xterm grayscale (using standard algorithm)
  643.    
  644.     r = 8;
  645.     g = 8;
  646.     b = 8;
  647.    
  648.     while (index < 256)
  649.     {
  650.         [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
  651.         [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
  652.         [m_colors addObject:MakeColor(r, g, b)];
  653.        
  654.         r += 10;
  655.         g += 10;
  656.         b += 10;
  657.        
  658.         index++;
  659.     }
  660.    
  661.     #endif
  662.    
  663.     codes_fg = [m_codes_fg copy];
  664.     codes_bg = [m_codes_bg copy];
  665.     colors   = [m_colors   copy];
  666.              
  667.     NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)");
  668.     NSAssert([codes_fg count] == [colors count],   @"Invalid colors/codes array(s)");
  669. }
  670.  
  671. + (void)getRed:(CGFloat *)rPtr green:(CGFloat *)gPtr blue:(CGFloat *)bPtr fromColor:(OSColor *)color
  672. {
  673.     #if TARGET_OS_IPHONE
  674.    
  675.     // iOS
  676.    
  677.     BOOL done = NO;
  678.    
  679.     if ([color respondsToSelector:@selector(getRed:green:blue:alpha:)])
  680.     {
  681.         done = [color getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
  682.     }
  683.    
  684.     if (!done)
  685.     {
  686.         // The method getRed:green:blue:alpha: was only available starting iOS 5.
  687.         // So in iOS 4 and earlier, we have to jump through hoops.
  688.        
  689.         CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
  690.        
  691.         unsigned char pixel[4];
  692.         CGContextRef context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, rgbColorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaNoneSkipLast);
  693.        
  694.         CGContextSetFillColorWithColor(context, [color CGColor]);
  695.         CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
  696.        
  697.         if (rPtr) { *rPtr = pixel[0] / 255.0f; }
  698.         if (gPtr) { *gPtr = pixel[1] / 255.0f; }
  699.         if (bPtr) { *bPtr = pixel[2] / 255.0f; }
  700.        
  701.         CGContextRelease(context);
  702.         CGColorSpaceRelease(rgbColorSpace);
  703.     }
  704.    
  705.     #else
  706.    
  707.     // Mac OS X
  708.    
  709.     NSColor *safeColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
  710.    
  711.     [safeColor getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
  712.    
  713.     #endif
  714. }
  715.  
  716. /**
  717.  * Maps the given color to the closest available color supported by the shell.
  718.  * The shell may support 256 colors, or only 16.
  719.  *
  720.  * This method loops through the known supported color set, and calculates the closest color.
  721.  * The array index of that color, within the colors array, is then returned.
  722.  * This array index may also be used as the index within the codes_fg and codes_bg arrays.
  723. **/
  724. + (NSUInteger)codeIndexForColor:(OSColor *)inColor
  725. {
  726.     CGFloat inR, inG, inB;
  727.     [self getRed:&inR green:&inG blue:&inB fromColor:inColor];
  728.    
  729.     NSUInteger bestIndex = 0;
  730.     CGFloat lowestDistance = 100.0f;
  731.    
  732.     NSUInteger i = 0;
  733.     for (OSColor *color in colors)
  734.     {
  735.         // Calculate Euclidean distance (lower value means closer to given color)
  736.        
  737.         CGFloat r, g, b;
  738.         [self getRed:&r green:&g blue:&b fromColor:color];
  739.        
  740.     #if CGFLOAT_IS_DOUBLE
  741.         CGFloat distance = sqrt(pow(r-inR, 2.0) + pow(g-inG, 2.0) + pow(b-inB, 2.0));
  742.     #else
  743.         CGFloat distance = sqrtf(powf(r-inR, 2.0f) + powf(g-inG, 2.0f) + powf(b-inB, 2.0f));
  744.     #endif
  745.        
  746.         NSLogVerbose(@"DDTTYLogger: %3lu : %.3f,%.3f,%.3f & %.3f,%.3f,%.3f = %.6f",
  747.                      (unsigned long)i, inR, inG, inB, r, g, b, distance);
  748.        
  749.         if (distance < lowestDistance)
  750.         {
  751.             bestIndex = i;
  752.             lowestDistance = distance;
  753.            
  754.             NSLogVerbose(@"DDTTYLogger: New best index = %lu", (unsigned long)bestIndex);
  755.         }
  756.        
  757.         i++;
  758.     }
  759.    
  760.     return bestIndex;
  761. }
  762.  
  763. /**
  764.  * The runtime sends initialize to each class in a program exactly one time just before the class,
  765.  * or any class that inherits from it, is sent its first message from within the program. (Thus the
  766.  * method may never be invoked if the class is not used.) The runtime sends the initialize message to
  767.  * classes in a thread-safe manner. Superclasses receive this message before their subclasses.
  768.  *
  769.  * This method may also be called directly (assumably by accident), hence the safety mechanism.
  770. **/
  771. + (void)initialize
  772. {
  773.     static BOOL initialized = NO;
  774.     if (!initialized)
  775.     {
  776.         initialized = YES;
  777.        
  778.         char *term = getenv("TERM");
  779.         if (term)
  780.         {
  781.             if (strcasestr(term, "color") != NULL)
  782.             {
  783.                 isaColorTTY = YES;
  784.                 isaColor256TTY = (strcasestr(term, "256") != NULL);
  785.                
  786.                 if (isaColor256TTY)
  787.                     [self initialize_colors_256];
  788.                 else
  789.                     [self initialize_colors_16];
  790.             }
  791.         }
  792.         else
  793.         {
  794.             // Xcode does NOT natively support colors in the Xcode debugging console.
  795.             // You'll need to install the XcodeColors plugin to see colors in the Xcode console.
  796.             //
  797.             // PS - Please read the header file before diving into the source code.
  798.            
  799.             char *xcode_colors = getenv("XcodeColors");
  800.             if (xcode_colors && (strcmp(xcode_colors, "YES") == 0))
  801.             {
  802.                 isaXcodeColorTTY = YES;
  803.             }
  804.         }
  805.        
  806.         NSLogInfo(@"DDTTYLogger: isaColorTTY = %@", (isaColorTTY ? @"YES" : @"NO"));
  807.         NSLogInfo(@"DDTTYLogger: isaColor256TTY: %@", (isaColor256TTY ? @"YES" : @"NO"));
  808.         NSLogInfo(@"DDTTYLogger: isaXcodeColorTTY: %@", (isaXcodeColorTTY ? @"YES" : @"NO"));
  809.        
  810.         sharedInstance = [[[self class] alloc] init];
  811.     }
  812. }
  813.  
  814. + (instancetype)sharedInstance
  815. {
  816.     return sharedInstance;
  817. }
  818.  
  819. - (id)init
  820. {
  821.     if (sharedInstance != nil)
  822.     {
  823.         return nil;
  824.     }
  825.    
  826.     if ((self = [super init]))
  827.     {
  828.         calendar = [NSCalendar autoupdatingCurrentCalendar];
  829.        
  830.         calendarUnitFlags = 0;
  831.         calendarUnitFlags |= NSYearCalendarUnit;
  832.         calendarUnitFlags |= NSMonthCalendarUnit;
  833.         calendarUnitFlags |= NSDayCalendarUnit;
  834.         calendarUnitFlags |= NSHourCalendarUnit;
  835.         calendarUnitFlags |= NSMinuteCalendarUnit;
  836.         calendarUnitFlags |= NSSecondCalendarUnit;
  837.        
  838.         // Initialze 'app' variable (char *)
  839.        
  840.         appName = [[NSProcessInfo processInfo] processName];
  841.        
  842.         appLen = [appName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  843.         app = (char *)malloc(appLen + 1);
  844.         if (app == NULL) return nil;
  845.        
  846.         [appName getCString:app maxLength:(appLen+1) encoding:NSUTF8StringEncoding];
  847.        
  848.         // Initialize 'pid' variable (char *)
  849.        
  850.         processID = [NSString stringWithFormat:@"%i", (int)getpid()];
  851.        
  852.         pidLen = [processID lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  853.         pid = (char *)malloc(pidLen + 1);
  854.         if (pid == NULL) return nil;
  855.        
  856.         BOOL processedID = [processID getCString:pid maxLength:(pidLen+1) encoding:NSUTF8StringEncoding];
  857.         if (NO == processedID) return nil;
  858.        
  859.         // Initialize color stuff
  860.        
  861.         colorsEnabled = NO;
  862.         colorProfilesArray = [[NSMutableArray alloc] initWithCapacity:8];
  863.         colorProfilesDict = [[NSMutableDictionary alloc] initWithCapacity:8];
  864.     }
  865.     return self;
  866. }
  867.  
  868. - (void)loadDefaultColorProfiles
  869. {
  870.     [self setForegroundColor:MakeColor(214,  57,  30) backgroundColor:nil forFlag:LOG_FLAG_ERROR];
  871.     [self setForegroundColor:MakeColor(204, 121,  32) backgroundColor:nil forFlag:LOG_FLAG_WARN];
  872. }
  873.  
  874. - (BOOL)colorsEnabled
  875. {
  876.     // The design of this method is taken from the DDAbstractLogger implementation.
  877.     // For extensive documentation please refer to the DDAbstractLogger implementation.
  878.    
  879.     // Note: The internal implementation MUST access the colorsEnabled variable directly,
  880.     // This method is designed explicitly for external access.
  881.     //
  882.     // Using "self." syntax to go through this method will cause immediate deadlock.
  883.     // This is the intended result. Fix it by accessing the ivar directly.
  884.     // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  885.    
  886.     NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  887.     NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  888.    
  889.     dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  890.    
  891.     __block BOOL result;
  892.    
  893.     dispatch_sync(globalLoggingQueue, ^{
  894.         dispatch_sync(loggerQueue, ^{
  895.             result = colorsEnabled;
  896.         });
  897.     });
  898.    
  899.     return result;
  900. }
  901.  
  902. - (void)setColorsEnabled:(BOOL)newColorsEnabled
  903. {
  904.     dispatch_block_t block = ^{ @autoreleasepool {
  905.        
  906.         colorsEnabled = newColorsEnabled;
  907.        
  908.         if ([colorProfilesArray count] == 0) {
  909.             [self loadDefaultColorProfiles];
  910.         }
  911.     }};
  912.    
  913.     // The design of this method is taken from the DDAbstractLogger implementation.
  914.     // For extensive documentation please refer to the DDAbstractLogger implementation.
  915.    
  916.     // Note: The internal implementation MUST access the colorsEnabled variable directly,
  917.     // This method is designed explicitly for external access.
  918.     //
  919.     // Using "self." syntax to go through this method will cause immediate deadlock.
  920.     // This is the intended result. Fix it by accessing the ivar directly.
  921.     // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
  922.    
  923.     NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  924.     NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
  925.    
  926.     dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  927.    
  928.     dispatch_async(globalLoggingQueue, ^{
  929.         dispatch_async(loggerQueue, block);
  930.     });
  931. }
  932.  
  933. - (void)setForegroundColor:(OSColor *)txtColor backgroundColor:(OSColor *)bgColor forFlag:(int)mask
  934. {
  935.     [self setForegroundColor:txtColor backgroundColor:bgColor forFlag:mask context:LOG_CONTEXT_ALL];
  936. }
  937.  
  938. - (void)setForegroundColor:(OSColor *)txtColor backgroundColor:(OSColor *)bgColor forFlag:(int)mask context:(int)ctxt
  939. {
  940.     dispatch_block_t block = ^{ @autoreleasepool {
  941.        
  942.         DDTTYLoggerColorProfile *newColorProfile =
  943.             [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
  944.                                                      backgroundColor:bgColor
  945.                                                                 flag:mask
  946.                                                              context:ctxt];
  947.        
  948.         NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile);
  949.        
  950.         NSUInteger i = 0;
  951.         for (DDTTYLoggerColorProfile *colorProfile in colorProfilesArray)
  952.         {
  953.             if ((colorProfile->mask == mask) && (colorProfile->context == ctxt))
  954.             {
  955.                 break;
  956.             }
  957.            
  958.             i++;
  959.         }
  960.        
  961.         if (i < [colorProfilesArray count])
  962.             [colorProfilesArray replaceObjectAtIndex:i withObject:newColorProfile];
  963.         else
  964.             [colorProfilesArray addObject:newColorProfile];
  965.     }};
  966.    
  967.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  968.     // For documentation please refer to the DDAbstractLogger implementation.
  969.    
  970.     if ([self isOnInternalLoggerQueue])
  971.     {
  972.         block();
  973.     }
  974.     else
  975.     {
  976.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  977.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  978.        
  979.         dispatch_async(globalLoggingQueue, ^{
  980.             dispatch_async(loggerQueue, block);
  981.         });
  982.     }
  983. }
  984.  
  985. - (void)setForegroundColor:(OSColor *)txtColor backgroundColor:(OSColor *)bgColor forTag:(id <NSCopying>)tag
  986. {
  987.     NSAssert([(id <NSObject>)tag conformsToProtocol:@protocol(NSCopying)], @"Invalid tag");
  988.    
  989.     dispatch_block_t block = ^{ @autoreleasepool {
  990.        
  991.         DDTTYLoggerColorProfile *newColorProfile =
  992.             [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
  993.                                                      backgroundColor:bgColor
  994.                                                                 flag:0
  995.                                                              context:0];
  996.        
  997.         NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile);
  998.        
  999.         [colorProfilesDict setObject:newColorProfile forKey:tag];
  1000.     }};
  1001.    
  1002.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  1003.     // For documentation please refer to the DDAbstractLogger implementation.
  1004.    
  1005.     if ([self isOnInternalLoggerQueue])
  1006.     {
  1007.         block();
  1008.     }
  1009.     else
  1010.     {
  1011.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  1012.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  1013.        
  1014.         dispatch_async(globalLoggingQueue, ^{
  1015.             dispatch_async(loggerQueue, block);
  1016.         });
  1017.     }
  1018. }
  1019.  
  1020. - (void)clearColorsForFlag:(int)mask
  1021. {
  1022.     [self clearColorsForFlag:mask context:0];
  1023. }
  1024.  
  1025. - (void)clearColorsForFlag:(int)mask context:(int)context
  1026. {
  1027.     dispatch_block_t block = ^{ @autoreleasepool {
  1028.        
  1029.         NSUInteger i = 0;
  1030.         for (DDTTYLoggerColorProfile *colorProfile in colorProfilesArray)
  1031.         {
  1032.             if ((colorProfile->mask == mask) && (colorProfile->context == context))
  1033.             {
  1034.                 break;
  1035.             }
  1036.            
  1037.             i++;
  1038.         }
  1039.        
  1040.         if (i < [colorProfilesArray count])
  1041.         {
  1042.             [colorProfilesArray removeObjectAtIndex:i];
  1043.         }
  1044.     }};
  1045.    
  1046.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  1047.     // For documentation please refer to the DDAbstractLogger implementation.
  1048.    
  1049.     if ([self isOnInternalLoggerQueue])
  1050.     {
  1051.         block();
  1052.     }
  1053.     else
  1054.     {
  1055.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  1056.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  1057.        
  1058.         dispatch_async(globalLoggingQueue, ^{
  1059.             dispatch_async(loggerQueue, block);
  1060.         });
  1061.     }
  1062. }
  1063.  
  1064. - (void)clearColorsForTag:(id <NSCopying>)tag
  1065. {
  1066.     NSAssert([(id <NSObject>)tag conformsToProtocol:@protocol(NSCopying)], @"Invalid tag");
  1067.    
  1068.     dispatch_block_t block = ^{ @autoreleasepool {
  1069.        
  1070.         [colorProfilesDict removeObjectForKey:tag];
  1071.     }};
  1072.    
  1073.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  1074.     // For documentation please refer to the DDAbstractLogger implementation.
  1075.    
  1076.     if ([self isOnInternalLoggerQueue])
  1077.     {
  1078.         block();
  1079.     }
  1080.     else
  1081.     {
  1082.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  1083.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  1084.        
  1085.         dispatch_async(globalLoggingQueue, ^{
  1086.             dispatch_async(loggerQueue, block);
  1087.         });
  1088.     }
  1089. }
  1090.  
  1091. - (void)clearColorsForAllFlags
  1092. {
  1093.     dispatch_block_t block = ^{ @autoreleasepool {
  1094.        
  1095.         [colorProfilesArray removeAllObjects];
  1096.     }};
  1097.    
  1098.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  1099.     // For documentation please refer to the DDAbstractLogger implementation.
  1100.    
  1101.     if ([self isOnInternalLoggerQueue])
  1102.     {
  1103.         block();
  1104.     }
  1105.     else
  1106.     {
  1107.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  1108.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  1109.        
  1110.         dispatch_async(globalLoggingQueue, ^{
  1111.             dispatch_async(loggerQueue, block);
  1112.         });
  1113.     }
  1114. }
  1115.  
  1116. - (void)clearColorsForAllTags
  1117. {
  1118.     dispatch_block_t block = ^{ @autoreleasepool {
  1119.        
  1120.         [colorProfilesDict removeAllObjects];
  1121.     }};
  1122.    
  1123.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  1124.     // For documentation please refer to the DDAbstractLogger implementation.
  1125.    
  1126.     if ([self isOnInternalLoggerQueue])
  1127.     {
  1128.         block();
  1129.     }
  1130.     else
  1131.     {
  1132.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  1133.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  1134.        
  1135.         dispatch_async(globalLoggingQueue, ^{
  1136.             dispatch_async(loggerQueue, block);
  1137.         });
  1138.     }
  1139. }
  1140.  
  1141. - (void)clearAllColors
  1142. {
  1143.     dispatch_block_t block = ^{ @autoreleasepool {
  1144.        
  1145.         [colorProfilesArray removeAllObjects];
  1146.         [colorProfilesDict removeAllObjects];
  1147.     }};
  1148.    
  1149.     // The design of the setter logic below is taken from the DDAbstractLogger implementation.
  1150.     // For documentation please refer to the DDAbstractLogger implementation.
  1151.    
  1152.     if ([self isOnInternalLoggerQueue])
  1153.     {
  1154.         block();
  1155.     }
  1156.     else
  1157.     {
  1158.         dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
  1159.         NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  1160.        
  1161.         dispatch_async(globalLoggingQueue, ^{
  1162.             dispatch_async(loggerQueue, block);
  1163.         });
  1164.     }
  1165. }
  1166.  
  1167. - (void)logMessage:(DDLogMessage *)logMessage
  1168. {
  1169.     NSString *logMsg = logMessage->logMsg;
  1170.     BOOL isFormatted = NO;
  1171.    
  1172.     if (formatter)
  1173.     {
  1174.         logMsg = [formatter formatLogMessage:logMessage];
  1175.         isFormatted = logMsg != logMessage->logMsg;
  1176.     }
  1177.    
  1178.     if (logMsg)
  1179.     {
  1180.         // Search for a color profile associated with the log message
  1181.        
  1182.         DDTTYLoggerColorProfile *colorProfile = nil;
  1183.        
  1184.         if (colorsEnabled)
  1185.         {
  1186.             if (logMessage->tag)
  1187.             {
  1188.                 colorProfile = [colorProfilesDict objectForKey:logMessage->tag];
  1189.             }
  1190.             if (colorProfile == nil)
  1191.             {
  1192.                 for (DDTTYLoggerColorProfile *cp in colorProfilesArray)
  1193.                 {
  1194.                     if (logMessage->logFlag & cp->mask)
  1195.                     {
  1196.                         // Color profile set for this context?
  1197.                         if (logMessage->logContext == cp->context)
  1198.                         {
  1199.                             colorProfile = cp;
  1200.                            
  1201.                             // Stop searching
  1202.                             break;
  1203.                         }
  1204.                        
  1205.                         // Check if LOG_CONTEXT_ALL was specified as a default color for this flag
  1206.                         if (cp->context == LOG_CONTEXT_ALL)
  1207.                         {
  1208.                             colorProfile = cp;
  1209.                            
  1210.                             // We don't break to keep searching for more specific color profiles for the context
  1211.                         }
  1212.                     }
  1213.                 }
  1214.             }
  1215.         }
  1216.        
  1217.         // Convert log message to C string.
  1218.         //
  1219.         // We use the stack instead of the heap for speed if possible.
  1220.         // But we're extra cautious to avoid a stack overflow.
  1221.        
  1222.         NSUInteger msgLen = [logMsg lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  1223.         const BOOL useStack = msgLen < (1024 * 4);
  1224.        
  1225.         char msgStack[useStack ? (msgLen + 1) : 1]; // Analyzer doesn't like zero-size array, hence the 1
  1226.         char *msg = useStack ? msgStack : (char *)malloc(msgLen + 1);
  1227.         if (msg == NULL) return;
  1228.        
  1229.         BOOL logMsgEnc = [logMsg getCString:msg maxLength:(msgLen + 1) encoding:NSUTF8StringEncoding];
  1230.         if (!logMsgEnc)
  1231.         {
  1232.             if (!useStack && msg != NULL) free(msg);
  1233.             return;
  1234.         }
  1235.        
  1236.         // Write the log message to STDERR
  1237.        
  1238.         if (isFormatted)
  1239.         {
  1240.             // The log message has already been formatted.
  1241.            
  1242.             struct iovec v[5];
  1243.            
  1244.             if (colorProfile)
  1245.             {
  1246.                 v[0].iov_base = colorProfile->fgCode;
  1247.                 v[0].iov_len = colorProfile->fgCodeLen;
  1248.  
  1249.                 v[1].iov_base = colorProfile->bgCode;
  1250.                 v[1].iov_len = colorProfile->bgCodeLen;
  1251.  
  1252.                 v[4].iov_base = colorProfile->resetCode;
  1253.                 v[4].iov_len = colorProfile->resetCodeLen;
  1254.             }
  1255.             else
  1256.             {
  1257.                 v[0].iov_base = "";
  1258.                 v[0].iov_len = 0;
  1259.                
  1260.                 v[1].iov_base = "";
  1261.                 v[1].iov_len = 0;
  1262.                
  1263.                 v[4].iov_base = "";
  1264.                 v[4].iov_len = 0;
  1265.             }
  1266.            
  1267.             v[2].iov_base = (char *)msg;
  1268.             v[2].iov_len = msgLen;
  1269.            
  1270.             v[3].iov_base = "\n";
  1271.             v[3].iov_len = (msg[msgLen] == '\n') ? 0 : 1;
  1272.            
  1273.             writev(STDERR_FILENO, v, 5);
  1274.         }
  1275.         else
  1276.         {
  1277.             // The log message is unformatted, so apply standard NSLog style formatting.
  1278.            
  1279.             int len;
  1280.            
  1281.             // Calculate timestamp.
  1282.             // The technique below is faster than using NSDateFormatter.
  1283.            
  1284.             NSDateComponents *components = [calendar components:calendarUnitFlags fromDate:logMessage->timestamp];
  1285.            
  1286.             NSTimeInterval epoch = [logMessage->timestamp timeIntervalSinceReferenceDate];
  1287.             int milliseconds = (int)((epoch - floor(epoch)) * 1000);
  1288.            
  1289.             char ts[24];
  1290.             len = snprintf(ts, 24, "%04ld-%02ld-%02ld %02ld:%02ld:%02ld:%03d", // yyyy-MM-dd HH:mm:ss:SSS
  1291.                            (long)components.year,
  1292.                            (long)components.month,
  1293.                            (long)components.day,
  1294.                            (long)components.hour,
  1295.                            (long)components.minute,
  1296.                            (long)components.second, milliseconds);
  1297.            
  1298.             size_t tsLen = MIN(24-1, len);
  1299.            
  1300.             // Calculate thread ID
  1301.             //
  1302.             // How many characters do we need for the thread id?
  1303.             // logMessage->machThreadID is of type mach_port_t, which is an unsigned int.
  1304.             //
  1305.             // 1 hex char = 4 bits
  1306.             // 8 hex chars for 32 bit, plus ending '\0' = 9
  1307.            
  1308.             char tid[9];
  1309.             len = snprintf(tid, 9, "%x", logMessage->machThreadID);
  1310.            
  1311.             size_t tidLen = MIN(9-1, len);
  1312.            
  1313.             // Here is our format: "%s %s[%i:%s] %s", timestamp, appName, processID, threadID, logMsg
  1314.            
  1315.             struct iovec v[13];
  1316.            
  1317.             if (colorProfile)
  1318.             {
  1319.                 v[0].iov_base = colorProfile->fgCode;
  1320.                 v[0].iov_len = colorProfile->fgCodeLen;
  1321.  
  1322.                 v[1].iov_base = colorProfile->bgCode;
  1323.                 v[1].iov_len = colorProfile->bgCodeLen;
  1324.  
  1325.                 v[12].iov_base = colorProfile->resetCode;
  1326.                 v[12].iov_len = colorProfile->resetCodeLen;
  1327.             }
  1328.             else
  1329.             {
  1330.                 v[0].iov_base = "";
  1331.                 v[0].iov_len = 0;
  1332.  
  1333.                 v[1].iov_base = "";
  1334.                 v[1].iov_len = 0;
  1335.  
  1336.                 v[12].iov_base = "";
  1337.                 v[12].iov_len = 0;
  1338.             }
  1339.            
  1340.             v[2].iov_base = ts;
  1341.             v[2].iov_len = tsLen;
  1342.            
  1343.             v[3].iov_base = " ";
  1344.             v[3].iov_len = 1;
  1345.            
  1346.             v[4].iov_base = app;
  1347.             v[4].iov_len = appLen;
  1348.            
  1349.             v[5].iov_base = "[";
  1350.             v[5].iov_len = 1;
  1351.            
  1352.             v[6].iov_base = pid;
  1353.             v[6].iov_len = pidLen;
  1354.            
  1355.             v[7].iov_base = ":";
  1356.             v[7].iov_len = 1;
  1357.            
  1358.             v[8].iov_base = tid;
  1359.             v[8].iov_len = MIN((size_t)8, tidLen); // snprintf doesn't return what you might think
  1360.            
  1361.             v[9].iov_base = "] ";
  1362.             v[9].iov_len = 2;
  1363.            
  1364.             v[10].iov_base = (char *)msg;
  1365.             v[10].iov_len = msgLen;
  1366.            
  1367.             v[11].iov_base = "\n";
  1368.             v[11].iov_len = (msg[msgLen] == '\n') ? 0 : 1;
  1369.            
  1370.             writev(STDERR_FILENO, v, 13);
  1371.         }
  1372.        
  1373.         if (!useStack) {
  1374.             free(msg);
  1375.         }
  1376.     }
  1377. }
  1378.  
  1379. - (NSString *)loggerName
  1380. {
  1381.     return @"cocoa.lumberjack.ttyLogger";
  1382. }
  1383.  
  1384. @end
  1385.  
  1386. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1387.  
  1388. @implementation DDTTYLoggerColorProfile
  1389.  
  1390. - (instancetype)initWithForegroundColor:(OSColor *)fgColor backgroundColor:(OSColor *)bgColor flag:(int)aMask context:(int)ctxt
  1391. {
  1392.     if ((self = [super init]))
  1393.     {
  1394.         mask = aMask;
  1395.         context = ctxt;
  1396.        
  1397.         CGFloat r, g, b;
  1398.        
  1399.         if (fgColor)
  1400.         {
  1401.             [DDTTYLogger getRed:&r green:&g blue:&b fromColor:fgColor];
  1402.            
  1403.             fg_r = (uint8_t)(r * 255.0f);
  1404.             fg_g = (uint8_t)(g * 255.0f);
  1405.             fg_b = (uint8_t)(b * 255.0f);
  1406.         }
  1407.         if (bgColor)
  1408.         {
  1409.             [DDTTYLogger getRed:&r green:&g blue:&b fromColor:bgColor];
  1410.            
  1411.             bg_r = (uint8_t)(r * 255.0f);
  1412.             bg_g = (uint8_t)(g * 255.0f);
  1413.             bg_b = (uint8_t)(b * 255.0f);
  1414.         }
  1415.        
  1416.         if (fgColor && isaColorTTY)
  1417.         {
  1418.             // Map foreground color to closest available shell color
  1419.            
  1420.             fgCodeIndex = [DDTTYLogger codeIndexForColor:fgColor];
  1421.             fgCodeRaw   = [codes_fg objectAtIndex:fgCodeIndex];
  1422.            
  1423.             NSString *escapeSeq = @"\033[";
  1424.            
  1425.             NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  1426.             NSUInteger len2 = [fgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  1427.            
  1428.             BOOL escapeSeqEnc = [escapeSeq getCString:(fgCode)      maxLength:(len1+1) encoding:NSUTF8StringEncoding];
  1429.             BOOL fgCodeRawEsc = [fgCodeRaw getCString:(fgCode+len1) maxLength:(len2+1) encoding:NSUTF8StringEncoding];
  1430.             if (!escapeSeqEnc || !fgCodeRawEsc) return nil;
  1431.            
  1432.             fgCodeLen = len1+len2;
  1433.         }
  1434.         else if (fgColor && isaXcodeColorTTY)
  1435.         {
  1436.             // Convert foreground color to color code sequence
  1437.            
  1438.             const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ;
  1439.            
  1440.             int result = snprintf(fgCode, 24, "%sfg%u,%u,%u;", escapeSeq, fg_r, fg_g, fg_b);
  1441.             fgCodeLen = MIN(result, (24-1));
  1442.         }
  1443.         else
  1444.         {
  1445.             // No foreground color or no color support
  1446.            
  1447.             fgCode[0] = '\0';
  1448.             fgCodeLen = 0;
  1449.         }
  1450.        
  1451.         if (bgColor && isaColorTTY)
  1452.         {
  1453.             // Map background color to closest available shell color
  1454.            
  1455.             bgCodeIndex = [DDTTYLogger codeIndexForColor:bgColor];
  1456.             bgCodeRaw   = [codes_bg objectAtIndex:bgCodeIndex];
  1457.            
  1458.             NSString *escapeSeq = @"\033[";
  1459.            
  1460.             NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  1461.             NSUInteger len2 = [bgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
  1462.            
  1463.             BOOL escapeSeqEnc = [escapeSeq getCString:(bgCode)      maxLength:(len1+1) encoding:NSUTF8StringEncoding];
  1464.             BOOL bgCodeRawEsc = [bgCodeRaw getCString:(bgCode+len1) maxLength:(len2+1) encoding:NSUTF8StringEncoding];
  1465.             if (!escapeSeqEnc || !bgCodeRawEsc) return nil;
  1466.            
  1467.             bgCodeLen = len1+len2;
  1468.         }
  1469.         else if (bgColor && isaXcodeColorTTY)
  1470.         {
  1471.             // Convert background color to color code sequence
  1472.            
  1473.             const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ;
  1474.            
  1475.             int result = snprintf(bgCode, 24, "%sbg%u,%u,%u;", escapeSeq, bg_r, bg_g, bg_b);
  1476.             bgCodeLen = MIN(result, (24-1));
  1477.         }
  1478.         else
  1479.         {
  1480.             // No background color or no color support
  1481.            
  1482.             bgCode[0] = '\0';
  1483.             bgCodeLen = 0;
  1484.         }
  1485.        
  1486.         if (isaColorTTY)
  1487.         {
  1488.             resetCodeLen = snprintf(resetCode, 8, "\033[0m");
  1489.         }
  1490.         else if (isaXcodeColorTTY)
  1491.         {
  1492.             resetCodeLen = snprintf(resetCode, 8, XCODE_COLORS_RESET);
  1493.         }
  1494.         else
  1495.         {
  1496.             resetCode[0] = '\0';
  1497.             resetCodeLen = 0;
  1498.         }
  1499.     }
  1500.     return self;
  1501. }
  1502.  
  1503. - (NSString *)description
  1504. {
  1505.     return [NSString stringWithFormat:
  1506.             @"<DDTTYLoggerColorProfile: %p mask:%i ctxt:%i fg:%u,%u,%u bg:%u,%u,%u fgCode:%@ bgCode:%@>",
  1507.             self, mask, context, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b, fgCodeRaw, bgCodeRaw];
  1508. }
  1509.  
  1510. @end
  1511.