summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/colors.h19
-rw-r--r--lib/colors.c502
2 files changed, 432 insertions, 89 deletions
diff --git a/include/colors.h b/include/colors.h
index 392bf38f1..e8ab3951a 100644
--- a/include/colors.h
+++ b/include/colors.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
*
* This file may be distributed under the terms of the
* GNU Lesser General Public License.
@@ -62,12 +63,18 @@ extern void colors_off(void);
extern void colors_on(void);
-/* Set the color to CLR_SCHEME */
-extern void color_fenable(const char *clr_scheme, FILE *f);
+/* Set the color */
+extern void color_fenable(const char *seq, FILE *f);
+extern void color_scheme_fenable(const char *name, const char *dflt, FILE *f);
-static inline void color_enable(const char *clr_scheme)
+static inline void color_enable(const char *seq)
{
- color_fenable(clr_scheme, stdout);
+ color_fenable(seq, stdout);
+}
+
+static inline void color_scheme_enable(const char *name, const char *dflt)
+{
+ color_scheme_fenable(name, dflt, stdout);
}
/* Reset colors to default */
@@ -78,8 +85,8 @@ static inline void color_disable(void)
color_fdisable(stdout);
}
-
-extern const char *colorscheme_from_string(const char *str);
+/* converts "red" to UL_COLOR_RED, etc. */
+extern const char *color_sequence_from_colorname(const char *str);
#endif /* UTIL_LINUX_COLORS_H */
diff --git a/lib/colors.c b/lib/colors.c
index 91677ba9b..cb342a0e2 100644
--- a/lib/colors.c
+++ b/lib/colors.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
- * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
*
* This file may be distributed under the terms of the
* GNU Lesser General Public License.
@@ -9,11 +9,12 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
+#include <ctype.h>
#include "c.h"
#include "colors.h"
-#include "xalloc.h"
#include "pathnames.h"
+#include "strutils.h"
/*
* terminal-colors.d file types
@@ -26,6 +27,11 @@ enum {
__UL_COLORFILE_COUNT
};
+struct ul_color_scheme {
+ char *name;
+ char *seq;
+};
+
/*
* Global colors control struct
*
@@ -44,11 +50,16 @@ struct ul_color_ctl {
const char *utilname; /* util name */
const char *termname; /* terminal name ($TERM) */
- char *scheme; /* path to scheme */
+ char *sfile; /* path to scheme */
+
+ struct ul_color_scheme *schemes; /* array with color schemes */
+ size_t nschemes; /* number of the items */
+ size_t schemes_sz; /* number of the allocated items */
int mode; /* UL_COLORMODE_* */
unsigned int has_colors : 1, /* based on mode and scores[] */
disabled : 1, /* disable colors */
+ cs_configured : 1, /* color schemes read */
configured : 1; /* terminal-colors.d parsed */
int scores[__UL_COLORFILE_COUNT]; /* the best match */
@@ -56,17 +67,67 @@ struct ul_color_ctl {
static struct ul_color_ctl ul_colors;
+static void colors_free_schemes(struct ul_color_ctl *cc);
+static int colors_read_schemes(struct ul_color_ctl *cc);
+
+/*
+ * qsort/bsearch buddy
+ */
+static int cmp_scheme_name(const void *a0, const void *b0)
+{
+ struct ul_color_scheme *a = (struct ul_color_scheme *) a0,
+ *b = (struct ul_color_scheme *) b0;
+ return strcmp(a->name, b->name);
+}
+
+/*
+ * Maintains human readable color names
+ */
+const char *color_sequence_from_colorname(const char *str)
+{
+ static const struct ul_color_scheme basic_schemes[] = {
+ { "black", UL_COLOR_BLACK },
+ { "blue", UL_COLOR_BLUE },
+ { "brown", UL_COLOR_BROWN },
+ { "cyan", UL_COLOR_CYAN },
+ { "darkgray", UL_COLOR_DARK_GRAY },
+ { "gray", UL_COLOR_GRAY },
+ { "green", UL_COLOR_GREEN },
+ { "lightblue", UL_COLOR_BOLD_BLUE },
+ { "lightcyan", UL_COLOR_BOLD_CYAN },
+ { "lightgray,", UL_COLOR_GRAY },
+ { "lightgreen", UL_COLOR_BOLD_GREEN },
+ { "lightmagenta", UL_COLOR_BOLD_MAGENTA },
+ { "lightred", UL_COLOR_BOLD_RED },
+ { "magenta", UL_COLOR_MAGENTA },
+ { "red", UL_COLOR_RED },
+ { "yellow", UL_COLOR_BOLD_YELLOW },
+ };
+ struct ul_color_scheme key = { .name = (char *) str }, *res;
+
+ if (!str)
+ return NULL;
+
+ res = bsearch(&key, basic_schemes, ARRAY_SIZE(basic_schemes),
+ sizeof(struct ul_color_scheme),
+ cmp_scheme_name);
+ return res ? res->seq : NULL;
+}
+
+
/*
- * Reset control struct (note that we don't allocate the struct)
+ * Resets control struct (note that we don't allocate the struct)
*/
static void colors_reset(struct ul_color_ctl *cc)
{
if (!cc)
return;
- free(cc->scheme);
+ colors_free_schemes(cc);
+
+ free(cc->sfile);
- cc->scheme = NULL;
+ cc->sfile = NULL;
cc->utilname = NULL;
cc->termname = NULL;
cc->mode = UL_COLORMODE_UNDEF;
@@ -85,12 +146,18 @@ static void colors_debug(struct ul_color_ctl *cc)
printf("Colors:\n");
printf("\tutilname = '%s'\n", cc->utilname);
printf("\ttermname = '%s'\n", cc->termname);
- printf("\tscheme = '%s'\n", cc->scheme);
+ printf("\tscheme file = '%s'\n", cc->sfile);
printf("\tmode = %s\n",
cc->mode == UL_COLORMODE_UNDEF ? "undefined" :
cc->mode == UL_COLORMODE_AUTO ? "auto" :
cc->mode == UL_COLORMODE_NEVER ? "never" :
cc->mode == UL_COLORMODE_ALWAYS ? "always" : "???");
+ printf("\thas_colors = %d\n", cc->has_colors);
+ printf("\tdisabled = %d\n", cc->disabled);
+ printf("\tconfigured = %d\n", cc->configured);
+ printf("\tcs configured = %d\n", cc->cs_configured);
+
+ fputc('\n', stdout);
for (i = 0; i < ARRAY_SIZE(cc->scores); i++)
printf("\tscore %s = %d\n",
@@ -98,6 +165,17 @@ static void colors_debug(struct ul_color_ctl *cc)
i == UL_COLORFILE_ENABLE ? "enable" :
i == UL_COLORFILE_SCHEME ? "scheme" : "???",
cc->scores[i]);
+
+ fputc('\n', stdout);
+
+ for (i = 0; i < cc->nschemes; i++) {
+ printf("\tscheme #%02d ", i);
+ color_scheme_enable(cc->schemes[i].name, NULL);
+ fputs(cc->schemes[i].name, stdout);
+ color_disable();
+ fputc('\n', stdout);
+ }
+ fputc('\n', stdout);
}
#endif
@@ -159,7 +237,7 @@ static int colors_readdir(struct ul_color_ctl *cc, const char *dirname)
DIR *dir;
int rc = 0;
struct dirent *d;
- char scheme[PATH_MAX] = { '\0' };
+ char sfile[PATH_MAX] = { '\0' };
size_t namesz, termsz;
if (!dirname || !cc || !cc->utilname || !*cc->utilname)
@@ -194,10 +272,12 @@ static int colors_readdir(struct ul_color_ctl *cc, const char *dirname)
score += 20;
if (tk_term)
score += 10;
+
/*
fprintf(stderr, "%20s score: %2d [cur max: %2d]\n",
d->d_name, score, cc->scores[type]);
*/
+
if (score < cc->scores[type])
continue;
@@ -205,19 +285,19 @@ static int colors_readdir(struct ul_color_ctl *cc, const char *dirname)
if (tk_namesz != namesz
|| strncmp(tk_name, cc->utilname, namesz) != 0)
continue;
- if (tk_termsz != termsz
- || termsz == 0
- || strncmp(tk_term, cc->termname, termsz) != 0)
+
+ if (tk_termsz && (termsz == 0 || tk_termsz != termsz ||
+ strncmp(tk_term, cc->termname, termsz) != 0))
continue;
cc->scores[type] = score;
if (type == UL_COLORFILE_SCHEME)
- strncpy(scheme, d->d_name, sizeof(scheme));
+ strncpy(sfile, d->d_name, sizeof(sfile));
}
- if (*scheme) {
- scheme[sizeof(scheme) - 1] = '\0';
- if (asprintf(&cc->scheme, "%s/%s", dirname, scheme) <= 0)
+ if (*sfile) {
+ sfile[sizeof(sfile) - 1] = '\0';
+ if (asprintf(&cc->sfile, "%s/%s", dirname, sfile) <= 0)
rc = -ENOMEM;
}
@@ -252,6 +332,194 @@ static char *colors_get_homedir(char *buf, size_t bufsz)
return NULL;
}
+/* canonicalize sequence */
+static int cn_sequence(const char *str, char **seq)
+{
+ char *in, *out;
+
+ if (!str)
+ return -EINVAL;
+
+ *seq = NULL;
+
+ /* convert logical names like "red" to the real sequence */
+ if (*str != '\\' && isalpha(*str)) {
+ const char *s = color_sequence_from_colorname(str);
+ *seq = strdup(s ? s : str);
+
+ return *seq ? 0 : -ENOMEM;
+ }
+
+ /* convert xx;yy sequences to "\033[xx;yy" */
+ if (asprintf(seq, "\033[%sm", str) < 1)
+ return -ENOMEM;
+
+ for (in = *seq, out = *seq; in && *in; in++) {
+ if (*in != '\\') {
+ *out++ = *in;
+ continue;
+ }
+ switch(*(in + 1)) {
+ case 'a':
+ *out++ = '\a'; /* Bell */
+ break;
+ case 'b':
+ *out++ = '\b'; /* Backspace */
+ break;
+ case 'e':
+ *out++ = '\033'; /* Escape */
+ break;
+ case 'f':
+ *out++ = '\f'; /* Form Feed */
+ break;
+ case 'n':
+ *out++ = '\n'; /* Newline */
+ break;
+ case 'r':
+ *out++ = '\r'; /* Carriage Return */
+ break;
+ case 't':
+ *out++ = '\t'; /* Tab */
+ break;
+ case 'v':
+ *out++ = '\v'; /* Vertical Tab */
+ break;
+ case '\\':
+ *out++ = '\\'; /* Backslash */
+ break;
+ case '_':
+ *out++ = ' '; /* Space */
+ break;
+ case '#':
+ *out++ = '#'; /* Hash mark */
+ break;
+ case '?':
+ *out++ = '?'; /* Qestion mark */
+ break;
+ default:
+ *out++ = *in;
+ *out++ = *(in + 1);
+ break;
+ }
+ in++;
+ }
+ *out = '\0';
+
+ return 0;
+}
+
+
+/*
+ * Adds one color sequence to array with color scheme,
+ * @seq and @name have to be allocated strings
+ */
+static int colors_add_scheme(struct ul_color_ctl *cc,
+ char *name,
+ char *seq0)
+{
+ struct ul_color_scheme *cs;
+ char *seq;
+ int rc;
+
+ if (!cc || !name || !*name || !seq0 || !*seq0)
+ return -EINVAL;
+
+ rc = cn_sequence(seq0, &seq);
+ if (rc)
+ return rc;
+ free(seq0);
+
+ /* convert logical name (e.g. "red") to real ESC code */
+ if (isalpha(*seq)) {
+ const char *s = color_sequence_from_colorname(seq);
+ char *p;
+
+ if (!s)
+ return -EINVAL;
+ p = strdup(s);
+ if (!p)
+ return -ENOMEM;
+ free(seq);
+ seq = p;
+ }
+
+ /* enlarge the array */
+ if (cc->nschemes == cc->schemes_sz) {
+ void *tmp = realloc(cc->schemes, (cc->nschemes + 10)
+ * sizeof(struct ul_color_scheme));
+ if (!tmp)
+ return -ENOMEM;
+ cc->schemes = tmp;
+ cc->schemes_sz = cc->nschemes + 10;
+ }
+
+ /* add a new item */
+ cs = &cc->schemes[cc->nschemes++];
+ cs->name = name;
+ cs->seq = seq;
+
+ return 0;
+}
+
+/*
+ * Deallocates all regards to color schemes
+ */
+static void colors_free_schemes(struct ul_color_ctl *cc)
+{
+ size_t i;
+
+ for (i = 0; i < cc->nschemes; i++) {
+ free(cc->schemes[i].name);
+ free(cc->schemes[i].seq);
+ }
+
+ free(cc->schemes);
+ cc->schemes = NULL;
+ cc->nschemes = 0;
+ cc->schemes_sz = 0;
+}
+
+/*
+ * The scheme configuration has to be sorted for bsearch
+ */
+static void colors_sort_schemes(struct ul_color_ctl *cc)
+{
+ if (!cc->nschemes)
+ return;
+
+ qsort(cc->schemes, cc->nschemes,
+ sizeof(struct ul_color_scheme), cmp_scheme_name);
+}
+
+/*
+ * Returns just one color scheme
+ */
+static struct ul_color_scheme *colors_get_scheme(struct ul_color_ctl *cc,
+ const char *name)
+{
+ struct ul_color_scheme key = { .name = (char *) name}, *res;
+
+ if (!cc || !name || !*name)
+ return NULL;
+
+ if (!cc->cs_configured) {
+ int rc = colors_read_schemes(cc);
+ if (rc)
+ return NULL;
+ }
+ if (!cc->nschemes)
+ return NULL;
+
+ res = bsearch(&key, cc->schemes, cc->nschemes,
+ sizeof(struct ul_color_scheme),
+ cmp_scheme_name);
+
+ return res && res->seq ? res : NULL;
+}
+
+/*
+ * Parses filenames in terminal-colors.d
+ */
static int colors_read_configuration(struct ul_color_ctl *cc)
{
int rc = -ENOENT;
@@ -270,8 +538,71 @@ static int colors_read_configuration(struct ul_color_ctl *cc)
}
/*
- * Initialize private color control struct, @mode is UL_COLORMODE_*
- * and @name is util argv[0]
+ * Reads terminal-colors.d/ scheme file into array schemes
+ */
+static int colors_read_schemes(struct ul_color_ctl *cc)
+{
+ int rc = 0;
+ FILE *f = NULL;
+ char buf[BUFSIZ];
+
+ if (!cc->configured)
+ rc = colors_read_configuration(cc);
+
+ cc->cs_configured = 1;
+
+ if (rc || !cc->sfile)
+ goto done;
+
+ f = fopen(cc->sfile, "r");
+ if (!f) {
+ rc = -errno;
+ goto done;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ char *cn = NULL, *seq = NULL;
+ char *p = strchr(buf, '\n');
+
+ if (!p) {
+ if (feof(f))
+ p = strchr(buf, '\0');
+ else {
+ rc = -errno;
+ goto done;
+ }
+ }
+ *p = '\0';
+ p = (char *) skip_blank(buf);
+ if (*p == '\0' || *p == '#')
+ continue;
+
+ rc = sscanf(p, UL_SCNsA" " /* name */
+ UL_SCNsA, /* color */
+ &cn, &seq);
+ if (rc == 2 && cn && seq)
+ rc = colors_add_scheme(cc, cn, seq); /* set rc=0 on success */
+ if (rc) {
+ free(cn);
+ free(seq);
+ }
+ rc = 0;
+ }
+
+done:
+ if (f)
+ fclose(f);
+ colors_sort_schemes(cc);
+
+ return rc;
+}
+
+/*
+ * Initialize private color control struct and initialize the colors
+ * status. The color schemes are parsed on demand by colors_get_scheme().
+ *
+ * @mode: UL_COLORMODE_*
+ * @name: util argv[0]
*/
int colors_init(int mode, const char *name)
{
@@ -311,33 +642,65 @@ int colors_init(int mode, const char *name)
return cc->has_colors;
}
+/*
+ * Temporary disable colors (this setting is independent on terminal-colors.d/)
+ */
void colors_off(void)
{
ul_colors.disabled = 1;
}
+/*
+ * Enable colors
+ */
void colors_on(void)
{
ul_colors.disabled = 0;
}
+/*
+ * Is terminal-colors.d/ configured to use colors?
+ */
int colors_wanted(void)
{
- return !ul_colors.disabled && ul_colors.has_colors;
+ return ul_colors.has_colors;
+}
+
+/*
+ * Enable @seq color
+ */
+void color_fenable(const char *seq, FILE *f)
+{
+ if (!ul_colors.disabled && ul_colors.has_colors && seq)
+ fputs(seq, f);
}
-void color_fenable(const char *color_scheme, FILE *f)
+/*
+ * Enable color by logical @name
+ */
+void color_scheme_fenable(const char *name, const char *dflt, FILE *f)
{
- if (!ul_colors.disabled && ul_colors.has_colors && color_scheme)
- fputs(color_scheme, f);
+ struct ul_color_scheme *cs;
+
+ if (ul_colors.disabled || !ul_colors.has_colors)
+ return;
+
+ cs = colors_get_scheme(&ul_colors, name);
+ color_fenable(cs && cs->seq ? cs->seq : dflt, f);
}
+/*
+ * Disable previously enabled color
+ */
void color_fdisable(FILE *f)
{
if (!ul_colors.disabled && ul_colors.has_colors)
fputs(UL_COLOR_RESET, f);
}
+/*
+ * Parses @str to return UL_COLORMODE_*
+ */
int colormode_from_string(const char *str)
{
size_t i;
@@ -361,6 +724,9 @@ int colormode_from_string(const char *str)
return -EINVAL;
}
+/*
+ * Parses @str and exit(EXIT_FAILURE) on error
+ */
int colormode_or_err(const char *str, const char *errmsg)
{
const char *p = str && *str == '=' ? str + 1 : str;
@@ -373,89 +739,59 @@ int colormode_or_err(const char *str, const char *errmsg)
return colormode;
}
-struct colorscheme {
- const char *name, *scheme;
-};
-
-static int cmp_colorscheme_name(const void *a0, const void *b0)
-{
- struct colorscheme *a = (struct colorscheme *) a0,
- *b = (struct colorscheme *) b0;
- return strcmp(a->name, b->name);
-}
-
-const char *colorscheme_from_string(const char *str)
-{
- static const struct colorscheme basic_schemes[] = {
- { "black", UL_COLOR_BLACK },
- { "blue", UL_COLOR_BLUE },
- { "brown", UL_COLOR_BROWN },
- { "cyan", UL_COLOR_CYAN },
- { "darkgray", UL_COLOR_DARK_GRAY },
- { "gray", UL_COLOR_GRAY },
- { "green", UL_COLOR_GREEN },
- { "lightblue", UL_COLOR_BOLD_BLUE },
- { "lightcyan", UL_COLOR_BOLD_CYAN },
- { "lightgray,", UL_COLOR_GRAY },
- { "lightgreen", UL_COLOR_BOLD_GREEN },
- { "lightmagenta", UL_COLOR_BOLD_MAGENTA },
- { "lightred", UL_COLOR_BOLD_RED },
- { "magenta", UL_COLOR_MAGENTA },
- { "red", UL_COLOR_RED },
- { "yellow", UL_COLOR_BOLD_YELLOW },
- };
- struct colorscheme key = { .name = str }, *res;
- if (!str)
- return NULL;
-
- res = bsearch(&key, basic_schemes, ARRAY_SIZE(basic_schemes),
- sizeof(struct colorscheme),
- cmp_colorscheme_name);
- return res ? res->scheme : NULL;
-}
-
#ifdef TEST_PROGRAM
# include <getopt.h>
int main(int argc, char *argv[])
{
static const struct option longopts[] = {
- { "mode", optional_argument, 0, 'm' },
- { "color", required_argument, 0, 'c' },
- { "read-dir", required_argument, 0, 'd' },
+ { "mode", required_argument, 0, 'm' },
+ { "color", required_argument, 0, 'c' },
+ { "color-scheme", required_argument, 0, 'C' },
+ { "name", required_argument, 0, 'n' },
{ NULL, 0, 0, 0 }
};
int c, mode = UL_COLORMODE_UNDEF; /* default */
- const char *color = "red", *dir = NULL;
+ const char *color = "red", *name = NULL, *color_scheme = NULL;
+ const char *seq = NULL;
- while ((c = getopt_long(argc, argv, "m::c:d:", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "C:c:m:n:", longopts, NULL)) != -1) {
switch (c) {
- case 'm':
- if (optarg)
- mode = colormode_or_err(optarg, "unsupported color mode");
- break;
case 'c':
color = optarg;
break;
- case 'd':
- dir = optarg;
+ case 'C':
+ color_scheme = optarg;
+ break;
+ case 'm':
+ mode = colormode_or_err(optarg, "unsupported color mode");
+ break;
+ case 'n':
+ name = optarg;
break;
+ default:
+ fprintf(stderr, "usage: %s [options]\n"
+ " -m, --mode <auto|never|always> default is undefined\n"
+ " -c, --color <red|blue|...> color for the test message\n"
+ " -C, --color-scheme <name> color for the test message\n"
+ " -n, --name <utilname> util name\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
}
}
- if (dir) {
- struct ul_color_ctl cc = { .utilname = "foo",
- .termname = "footerm" };
-
- colors_readdir(&cc, dir);
- colors_debug(&cc);
- colors_reset(&cc);
- }
+ colors_init(mode, name ? name : program_invocation_short_name);
- colors_init(mode, program_invocation_short_name);
+ seq = color_sequence_from_colorname(color);
- color_enable(colorscheme_from_string(color));
+ if (color_scheme)
+ color_scheme_enable(color_scheme, seq);
+ else
+ color_enable(seq);
printf("Hello World!");
color_disable();
+ fputc('\n', stdout);
+
+ colors_debug(&ul_colors);
return EXIT_SUCCESS;
}