/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022-2024 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "util.h" static struct bsddialog_theme t; static char title[1024]; #define NPROPERTY 41 #define NCOLOR 8 #define NATTR 6 #define PROP_ERROR(name, error) do { \ fclose(fp); \ exit_error(false, "%s for \"%s\"", error, name); \ } while (0) enum typeproperty { BOOL, CHAR, INT, UINT, COLOR, COMPAT }; struct property { const char *comment; const char *name; enum typeproperty type; void *value; }; struct namevalue { const char *name; unsigned int value; }; static struct namevalue color[NCOLOR] = { {"black", BSDDIALOG_BLACK}, {"red", BSDDIALOG_RED}, {"green", BSDDIALOG_GREEN}, {"yellow", BSDDIALOG_YELLOW}, {"blue", BSDDIALOG_BLUE}, {"magenta", BSDDIALOG_MAGENTA}, {"cyan", BSDDIALOG_CYAN}, {"white", BSDDIALOG_WHITE} }; static struct namevalue attr[NATTR] = { {"bold", BSDDIALOG_BOLD}, {"reverse", BSDDIALOG_REVERSE}, {"underline", BSDDIALOG_UNDERLINE}, {"blink", BSDDIALOG_BLINK}, {"halfbright", BSDDIALOG_HALFBRIGHT}, {"highlight", BSDDIALOG_HIGHLIGHT} }; static struct property p[NPROPERTY] = { {"\n#Terminal\n", "theme.screen.color", COLOR, &t.screen.color}, {"\n# Shadow\n", "theme.shadow.color", COLOR, &t.shadow.color}, {"# shift down right from main widget\n", "theme.shadow.y", UINT, &t.shadow.y}, {"", "theme.shadow.x", UINT, &t.shadow.x}, {"\n# Main widget\n", "theme.dialog.color", COLOR, &t.dialog.color}, {"", "theme.dialog.delimtitle", BOOL, &t.dialog.delimtitle}, {"", "theme.dialog.titlecolor", COLOR, &t.dialog.titlecolor}, {"", "theme.dialog.lineraisecolor", COLOR, &t.dialog.lineraisecolor}, {"", "theme.dialog.linelowercolor", COLOR, &t.dialog.linelowercolor}, {"", "theme.dialog.bottomtitlecolor", COLOR, &t.dialog.bottomtitlecolor}, {"", "theme.dialog.arrowcolor", COLOR, &t.dialog.arrowcolor}, {"\n# Menus: --checklist, --menu, --radiolist\n" "# prefix [selector] shortcut name desc bottomdesc\n", "theme.menu.f_prefixcolor", COLOR, &t.menu.f_prefixcolor}, {"", "theme.menu.prefixcolor", COLOR, &t.menu.prefixcolor}, {"", "theme.menu.f_selectorcolor", COLOR, &t.menu.f_selectorcolor}, {"", "theme.menu.selectorcolor", COLOR, &t.menu.selectorcolor}, {"", "theme.menu.f_namecolor", COLOR, &t.menu.f_namecolor}, {"", "theme.menu.namecolor", COLOR, &t.menu.namecolor}, {"", "theme.menu.f_desccolor", COLOR, &t.menu.f_desccolor}, {"", "theme.menu.desccolor", COLOR, &t.menu.desccolor}, {"", "theme.menu.f_shortcutcolor", COLOR, &t.menu.f_shortcutcolor}, {"", "theme.menu.shortcutcolor", COLOR, &t.menu.shortcutcolor}, {"", "theme.menu.bottomdesccolor", COLOR, &t.menu.bottomdesccolor}, {"# bsddialog_menutype BSDDIALOG_SEPARATOR\n", "theme.menu.sepnamecolor", COLOR, &t.menu.sepnamecolor}, {"", "theme.menu.sepdesccolor", COLOR, &t.menu.sepdesccolor}, {"\n# Forms\n", "theme.form.f_fieldcolor", COLOR, &t.form.f_fieldcolor}, {"", "theme.form.fieldcolor", COLOR, &t.form.fieldcolor}, {"", "theme.form.readonlycolor", COLOR, &t.form.readonlycolor}, {"", "theme.form.bottomdesccolor", COLOR, &t.form.bottomdesccolor}, {"\n# Bar of --gauge, --mixedgauge, --pause, --rangebox\n", "theme.bar.f_color", COLOR, &t.bar.f_color}, {"", "theme.bar.color", COLOR, &t.bar.color}, {"\n# Buttons\n", "theme.button.minmargin", UINT, &t.button.minmargin}, {"", "theme.button.maxmargin", UINT, &t.button.maxmargin}, {"", "theme.button.leftdelim", CHAR, &t.button.leftdelim}, {"", "theme.button.rightdelim", CHAR, &t.button.rightdelim}, {"", "theme.button.f_delimcolor", COLOR, &t.button.f_delimcolor}, {"", "theme.button.delimcolor", COLOR, &t.button.delimcolor}, {"", "theme.button.f_color", COLOR, &t.button.f_color}, {"", "theme.button.color", COLOR, &t.button.color}, {"", "theme.button.f_shortcutcolor", COLOR, &t.button.f_shortcutcolor}, {"", "theme.button.shortcutcolor", COLOR, &t.button.shortcutcolor}, {"\n#Compatibility. Do not use, can be deleted\n", "use_shadow", COMPAT, NULL} }; void savetheme(const char *file) { int i, j; unsigned int flags; enum bsddialog_color bg, fg; time_t clock; FILE *fp; if (bsddialog_get_theme(&t) != BSDDIALOG_OK) exit_error(false, "cannot save theme: %s", bsddialog_geterror()); if (time(&clock) < 0) exit_error(false, "cannot save profile getting current time"); if ((fp = fopen(file, "w")) == NULL) exit_error(false, "cannot open %s to save profile", file); fprintf(fp, "### bsddialog theme - %s\n", ctime(&clock)); fputs("# Colors: ", fp); fputs("black red green yellow blue magenta cyan white.\n", fp); fputs("# Attributes: ", fp); fputs("bold reverse underline blink halfbright highlight.\n", fp); fputs("# f_* refers to focus for an element with selected or ", fp); fputs("unselected state.\n\n", fp); fprintf(fp, "version %s\n", LIBBSDDIALOG_VERSION); for (i = 0; i < NPROPERTY; i++) { if (p[i].type == COMPAT) continue; fprintf(fp, "%s%s", p[i].comment, p[i].name); switch (p[i].type) { case CHAR: fprintf(fp, " %c\n", *((char*)p[i].value)); break; case INT: fprintf(fp, " %d\n", *((int*)p[i].value)); break; case UINT: fprintf(fp, " %u\n", *((unsigned int*)p[i].value)); break; case BOOL: fprintf(fp, " %s\n", *((bool*)p[i].value) ? "true" : "false"); break; case COLOR: bsddialog_color_attrs(*(int*)p[i].value, &fg, &bg, &flags); fprintf(fp, " %s %s", color[fg].name, color[bg].name); for (j = 0; j < NATTR; j++) if (flags & attr[j].value) fprintf(fp, " %s", attr[j].name); fputs("\n", fp); break; case COMPAT: /* Do not save compat property for now */ break; } } fclose(fp); } void loadtheme(const char *file, bool compatibility) { bool boolvalue; char charvalue, *value; char line[BUFSIZ], name[BUFSIZ], c1[BUFSIZ], c2[BUFSIZ]; int i, j, intvalue; unsigned int uintvalue, flags; enum bsddialog_color bg, fg; FILE *fp; if (bsddialog_hascolors() == false) return; if (bsddialog_get_theme(&t) != BSDDIALOG_OK) exit_error(false, "Cannot get current theme: %s", bsddialog_geterror()); if ((fp = fopen(file, "r")) == NULL) exit_error(false, "Cannot open theme \"%s\" file", file); while (fgets(line, BUFSIZ, fp) != NULL) { if (line[0] == '#' || line[0] == '\n') continue; /* superfluous, only for efficiency */ sscanf(line, "%s", name); value = NULL; /* useless init, fix compiler warning */ for (i = 0; i < NPROPERTY; i++) { if (strcmp(name, p[i].name) == 0) { value = &line[strlen(name)]; break; } } if (i >= NPROPERTY) { /* unknown name in property p[] */ if (strcmp(name, "version") == 0) continue; /* nothing for now */ else if (compatibility) continue; /* just ignore */ else PROP_ERROR(name, "Unknown theme property name"); } switch (p[i].type) { case CHAR: while (value[0] == ' ' || value[0] == '\n' || value[0] == '\0') value++; if (sscanf(value, "%c", &charvalue) != 1) PROP_ERROR(p[i].name, "Cannot get a char"); *((int*)p[i].value) = charvalue; break; case INT: if (sscanf(value, "%d", &intvalue) != 1) PROP_ERROR(p[i].name, "Cannot get a int"); *((int*)p[i].value) = intvalue; break; case UINT: if (sscanf(value, "%u", &uintvalue) != 1) PROP_ERROR(p[i].name, "Cannot get a uint"); *((unsigned int*)p[i].value) = uintvalue; break; case BOOL: boolvalue = (strstr(value, "true") != NULL) ? true :false; *((bool*)p[i].value) = boolvalue; break; case COLOR: if (sscanf(value, "%s %s", c1, c2) != 2) PROP_ERROR(p[i].name, "Cannot get 2 colors"); /* Foreground */ for (j = 0; j < NCOLOR ; j++) if ((strstr(c1, color[j].name)) != NULL) break; if (j >= NCOLOR) PROP_ERROR(p[i].name, "Bad foreground"); fg = color[j].value; /* Background */ for (j = 0; j < NCOLOR ; j++) if ((strstr(c2, color[j].name)) != NULL) break; if (j >= NCOLOR) PROP_ERROR(p[i].name, "Bad background"); bg = color[j].value; /* Flags */ flags = 0; for (j = 0; j < NATTR; j++) if (strstr(value, attr[j].name) != NULL) flags |= attr[j].value; *((int*)p[i].value) = bsddialog_color(fg, bg, flags); break; case COMPAT: /* * usr.sbin/bsdconfig/share/dialog.subr:2255 * uses this parameter to set NO_SHADOW. * Set t.shadow.[y|x] for compatibilty. */ if (strcmp(name, "use_shadow") == 0) { if (strcasestr(value, "off") != NULL) t.shadow.y = t.shadow.x = 0; } break; } } fclose(fp); if (bsddialog_set_theme(&t) != BSDDIALOG_OK) exit_error(false, bsddialog_geterror()); } void setdeftheme(enum bsddialog_default_theme theme) { if (bsddialog_hascolors() == false) return; if (bsddialog_set_default_theme(theme) != BSDDIALOG_OK) exit_error(false, bsddialog_geterror()); } void startuptheme(void) { bool sep; char *env, *file, *home, path[PATH_MAX]; env = getenv("NO_COLOR"); if (env != NULL && env[0] != '\0') setdeftheme(BSDDIALOG_THEME_BLACKWHITE); if ((home = getenv("HOME")) != NULL) { sep = (strcmp(home, "/") == 0) ? false : true; snprintf(path, PATH_MAX, "%s%s.bsddialog.conf", home, sep ? "/" : ""); if (access(path, F_OK) == 0) loadtheme(path, false); if ((file = getenv("BSDDIALOG_COMPATRC")) != NULL) { snprintf(path, PATH_MAX, "%s%s%s", home, sep ? "/" : "", file); if (access(path, F_OK) == 0) loadtheme(path, true); } } if ((file = getenv("BSDDIALOG_THEMEFILE")) != NULL) { if (access(file, F_OK) == 0) loadtheme(file, false); } } void bikeshed(struct bsddialog_conf *conf) { int margin, i; int colors[8] = {0, 0, 0, 0, 0, 0, 0, 0}; char delim[8] = {'[', '<', '(', '|', ']', '>', ')', '|'}; enum bsddialog_color col[6]; struct timeval tv; /* theme */ if (bsddialog_get_theme(&t) != BSDDIALOG_OK) exit_error(false, bsddialog_geterror()); gettimeofday(&tv, NULL); srand(tv.tv_usec); for (i = 0; i < 6; i++) { do { col[i] = rand() % 8; } while (colors[col[i]] == 1); colors[col[i]] = 1; } t.screen.color = bsddialog_color(col[4], col[3], 0); t.shadow.color = bsddialog_color(col[0], col[0], 0); t.shadow.y = 1, t.shadow.x = 2, t.dialog.delimtitle = (~rand() & 1) ? true : false; t.dialog.titlecolor = bsddialog_color(col[3], col[5], 0); t.dialog.lineraisecolor = bsddialog_color(col[0], col[5], 0); t.dialog.linelowercolor = bsddialog_color(col[0], col[5], 0); t.dialog.color = bsddialog_color(col[0], col[5], 0); t.dialog.bottomtitlecolor = bsddialog_color(col[0], col[5], 0); t.dialog.arrowcolor = bsddialog_color(col[3], col[5], 0); t.menu.f_prefixcolor = bsddialog_color(col[5], col[3], 0); t.menu.prefixcolor = bsddialog_color(col[0], col[5], 0); t.menu.f_selectorcolor = bsddialog_color(col[5], col[3], 0); t.menu.selectorcolor = bsddialog_color(col[0], col[5], 0); t.menu.f_desccolor = bsddialog_color(col[5], col[3], 0); t.menu.desccolor = bsddialog_color(col[0], col[5], 0); t.menu.f_namecolor = bsddialog_color(col[5], col[3], 0); t.menu.namecolor = bsddialog_color(col[3], col[5], 0); t.menu.f_shortcutcolor = bsddialog_color(col[1], col[3], 0); t.menu.shortcutcolor = bsddialog_color(col[1], col[5], 0); t.menu.bottomdesccolor = bsddialog_color(col[4], col[3], 0); t.menu.sepnamecolor = bsddialog_color(col[1], col[5], 0); t.menu.sepdesccolor = bsddialog_color(col[1], col[5], 0); t.form.f_fieldcolor = bsddialog_color(col[5], col[3], 0); t.form.fieldcolor = bsddialog_color(col[5], col[4], 0); t.form.readonlycolor = bsddialog_color(col[4], col[5], 0); t.form.bottomdesccolor = bsddialog_color(col[4], col[3], 0); t.bar.f_color = bsddialog_color(col[5], col[3], 0); t.bar.color = bsddialog_color(col[3], col[5], 0); t.button.minmargin = 1, t.button.maxmargin = 5, i = rand() % 4; t.button.leftdelim = delim[i]; t.button.rightdelim = delim[i + 4]; t.button.f_delimcolor = bsddialog_color(col[5], col[3], 0); t.button.delimcolor = bsddialog_color(col[0], col[5], 0); t.button.f_color = bsddialog_color(col[2], col[3], 0); t.button.color = bsddialog_color(col[0], col[5], 0); t.button.f_shortcutcolor = bsddialog_color(col[5], col[3], 0); t.button.shortcutcolor = bsddialog_color(col[1], col[5], 0); if (bsddialog_set_theme(&t)) exit_error(false, bsddialog_geterror()); /* conf */ conf->button.always_active = (~rand() & 1) ? true : false; if ((i = rand() % 3) != 0) /* default "d/m/y" */ conf->date.format = (i & 1) ? "m/d/y" : "y/m/d" ; if (conf->title != NULL) { memset(title, 0, 1024); margin = rand() % 5; memset(title, ' ', margin); strcpy(title + margin, conf->title); memset(title + strlen(title), ' ', margin); conf->title = title; } }