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