1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2010 - 2015, Göteborg Bit Factory.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 //
23 // http://www.opensource.org/licenses/mit-license.php
24 //
25 ////////////////////////////////////////////////////////////////////////////////
26
27 #include <cmake.h>
28 #include <iostream>
29 #include <iomanip>
30 #include <sstream>
31 #include <vector>
32 #include <algorithm>
33 #include <Color.h>
34 #include <text.h>
35
36 ////////////////////////////////////////////////////////////////////////////////
37 static struct
38 {
39 Color::color_id id;
40 std::string english_name;
41 int index; // offset red=3 (therefore fg=33, bg=43)
42 } allColors[] =
43 {
44 // Color.h enum English Index
45 { Color::nocolor, "none", 0},
46 { Color::black, "black", 1}, // fg 29+0 bg 39+0
47 { Color::red, "red", 2},
48 { Color::green, "green", 3},
49 { Color::yellow, "yellow", 4},
50 { Color::blue, "blue", 5},
51 { Color::magenta, "magenta", 6},
52 { Color::cyan, "cyan", 7},
53 { Color::white, "white", 8},
54
55 };
56
57 #define NUM_COLORS (sizeof (allColors) / sizeof (allColors[0]))
58
59 ////////////////////////////////////////////////////////////////////////////////
Color()60 Color::Color ()
61 : _value (0)
62 {
63 }
64
65 ////////////////////////////////////////////////////////////////////////////////
Color(const Color & other)66 Color::Color (const Color& other)
67 {
68 _value = other._value;
69 }
70
71 ////////////////////////////////////////////////////////////////////////////////
Color(unsigned int c)72 Color::Color (unsigned int c)
73 : _value (0)
74 {
75 #ifdef FEATURE_COLOR
76 if (!(c & _COLOR_HASFG)) _value &= ~_COLOR_FG;
77 if (!(c & _COLOR_HASBG)) _value &= ~_COLOR_BG;
78
79 _value = c & (_COLOR_256 | _COLOR_HASBG | _COLOR_HASFG |_COLOR_UNDERLINE |
80 _COLOR_INVERSE | _COLOR_BOLD | _COLOR_BRIGHT | _COLOR_BG |
81 _COLOR_FG);
82 #endif
83 }
84
85 ////////////////////////////////////////////////////////////////////////////////
86 // Supports the following constructs:
87 // [bright] [color] [on color] [bright] [underline]
88 //
89 // Where [color] is one of:
90 // black
91 // red
92 // ...
93 // grayN 0 <= N <= 23 fg 38;5;232 + N bg 48;5;232 + N
94 // greyN 0 <= N <= 23 fg 38;5;232 + N bg 48;5;232 + N
95 // colorN 0 <= N <= 255 fg 38;5;N bg 48;5;N
96 // rgbRGB 0 <= R,G,B <= 5 fg 38;5;16 + R*36 + G*6 + B bg 48;5;16 + R*36 + G*6 + B
Color(const std::string & spec)97 Color::Color (const std::string& spec)
98 : _value (0)
99 {
100 #ifdef FEATURE_COLOR
101 // By converting underscores to spaces, we inherently support the old "on_red"
102 // style of specifying background colors. We consider underscores to be
103 // deprecated.
104 std::string modifiable_spec = spec;
105 std::replace (modifiable_spec.begin (), modifiable_spec.end (), '_', ' ');
106
107 // Split spec into words.
108 std::vector <std::string> words;
109 split (words, modifiable_spec, ' ');
110
111 // Construct the color as two separate colors, then blend them later. This
112 // make it possible to declare a color such as "color1 on black", and have
113 // the upgrade work properly.
114 unsigned int fg_value = 0;
115 unsigned int bg_value = 0;
116
117 bool bg = false;
118 int index;
119 std::string word;
120 std::vector <std::string>::iterator it;
121 for (it = words.begin (); it != words.end (); ++it)
122 {
123 word = lowerCase (trim (*it));
124
125 if (word == "bold") fg_value |= _COLOR_BOLD;
126 else if (word == "bright") bg_value |= _COLOR_BRIGHT;
127 else if (word == "underline") fg_value |= _COLOR_UNDERLINE;
128 else if (word == "inverse") fg_value |= _COLOR_INVERSE;
129 else if (word == "on") bg = true;
130
131 // X where X is one of black, red, blue ...
132 else if ((index = find (word)) != -1)
133 {
134 if (index)
135 {
136 if (bg)
137 {
138 bg_value |= _COLOR_HASBG;
139 bg_value |= index << 8;
140 }
141 else
142 {
143 fg_value |= _COLOR_HASFG;
144 fg_value |= index;
145 }
146 }
147 }
148
149 // greyN/grayN, where 0 <= N <= 23.
150 else if (word.substr (0, 4) == "grey" ||
151 word.substr (0, 4) == "gray")
152 {
153 index = atoi (word.substr (4).c_str ());
154 if (index < 0 || index > 23)
155 throw format ("ERROR: The color '{1}' is not recognized.", *it);
156
157 if (bg)
158 {
159 bg_value |= _COLOR_HASBG;
160 bg_value |= (index + 232) << 8;
161 bg_value |= _COLOR_256;
162 }
163 else
164 {
165 fg_value |= _COLOR_HASFG;
166 fg_value |= index + 232;
167 fg_value |= _COLOR_256;
168 }
169 }
170
171 // rgbRGB, where 0 <= R,G,B <= 5.
172 else if (word.substr (0, 3) == "rgb")
173 {
174 index = atoi (word.substr (3).c_str ());
175 if (word.length () != 6 ||
176 index < 0 || index > 555)
177 throw format ("ERROR: The color '{1}' is not recognized.", *it);
178
179 int r = atoi (word.substr (3, 1).c_str ());
180 int g = atoi (word.substr (4, 1).c_str ());
181 int b = atoi (word.substr (5, 1).c_str ());
182 if (r < 0 || r > 5 ||
183 g < 0 || g > 5 ||
184 b < 0 || b > 5)
185 throw format ("ERROR: The color '{1}' is not recognized.", *it);
186
187 index = 16 + r*36 + g*6 + b;
188
189 if (bg)
190 {
191 bg_value |= _COLOR_HASBG;
192 bg_value |= index << 8;
193 bg_value |= _COLOR_256;
194 }
195 else
196 {
197 fg_value |= _COLOR_HASFG;
198 fg_value |= index;
199 fg_value |= _COLOR_256;
200 }
201 }
202
203 // colorN, where 0 <= N <= 255.
204 else if (word.substr (0, 5) == "color")
205 {
206 index = atoi (word.substr (5).c_str ());
207 if (index < 0 || index > 255)
208 throw format ("ERROR: The color '{1}' is not recognized.", *it);
209
210 upgrade ();
211
212 if (bg)
213 {
214 bg_value |= _COLOR_HASBG;
215 bg_value |= index << 8;
216 bg_value |= _COLOR_256;
217 }
218 else
219 {
220 fg_value |= _COLOR_HASFG;
221 fg_value |= index;
222 fg_value |= _COLOR_256;
223 }
224 }
225 else if (word != "")
226 throw format ("ERROR: The color '{1}' is not recognized.", *it);
227 }
228
229 // Now combine the fg and bg into a single color.
230 _value = fg_value;
231 blend (Color (bg_value));
232 #endif
233 }
234
235 ////////////////////////////////////////////////////////////////////////////////
Color(color_id fg)236 Color::Color (color_id fg)
237 : _value (0)
238 {
239 #ifdef FEATURE_COLOR
240 if (fg != Color::nocolor)
241 {
242 _value |= _COLOR_HASFG;
243 _value |= fg;
244 }
245 #endif
246 }
247
248 ////////////////////////////////////////////////////////////////////////////////
Color(color_id fg,color_id bg)249 Color::Color (color_id fg, color_id bg)
250 : _value (0)
251 {
252 #ifdef FEATURE_COLOR
253 if (bg != Color::nocolor)
254 {
255 _value |= _COLOR_HASBG;
256 _value |= (bg << 8);
257 }
258
259 if (fg != Color::nocolor)
260 {
261 _value |= _COLOR_HASFG;
262 _value |= fg;
263 }
264 #endif
265 }
266
267 ////////////////////////////////////////////////////////////////////////////////
Color(color_id fg,color_id bg,bool underline,bool bold,bool bright)268 Color::Color (color_id fg, color_id bg, bool underline, bool bold, bool bright)
269 : _value (0)
270 {
271 #ifdef FEATURE_COLOR
272 _value |= ((underline ? 1 : 0) << 18)
273 | ((bold ? 1 : 0) << 17)
274 | ((bright ? 1 : 0) << 16);
275
276 if (bg != Color::nocolor)
277 {
278 _value |= _COLOR_HASBG;
279 _value |= (bg << 8);
280 }
281
282 if (fg != Color::nocolor)
283 {
284 _value |= _COLOR_HASFG;
285 _value |= fg;
286 }
287 #endif
288 }
289
290 ////////////////////////////////////////////////////////////////////////////////
~Color()291 Color::~Color ()
292 {
293 }
294
295 ////////////////////////////////////////////////////////////////////////////////
operator =(const Color & other)296 Color& Color::operator= (const Color& other)
297 {
298 if (this != &other)
299 _value = other._value;
300
301 return *this;
302 }
303
304 ////////////////////////////////////////////////////////////////////////////////
operator std::string() const305 Color::operator std::string () const
306 {
307 std::string description;
308 #ifdef FEATURE_COLOR
309 if (_value & _COLOR_BOLD) description += "bold";
310
311 if (_value & _COLOR_UNDERLINE)
312 description += std::string (description.length () ? " " : "") + "underline";
313
314 if (_value & _COLOR_INVERSE)
315 description += std::string (description.length () ? " " : "") + "inverse";
316
317 if (_value & _COLOR_HASFG)
318 description += std::string (description.length () ? " " : "") + fg ();
319
320 if (_value & _COLOR_HASBG)
321 {
322 description += std::string (description.length () ? " " : "") + "on";
323
324 if (_value & _COLOR_BRIGHT)
325 description += std::string (description.length () ? " " : "") + "bright";
326
327 description += " " + bg ();
328 }
329 #endif
330
331 return description;
332 }
333
334 ////////////////////////////////////////////////////////////////////////////////
operator int() const335 Color::operator int () const
336 {
337 return (int) _value;
338 }
339
340 ////////////////////////////////////////////////////////////////////////////////
341 // If 'other' has styles that are compatible, merge them into this. Colors in
342 // other take precedence.
blend(const Color & other)343 void Color::blend (const Color& other)
344 {
345 #ifdef FEATURE_COLOR
346 if (!other.nontrivial ())
347 return;
348
349 Color c (other);
350 _value |= (c._value & _COLOR_UNDERLINE); // Always inherit underline.
351 _value |= (c._value & _COLOR_INVERSE); // Always inherit inverse.
352
353 // 16 <-- 16.
354 if (!(_value & _COLOR_256) &&
355 !(c._value & _COLOR_256))
356 {
357 _value |= (c._value & _COLOR_BOLD); // Inherit bold.
358 _value |= (c._value & _COLOR_BRIGHT); // Inherit bright.
359
360 if (c._value & _COLOR_HASFG)
361 {
362 _value |= _COLOR_HASFG; // There is now a color.
363 _value &= ~_COLOR_FG; // Remove previous color.
364 _value |= (c._value & _COLOR_FG); // Apply other color.
365 }
366
367 if (c._value & _COLOR_HASBG)
368 {
369 _value |= _COLOR_HASBG; // There is now a color.
370 _value &= ~_COLOR_BG; // Remove previous color.
371 _value |= (c._value & _COLOR_BG); // Apply other color.
372 }
373
374 return;
375 }
376 else
377 {
378 // Upgrade either color, if necessary.
379 if (!(_value & _COLOR_256)) upgrade ();
380 if (!(c._value & _COLOR_256)) c.upgrade ();
381
382 // 256 <-- 256.
383 if (c._value & _COLOR_HASFG)
384 {
385 _value |= _COLOR_HASFG; // There is now a color.
386 _value &= ~_COLOR_FG; // Remove previous color.
387 _value |= (c._value & _COLOR_FG); // Apply other color.
388 }
389
390 if (c._value & _COLOR_HASBG)
391 {
392 _value |= _COLOR_HASBG; // There is now a color.
393 _value &= ~_COLOR_BG; // Remove previous color.
394 _value |= (c._value & _COLOR_BG); // Apply other color.
395 }
396 }
397 #endif
398 }
399
400 ////////////////////////////////////////////////////////////////////////////////
upgrade()401 void Color::upgrade ()
402 {
403 #ifdef FEATURE_COLOR
404 if (!(_value & _COLOR_256))
405 {
406 if (_value & _COLOR_HASFG)
407 {
408 bool bold = _value & _COLOR_BOLD;
409 unsigned int fg = _value & _COLOR_FG;
410 _value &= ~_COLOR_FG;
411 _value &= ~_COLOR_BOLD;
412 _value |= (bold ? fg + 7 : fg - 1);
413 }
414
415 if (_value & _COLOR_HASBG)
416 {
417 bool bright = _value & _COLOR_BRIGHT;
418 unsigned int bg = (_value & _COLOR_BG) >> 8;
419 _value &= ~_COLOR_BG;
420 _value &= ~_COLOR_BRIGHT;
421 _value |= (bright ? bg + 7 : bg - 1) << 8;
422 }
423
424 _value |= _COLOR_256;
425 }
426 #endif
427 }
428
429 ////////////////////////////////////////////////////////////////////////////////
430 // Sample color codes:
431 // red \033[31m
432 // bold red \033[91m
433 // underline red \033[4;31m
434 // bold underline red \033[1;4;31m
435 //
436 // on red \033[41m
437 // on bright red \033[101m
438 //
439 // 256 fg \033[38;5;Nm
440 // 256 bg \033[48;5;Nm
colorize(const std::string & input)441 std::string Color::colorize (const std::string& input)
442 {
443 #ifdef FEATURE_COLOR
444 if (!nontrivial ())
445 return input;
446
447 int count = 0;
448 std::stringstream result;
449
450 // 256 color
451 if (_value & _COLOR_256)
452 {
453 bool needTerminator = false;
454
455 if (_value & _COLOR_UNDERLINE)
456 {
457 result << "\033[4m";
458 needTerminator = true;
459 }
460
461 if (_value & _COLOR_INVERSE)
462 {
463 result << "\033[7m";
464 needTerminator = true;
465 }
466
467 if (_value & _COLOR_HASFG)
468 {
469 result << "\033[38;5;" << (_value & _COLOR_FG) << "m";
470 needTerminator = true;
471 }
472
473 if (_value & _COLOR_HASBG)
474 {
475 result << "\033[48;5;" << ((_value & _COLOR_BG) >> 8) << "m";
476 needTerminator = true;
477 }
478
479 result << input;
480 if (needTerminator)
481 result << "\033[0m";
482
483 return result.str ();
484 }
485
486 // 16 color
487 else
488 {
489 result << "\033[";
490
491 if (_value & _COLOR_BOLD)
492 {
493 if (count++) result << ";";
494 result << "1";
495 }
496
497 if (_value & _COLOR_UNDERLINE)
498 {
499 if (count++) result << ";";
500 result << "4";
501 }
502
503 if (_value & _COLOR_INVERSE)
504 {
505 if (count++) result << ";";
506 result << "7";
507 }
508
509 if (_value & _COLOR_HASFG)
510 {
511 if (count++) result << ";";
512 result << (29 + (_value & _COLOR_FG));
513 }
514
515 if (_value & _COLOR_HASBG)
516 {
517 if (count++) result << ";";
518 result << ((_value & _COLOR_BRIGHT ? 99 : 39) + ((_value & _COLOR_BG) >> 8));
519 }
520
521 result << "m" << input << "\033[0m";
522 return result.str ();
523 }
524 #endif
525
526 return input;
527 }
528
529 ////////////////////////////////////////////////////////////////////////////////
530 // Remove color codes from a string.
strip(const std::string & input)531 std::string Color::strip (const std::string& input)
532 {
533 #ifdef FEATURE_COLOR
534 int length = input.length ();
535 bool inside = false;
536 std::string output;
537 for (int i = 0; i < length; ++i)
538 {
539 if (inside)
540 {
541 if (input[i] == 'm')
542 inside = false;
543 }
544 else
545 {
546 if (input[i] == 033)
547 inside = true;
548 else
549 output += input[i];
550 }
551 }
552
553 return output;
554 #else
555 return input;
556 #endif
557 }
558
559 ////////////////////////////////////////////////////////////////////////////////
colorize(const std::string & input,const std::string & spec)560 std::string Color::colorize (const std::string& input, const std::string& spec)
561 {
562 #ifdef FEATURE_COLOR
563 Color c (spec);
564 return c.colorize (input);
565 #else
566 return input;
567 #endif
568 }
569
570 ////////////////////////////////////////////////////////////////////////////////
nontrivial() const571 bool Color::nontrivial () const
572 {
573 return _value != 0 ? true : false;
574 }
575
576 ////////////////////////////////////////////////////////////////////////////////
find(const std::string & input)577 int Color::find (const std::string& input)
578 {
579 for (unsigned int i = 0; i < NUM_COLORS; ++i)
580 if (allColors[i].english_name == input)
581 return (int) i;
582
583 return -1;
584 }
585
586 ////////////////////////////////////////////////////////////////////////////////
fg() const587 std::string Color::fg () const
588 {
589 #ifdef FEATURE_COLOR
590 int index = _value & _COLOR_FG;
591
592 if (_value & _COLOR_256)
593 {
594 if (_value & _COLOR_HASFG)
595 {
596 std::stringstream s;
597 s << "color" << (_value & _COLOR_FG);
598 return s.str ();
599 }
600 }
601 else
602 {
603 for (unsigned int i = 0; i < NUM_COLORS; ++i)
604 if (allColors[i].index == index)
605 return allColors[i].english_name;
606 }
607 #endif
608
609 return "";
610 }
611
612 ////////////////////////////////////////////////////////////////////////////////
bg() const613 std::string Color::bg () const
614 {
615 #ifdef FEATURE_COLOR
616 int index = (_value & _COLOR_BG) >> 8;
617
618 if (_value & _COLOR_256)
619 {
620 if (_value & _COLOR_HASBG)
621 {
622 std::stringstream s;
623 s << "color" << ((_value & _COLOR_BG) >> 8);
624 return s.str ();
625 }
626 }
627 else
628 {
629 for (unsigned int i = 0; i < NUM_COLORS; ++i)
630 if (allColors[i].index == index)
631 return allColors[i].english_name;
632 }
633 #endif
634
635 return "";
636 }
637
638 ////////////////////////////////////////////////////////////////////////////////
639