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