]>
Commit | Line | Data |
---|---|---|
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 |
22 | enum { |
23 | UL_COLORFILE_DISABLE, /* .disable */ | |
24 | UL_COLORFILE_ENABLE, /* .enable */ | |
25 | UL_COLORFILE_SCHEME, /* .scheme */ | |
26 | ||
27 | __UL_COLORFILE_COUNT | |
28 | }; | |
29 | ||
7a4704d8 KZ |
30 | struct 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 |
49 | struct 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 | ||
68 | static struct ul_color_ctl ul_colors; | |
69 | ||
7a4704d8 KZ |
70 | static void colors_free_schemes(struct ul_color_ctl *cc); |
71 | static int colors_read_schemes(struct ul_color_ctl *cc); | |
72 | ||
73 | /* | |
74 | * qsort/bsearch buddy | |
75 | */ | |
76 | static 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 | */ | |
86 | const 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 |
121 | static 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 | |
139 | static 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 |
186 | static 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 | */ |
235 | static 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 |
309 | static 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 |
317 | static 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 */ |
336 | static 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 | */ | |
416 | static 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 | */ | |
467 | static 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 | */ | |
485 | static 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 | */ | |
497 | static 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 |
523 | static 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 | */ | |
543 | static 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 | ||
592 | done: | |
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 | 607 | int 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 |
648 | void colors_off(void) |
649 | { | |
650 | ul_colors.disabled = 1; | |
651 | } | |
652 | ||
7a4704d8 KZ |
653 | /* |
654 | * Enable colors | |
655 | */ | |
e66a6627 KZ |
656 | void 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 |
664 | int colors_wanted(void) |
665 | { | |
7a4704d8 KZ |
666 | return ul_colors.has_colors; |
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); | |
80a1712f KZ |
676 | } |
677 | ||
7a4704d8 KZ |
678 | /* |
679 | * Enable color by logical @name | |
680 | */ | |
681 | void 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 | 695 | void 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 |
704 | int 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 |
730 | int 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 |
744 | int 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 |