• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Color and styling handling.
2    Copyright (C) 2006-2008, 2019-2020 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
handle_color_option(const char * option)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
handle_style_option(const char * option)76 handle_style_option (const char *option)
77 {
78   style_file_name = option;
79 }
80 
81 /* Print a color test page.  */
82 void
print_color_test()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 }
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 *
style_file_lookup(const char * file_name,const char * stylesdir_after_install)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
style_file_prepare(const char * style_file_envvar,const char * stylesdir_envvar,const char * stylesdir_after_install,const char * default_style_file)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 }
450