]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/colors.c
kill: add missing ifdefs
[thirdparty/util-linux.git] / lib / colors.c
CommitLineData
dfa68ad1
OO
1/*
2 * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
7a4704d8 3 * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
dfa68ad1
OO
4 *
5 * This file may be distributed under the terms of the
6 * GNU Lesser General Public License.
7 */
a10c0434 8#include <assert.h>
d0c9ddc3 9#include <sys/stat.h>
570b3210
KZ
10#include <sys/types.h>
11#include <dirent.h>
7a4704d8 12#include <ctype.h>
23d47267
KZ
13
14#if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW)
3947ca4c
KZ
15# if defined(HAVE_NCURSESW_NCURSES_H)
16# include <ncursesw/ncurses.h>
17# elif defined(HAVE_NCURSES_NCURSES_H)
18# include <ncurses/ncurses.h>
19# elif defined(HAVE_NCURSES_H)
20# include <ncurses.h>
21# endif
22# if defined(HAVE_NCURSESW_TERM_H)
23# include <ncursesw/term.h>
23d47267
KZ
24# elif defined(HAVE_NCURSES_TERM_H)
25# include <ncurses/term.h>
3947ca4c
KZ
26# elif defined(HAVE_TERM_H)
27# include <term.h>
23d47267 28# endif
4310faf9 29#endif
dfa68ad1 30
51dfd171 31#include "c.h"
dfa68ad1 32#include "colors.h"
d0c9ddc3 33#include "pathnames.h"
7a4704d8 34#include "strutils.h"
dfa68ad1 35
b73cc390
KZ
36#include "debug.h"
37
81f55ab9 38/*
218b1dd6 39 * Default behavior, may be overridden by terminal-colors.d/{enable,disable}.
81f55ab9
KZ
40 */
41#ifdef USE_COLORS_BY_DEFAULT
42# define UL_COLORMODE_DEFAULT UL_COLORMODE_AUTO /* check isatty() */
43#else
44# define UL_COLORMODE_DEFAULT UL_COLORMODE_NEVER /* no colors by default */
45#endif
46
b73cc390
KZ
47/*
48 * terminal-colors.d debug stuff
49 */
2ba641e5 50static UL_DEBUG_DEFINE_MASK(termcolors);
819d9a29 51UL_DEBUG_DEFINE_MASKNAMES(termcolors) = UL_DEBUG_EMPTY_MASKNAMES;
b73cc390
KZ
52
53#define TERMCOLORS_DEBUG_INIT (1 << 1)
54#define TERMCOLORS_DEBUG_CONF (1 << 2)
55#define TERMCOLORS_DEBUG_SCHEME (1 << 3)
56#define TERMCOLORS_DEBUG_ALL 0xFFFF
57
58#define DBG(m, x) __UL_DBG(termcolors, TERMCOLORS_DEBUG_, m, x)
59#define ON_DBG(m, x) __UL_DBG_CALL(termcolors, TERMCOLORS_DEBUG_, m, x)
60
e66a6627
KZ
61/*
62 * terminal-colors.d file types
63 */
570b3210
KZ
64enum {
65 UL_COLORFILE_DISABLE, /* .disable */
66 UL_COLORFILE_ENABLE, /* .enable */
67 UL_COLORFILE_SCHEME, /* .scheme */
68
69 __UL_COLORFILE_COUNT
70};
71
7a4704d8
KZ
72struct ul_color_scheme {
73 char *name;
74 char *seq;
75};
76
e66a6627
KZ
77/*
78 * Global colors control struct
79 *
80 * The terminal-colors.d/ evaluation is based on "scores":
81 *
82 * filename score
83 * ---------------------------------------
84 * type 1
85 * @termname.type 10 + 1
86 * utilname.type 20 + 1
87 * utilname@termname.type 20 + 10 + 1
88 *
89 * the match with higher score wins. The score is per type.
90 */
570b3210
KZ
91struct ul_color_ctl {
92 const char *utilname; /* util name */
93 const char *termname; /* terminal name ($TERM) */
94
7a4704d8
KZ
95 char *sfile; /* path to scheme */
96
97 struct ul_color_scheme *schemes; /* array with color schemes */
98 size_t nschemes; /* number of the items */
99 size_t schemes_sz; /* number of the allocated items */
570b3210
KZ
100
101 int mode; /* UL_COLORMODE_* */
e66a6627
KZ
102 unsigned int has_colors : 1, /* based on mode and scores[] */
103 disabled : 1, /* disable colors */
7a4704d8 104 cs_configured : 1, /* color schemes read */
e66a6627
KZ
105 configured : 1; /* terminal-colors.d parsed */
106
107 int scores[__UL_COLORFILE_COUNT]; /* the best match */
570b3210
KZ
108};
109
b8731ebc
KZ
110/*
111 * Control struct, globally shared.
112 */
570b3210
KZ
113static struct ul_color_ctl ul_colors;
114
7a4704d8
KZ
115static void colors_free_schemes(struct ul_color_ctl *cc);
116static int colors_read_schemes(struct ul_color_ctl *cc);
117
118/*
119 * qsort/bsearch buddy
120 */
121static int cmp_scheme_name(const void *a0, const void *b0)
122{
892fc7d9
KZ
123 const struct ul_color_scheme *a = (const struct ul_color_scheme *) a0,
124 *b = (const struct ul_color_scheme *) b0;
7a4704d8
KZ
125 return strcmp(a->name, b->name);
126}
127
e66a6627 128/*
7a4704d8 129 * Resets control struct (note that we don't allocate the struct)
e66a6627 130 */
570b3210
KZ
131static void colors_reset(struct ul_color_ctl *cc)
132{
133 if (!cc)
134 return;
135
7a4704d8
KZ
136 colors_free_schemes(cc);
137
138 free(cc->sfile);
570b3210 139
7a4704d8 140 cc->sfile = NULL;
570b3210
KZ
141 cc->utilname = NULL;
142 cc->termname = NULL;
143 cc->mode = UL_COLORMODE_UNDEF;
144
145 memset(cc->scores, 0, sizeof(cc->scores));
146}
147
570b3210
KZ
148static void colors_debug(struct ul_color_ctl *cc)
149{
b73cc390 150 size_t i;
570b3210
KZ
151
152 if (!cc)
153 return;
154
155 printf("Colors:\n");
156 printf("\tutilname = '%s'\n", cc->utilname);
157 printf("\ttermname = '%s'\n", cc->termname);
7a4704d8 158 printf("\tscheme file = '%s'\n", cc->sfile);
570b3210
KZ
159 printf("\tmode = %s\n",
160 cc->mode == UL_COLORMODE_UNDEF ? "undefined" :
161 cc->mode == UL_COLORMODE_AUTO ? "auto" :
162 cc->mode == UL_COLORMODE_NEVER ? "never" :
163 cc->mode == UL_COLORMODE_ALWAYS ? "always" : "???");
7a4704d8
KZ
164 printf("\thas_colors = %d\n", cc->has_colors);
165 printf("\tdisabled = %d\n", cc->disabled);
166 printf("\tconfigured = %d\n", cc->configured);
167 printf("\tcs configured = %d\n", cc->cs_configured);
168
169 fputc('\n', stdout);
570b3210
KZ
170
171 for (i = 0; i < ARRAY_SIZE(cc->scores); i++)
172 printf("\tscore %s = %d\n",
173 i == UL_COLORFILE_DISABLE ? "disable" :
174 i == UL_COLORFILE_ENABLE ? "enable" :
175 i == UL_COLORFILE_SCHEME ? "scheme" : "???",
176 cc->scores[i]);
7a4704d8
KZ
177
178 fputc('\n', stdout);
179
180 for (i = 0; i < cc->nschemes; i++) {
b73cc390 181 printf("\tscheme #%02zu ", i);
7a4704d8
KZ
182 color_scheme_enable(cc->schemes[i].name, NULL);
183 fputs(cc->schemes[i].name, stdout);
184 color_disable();
185 fputc('\n', stdout);
186 }
187 fputc('\n', stdout);
570b3210 188}
570b3210 189
e66a6627
KZ
190/*
191 * Parses [[<utilname>][@<termname>].]<type>
192 */
570b3210
KZ
193static int filename_to_tokens(const char *str,
194 const char **name, size_t *namesz,
195 const char **term, size_t *termsz,
196 int *filetype)
197{
198 const char *type_start, *term_start, *p;
199
200 if (!str || !*str || *str == '.' || strlen(str) > PATH_MAX)
201 return -EINVAL;
202
203 /* parse .type */
204 p = strrchr(str, '.');
205 type_start = p ? p + 1 : str;
206
207 if (strcmp(type_start, "disable") == 0)
208 *filetype = UL_COLORFILE_DISABLE;
209 else if (strcmp(type_start, "enable") == 0)
210 *filetype = UL_COLORFILE_ENABLE;
211 else if (strcmp(type_start, "scheme") == 0)
212 *filetype = UL_COLORFILE_SCHEME;
b73cc390
KZ
213 else {
214 DBG(CONF, ul_debug("unknown type '%s'", type_start));
570b3210 215 return 1; /* unknown type */
b73cc390 216 }
570b3210
KZ
217
218 if (type_start == str)
219 return 0; /* "type" only */
220
221 /* parse @termname */
222 p = strchr(str, '@');
223 term_start = p ? p + 1 : NULL;
224 if (term_start) {
225 *term = term_start;
226 *termsz = type_start - term_start - 1;
227 if (term_start - 1 == str)
228 return 0; /* "@termname.type" */
229 }
230
231 /* parse utilname */
232 p = term_start ? term_start : type_start;
233 *name = str;
234 *namesz = p - str - 1;
235
236 return 0;
237}
238
570b3210 239/*
e66a6627
KZ
240 * Scans @dirname and select the best matches for UL_COLORFILE_* types.
241 * The result is stored to cc->scores. The path to the best "scheme"
242 * file is stored to cc->scheme.
570b3210
KZ
243 */
244static int colors_readdir(struct ul_color_ctl *cc, const char *dirname)
245{
246 DIR *dir;
247 int rc = 0;
248 struct dirent *d;
7a4704d8 249 char sfile[PATH_MAX] = { '\0' };
570b3210
KZ
250 size_t namesz, termsz;
251
252 if (!dirname || !cc || !cc->utilname || !*cc->utilname)
253 return -EINVAL;
b73cc390
KZ
254
255 DBG(CONF, ul_debug("reading dir: '%s'", dirname));
256
570b3210
KZ
257 dir = opendir(dirname);
258 if (!dir)
259 return -errno;
260
261 namesz = strlen(cc->utilname);
262 termsz = cc->termname ? strlen(cc->termname) : 0;
263
264 while ((d = readdir(dir))) {
265 int type, score = 1;
266 const char *tk_name = NULL, *tk_term = NULL;
267 size_t tk_namesz = 0, tk_termsz = 0;
268
269 if (*d->d_name == '.')
270 continue;
271#ifdef _DIRENT_HAVE_D_TYPE
272 if (d->d_type != DT_UNKNOWN && d->d_type != DT_LNK &&
273 d->d_type != DT_REG)
274 continue;
275#endif
276 if (filename_to_tokens(d->d_name,
277 &tk_name, &tk_namesz,
278 &tk_term, &tk_termsz, &type) != 0)
279 continue;
280
9e930041 281 /* count theoretical score before we check names to avoid
570b3210
KZ
282 * unnecessary strcmp() */
283 if (tk_name)
284 score += 20;
285 if (tk_term)
286 score += 10;
7a4704d8 287
b73cc390
KZ
288 DBG(CONF, ul_debug("item '%s': score=%d "
289 "[cur: %d, name(%zu): %s, term(%zu): %s]",
290 d->d_name, score, cc->scores[type],
291 tk_namesz, tk_name,
292 tk_termsz, tk_term));
293
7a4704d8 294
570b3210
KZ
295 if (score < cc->scores[type])
296 continue;
297
298 /* filter out by names */
eb728f96
KZ
299 if (tk_namesz && (tk_namesz != namesz ||
300 strncmp(tk_name, cc->utilname, namesz) != 0))
570b3210 301 continue;
7a4704d8
KZ
302
303 if (tk_termsz && (termsz == 0 || tk_termsz != termsz ||
304 strncmp(tk_term, cc->termname, termsz) != 0))
570b3210
KZ
305 continue;
306
b73cc390
KZ
307 DBG(CONF, ul_debug("setting '%s' from %d -to-> %d",
308 type == UL_COLORFILE_SCHEME ? "scheme" :
309 type == UL_COLORFILE_DISABLE ? "disable" :
310 type == UL_COLORFILE_ENABLE ? "enable" : "???",
311 cc->scores[type], score));
570b3210
KZ
312 cc->scores[type] = score;
313 if (type == UL_COLORFILE_SCHEME)
7a4704d8 314 strncpy(sfile, d->d_name, sizeof(sfile));
570b3210
KZ
315 }
316
7a4704d8
KZ
317 if (*sfile) {
318 sfile[sizeof(sfile) - 1] = '\0';
319 if (asprintf(&cc->sfile, "%s/%s", dirname, sfile) <= 0)
570b3210
KZ
320 rc = -ENOMEM;
321 }
322
323 closedir(dir);
324 return rc;
325}
326
e66a6627 327/* atexit() wrapper */
570b3210
KZ
328static void colors_deinit(void)
329{
330 colors_reset(&ul_colors);
331}
332
e66a6627
KZ
333/*
334 * Returns path to $XDG_CONFIG_HOME/terminal-colors.d
335 */
570b3210
KZ
336static char *colors_get_homedir(char *buf, size_t bufsz)
337{
338 char *p = getenv("XDG_CONFIG_HOME");
339
340 if (p) {
341 snprintf(buf, bufsz, "%s/" _PATH_TERMCOLORS_DIRNAME, p);
342 return buf;
343 }
344
345 p = getenv("HOME");
346 if (p) {
347 snprintf(buf, bufsz, "%s/.config/" _PATH_TERMCOLORS_DIRNAME, p);
348 return buf;
349 }
350
351 return NULL;
352}
dfa68ad1 353
7a4704d8
KZ
354/* canonicalize sequence */
355static int cn_sequence(const char *str, char **seq)
356{
357 char *in, *out;
ab709377 358 int len;
7a4704d8
KZ
359
360 if (!str)
361 return -EINVAL;
362
363 *seq = NULL;
364
365 /* convert logical names like "red" to the real sequence */
366 if (*str != '\\' && isalpha(*str)) {
367 const char *s = color_sequence_from_colorname(str);
368 *seq = strdup(s ? s : str);
369
370 return *seq ? 0 : -ENOMEM;
371 }
372
373 /* convert xx;yy sequences to "\033[xx;yy" */
ab709377 374 if ((len = asprintf(seq, "\033[%sm", str)) < 1)
7a4704d8
KZ
375 return -ENOMEM;
376
377 for (in = *seq, out = *seq; in && *in; in++) {
378 if (*in != '\\') {
379 *out++ = *in;
380 continue;
381 }
382 switch(*(in + 1)) {
383 case 'a':
384 *out++ = '\a'; /* Bell */
385 break;
386 case 'b':
387 *out++ = '\b'; /* Backspace */
388 break;
389 case 'e':
390 *out++ = '\033'; /* Escape */
391 break;
392 case 'f':
393 *out++ = '\f'; /* Form Feed */
394 break;
395 case 'n':
396 *out++ = '\n'; /* Newline */
397 break;
398 case 'r':
399 *out++ = '\r'; /* Carriage Return */
400 break;
401 case 't':
402 *out++ = '\t'; /* Tab */
403 break;
404 case 'v':
405 *out++ = '\v'; /* Vertical Tab */
406 break;
407 case '\\':
408 *out++ = '\\'; /* Backslash */
409 break;
410 case '_':
411 *out++ = ' '; /* Space */
412 break;
413 case '#':
414 *out++ = '#'; /* Hash mark */
415 break;
416 case '?':
9e930041 417 *out++ = '?'; /* Question mark */
7a4704d8
KZ
418 break;
419 default:
420 *out++ = *in;
421 *out++ = *(in + 1);
422 break;
423 }
424 in++;
425 }
ab709377 426
dcf6f5b3
KZ
427 if (out) {
428 assert ((out - *seq) <= len);
429 *out = '\0';
430 }
7a4704d8
KZ
431
432 return 0;
433}
434
435
436/*
a72de3cf
AH
437 * Adds one color sequence to array with color scheme.
438 * When returning success (0) this function takes ownership of
439 * @seq and @name, which have to be allocated strings.
7a4704d8
KZ
440 */
441static int colors_add_scheme(struct ul_color_ctl *cc,
442 char *name,
443 char *seq0)
444{
6508db29
KZ
445 struct ul_color_scheme *cs = NULL;
446 char *seq = NULL;
7a4704d8
KZ
447 int rc;
448
449 if (!cc || !name || !*name || !seq0 || !*seq0)
450 return -EINVAL;
451
b73cc390
KZ
452 DBG(SCHEME, ul_debug("add '%s'", name));
453
7a4704d8
KZ
454 rc = cn_sequence(seq0, &seq);
455 if (rc)
456 return rc;
7a4704d8 457
6508db29
KZ
458 rc = -ENOMEM;
459
7a4704d8
KZ
460 /* convert logical name (e.g. "red") to real ESC code */
461 if (isalpha(*seq)) {
462 const char *s = color_sequence_from_colorname(seq);
463 char *p;
464
b73cc390
KZ
465 if (!s) {
466 DBG(SCHEME, ul_debug("unknown logical name: %s", seq));
6508db29
KZ
467 rc = -EINVAL;
468 goto err;
b73cc390 469 }
6508db29 470
7a4704d8
KZ
471 p = strdup(s);
472 if (!p)
6508db29 473 goto err;
7a4704d8
KZ
474 free(seq);
475 seq = p;
476 }
477
478 /* enlarge the array */
479 if (cc->nschemes == cc->schemes_sz) {
480 void *tmp = realloc(cc->schemes, (cc->nschemes + 10)
481 * sizeof(struct ul_color_scheme));
482 if (!tmp)
6508db29 483 goto err;
7a4704d8
KZ
484 cc->schemes = tmp;
485 cc->schemes_sz = cc->nschemes + 10;
486 }
487
488 /* add a new item */
6508db29 489 cs = &cc->schemes[cc->nschemes];
7a4704d8 490 cs->seq = seq;
6508db29
KZ
491 cs->name = strdup(name);
492 if (!cs->name)
493 goto err;
7a4704d8 494
6508db29 495 cc->nschemes++;
7a4704d8 496 return 0;
6508db29
KZ
497err:
498 if (cs) {
499 free(cs->seq);
500 free(cs->name);
501 cs->seq = cs->name = NULL;
502 } else
503 free(seq);
504 return rc;
7a4704d8
KZ
505}
506
507/*
508 * Deallocates all regards to color schemes
509 */
510static void colors_free_schemes(struct ul_color_ctl *cc)
511{
512 size_t i;
513
b73cc390
KZ
514 DBG(SCHEME, ul_debug("free scheme"));
515
7a4704d8
KZ
516 for (i = 0; i < cc->nschemes; i++) {
517 free(cc->schemes[i].name);
518 free(cc->schemes[i].seq);
519 }
520
521 free(cc->schemes);
522 cc->schemes = NULL;
523 cc->nschemes = 0;
524 cc->schemes_sz = 0;
525}
526
527/*
528 * The scheme configuration has to be sorted for bsearch
529 */
530static void colors_sort_schemes(struct ul_color_ctl *cc)
531{
532 if (!cc->nschemes)
533 return;
534
b73cc390
KZ
535 DBG(SCHEME, ul_debug("sort scheme"));
536
7a4704d8
KZ
537 qsort(cc->schemes, cc->nschemes,
538 sizeof(struct ul_color_scheme), cmp_scheme_name);
539}
540
541/*
542 * Returns just one color scheme
543 */
544static struct ul_color_scheme *colors_get_scheme(struct ul_color_ctl *cc,
545 const char *name)
546{
547 struct ul_color_scheme key = { .name = (char *) name}, *res;
548
549 if (!cc || !name || !*name)
550 return NULL;
551
552 if (!cc->cs_configured) {
553 int rc = colors_read_schemes(cc);
554 if (rc)
555 return NULL;
556 }
557 if (!cc->nschemes)
558 return NULL;
559
b73cc390
KZ
560 DBG(SCHEME, ul_debug("search '%s'", name));
561
7a4704d8
KZ
562 res = bsearch(&key, cc->schemes, cc->nschemes,
563 sizeof(struct ul_color_scheme),
564 cmp_scheme_name);
565
566 return res && res->seq ? res : NULL;
567}
568
569/*
570 * Parses filenames in terminal-colors.d
571 */
e66a6627
KZ
572static int colors_read_configuration(struct ul_color_ctl *cc)
573{
574 int rc = -ENOENT;
575 char *dirname, buf[PATH_MAX];
576
577 cc->termname = getenv("TERM");
578
579 dirname = colors_get_homedir(buf, sizeof(buf));
580 if (dirname)
581 rc = colors_readdir(cc, dirname); /* ~/.config */
582 if (rc == -EPERM || rc == -EACCES || rc == -ENOENT)
583 rc = colors_readdir(cc, _PATH_TERMCOLORS_DIR); /* /etc */
584
585 cc->configured = 1;
586 return rc;
587}
588
589/*
7a4704d8
KZ
590 * Reads terminal-colors.d/ scheme file into array schemes
591 */
592static int colors_read_schemes(struct ul_color_ctl *cc)
593{
594 int rc = 0;
595 FILE *f = NULL;
6508db29
KZ
596 char buf[BUFSIZ],
597 cn[129], seq[129];
7a4704d8
KZ
598
599 if (!cc->configured)
600 rc = colors_read_configuration(cc);
601
602 cc->cs_configured = 1;
603
604 if (rc || !cc->sfile)
605 goto done;
606
b73cc390
KZ
607 DBG(SCHEME, ul_debug("reading file '%s'", cc->sfile));
608
7a4704d8
KZ
609 f = fopen(cc->sfile, "r");
610 if (!f) {
611 rc = -errno;
612 goto done;
613 }
614
615 while (fgets(buf, sizeof(buf), f)) {
7a4704d8
KZ
616 char *p = strchr(buf, '\n');
617
618 if (!p) {
619 if (feof(f))
620 p = strchr(buf, '\0');
621 else {
622 rc = -errno;
623 goto done;
624 }
625 }
626 *p = '\0';
627 p = (char *) skip_blank(buf);
628 if (*p == '\0' || *p == '#')
629 continue;
630
6508db29
KZ
631 rc = sscanf(p, "%128[^ ] %128[^\n ]", cn, seq);
632 if (rc == 2 && *cn && *seq) {
7a4704d8 633 rc = colors_add_scheme(cc, cn, seq); /* set rc=0 on success */
6508db29
KZ
634 if (rc)
635 goto done;
7a4704d8 636 }
7a4704d8 637 }
6508db29 638 rc = 0;
7a4704d8
KZ
639
640done:
641 if (f)
642 fclose(f);
643 colors_sort_schemes(cc);
644
645 return rc;
646}
647
b73cc390
KZ
648
649static void termcolors_init_debug(void)
650{
a15dca2f 651 __UL_INIT_DEBUG_FROM_ENV(termcolors, TERMCOLORS_DEBUG_, 0, TERMINAL_COLORS_DEBUG);
b73cc390
KZ
652}
653
4310faf9
KZ
654static int colors_terminal_is_ready(void)
655{
656 int ncolors = -1;
657
23d47267 658#if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW)
4310faf9
KZ
659 {
660 int ret;
661
9d6852d3
KZ
662 if (setupterm(NULL, STDOUT_FILENO, &ret) == 0 && ret == 1)
663 ncolors = tigetnum("colors");
4310faf9
KZ
664 }
665#endif
cc7ffe12 666 if (1 < ncolors) {
23d47267
KZ
667 DBG(CONF, ul_debug("terminal is ready (supports %d colors)", ncolors));
668 return 1;
669 }
9d6852d3
KZ
670
671 DBG(CONF, ul_debug("terminal is NOT ready (no colors)"));
4310faf9
KZ
672 return 0;
673}
674
b8731ebc
KZ
675/**
676 * colors_init:
677 * @mode: UL_COLORMODE_*
678 * @name: util argv[0]
679 *
7a4704d8
KZ
680 * Initialize private color control struct and initialize the colors
681 * status. The color schemes are parsed on demand by colors_get_scheme().
682 *
b8731ebc 683 * Returns: >0 on success.
e66a6627 684 */
d0c9ddc3 685int colors_init(int mode, const char *name)
dfa68ad1 686{
4310faf9 687 int ready = -1;
570b3210
KZ
688 struct ul_color_ctl *cc = &ul_colors;
689
690 cc->utilname = name;
c1a54286 691
b73cc390
KZ
692 termcolors_init_debug();
693
5db59623
KZ
694 if (mode != UL_COLORMODE_ALWAYS && !isatty(STDOUT_FILENO))
695 cc->mode = UL_COLORMODE_NEVER;
696 else
697 cc->mode = mode;
698
699 if (cc->mode == UL_COLORMODE_UNDEF
700 && (ready = colors_terminal_is_ready())) {
e66a6627 701 int rc = colors_read_configuration(cc);
570b3210 702 if (rc)
81f55ab9 703 cc->mode = UL_COLORMODE_DEFAULT;
570b3210 704 else {
b73cc390 705
570b3210
KZ
706 /* evaluate scores */
707 if (cc->scores[UL_COLORFILE_DISABLE] >
708 cc->scores[UL_COLORFILE_ENABLE])
709 cc->mode = UL_COLORMODE_NEVER;
329b0ee7 710 else
81f55ab9 711 cc->mode = UL_COLORMODE_DEFAULT;
570b3210
KZ
712
713 atexit(colors_deinit);
d0c9ddc3 714 }
329b0ee7
KZ
715 }
716
570b3210 717 switch (cc->mode) {
a10c0434 718 case UL_COLORMODE_AUTO:
4310faf9 719 cc->has_colors = ready == -1 ? colors_terminal_is_ready() : ready;
a10c0434
KZ
720 break;
721 case UL_COLORMODE_ALWAYS:
e66a6627 722 cc->has_colors = 1;
a10c0434
KZ
723 break;
724 case UL_COLORMODE_NEVER:
725 default:
e66a6627 726 cc->has_colors = 0;
a10c0434 727 }
b73cc390
KZ
728
729 ON_DBG(CONF, colors_debug(cc));
730
e66a6627
KZ
731 return cc->has_colors;
732}
733
7a4704d8
KZ
734/*
735 * Temporary disable colors (this setting is independent on terminal-colors.d/)
736 */
e66a6627
KZ
737void colors_off(void)
738{
739 ul_colors.disabled = 1;
740}
741
7a4704d8
KZ
742/*
743 * Enable colors
744 */
e66a6627
KZ
745void colors_on(void)
746{
747 ul_colors.disabled = 0;
dfa68ad1
OO
748}
749
7a4704d8
KZ
750/*
751 * Is terminal-colors.d/ configured to use colors?
752 */
80a1712f
KZ
753int colors_wanted(void)
754{
7a4704d8
KZ
755 return ul_colors.has_colors;
756}
757
5db59623
KZ
758/*
759 * Returns mode
760 */
761int colors_mode(void)
762{
763 return ul_colors.mode;
764}
765
7a4704d8
KZ
766/*
767 * Enable @seq color
768 */
769void color_fenable(const char *seq, FILE *f)
770{
771 if (!ul_colors.disabled && ul_colors.has_colors && seq)
772 fputs(seq, f);
80a1712f
KZ
773}
774
7a4704d8 775/*
b8731ebc 776 * Returns escape sequence by logical @name, if undefined then returns @dflt.
7a4704d8 777 */
b8731ebc 778const char *color_scheme_get_sequence(const char *name, const char *dflt)
dfa68ad1 779{
7a4704d8
KZ
780 struct ul_color_scheme *cs;
781
782 if (ul_colors.disabled || !ul_colors.has_colors)
b8731ebc 783 return NULL;
7a4704d8
KZ
784
785 cs = colors_get_scheme(&ul_colors, name);
b8731ebc
KZ
786 return cs && cs->seq ? cs->seq : dflt;
787}
788
789/*
790 * Enable color by logical @name, if undefined enable @dflt.
791 */
792void color_scheme_fenable(const char *name, const char *dflt, FILE *f)
793{
794 const char *seq = color_scheme_get_sequence(name, dflt);
795
796 if (!seq)
797 return;
798 color_fenable(seq, f);
dfa68ad1
OO
799}
800
b8731ebc 801
7a4704d8
KZ
802/*
803 * Disable previously enabled color
804 */
80a1712f 805void color_fdisable(FILE *f)
dfa68ad1 806{
e66a6627 807 if (!ul_colors.disabled && ul_colors.has_colors)
80a1712f 808 fputs(UL_COLOR_RESET, f);
dfa68ad1 809}
a10c0434 810
7a4704d8
KZ
811/*
812 * Parses @str to return UL_COLORMODE_*
813 */
a10c0434
KZ
814int colormode_from_string(const char *str)
815{
816 size_t i;
817 static const char *modes[] = {
818 [UL_COLORMODE_AUTO] = "auto",
819 [UL_COLORMODE_NEVER] = "never",
d0c9ddc3
OO
820 [UL_COLORMODE_ALWAYS] = "always",
821 [UL_COLORMODE_UNDEF] = ""
a10c0434
KZ
822 };
823
824 if (!str || !*str)
825 return -EINVAL;
826
827 assert(ARRAY_SIZE(modes) == __UL_NCOLORMODES);
828
829 for (i = 0; i < ARRAY_SIZE(modes); i++) {
830 if (strcasecmp(str, modes[i]) == 0)
831 return i;
832 }
833
834 return -EINVAL;
835}
836
7a4704d8
KZ
837/*
838 * Parses @str and exit(EXIT_FAILURE) on error
839 */
b7faf991
KZ
840int colormode_or_err(const char *str, const char *errmsg)
841{
842 const char *p = str && *str == '=' ? str + 1 : str;
843 int colormode;
844
845 colormode = colormode_from_string(p);
846 if (colormode < 0)
847 errx(EXIT_FAILURE, "%s: '%s'", errmsg, p);
848
849 return colormode;
850}
851
e8f7acb0 852#ifdef TEST_PROGRAM_COLORS
a10c0434 853# include <getopt.h>
a10c0434
KZ
854int main(int argc, char *argv[])
855{
856 static const struct option longopts[] = {
71f08e97
SK
857 { "mode", required_argument, NULL, 'm' },
858 { "color", required_argument, NULL, 'c' },
859 { "color-scheme", required_argument, NULL, 'C' },
860 { "name", required_argument, NULL, 'n' },
861 { NULL, 0, NULL, 0 }
a10c0434 862 };
570b3210 863 int c, mode = UL_COLORMODE_UNDEF; /* default */
7a4704d8
KZ
864 const char *color = "red", *name = NULL, *color_scheme = NULL;
865 const char *seq = NULL;
a10c0434 866
7a4704d8 867 while ((c = getopt_long(argc, argv, "C:c:m:n:", longopts, NULL)) != -1) {
a10c0434 868 switch (c) {
570b3210
KZ
869 case 'c':
870 color = optarg;
871 break;
7a4704d8
KZ
872 case 'C':
873 color_scheme = optarg;
874 break;
875 case 'm':
876 mode = colormode_or_err(optarg, "unsupported color mode");
877 break;
878 case 'n':
879 name = optarg;
68f7b572 880 break;
7a4704d8
KZ
881 default:
882 fprintf(stderr, "usage: %s [options]\n"
883 " -m, --mode <auto|never|always> default is undefined\n"
884 " -c, --color <red|blue|...> color for the test message\n"
885 " -C, --color-scheme <name> color for the test message\n"
886 " -n, --name <utilname> util name\n",
887 program_invocation_short_name);
888 return EXIT_FAILURE;
a10c0434
KZ
889 }
890 }
891
7a4704d8 892 colors_init(mode, name ? name : program_invocation_short_name);
570b3210 893
7a4704d8 894 seq = color_sequence_from_colorname(color);
d0c9ddc3 895
7a4704d8
KZ
896 if (color_scheme)
897 color_scheme_enable(color_scheme, seq);
898 else
899 color_enable(seq);
a10c0434
KZ
900 printf("Hello World!");
901 color_disable();
7a4704d8
KZ
902 fputc('\n', stdout);
903
a10c0434
KZ
904 return EXIT_SUCCESS;
905}
e8f7acb0 906#endif /* TEST_PROGRAM_COLORS */
a10c0434 907