1/* Color and styling handling.
2   Copyright (C) 2006-2007 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 <http://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 "relocatable.h"
34#include "filename.h"
35
36
37/* Whether to output a test page.  */
38bool color_test_mode;
39
40/* Color option.  */
41enum color_option color_mode = color_tty;
42
43/* Style to use when coloring.  */
44const char *style_file_name;
45
46/* --color argument handling.  Return an error indicator.  */
47bool
48handle_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.  */
75void
76handle_style_option (const char *option)
77{
78  style_file_name = option;
79}
80
81/* Print a color test page.  */
82void
83print_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");
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.  */
388static const char *
389style_file_lookup (const char *file_name)
390{
391  if (!IS_PATH_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	  const char *gettextstylesdir = relocate (GETTEXTDATADIR "/styles");
401	  char *possible_file_name =
402	    concatenated_filename (gettextstylesdir, file_name, NULL);
403
404	  if (stat (possible_file_name, &statbuf) >= 0)
405	    {
406	      /* ... then use the file in the styles installation directory.  */
407	      return possible_file_name;
408	    }
409	  free (possible_file_name);
410	}
411
412      /* Let the CSS library show a warning.  */
413    }
414  return file_name;
415}
416
417/* Assign a default value to style_file_name if necessary.  */
418void
419style_file_prepare ()
420{
421  if (style_file_name == NULL)
422    {
423      const char *user_preference = getenv ("PO_STYLE");
424
425      if (user_preference != NULL && user_preference[0] != '\0')
426	style_file_name = style_file_lookup (xstrdup (user_preference));
427      else
428	{
429	  const char *gettextdatadir;
430
431	  /* Make it possible to override the po-default.css location.  This is
432	     necessary for running the testsuite before "make install".  */
433	  gettextdatadir = getenv ("GETTEXTDATADIR");
434	  if (gettextdatadir == NULL || gettextdatadir[0] == '\0')
435	    gettextdatadir = relocate (GETTEXTDATADIR);
436
437	  style_file_name =
438	    concatenated_filename (gettextdatadir, "styles/po-default.css",
439				   NULL);
440	}
441    }
442  else
443    style_file_name = style_file_lookup (style_file_name);
444}
445