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