Subversion Repositories Mobile Apps.GyroMouse

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 pmbaty 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