(root)/
gettext-0.22.4/
libtextstyle/
lib/
color.c
       1  /* Color and styling handling.
       2     Copyright (C) 2006-2008, 2019-2020, 2023 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2006.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation; either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #ifdef HAVE_CONFIG_H
      19  # include <config.h>
      20  #endif
      21  
      22  /* Specification.  */
      23  #include "color.h"
      24  
      25  #include <stdio.h>
      26  #include <stdlib.h>
      27  #include <string.h>
      28  #include <sys/types.h>
      29  #include <sys/stat.h>
      30  
      31  #include "term-ostream.h"
      32  #include "xalloc.h"
      33  #include "filename.h"
      34  #include "concat-filename.h"
      35  
      36  
      37  /* Whether to output a test page.  */
      38  bool color_test_mode;
      39  
      40  /* Color option.  */
      41  enum color_option color_mode = color_tty;
      42  
      43  /* Style to use when coloring.  */
      44  const char *style_file_name;
      45  
      46  /* --color argument handling.  Return an error indicator.  */
      47  bool
      48  handle_color_option (const char *option)
      49  {
      50    if (option != NULL)
      51      {
      52        if (strcmp (option, "never") == 0 || strcmp (option, "no") == 0)
      53          color_mode = color_no;
      54        else if (strcmp (option, "auto") == 0 || strcmp (option, "tty") == 0)
      55          color_mode = color_tty;
      56        else if (strcmp (option, "always") == 0 || strcmp (option, "yes") == 0)
      57          color_mode = color_yes;
      58        else if (strcmp (option, "html") == 0)
      59          color_mode = color_html;
      60        else if (strcmp (option, "test") == 0)
      61          color_test_mode = true;
      62        else
      63          {
      64            fprintf (stderr, "invalid --color argument: %s\n", option);
      65            return true;
      66          }
      67      }
      68    else
      69      /* --color is equivalent to --color=yes.  */
      70      color_mode = color_yes;
      71    return false;
      72  }
      73  
      74  /* --style argument handling.  */
      75  void
      76  handle_style_option (const char *option)
      77  {
      78    style_file_name = option;
      79  }
      80  
      81  /* Print a color test page.  */
      82  void
      83  print_color_test ()
      84  {
      85    /* Code copied from test-term-ostream.c.  */
      86    static struct { const char *name; term_color_t c; int r; int g; int b; }
      87           colors[] =
      88      {
      89        { "black",   -2,   0,   0,   0 },
      90        { "blue",    -2,   0,   0, 255 },
      91        { "green",   -2,   0, 255,   0 },
      92        { "cyan",    -2,   0, 255, 255 },
      93        { "red",     -2, 255,   0,   0 },
      94        { "magenta", -2, 255,   0, 255 },
      95        { "yellow",  -2, 255, 255,   0 },
      96        { "white",   -2, 255, 255, 255 },
      97        { "default", COLOR_DEFAULT, /* unused: */ -1, -1, -1 }
      98      };
      99    term_ostream_t stream;
     100    int i, row, col;
     101  
     102    stream = term_ostream_create (1, "stdout", TTYCTL_AUTO);
     103  
     104    for (i = 0; i < 8; i++)
     105      colors[i].c =
     106        term_ostream_rgb_to_color (stream, colors[i].r, colors[i].g, colors[i].b);
     107  
     108    ostream_write_str (stream, "Colors (foreground/background):\n");
     109    ostream_write_str (stream, "       ");
     110    for (col = 0; col <= 8; col++)
     111      {
     112        const char *name = colors[col].name;
     113        ostream_write_str (stream, "|");
     114        ostream_write_str (stream, name);
     115        ostream_write_mem (stream, "        ", 7 - strlen (name));
     116      }
     117    ostream_write_str (stream, "\n");
     118    for (row = 0; row <= 8; row++)
     119      {
     120        const char *name = colors[row].name;
     121        ostream_write_str (stream, name);
     122        ostream_write_mem (stream, "        ", 7 - strlen (name));
     123        for (col = 0; col <= 8; col++)
     124          {
     125            term_color_t row_color = colors[row].c;
     126            term_color_t col_color = colors[col].c;
     127  
     128            ostream_write_str (stream, "|");
     129            term_ostream_set_color (stream, row_color);
     130            term_ostream_set_bgcolor (stream, col_color);
     131            if (!(term_ostream_get_color (stream) == row_color
     132                  && term_ostream_get_bgcolor (stream) == col_color))
     133              abort ();
     134            ostream_write_str (stream, " Words ");
     135            term_ostream_set_color (stream, COLOR_DEFAULT);
     136            term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
     137            if (!(term_ostream_get_color (stream) == COLOR_DEFAULT
     138                  && term_ostream_get_bgcolor (stream) == COLOR_DEFAULT))
     139              abort ();
     140          }
     141        ostream_write_str (stream, "\n");
     142      }
     143    ostream_write_str (stream, "\n");
     144  
     145    ostream_write_str (stream, "Colors (hue/saturation):\n");
     146    /* Hue from 0 to 1.  */
     147    for (row = 0; row <= 17; row++)
     148      {
     149        ostream_write_str (stream, row == 0 ? "red:     " : "         ");
     150        for (col = 0; col <= 64; col++)
     151          {
     152            int r = 255;
     153            int b = (int) (255.0f / 64.0f * col + 0.5f);
     154            int g = b + (int) (row / 17.0f * (r - b) + 0.5f);
     155            term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
     156            term_ostream_set_bgcolor (stream, c);
     157            ostream_write_str (stream, " ");
     158            term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
     159          }
     160        ostream_write_str (stream, "\n");
     161      }
     162    /* Hue from 1 to 2.  */
     163    for (row = 17; row >= 0; row--)
     164      {
     165        ostream_write_str (stream, row == 17 ? "yellow:  " : "         ");
     166        for (col = 0; col <= 64; col++)
     167          {
     168            int g = 255;
     169            int b = (int) (255.0f / 64.0f * col + 0.5f);
     170            int r = b + (int) (row / 17.0f * (g - b) + 0.5f);
     171            term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
     172            term_ostream_set_bgcolor (stream, c);
     173            ostream_write_str (stream, " ");
     174            term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
     175          }
     176        ostream_write_str (stream, "\n");
     177      }
     178    /* Hue from 2 to 3.  */
     179    for (row = 0; row <= 17; row++)
     180      {
     181        ostream_write_str (stream, row == 0 ? "green:   " : "         ");
     182        for (col = 0; col <= 64; col++)
     183          {
     184            int g = 255;
     185            int r = (int) (255.0f / 64.0f * col + 0.5f);
     186            int b = r + (int) (row / 17.0f * (g - r) + 0.5f);
     187            term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
     188            term_ostream_set_bgcolor (stream, c);
     189            ostream_write_str (stream, " ");
     190            term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
     191          }
     192        ostream_write_str (stream, "\n");
     193      }
     194    /* Hue from 3 to 4.  */
     195    for (row = 17; row >= 0; row--)
     196      {
     197        ostream_write_str (stream, row == 17 ? "cyan:    " : "         ");
     198        for (col = 0; col <= 64; col++)
     199          {
     200            int b = 255;
     201            int r = (int) (255.0f / 64.0f * col + 0.5f);
     202            int g = r + (int) (row / 17.0f * (b - r) + 0.5f);
     203            term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
     204            term_ostream_set_bgcolor (stream, c);
     205            ostream_write_str (stream, " ");
     206            term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
     207          }
     208        ostream_write_str (stream, "\n");
     209      }
     210    /* Hue from 4 to 5.  */
     211    for (row = 0; row <= 17; row++)
     212      {
     213        ostream_write_str (stream, row == 0 ? "blue:    " : "         ");
     214        for (col = 0; col <= 64; col++)
     215          {
     216            int b = 255;
     217            int g = (int) (255.0f / 64.0f * col + 0.5f);
     218            int r = g + (int) (row / 17.0f * (b - g) + 0.5f);
     219            term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
     220            term_ostream_set_bgcolor (stream, c);
     221            ostream_write_str (stream, " ");
     222            term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
     223          }
     224        ostream_write_str (stream, "\n");
     225      }
     226    /* Hue from 5 to 6.  */
     227    for (row = 17; row >= 0; row--)
     228      {
     229        ostream_write_str (stream, row == 17 ? "magenta: " :
     230                                   row == 0 ? "red:     " : "         ");
     231        for (col = 0; col <= 64; col++)
     232          {
     233            int r = 255;
     234            int g = (int) (255.0f / 64.0f * col + 0.5f);
     235            int b = g + (int) (row / 17.0f * (r - g) + 0.5f);
     236            term_color_t c = term_ostream_rgb_to_color (stream, r, g, b);
     237            term_ostream_set_bgcolor (stream, c);
     238            ostream_write_str (stream, " ");
     239            term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
     240          }
     241        ostream_write_str (stream, "\n");
     242      }
     243    ostream_write_str (stream, "\n");
     244  
     245    ostream_write_str (stream, "Weights:\n");
     246    term_ostream_set_weight (stream, WEIGHT_NORMAL);
     247    if (term_ostream_get_weight (stream) != WEIGHT_NORMAL)
     248      abort ();
     249    ostream_write_str (stream, "normal, ");
     250    term_ostream_set_weight (stream, WEIGHT_BOLD);
     251    if (term_ostream_get_weight (stream) != WEIGHT_BOLD)
     252      abort ();
     253    ostream_write_str (stream, "bold, ");
     254    term_ostream_set_weight (stream, WEIGHT_DEFAULT);
     255    if (term_ostream_get_weight (stream) != WEIGHT_DEFAULT)
     256      abort ();
     257    ostream_write_str (stream, "default \n");
     258    ostream_write_str (stream, "\n");
     259  
     260    ostream_write_str (stream, "Postures:\n");
     261    term_ostream_set_posture (stream, POSTURE_NORMAL);
     262    if (term_ostream_get_posture (stream) != POSTURE_NORMAL)
     263      abort ();
     264    ostream_write_str (stream, "normal, ");
     265    term_ostream_set_posture (stream, POSTURE_ITALIC);
     266    if (term_ostream_get_posture (stream) != POSTURE_ITALIC)
     267      abort ();
     268    ostream_write_str (stream, "italic, ");
     269    term_ostream_set_posture (stream, POSTURE_DEFAULT);
     270    if (term_ostream_get_posture (stream) != POSTURE_DEFAULT)
     271      abort ();
     272    ostream_write_str (stream, "default \n");
     273    ostream_write_str (stream, "\n");
     274  
     275    ostream_write_str (stream, "Text decorations:\n");
     276    term_ostream_set_underline (stream, UNDERLINE_OFF);
     277    if (term_ostream_get_underline (stream) != UNDERLINE_OFF)
     278      abort ();
     279    ostream_write_str (stream, "normal, ");
     280    term_ostream_set_underline (stream, UNDERLINE_ON);
     281    if (term_ostream_get_underline (stream) != UNDERLINE_ON)
     282      abort ();
     283    ostream_write_str (stream, "underlined, ");
     284    term_ostream_set_underline (stream, UNDERLINE_DEFAULT);
     285    if (term_ostream_get_underline (stream) != UNDERLINE_DEFAULT)
     286      abort ();
     287    ostream_write_str (stream, "default \n");
     288    ostream_write_str (stream, "\n");
     289  
     290    ostream_write_str (stream, "Colors (foreground) mixed with attributes:\n");
     291    for (row = 0; row <= 8; row++)
     292      {
     293        const char *name = colors[row].name;
     294        ostream_write_str (stream, name);
     295        ostream_write_mem (stream, "        ", 7 - strlen (name));
     296        term_ostream_set_color (stream, colors[row].c);
     297        ostream_write_str (stream, "|normal|");
     298        term_ostream_set_weight (stream, WEIGHT_BOLD);
     299        ostream_write_str (stream, "bold");
     300        term_ostream_set_weight (stream, WEIGHT_NORMAL);
     301        ostream_write_str (stream, "|normal|");
     302        term_ostream_set_posture (stream, POSTURE_ITALIC);
     303        ostream_write_str (stream, "italic");
     304        term_ostream_set_posture (stream, POSTURE_NORMAL);
     305        ostream_write_str (stream, "|normal|");
     306        term_ostream_set_underline (stream, UNDERLINE_ON);
     307        ostream_write_str (stream, "underlined");
     308        term_ostream_set_underline (stream, UNDERLINE_OFF);
     309        ostream_write_str (stream, "|normal|");
     310        term_ostream_set_color (stream, COLOR_DEFAULT);
     311        ostream_write_str (stream, "\n       ");
     312        term_ostream_set_color (stream, colors[row].c);
     313        ostream_write_str (stream, "|normal|");
     314        term_ostream_set_weight (stream, WEIGHT_BOLD);
     315        term_ostream_set_posture (stream, POSTURE_ITALIC);
     316        ostream_write_str (stream, "bold+italic");
     317        term_ostream_set_weight (stream, WEIGHT_NORMAL);
     318        term_ostream_set_posture (stream, POSTURE_NORMAL);
     319        ostream_write_str (stream, "|normal|");
     320        term_ostream_set_weight (stream, WEIGHT_BOLD);
     321        term_ostream_set_underline (stream, UNDERLINE_ON);
     322        ostream_write_str (stream, "bold+underl");
     323        term_ostream_set_weight (stream, WEIGHT_NORMAL);
     324        term_ostream_set_underline (stream, UNDERLINE_OFF);
     325        ostream_write_str (stream, "|normal|");
     326        term_ostream_set_posture (stream, POSTURE_ITALIC);
     327        term_ostream_set_underline (stream, UNDERLINE_ON);
     328        ostream_write_str (stream, "italic+underl");
     329        term_ostream_set_posture (stream, POSTURE_NORMAL);
     330        term_ostream_set_underline (stream, UNDERLINE_OFF);
     331        ostream_write_str (stream, "|normal|");
     332        term_ostream_set_color (stream, COLOR_DEFAULT);
     333        ostream_write_str (stream, "\n");
     334      }
     335    ostream_write_str (stream, "\n");
     336  
     337    ostream_write_str (stream, "Colors (background) mixed with attributes:\n");
     338    for (row = 0; row <= 8; row++)
     339      {
     340        const char *name = colors[row].name;
     341        ostream_write_str (stream, name);
     342        ostream_write_mem (stream, "        ", 7 - strlen (name));
     343        term_ostream_set_bgcolor (stream, colors[row].c);
     344        ostream_write_str (stream, "|normal|");
     345        term_ostream_set_weight (stream, WEIGHT_BOLD);
     346        ostream_write_str (stream, "bold");
     347        term_ostream_set_weight (stream, WEIGHT_NORMAL);
     348        ostream_write_str (stream, "|normal|");
     349        term_ostream_set_posture (stream, POSTURE_ITALIC);
     350        ostream_write_str (stream, "italic");
     351        term_ostream_set_posture (stream, POSTURE_NORMAL);
     352        ostream_write_str (stream, "|normal|");
     353        term_ostream_set_underline (stream, UNDERLINE_ON);
     354        ostream_write_str (stream, "underlined");
     355        term_ostream_set_underline (stream, UNDERLINE_OFF);
     356        ostream_write_str (stream, "|normal|");
     357        term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
     358        ostream_write_str (stream, "\n       ");
     359        term_ostream_set_bgcolor (stream, colors[row].c);
     360        ostream_write_str (stream, "|normal|");
     361        term_ostream_set_weight (stream, WEIGHT_BOLD);
     362        term_ostream_set_posture (stream, POSTURE_ITALIC);
     363        ostream_write_str (stream, "bold+italic");
     364        term_ostream_set_weight (stream, WEIGHT_NORMAL);
     365        term_ostream_set_posture (stream, POSTURE_NORMAL);
     366        ostream_write_str (stream, "|normal|");
     367        term_ostream_set_weight (stream, WEIGHT_BOLD);
     368        term_ostream_set_underline (stream, UNDERLINE_ON);
     369        ostream_write_str (stream, "bold+underl");
     370        term_ostream_set_weight (stream, WEIGHT_NORMAL);
     371        term_ostream_set_underline (stream, UNDERLINE_OFF);
     372        ostream_write_str (stream, "|normal|");
     373        term_ostream_set_posture (stream, POSTURE_ITALIC);
     374        term_ostream_set_underline (stream, UNDERLINE_ON);
     375        ostream_write_str (stream, "italic+underl");
     376        term_ostream_set_posture (stream, POSTURE_NORMAL);
     377        term_ostream_set_underline (stream, UNDERLINE_OFF);
     378        ostream_write_str (stream, "|normal|");
     379        term_ostream_set_bgcolor (stream, COLOR_DEFAULT);
     380        ostream_write_str (stream, "\n");
     381      }
     382    ostream_write_str (stream, "\n");
     383  
     384    ostream_free (stream);
     385  }
     386  
     387  /* Lookup the location of the style file.  */
     388  static const char *
     389  style_file_lookup (const char *file_name, const char *stylesdir_after_install)
     390  {
     391    if (!IS_FILE_NAME_WITH_DIR (file_name))
     392      {
     393        /* It's a file name without a directory specification.
     394           If it does not exist in the current directory...  */
     395        struct stat statbuf;
     396  
     397        if (stat (file_name, &statbuf) < 0)
     398          {
     399            /* ... but it exists in the styles installation location...  */
     400            char *possible_file_name =
     401              xconcatenated_filename (stylesdir_after_install, file_name, NULL);
     402  
     403            if (stat (possible_file_name, &statbuf) >= 0)
     404              {
     405                /* ... then use the file in the styles installation directory.  */
     406                return possible_file_name;
     407              }
     408            free (possible_file_name);
     409          }
     410  
     411        /* Let the CSS library show a warning.  */
     412      }
     413    return file_name;
     414  }
     415  
     416  /* Assign a default value to style_file_name if necessary.  */
     417  void
     418  style_file_prepare (const char *style_file_envvar,
     419                      const char *stylesdir_envvar,
     420                      const char *stylesdir_after_install,
     421                      const char *default_style_file)
     422  {
     423    if (style_file_name == NULL)
     424      {
     425        const char *user_preference = getenv (style_file_envvar);
     426  
     427        if (user_preference != NULL && user_preference[0] != '\0')
     428          style_file_name =
     429            style_file_lookup (xstrdup (user_preference),
     430                               stylesdir_after_install);
     431        else
     432          {
     433            const char *stylesdir;
     434  
     435            /* Make it possible to override the default style file location.  This
     436               is necessary for running the testsuite before "make install".  */
     437            stylesdir = getenv (stylesdir_envvar);
     438            if (stylesdir == NULL || stylesdir[0] == '\0')
     439              stylesdir = stylesdir_after_install;
     440  
     441            style_file_name =
     442              xconcatenated_filename (stylesdir, default_style_file,
     443                                     NULL);
     444          }
     445      }
     446    else
     447      style_file_name =
     448        style_file_lookup (style_file_name, stylesdir_after_install);
     449  }