1 /* pk-cmd.c - terminal related stuff. */
2
3 /* Copyright (C) 2019, 2020, 2021 Jose E. Marchesi */
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
19 #include <config.h>
20
21 #include <stdlib.h> /* For exit. */
22 #include <assert.h> /* For assert. */
23 #include <string.h>
24 #include <unistd.h> /* For isatty */
25 #include <textstyle.h>
26 #include <assert.h>
27 #include <xalloc.h>
28
29 #include "poke.h"
30 #include "pk-utils.h"
31
32 /* The following global is the libtextstyle output stream to use to
33 emit contents to the terminal. */
34 static styled_ostream_t pk_ostream;
35
36 /* Stack of active classes. */
37
38 struct class_entry
39 {
40 char *class;
41 struct class_entry *next;
42 };
43
44 static struct class_entry *active_classes;
45
46 static void
push_active_class(const char * name)47 push_active_class (const char *name)
48 {
49 struct class_entry *new;
50
51 new = xmalloc (sizeof (struct class_entry));
52 new->class = xstrdup (name);
53 new->next = active_classes;
54 active_classes = new;
55 }
56
57 static int
pop_active_class(const char * name)58 pop_active_class (const char *name)
59 {
60 struct class_entry *tmp;
61
62 if (!active_classes || !STREQ (active_classes->class, name))
63 return 0;
64
65 tmp = active_classes;
66 active_classes = active_classes->next;
67 free (tmp->class);
68 free (tmp);
69 return 1;
70 }
71
72 /* Color registry.
73
74 The libtextstyle streams identify colors with integers, whose
75 particular values depend on the kind of the underlying physical
76 terminal and its capabilities. The pk term, however, identifies
77 colors using a triplet of beams signifying levels of red, green and
78 blue.
79
80 We therefore maintain a registry of colors that associate RGB
81 triplets with libtextstyle color codes. The libtextstyled output
82 stream provides a mechanism to translate from RGB codes to color
83 codes, but not the other way around (since it is really a 1-N
84 relationship.) */
85
86 static int default_color;
87 static int default_bgcolor;
88
89 struct color_entry
90 {
91 term_color_t code;
92 struct pk_color color;
93
94 struct color_entry *next;
95 };
96
97 static struct color_entry *color_registry;
98
99 static struct color_entry *
lookup_color(int red,int green,int blue)100 lookup_color (int red, int green, int blue)
101 {
102 struct color_entry *entry;
103
104 for (entry = color_registry; entry; entry = entry->next)
105 {
106 if (entry->color.red == red
107 && entry->color.green == green
108 && entry->color.blue == blue)
109 break;
110 }
111
112 return entry;
113 }
114
115 #if defined HAVE_TEXTSTYLE_ACCESSORS_SUPPORT
116
117 static struct color_entry *
lookup_color_code(int code)118 lookup_color_code (int code)
119 {
120 struct color_entry *entry;
121
122 for (entry = color_registry; entry; entry = entry->next)
123 {
124 if (entry->code == code)
125 break;
126 }
127
128 return entry;
129 }
130
131 static void
register_color(int code,int red,int green,int blue)132 register_color (int code, int red, int green, int blue)
133 {
134 struct color_entry *entry = lookup_color (red, green, blue);
135
136 if (entry)
137 entry->code = code;
138 else
139 {
140 entry = xmalloc (sizeof (struct color_entry));
141 entry->code = code;
142 entry->color.red = red;
143 entry->color.green = green;
144 entry->color.blue = blue;
145
146 entry->next = color_registry;
147 color_registry = entry;
148 }
149 }
150
151 #endif /* HAVE_TEXTSTYLE_ACCESSORS_SUPPORT */
152
153 static void
dispose_color_registry(void)154 dispose_color_registry (void)
155 {
156 struct color_entry *entry;
157
158 while (color_registry)
159 {
160 entry = color_registry->next;
161 free (color_registry);
162 color_registry = entry;
163 }
164 }
165
166 void
pk_term_init(int argc,char * argv[])167 pk_term_init (int argc, char *argv[])
168 {
169 int i;
170
171 /* Process terminal-related command-line options. */
172 for (i = 1; i < argc; i++)
173 {
174 const char *arg = argv[i];
175
176 if (strncmp (arg, "--color=", 8) == 0)
177 {
178 if (handle_color_option (arg + 8))
179 pk_fatal ("handle_color_option failed");
180 }
181 else if (strncmp (arg, "--style=", 8) == 0)
182 handle_style_option (arg + 8);
183 }
184
185 /* Handle the --color=test special argument. */
186 if (color_test_mode)
187 {
188 print_color_test ();
189 exit (EXIT_SUCCESS);
190 }
191
192 /* Note that the following code needs to be compiled conditionally
193 because the textstyle.h file provided by the gnulib module
194 libtextstyle-optional defines style_file_name as an r-value. */
195
196 #ifdef HAVE_LIBTEXTSTYLE
197 /* Open the specified style. */
198 if (color_mode == color_yes
199 || (color_mode == color_tty
200 && isatty (STDOUT_FILENO)
201 && getenv ("NO_COLOR") == NULL)
202 || color_mode == color_html)
203 {
204 /* Find the style file. */
205 style_file_prepare ("POKE_STYLE", "POKESTYLESDIR", PKGDATADIR,
206 "poke-default.css");
207 }
208 else
209 /* No styling. */
210 style_file_name = NULL;
211 #endif
212
213 /* Create the output styled stream. */
214 pk_ostream =
215 (color_mode == color_html
216 ? html_styled_ostream_create (file_ostream_create (stdout),
217 style_file_name)
218 : styled_ostream_create (STDOUT_FILENO, "(stdout)",
219 TTYCTL_AUTO, style_file_name));
220
221 /* Initialize the default colors and register them associated to the
222 RGB (-1,-1,-1). */
223 #if defined HAVE_TEXTSTYLE_ACCESSORS_SUPPORT
224 if (color_mode != color_html
225 && is_instance_of_term_styled_ostream (pk_ostream))
226 {
227 term_ostream_t term_ostream =
228 term_styled_ostream_get_destination ((term_styled_ostream_t) pk_ostream);
229
230 default_color = term_ostream_get_color (term_ostream);
231 default_bgcolor = term_ostream_get_bgcolor (term_ostream);
232
233 register_color (default_color, -1, -1, -1);
234 register_color (default_bgcolor, -1, -1, -1);
235 }
236 #endif
237 }
238
239 void
pk_term_shutdown()240 pk_term_shutdown ()
241 {
242 while (active_classes)
243 pop_active_class (active_classes->class);
244 dispose_color_registry ();
245 styled_ostream_free (pk_ostream);
246 }
247
248 void
pk_term_flush()249 pk_term_flush ()
250 {
251 ostream_flush (pk_ostream, FLUSH_THIS_STREAM);
252 }
253
254 void
pk_puts(const char * str)255 pk_puts (const char *str)
256 {
257 ostream_write_str (pk_ostream, str);
258 }
259
260 __attribute__ ((__format__ (__printf__, 1, 2)))
261 void
pk_printf(const char * format,...)262 pk_printf (const char *format, ...)
263 {
264 va_list ap;
265 char *str;
266 int r;
267
268 va_start (ap, format);
269 r = vasprintf (&str, format, ap);
270 assert (r != -1);
271 va_end (ap);
272
273 ostream_write_str (pk_ostream, str);
274 free (str);
275 }
276
277 void
pk_vprintf(const char * format,va_list ap)278 pk_vprintf (const char *format, va_list ap)
279 {
280 char *str;
281 int r;
282
283 r = vasprintf (&str, format, ap);
284 assert (r != -1);
285
286 ostream_write_str (pk_ostream, str);
287 free (str);
288 }
289
290
291 void
pk_term_indent(unsigned int lvl,unsigned int step)292 pk_term_indent (unsigned int lvl,
293 unsigned int step)
294 {
295 pk_printf ("\n%*s", (step * lvl), "");
296 }
297
298 void
pk_term_class(const char * class)299 pk_term_class (const char *class)
300 {
301 styled_ostream_begin_use_class (pk_ostream, class);
302 push_active_class (class);
303 }
304
305 int
pk_term_end_class(const char * class)306 pk_term_end_class (const char *class)
307 {
308 if (!pop_active_class (class))
309 return 0;
310
311 styled_ostream_end_use_class (pk_ostream, class);
312 return 1;
313 }
314
315 /* Counter of open hyperlinks. */
316 static int hlcount = 0;
317
318 void
pk_term_hyperlink(const char * url,const char * id)319 pk_term_hyperlink (const char *url, const char *id)
320 {
321 #ifdef HAVE_TEXTSTYLE_HYPERLINK_SUPPORT
322 styled_ostream_set_hyperlink (pk_ostream, url, id);
323 hlcount += 1;
324 #endif
325 }
326
327 int
pk_term_end_hyperlink(void)328 pk_term_end_hyperlink (void)
329 {
330 #ifdef HAVE_TEXTSTYLE_HYPERLINK_SUPPORT
331 if (hlcount == 0)
332 return 0;
333
334 styled_ostream_set_hyperlink (pk_ostream, NULL, NULL);
335 hlcount -= 1;
336 return 1;
337 #endif
338 }
339
340 int
pk_term_color_p(void)341 pk_term_color_p (void)
342 {
343 return (color_mode == color_yes
344 || (color_mode == color_tty
345 && isatty (STDOUT_FILENO)
346 && getenv ("NO_COLOR") == NULL));
347 }
348
349 struct pk_color
pk_term_get_color(void)350 pk_term_get_color (void)
351 {
352 #if defined HAVE_TEXTSTYLE_ACCESSORS_SUPPORT
353 if (color_mode != color_html
354 && is_instance_of_term_styled_ostream (pk_ostream))
355 {
356 term_ostream_t term_ostream =
357 term_styled_ostream_get_destination ((term_styled_ostream_t) pk_ostream);
358 struct color_entry *entry
359 = lookup_color_code (term_ostream_get_color (term_ostream));
360
361 assert (entry);
362 return entry->color;
363 }
364 else
365 #endif
366 {
367 struct pk_color dfl = {-1,-1,-1};
368 return dfl;
369 }
370 }
371
372 struct pk_color
pk_term_get_bgcolor()373 pk_term_get_bgcolor ()
374 {
375 #if defined HAVE_TEXTSTYLE_ACCESSORS_SUPPORT
376 if (color_mode != color_html
377 && is_instance_of_term_styled_ostream (pk_ostream))
378 {
379 term_ostream_t term_ostream =
380 term_styled_ostream_get_destination ((term_styled_ostream_t) pk_ostream);
381 struct color_entry *entry
382 = lookup_color_code (term_ostream_get_bgcolor (term_ostream));
383
384 assert (entry);
385 return entry->color;
386 }
387 else
388 #endif
389 {
390 struct pk_color dfl = {-1,-1,-1};
391 return dfl;
392 }
393 }
394
395 void
pk_term_set_color(struct pk_color color)396 pk_term_set_color (struct pk_color color)
397 {
398 #if defined HAVE_TEXTSTYLE_ACCESSORS_SUPPORT
399 if (color_mode != color_html)
400 {
401 if (is_instance_of_term_styled_ostream (pk_ostream))
402 {
403 term_ostream_t term_ostream =
404 term_styled_ostream_get_destination ((term_styled_ostream_t) pk_ostream);
405 term_color_t term_color;
406
407 if (color.red == -1 && color.green == -1 && color.blue == -1)
408 term_color = default_color;
409 else
410 {
411 term_color = term_ostream_rgb_to_color (term_ostream,
412 color.red, color.green, color.blue);
413 register_color (term_color,
414 color.red, color.green, color.blue);
415 }
416
417 term_ostream_set_color (term_ostream, term_color);
418 }
419 }
420 #endif
421 }
422
423 void
pk_term_set_bgcolor(struct pk_color color)424 pk_term_set_bgcolor (struct pk_color color)
425 {
426 #if defined HAVE_TEXTSTYLE_ACCESSORS_SUPPORT
427 if (color_mode != color_html)
428 {
429 if (is_instance_of_term_styled_ostream (pk_ostream))
430 {
431 term_ostream_t term_ostream =
432 term_styled_ostream_get_destination ((term_styled_ostream_t) pk_ostream);
433 term_color_t term_color;
434
435 if (color.red == -1 && color.green == -1 && color.blue == -1)
436 term_color = default_bgcolor;
437 else
438 {
439 term_color = term_ostream_rgb_to_color (term_ostream,
440 color.red, color.green, color.blue);
441
442 register_color (term_color,
443 color.red, color.green, color.blue);
444 }
445
446 term_ostream_set_bgcolor (term_ostream, term_color);
447 }
448 }
449 #endif
450 }
451