Home | History | Annotate | Line # | Download | only in gdb
ui-style.c revision 1.1.1.2
      1      1.1  christos /* Styling for ui_file
      2  1.1.1.2  christos    Copyright (C) 2018-2020 Free Software Foundation, Inc.
      3      1.1  christos 
      4      1.1  christos    This file is part of GDB.
      5      1.1  christos 
      6      1.1  christos    This program is free software; you can redistribute it and/or modify
      7      1.1  christos    it under the terms of the GNU General Public License as published by
      8      1.1  christos    the Free Software Foundation; either version 3 of the License, or
      9      1.1  christos    (at your option) any later version.
     10      1.1  christos 
     11      1.1  christos    This program is distributed in the hope that it will be useful,
     12      1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     13      1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14      1.1  christos    GNU General Public License for more details.
     15      1.1  christos 
     16      1.1  christos    You should have received a copy of the GNU General Public License
     17      1.1  christos    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     18      1.1  christos 
     19      1.1  christos #include "defs.h"
     20      1.1  christos #include "ui-style.h"
     21  1.1.1.2  christos #include "gdb_regex.h"
     22      1.1  christos 
     23      1.1  christos /* A regular expression that is used for matching ANSI terminal escape
     24      1.1  christos    sequences.  */
     25      1.1  christos 
     26      1.1  christos static const char *ansi_regex_text =
     27      1.1  christos   /* Introduction.  */
     28      1.1  christos   "^\033\\["
     29      1.1  christos #define DATA_SUBEXP 1
     30      1.1  christos   /* Capture parameter and intermediate bytes.  */
     31      1.1  christos   "("
     32      1.1  christos   /* Parameter bytes.  */
     33      1.1  christos   "[\x30-\x3f]*"
     34      1.1  christos   /* Intermediate bytes.  */
     35      1.1  christos   "[\x20-\x2f]*"
     36      1.1  christos   /* End the first capture.  */
     37      1.1  christos   ")"
     38      1.1  christos   /* The final byte.  */
     39      1.1  christos #define FINAL_SUBEXP 2
     40      1.1  christos   "([\x40-\x7e])";
     41      1.1  christos 
     42      1.1  christos /* The number of subexpressions to allocate space for, including the
     43      1.1  christos    "0th" whole match subexpression.  */
     44      1.1  christos #define NUM_SUBEXPRESSIONS 3
     45      1.1  christos 
     46      1.1  christos /* The compiled form of ansi_regex_text.  */
     47      1.1  christos 
     48      1.1  christos static regex_t ansi_regex;
     49      1.1  christos 
     50      1.1  christos /* This maps bright colors to RGB triples.  The index is the bright
     51      1.1  christos    color index, starting with bright black.  The values come from
     52      1.1  christos    xterm.  */
     53      1.1  christos 
     54      1.1  christos static const uint8_t bright_colors[][3] = {
     55      1.1  christos   { 127, 127, 127 },		/* Black.  */
     56      1.1  christos   { 255, 0, 0 },		/* Red.  */
     57      1.1  christos   { 0, 255, 0 },		/* Green.  */
     58      1.1  christos   { 255, 255, 0 },		/* Yellow.  */
     59      1.1  christos   { 92, 92, 255 },		/* Blue.  */
     60      1.1  christos   { 255, 0, 255 },		/* Magenta.  */
     61      1.1  christos   { 0, 255, 255 },		/* Cyan.  */
     62      1.1  christos   { 255, 255, 255 }		/* White.  */
     63      1.1  christos };
     64      1.1  christos 
     65      1.1  christos /* See ui-style.h.  */
     66      1.1  christos 
     67      1.1  christos bool
     68      1.1  christos ui_file_style::color::append_ansi (bool is_fg, std::string *str) const
     69      1.1  christos {
     70      1.1  christos   if (m_simple)
     71      1.1  christos     {
     72      1.1  christos       if (m_value >= BLACK && m_value <= WHITE)
     73      1.1  christos 	str->append (std::to_string (m_value + (is_fg ? 30 : 40)));
     74      1.1  christos       else if (m_value > WHITE && m_value <= WHITE + 8)
     75      1.1  christos 	str->append (std::to_string (m_value - WHITE + (is_fg ? 90 : 100)));
     76      1.1  christos       else if (m_value != -1)
     77      1.1  christos 	{
     78      1.1  christos 	  str->append (is_fg ? "38;5;" : "48;5;");
     79      1.1  christos 	  str->append (std::to_string (m_value));
     80      1.1  christos 	}
     81      1.1  christos       else
     82      1.1  christos 	return false;
     83      1.1  christos     }
     84      1.1  christos   else
     85      1.1  christos     {
     86      1.1  christos       str->append (is_fg ? "38;2;" : "48;2;");
     87      1.1  christos       str->append (std::to_string (m_red)
     88      1.1  christos 		   + ";" + std::to_string (m_green)
     89      1.1  christos 		   + ";" + std::to_string (m_blue));
     90      1.1  christos     }
     91      1.1  christos   return true;
     92      1.1  christos }
     93      1.1  christos 
     94      1.1  christos /* See ui-style.h.  */
     95      1.1  christos 
     96      1.1  christos void
     97      1.1  christos ui_file_style::color::get_rgb (uint8_t *rgb) const
     98      1.1  christos {
     99      1.1  christos   if (m_simple)
    100      1.1  christos     {
    101      1.1  christos       /* Can't call this for a basic color or NONE -- those will end
    102      1.1  christos 	 up in the assert below.  */
    103      1.1  christos       if (m_value >= 8 && m_value <= 15)
    104      1.1  christos 	memcpy (rgb, bright_colors[m_value - 8], 3 * sizeof (uint8_t));
    105      1.1  christos       else if (m_value >= 16 && m_value <= 231)
    106      1.1  christos 	{
    107      1.1  christos 	  int value = m_value;
    108      1.1  christos 	  value -= 16;
    109      1.1  christos 	  /* This obscure formula seems to be what terminals actually
    110      1.1  christos 	     do.  */
    111      1.1  christos 	  int component = value / 36;
    112      1.1  christos 	  rgb[0] = component == 0 ? 0 : (55 + component * 40);
    113      1.1  christos 	  value %= 36;
    114      1.1  christos 	  component = value / 6;
    115      1.1  christos 	  rgb[1] = component == 0 ? 0 : (55 + component * 40);
    116      1.1  christos 	  value %= 6;
    117      1.1  christos 	  rgb[2] = value == 0 ? 0 : (55 + value * 40);
    118      1.1  christos 	}
    119      1.1  christos       else if (m_value >= 232)
    120      1.1  christos 	{
    121      1.1  christos 	  uint8_t v = (m_value - 232) * 10 + 8;
    122      1.1  christos 	  rgb[0] = v;
    123      1.1  christos 	  rgb[1] = v;
    124      1.1  christos 	  rgb[2] = v;
    125      1.1  christos 	}
    126      1.1  christos       else
    127      1.1  christos 	gdb_assert_not_reached ("get_rgb called on invalid color");
    128      1.1  christos     }
    129      1.1  christos   else
    130      1.1  christos     {
    131      1.1  christos       rgb[0] = m_red;
    132      1.1  christos       rgb[1] = m_green;
    133      1.1  christos       rgb[2] = m_blue;
    134      1.1  christos     }
    135      1.1  christos }
    136      1.1  christos 
    137      1.1  christos /* See ui-style.h.  */
    138      1.1  christos 
    139      1.1  christos std::string
    140      1.1  christos ui_file_style::to_ansi () const
    141      1.1  christos {
    142      1.1  christos   std::string result ("\033[");
    143      1.1  christos   bool need_semi = m_foreground.append_ansi (true, &result);
    144      1.1  christos   if (!m_background.is_none ())
    145      1.1  christos     {
    146      1.1  christos       if (need_semi)
    147      1.1  christos 	result.push_back (';');
    148      1.1  christos       m_background.append_ansi (false, &result);
    149      1.1  christos       need_semi = true;
    150      1.1  christos     }
    151      1.1  christos   if (m_intensity != NORMAL)
    152      1.1  christos     {
    153      1.1  christos       if (need_semi)
    154      1.1  christos 	result.push_back (';');
    155      1.1  christos       result.append (std::to_string (m_intensity));
    156      1.1  christos       need_semi = true;
    157      1.1  christos     }
    158      1.1  christos   if (m_reverse)
    159      1.1  christos     {
    160      1.1  christos       if (need_semi)
    161      1.1  christos 	result.push_back (';');
    162      1.1  christos       result.push_back ('7');
    163      1.1  christos     }
    164      1.1  christos   result.push_back ('m');
    165      1.1  christos   return result;
    166      1.1  christos }
    167      1.1  christos 
    168      1.1  christos /* Read a ";" and a number from STRING.  Return the number of
    169      1.1  christos    characters read and put the number into *NUM.  */
    170      1.1  christos 
    171      1.1  christos static bool
    172      1.1  christos read_semi_number (const char *string, int *idx, long *num)
    173      1.1  christos {
    174      1.1  christos   if (string[*idx] != ';')
    175      1.1  christos     return false;
    176      1.1  christos   ++*idx;
    177      1.1  christos   if (string[*idx] < '0' || string[*idx] > '9')
    178      1.1  christos     return false;
    179      1.1  christos   char *tail;
    180      1.1  christos   *num = strtol (string + *idx, &tail, 10);
    181      1.1  christos   *idx = tail - string;
    182      1.1  christos   return true;
    183      1.1  christos }
    184      1.1  christos 
    185      1.1  christos /* A helper for ui_file_style::parse that reads an extended color
    186      1.1  christos    sequence; that is, and 8- or 24- bit color.  */
    187      1.1  christos 
    188      1.1  christos static bool
    189      1.1  christos extended_color (const char *str, int *idx, ui_file_style::color *color)
    190      1.1  christos {
    191      1.1  christos   long value;
    192      1.1  christos 
    193      1.1  christos   if (!read_semi_number (str, idx, &value))
    194      1.1  christos     return false;
    195      1.1  christos 
    196      1.1  christos   if (value == 5)
    197      1.1  christos     {
    198      1.1  christos       /* 8-bit color.  */
    199      1.1  christos       if (!read_semi_number (str, idx, &value))
    200      1.1  christos 	return false;
    201      1.1  christos 
    202      1.1  christos       if (value >= 0 && value <= 255)
    203      1.1  christos 	*color = ui_file_style::color (value);
    204      1.1  christos       else
    205      1.1  christos 	return false;
    206      1.1  christos     }
    207      1.1  christos   else if (value == 2)
    208      1.1  christos     {
    209      1.1  christos       /* 24-bit color.  */
    210      1.1  christos       long r, g, b;
    211      1.1  christos       if (!read_semi_number (str, idx, &r)
    212      1.1  christos 	  || r > 255
    213      1.1  christos 	  || !read_semi_number (str, idx, &g)
    214      1.1  christos 	  || g > 255
    215      1.1  christos 	  || !read_semi_number (str, idx, &b)
    216      1.1  christos 	  || b > 255)
    217      1.1  christos 	return false;
    218      1.1  christos       *color = ui_file_style::color (r, g, b);
    219      1.1  christos     }
    220      1.1  christos   else
    221      1.1  christos     {
    222      1.1  christos       /* Unrecognized sequence.  */
    223      1.1  christos       return false;
    224      1.1  christos     }
    225      1.1  christos 
    226      1.1  christos   return true;
    227      1.1  christos }
    228      1.1  christos 
    229      1.1  christos /* See ui-style.h.  */
    230      1.1  christos 
    231      1.1  christos bool
    232      1.1  christos ui_file_style::parse (const char *buf, size_t *n_read)
    233      1.1  christos {
    234      1.1  christos   regmatch_t subexps[NUM_SUBEXPRESSIONS];
    235      1.1  christos 
    236      1.1  christos   int match = regexec (&ansi_regex, buf, ARRAY_SIZE (subexps), subexps, 0);
    237      1.1  christos   if (match == REG_NOMATCH)
    238      1.1  christos     {
    239      1.1  christos       *n_read = 0;
    240      1.1  christos       return false;
    241      1.1  christos     }
    242      1.1  christos   /* Other failures mean the regexp is broken.  */
    243      1.1  christos   gdb_assert (match == 0);
    244      1.1  christos   /* The regexp is anchored.  */
    245      1.1  christos   gdb_assert (subexps[0].rm_so == 0);
    246      1.1  christos   /* The final character exists.  */
    247      1.1  christos   gdb_assert (subexps[FINAL_SUBEXP].rm_eo - subexps[FINAL_SUBEXP].rm_so == 1);
    248      1.1  christos 
    249      1.1  christos   if (buf[subexps[FINAL_SUBEXP].rm_so] != 'm')
    250      1.1  christos     {
    251      1.1  christos       /* We don't handle this sequence, so just drop it.  */
    252      1.1  christos       *n_read = subexps[0].rm_eo;
    253      1.1  christos       return false;
    254      1.1  christos     }
    255      1.1  christos 
    256      1.1  christos   /* Examine each setting in the match and apply it to the result.
    257      1.1  christos      See the Select Graphic Rendition section of
    258      1.1  christos      https://en.wikipedia.org/wiki/ANSI_escape_code.  In essence each
    259      1.1  christos      code is just a number, separated by ";"; there are some more
    260      1.1  christos      wrinkles but we don't support them all..  */
    261      1.1  christos 
    262      1.1  christos   /* "\033[m" means the same thing as "\033[0m", so handle that
    263      1.1  christos      specially here.  */
    264      1.1  christos   if (subexps[DATA_SUBEXP].rm_so == subexps[DATA_SUBEXP].rm_eo)
    265      1.1  christos     *this = ui_file_style ();
    266      1.1  christos 
    267      1.1  christos   for (regoff_t i = subexps[DATA_SUBEXP].rm_so;
    268      1.1  christos        i < subexps[DATA_SUBEXP].rm_eo;
    269      1.1  christos        ++i)
    270      1.1  christos     {
    271      1.1  christos       if (buf[i] == ';')
    272      1.1  christos 	{
    273      1.1  christos 	  /* Skip.  */
    274      1.1  christos 	}
    275      1.1  christos       else if (buf[i] >= '0' && buf[i] <= '9')
    276      1.1  christos 	{
    277      1.1  christos 	  char *tail;
    278      1.1  christos 	  long value = strtol (buf + i, &tail, 10);
    279      1.1  christos 	  i = tail - buf;
    280      1.1  christos 
    281      1.1  christos 	  switch (value)
    282      1.1  christos 	    {
    283      1.1  christos 	    case 0:
    284      1.1  christos 	      /* Reset.  */
    285      1.1  christos 	      *this = ui_file_style ();
    286      1.1  christos 	      break;
    287      1.1  christos 	    case 1:
    288      1.1  christos 	      /* Bold.  */
    289      1.1  christos 	      m_intensity = BOLD;
    290      1.1  christos 	      break;
    291      1.1  christos 	    case 2:
    292      1.1  christos 	      /* Dim.  */
    293      1.1  christos 	      m_intensity = DIM;
    294      1.1  christos 	      break;
    295      1.1  christos 	    case 7:
    296      1.1  christos 	      /* Reverse.  */
    297      1.1  christos 	      m_reverse = true;
    298      1.1  christos 	      break;
    299      1.1  christos 	    case 21:
    300      1.1  christos 	      m_intensity = NORMAL;
    301      1.1  christos 	      break;
    302      1.1  christos 	    case 22:
    303      1.1  christos 	      /* Normal.  */
    304      1.1  christos 	      m_intensity = NORMAL;
    305      1.1  christos 	      break;
    306      1.1  christos 	    case 27:
    307      1.1  christos 	      /* Inverse off.  */
    308      1.1  christos 	      m_reverse = false;
    309      1.1  christos 	      break;
    310      1.1  christos 
    311      1.1  christos 	    case 30:
    312      1.1  christos 	    case 31:
    313      1.1  christos 	    case 32:
    314      1.1  christos 	    case 33:
    315      1.1  christos 	    case 34:
    316      1.1  christos 	    case 35:
    317      1.1  christos 	    case 36:
    318      1.1  christos 	    case 37:
    319      1.1  christos 	      /* Note: not 38.  */
    320      1.1  christos 	    case 39:
    321      1.1  christos 	      m_foreground = color (value - 30);
    322      1.1  christos 	      break;
    323      1.1  christos 
    324      1.1  christos 	    case 40:
    325      1.1  christos 	    case 41:
    326      1.1  christos 	    case 42:
    327      1.1  christos 	    case 43:
    328      1.1  christos 	    case 44:
    329      1.1  christos 	    case 45:
    330      1.1  christos 	    case 46:
    331      1.1  christos 	    case 47:
    332      1.1  christos 	      /* Note: not 48.  */
    333      1.1  christos 	    case 49:
    334      1.1  christos 	      m_background = color (value - 40);
    335      1.1  christos 	      break;
    336      1.1  christos 
    337      1.1  christos 	    case 90:
    338      1.1  christos 	    case 91:
    339      1.1  christos 	    case 92:
    340      1.1  christos 	    case 93:
    341      1.1  christos 	    case 94:
    342      1.1  christos 	    case 95:
    343      1.1  christos 	    case 96:
    344      1.1  christos 	    case 97:
    345      1.1  christos 	      m_foreground = color (value - 90 + 8);
    346      1.1  christos 	      break;
    347      1.1  christos 
    348      1.1  christos 	    case 100:
    349      1.1  christos 	    case 101:
    350      1.1  christos 	    case 102:
    351      1.1  christos 	    case 103:
    352      1.1  christos 	    case 104:
    353      1.1  christos 	    case 105:
    354      1.1  christos 	    case 106:
    355      1.1  christos 	    case 107:
    356      1.1  christos 	      m_background = color (value - 100 + 8);
    357      1.1  christos 	      break;
    358      1.1  christos 
    359      1.1  christos 	    case 38:
    360      1.1  christos 	      /* If we can't parse the extended color, fail.  */
    361      1.1  christos 	      if (!extended_color (buf, &i, &m_foreground))
    362      1.1  christos 		{
    363      1.1  christos 		  *n_read = subexps[0].rm_eo;
    364      1.1  christos 		  return false;
    365      1.1  christos 		}
    366      1.1  christos 	      break;
    367      1.1  christos 
    368      1.1  christos 	    case 48:
    369      1.1  christos 	      /* If we can't parse the extended color, fail.  */
    370      1.1  christos 	      if (!extended_color (buf, &i, &m_background))
    371      1.1  christos 		{
    372      1.1  christos 		  *n_read = subexps[0].rm_eo;
    373      1.1  christos 		  return false;
    374      1.1  christos 		}
    375      1.1  christos 	      break;
    376      1.1  christos 
    377      1.1  christos 	    default:
    378      1.1  christos 	      /* Ignore everything else.  */
    379      1.1  christos 	      break;
    380      1.1  christos 	    }
    381      1.1  christos 	}
    382      1.1  christos       else
    383      1.1  christos 	{
    384      1.1  christos 	  /* Unknown, let's just ignore.  */
    385      1.1  christos 	}
    386      1.1  christos     }
    387      1.1  christos 
    388      1.1  christos   *n_read = subexps[0].rm_eo;
    389      1.1  christos   return true;
    390      1.1  christos }
    391      1.1  christos 
    392      1.1  christos /* See ui-style.h.  */
    393      1.1  christos 
    394      1.1  christos bool
    395      1.1  christos skip_ansi_escape (const char *buf, int *n_read)
    396      1.1  christos {
    397      1.1  christos   regmatch_t subexps[NUM_SUBEXPRESSIONS];
    398      1.1  christos 
    399      1.1  christos   int match = regexec (&ansi_regex, buf, ARRAY_SIZE (subexps), subexps, 0);
    400      1.1  christos   if (match == REG_NOMATCH || buf[subexps[FINAL_SUBEXP].rm_so] != 'm')
    401      1.1  christos     return false;
    402      1.1  christos 
    403      1.1  christos   *n_read = subexps[FINAL_SUBEXP].rm_eo;
    404      1.1  christos   return true;
    405      1.1  christos }
    406      1.1  christos 
    407  1.1.1.2  christos void _initialize_ui_style ();
    408      1.1  christos void
    409      1.1  christos _initialize_ui_style ()
    410      1.1  christos {
    411      1.1  christos   int code = regcomp (&ansi_regex, ansi_regex_text, REG_EXTENDED);
    412      1.1  christos   /* If the regular expression was incorrect, it was a programming
    413      1.1  christos      error.  */
    414      1.1  christos   gdb_assert (code == 0);
    415      1.1  christos }
    416