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