]> git.armaanb.net Git - gen-shell.git/blob - src/libshared/src/Color.cpp
added install instructions
[gen-shell.git] / src / libshared / src / Color.cpp
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
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 <Color.h>
29 #include <sstream>
30 #include <vector>
31 #include <cstdlib>
32 #include <shared.h>
33 #include <format.h>
34
35 // uint to string lookup table for Color::_colorize()
36 // _colorize() gets called _a lot_, having this lookup table is a cheap
37 // performance optimization.
38 const char *colorstring[] = {
39     "0",   "1",   "2",   "3",   "4",   "5",   "6",   "7",   "8",   "9",
40    "10",  "11",  "12",  "13",  "14",  "15",  "16",  "17",  "18",  "19",
41    "20",  "21",  "22",  "23",  "24",  "25",  "26",  "27",  "28",  "29",
42    "30",  "31",  "32",  "33",  "34",  "35",  "36",  "37",  "38",  "39",
43    "40",  "41",  "42",  "43",  "44",  "45",  "46",  "47",  "48",  "49",
44    "50",  "51",  "52",  "53",  "54",  "55",  "56",  "57",  "58",  "59",
45    "60",  "61",  "62",  "63",  "64",  "65",  "66",  "67",  "68",  "69",
46    "70",  "71",  "72",  "73",  "74",  "75",  "76",  "77",  "78",  "79",
47    "80",  "81",  "82",  "83",  "84",  "85",  "86",  "87",  "88",  "89",
48    "90",  "91",  "92",  "93",  "94",  "95",  "96",  "97",  "98",  "99",
49   "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
50   "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
51   "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
52   "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
53   "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
54   "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
55   "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
56   "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
57   "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
58   "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
59   "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
60   "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
61   "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
62   "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
63   "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
64   "250", "251", "252", "253", "254", "255"
65 };
66
67 ////////////////////////////////////////////////////////////////////////////////
68 static struct
69 {
70   Color::color_id id;
71   std::string english_name;
72   int index;                    // offset red=3 (therefore fg=33, bg=43)
73 } allColors[] =
74 {
75   // Color.h enum    English     Index
76   { Color::nocolor,  "none",     0},
77   { Color::black,    "black",    1}, // fg 29+0  bg 39+0
78   { Color::red,      "red",      2},
79   { Color::green,    "green",    3},
80   { Color::yellow,   "yellow",   4},
81   { Color::blue,     "blue",     5},
82   { Color::magenta,  "magenta",  6},
83   { Color::cyan,     "cyan",     7},
84   { Color::white,    "white",    8},
85
86 };
87
88 #define NUM_COLORS (sizeof (allColors) / sizeof (allColors[0]))
89
90 ////////////////////////////////////////////////////////////////////////////////
91 Color::Color ()
92 : _value (0)
93 {
94 }
95
96 ////////////////////////////////////////////////////////////////////////////////
97 Color::Color (const Color& other)
98 {
99   _value = other._value;
100 }
101
102 ////////////////////////////////////////////////////////////////////////////////
103 Color::Color (unsigned int c)
104 : _value (0)
105 {
106   if (!(c & _COLOR_HASFG)) _value &= ~_COLOR_FG;
107   if (!(c & _COLOR_HASBG)) _value &= ~_COLOR_BG;
108
109   _value = c & (_COLOR_256 | _COLOR_HASBG | _COLOR_HASFG |_COLOR_UNDERLINE |
110                 _COLOR_INVERSE | _COLOR_BOLD | _COLOR_BRIGHT | _COLOR_BG |
111                 _COLOR_FG);
112 }
113
114 ////////////////////////////////////////////////////////////////////////////////
115 // Supports the following constructs:
116 //   [bright] [color] [on color] [bright] [underline]
117 //
118 // Where [color] is one of:
119 //   black
120 //   red
121 //   ...
122 //   grayN  0 <= N <= 23       fg 38;5;232 + N              bg 48;5;232 + N
123 //   greyN  0 <= N <= 23       fg 38;5;232 + N              bg 48;5;232 + N
124 //   colorN 0 <= N <= 255      fg 38;5;N                    bg 48;5;N
125 //   rgbRGB 0 <= R,G,B <= 5    fg 38;5;16 + R*36 + G*6 + B  bg 48;5;16 + R*36 + G*6 + B
126 Color::Color (const std::string& spec)
127 : _value (0)
128 {
129   // Split spec into words.
130   auto words = split (spec, ' ');
131
132   // Construct the color as two separate colors, then blend them later.  This
133   // make it possible to declare a color such as "color1 on black", and have
134   // the upgrade work properly.
135   unsigned int fg_value = 0;
136   unsigned int bg_value = 0;
137
138   bool bg = false;
139   int index;
140   for (auto& word : words)
141   {
142     word = lowerCase (trim (word));
143
144          if (word == "bold")      fg_value |= _COLOR_BOLD;
145     else if (word == "bright")    bg_value |= _COLOR_BRIGHT;
146     else if (word == "underline") fg_value |= _COLOR_UNDERLINE;
147     else if (word == "inverse")   fg_value |= _COLOR_INVERSE;
148     else if (word == "on")        bg = true;
149
150     // X where X is one of black, red, blue ...
151     else if ((index = find (word)) != -1)
152     {
153       if (index)
154       {
155         if (bg)
156         {
157           bg_value |= _COLOR_HASBG;
158           bg_value |= index << 8;
159         }
160         else
161         {
162           fg_value |= _COLOR_HASFG;
163           fg_value |= index;
164         }
165       }
166     }
167
168     // greyN/grayN, where 0 <= N <= 23.
169     else if (! word.compare (0, 4, "grey", 4) ||
170              ! word.compare (0, 4, "gray", 4))
171     {
172       index = strtol (word.substr (4).c_str (), nullptr, 10);
173       if (index < 0 || index > 23)
174         throw format ("The color '{1}' is not recognized.", word);
175
176       if (bg)
177       {
178         bg_value |= _COLOR_HASBG;
179         bg_value |= (index + 232) << 8;
180         bg_value |= _COLOR_256;
181       }
182       else
183       {
184         fg_value |= _COLOR_HASFG;
185         fg_value |= index + 232;
186         fg_value |= _COLOR_256;
187       }
188     }
189
190     // rgbRGB, where 0 <= R,G,B <= 5.
191     else if (! word.compare (0, 3, "rgb", 3))
192     {
193       index = strtol (word.substr (3).c_str (), nullptr, 10);
194       if (word.length () != 6 ||
195           index < 0 || index > 555)
196         throw format ("The color '{1}' is not recognized.", word);
197
198       int r = strtol (word.substr (3, 1).c_str (), nullptr, 10);
199       int g = strtol (word.substr (4, 1).c_str (), nullptr, 10);
200       int b = strtol (word.substr (5, 1).c_str (), nullptr, 10);
201       if (r < 0 || r > 5 ||
202           g < 0 || g > 5 ||
203           b < 0 || b > 5)
204         throw format ("The color '{1}' is not recognized.", word);
205
206       index = 16 + r*36 + g*6 + b;
207
208       if (bg)
209       {
210         bg_value |= _COLOR_HASBG;
211         bg_value |= index << 8;
212         bg_value |= _COLOR_256;
213       }
214       else
215       {
216         fg_value |= _COLOR_HASFG;
217         fg_value |= index;
218         fg_value |= _COLOR_256;
219       }
220     }
221
222     // colorN, where 0 <= N <= 255.
223     else if (! word.compare (0, 5, "color", 5))
224     {
225       index = strtol (word.substr (5).c_str (), nullptr, 10);
226       if (index < 0 || index > 255)
227         throw format ("The color '{1}' is not recognized.", word);
228
229       upgrade ();
230
231       if (bg)
232       {
233         bg_value |= _COLOR_HASBG;
234         bg_value |= index << 8;
235         bg_value |= _COLOR_256;
236       }
237       else
238       {
239         fg_value |= _COLOR_HASFG;
240         fg_value |= index;
241         fg_value |= _COLOR_256;
242       }
243     }
244     else if (word != "")
245       throw format ("The color '{1}' is not recognized.", word);
246   }
247
248   // Now combine the fg and bg into a single color.
249   _value = fg_value;
250   blend (Color (bg_value));
251 }
252
253 ////////////////////////////////////////////////////////////////////////////////
254 Color::Color (color_id fg)
255 : _value (0)
256 {
257   if (fg != Color::nocolor)
258   {
259     _value |= _COLOR_HASFG;
260     _value |= fg;
261   }
262 }
263
264 ////////////////////////////////////////////////////////////////////////////////
265 Color::Color (color_id fg, color_id bg, bool underline, bool bold, bool bright)
266 : _value (0)
267 {
268   _value |= ((underline ? 1 : 0) << 18)
269          |  ((bold      ? 1 : 0) << 17)
270          |  ((bright    ? 1 : 0) << 16);
271
272   if (bg != Color::nocolor)
273   {
274     _value |= _COLOR_HASBG;
275     _value |= (bg << 8);
276   }
277
278   if (fg != Color::nocolor)
279   {
280     _value |= _COLOR_HASFG;
281     _value |= fg;
282   }
283 }
284
285 ////////////////////////////////////////////////////////////////////////////////
286 Color::operator std::string () const
287 {
288   std::string description;
289   if (_value & _COLOR_BOLD) description += "bold";
290
291   if (_value & _COLOR_UNDERLINE)
292     description += std::string (description.length () ? " " : "") + "underline";
293
294   if (_value & _COLOR_INVERSE)
295     description += std::string (description.length () ? " " : "") + "inverse";
296
297   if (_value & _COLOR_HASFG)
298     description += std::string (description.length () ? " " : "") + fg ();
299
300   if (_value & _COLOR_HASBG)
301   {
302     description += std::string (description.length () ? " " : "") + "on";
303
304     if (_value & _COLOR_BRIGHT)
305       description += std::string (description.length () ? " " : "") + "bright";
306
307     description += " " + bg ();
308   }
309
310   return description;
311 }
312
313 ////////////////////////////////////////////////////////////////////////////////
314 Color::operator int () const
315 {
316   return (int) _value;
317 }
318
319 ////////////////////////////////////////////////////////////////////////////////
320 // If 'other' has styles that are compatible, merge them into this.  Colors in
321 // other take precedence.
322 void Color::blend (const Color& other)
323 {
324   if (!other.nontrivial ())
325     return;
326
327   Color c (other);
328   _value |= (c._value & _COLOR_UNDERLINE);    // Always inherit underline.
329   _value |= (c._value & _COLOR_INVERSE);      // Always inherit inverse.
330
331   // 16 <-- 16.
332   if (!(_value   & _COLOR_256) &&
333       !(c._value & _COLOR_256))
334   {
335     _value |= (c._value & _COLOR_BOLD);       // Inherit bold.
336     _value |= (c._value & _COLOR_BRIGHT);     // Inherit bright.
337
338     if (c._value & _COLOR_HASFG)
339     {
340       _value |= _COLOR_HASFG;                 // There is now a color.
341       _value &= ~_COLOR_FG;                   // Remove previous color.
342       _value |= (c._value & _COLOR_FG);       // Apply other color.
343     }
344
345     if (c._value & _COLOR_HASBG)
346     {
347       _value |= _COLOR_HASBG;                 // There is now a color.
348       _value &= ~_COLOR_BG;                   // Remove previous color.
349       _value |= (c._value & _COLOR_BG);       // Apply other color.
350     }
351
352     return;
353   }
354   else
355   {
356     // Upgrade either color, if necessary.
357     if (!(_value   & _COLOR_256)) upgrade ();
358     if (!(c._value & _COLOR_256)) c.upgrade ();
359
360     // 256 <-- 256.
361     if (c._value & _COLOR_HASFG)
362     {
363       _value |= _COLOR_HASFG;                  // There is now a color.
364       _value &= ~_COLOR_FG;                    // Remove previous color.
365       _value |= (c._value & _COLOR_FG);        // Apply other color.
366     }
367
368     if (c._value & _COLOR_HASBG)
369     {
370       _value |= _COLOR_HASBG;                  // There is now a color.
371       _value &= ~_COLOR_BG;                    // Remove previous color.
372       _value |= (c._value & _COLOR_BG);        // Apply other color.
373     }
374   }
375 }
376
377 ////////////////////////////////////////////////////////////////////////////////
378 void Color::upgrade ()
379 {
380   if (!(_value & _COLOR_256))
381   {
382     if (_value & _COLOR_HASFG)
383     {
384       bool bold = _value & _COLOR_BOLD;
385       unsigned int fg = _value & _COLOR_FG;
386       _value &= ~_COLOR_FG;
387       _value &= ~_COLOR_BOLD;
388       _value |= (bold ? fg + 7 : fg - 1);
389     }
390
391     if (_value & _COLOR_HASBG)
392     {
393       bool bright = _value & _COLOR_BRIGHT;
394       unsigned int bg = (_value & _COLOR_BG) >> 8;
395       _value &= ~_COLOR_BG;
396       _value &= ~_COLOR_BRIGHT;
397       _value |= (bright ? bg + 7 : bg - 1) << 8;
398     }
399
400     _value |= _COLOR_256;
401   }
402 }
403
404 ////////////////////////////////////////////////////////////////////////////////
405 std::string Color::colorize (const std::string& input) const
406 {
407   std::string result;
408   _colorize (result, input);
409   return result;
410 }
411
412 ////////////////////////////////////////////////////////////////////////////////
413 // Sample color codes:
414 //   red                  \033[31m
415 //   bold red             \033[91m
416 //   underline red        \033[4;31m
417 //   bold underline red   \033[1;4;31m
418 //
419 //   on red               \033[41m
420 //   on bright red        \033[101m
421 //
422 //   256 fg               \033[38;5;Nm
423 //   256 bg               \033[48;5;Nm
424 void Color::_colorize (std::string &result, const std::string& input) const
425 {
426   if (!nontrivial ())
427   {
428     result += input;
429     return;
430   }
431
432   int count = 0;
433
434   // 256 color
435   if (_value & _COLOR_256)
436   {
437     if (_value & _COLOR_UNDERLINE)
438       result += "\033[4m";
439
440     if (_value & _COLOR_INVERSE)
441       result += "\033[7m";
442
443     if (_value & _COLOR_HASFG)
444     {
445       result += "\033[38;5;";
446       result += colorstring[(_value & _COLOR_FG)];
447       result += 'm';
448     }
449
450     if (_value & _COLOR_HASBG)
451     {
452       result += "\033[48;5;";
453       result += colorstring[((_value & _COLOR_BG) >> 8)];
454       result += 'm';
455     }
456
457     result += input;
458     result += "\033[0m";
459   }
460
461   // 16 color
462   else
463   {
464     result += "\033[";
465
466     if (_value & _COLOR_BOLD)
467     {
468       if (count++) result += ';';
469       result += '1';
470     }
471
472     if (_value & _COLOR_UNDERLINE)
473     {
474       if (count++) result += ';';
475       result += '4';
476     }
477
478     if (_value & _COLOR_INVERSE)
479     {
480       if (count++) result += ';';
481       result += '7';
482     }
483
484     if (_value & _COLOR_HASFG)
485     {
486       if (count++) result += ';';
487       result += colorstring[(29 + (_value & _COLOR_FG))];
488     }
489
490     if (_value & _COLOR_HASBG)
491     {
492       if (count++) result += ';';
493       result += colorstring[((_value & _COLOR_BRIGHT ? 99 : 39) + ((_value & _COLOR_BG) >> 8))];
494     }
495
496     result += 'm';
497     result += input;
498     result += "\033[0m";
499   }
500 }
501
502 ////////////////////////////////////////////////////////////////////////////////
503 // Remove color codes from a string.
504 std::string Color::strip (const std::string& input)
505 {
506   int length = input.length ();
507   bool inside = false;
508   std::string output;
509   for (int i = 0; i < length; ++i)
510   {
511     if (inside)
512     {
513       if (input[i] == 'm')
514         inside = false;
515     }
516     else
517     {
518       if (input[i] == 033)
519         inside = true;
520       else
521         output += input[i];
522     }
523   }
524
525   return output;
526 }
527
528 ////////////////////////////////////////////////////////////////////////////////
529 std::string Color::colorize (const std::string& input, const std::string& spec)
530 {
531   Color c (spec);
532   return c.colorize (input);
533 }
534
535 ////////////////////////////////////////////////////////////////////////////////
536 std::string Color::code () const
537 {
538   if (! nontrivial ())
539     return "";
540
541   std::string result;
542
543   // 256 color
544   if (_value & _COLOR_256)
545   {
546     if (_value & _COLOR_UNDERLINE)
547       result += "\033[4m";
548
549     if (_value & _COLOR_INVERSE)
550       result += "\033[7m";
551
552     if (_value & _COLOR_HASFG)
553     {
554       result += "\033[38;5;";
555       result += colorstring[(_value & _COLOR_FG)];
556       result += 'm';
557     }
558
559     if (_value & _COLOR_HASBG)
560     {
561       result += "\033[48;5;";
562       result += colorstring[((_value & _COLOR_BG) >> 8)];
563       result += 'm';
564     }
565   }
566
567   // 16 color
568   else
569   {
570     int count = 0;
571     result += "\033[";
572
573     if (_value & _COLOR_BOLD)
574     {
575       if (count++) result += ';';
576       result += '1';
577     }
578
579     if (_value & _COLOR_UNDERLINE)
580     {
581       if (count++) result += ';';
582       result += '4';
583     }
584
585     if (_value & _COLOR_INVERSE)
586     {
587       if (count++) result += ';';
588       result += '7';
589     }
590
591     if (_value & _COLOR_HASFG)
592     {
593       if (count++) result += ';';
594       result += colorstring[(29 + (_value & _COLOR_FG))];
595     }
596
597     if (_value & _COLOR_HASBG)
598     {
599       if (count++) result += ';';
600       result += colorstring[((_value & _COLOR_BRIGHT ? 99 : 39) + ((_value & _COLOR_BG) >> 8))];
601     }
602
603     result += 'm';
604   }
605
606   return result;
607 }
608
609 ////////////////////////////////////////////////////////////////////////////////
610 std::string Color::end () const
611 {
612   if (nontrivial ())
613     return "\033[0m";
614
615   return "";
616 }
617
618 ////////////////////////////////////////////////////////////////////////////////
619 bool Color::nontrivial () const
620 {
621   return _value != 0 ? true : false;
622 }
623
624 ////////////////////////////////////////////////////////////////////////////////
625 int Color::find (const std::string& input)
626 {
627   for (unsigned int i = 0; i < NUM_COLORS; ++i)
628     if (allColors[i].english_name == input)
629       return (int) i;
630
631   return -1;
632 }
633
634 ////////////////////////////////////////////////////////////////////////////////
635 std::string Color::fg () const
636 {
637   int index = _value & _COLOR_FG;
638
639   if (_value & _COLOR_256)
640   {
641     if (_value & _COLOR_HASFG)
642     {
643       std::stringstream s;
644       s << "color" << (_value & _COLOR_FG);
645       return s.str ();
646     }
647   }
648   else
649   {
650     for (unsigned int i = 0; i < NUM_COLORS; ++i)
651       if (allColors[i].index == index)
652         return allColors[i].english_name;
653   }
654
655   return "";
656 }
657
658 ////////////////////////////////////////////////////////////////////////////////
659 std::string Color::bg () const
660 {
661   int index = (_value & _COLOR_BG) >> 8;
662
663   if (_value & _COLOR_256)
664   {
665     if (_value & _COLOR_HASBG)
666     {
667       std::stringstream s;
668       s << "color" << ((_value & _COLOR_BG) >> 8);
669       return s.str ();
670     }
671   }
672   else
673   {
674     for (unsigned int i = 0; i < NUM_COLORS; ++i)
675       if (allColors[i].index == index)
676         return allColors[i].english_name;
677   }
678
679   return "";
680 }
681
682 ////////////////////////////////////////////////////////////////////////////////