/* * rofi * * MIT/X11 License * Copyright © 2013-2023 Qball Cow * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** Log domain for this module */ #define G_LOG_DOMAIN "XrmOptions" #include "xrmoptions.h" #include "helper.h" #include "rofi-types.h" #include "rofi.h" #include "settings.h" #include "xcb-internal.h" #include "xcb.h" #include #include #include #include #include #include #include #include ThemeWidget *rofi_configuration = NULL; /** Different sources of configuration. */ const char *const ConfigSourceStr[] = {"Default", "File", "Rasi File", "Commandline", "Don't Display"}; /** Enumerator of different sources of configuration. */ enum ConfigSource { CONFIG_DEFAULT = 0, CONFIG_FILE = 1, CONFIG_FILE_THEME = 2, CONFIG_CMDLINE = 3, CONFIG_NO_DISPLAY = 4 }; typedef struct { int type; const char *name; union { unsigned int *num; int *snum; char **str; void *pointer; char *charc; } value; char *mem; const char *comment; enum ConfigSource source; } XrmOption; /** * Map X resource and commandline options to internal options * Currently supports string, boolean and number (signed and unsigned). */ static XrmOption xrmOptions[] = { {xrm_String, "switchers", {.str = &config.modes}, NULL, "", CONFIG_DEFAULT}, {xrm_String, "modi", {.str = &config.modes}, NULL, "Enabled modes", CONFIG_DEFAULT}, {xrm_String, "modes", {.str = &config.modes}, NULL, "Enable modes", CONFIG_DEFAULT}, {xrm_String, "font", {.str = &config.menu_font}, NULL, "Font to use", CONFIG_DEFAULT}, {xrm_Number, "location", {.num = &config.location}, NULL, "Location on screen", CONFIG_DEFAULT}, {xrm_SNumber, "yoffset", {.snum = &config.y_offset}, NULL, "Y-offset relative to location. *DEPRECATED* see rofi-theme manpage for " "new option", CONFIG_DEFAULT}, {xrm_SNumber, "xoffset", {.snum = &config.x_offset}, NULL, "X-offset relative to location. *DEPRECATED* see rofi-theme manpage for " "new option", CONFIG_DEFAULT}, {xrm_Boolean, "fixed-num-lines", {.num = &config.fixed_num_lines}, NULL, "Always show number of lines", CONFIG_DEFAULT}, {xrm_Boolean, "show-icons", {.snum = &config.show_icons}, NULL, "Whether to load and show icons", CONFIG_DEFAULT}, {xrm_String, "preview-cmd", {.str = &config.preview_cmd}, NULL, "Custom command to generate preview icons", CONFIG_DEFAULT}, {xrm_String, "terminal", {.str = &config.terminal_emulator}, NULL, "Terminal to use", CONFIG_DEFAULT}, {xrm_String, "ssh-client", {.str = &config.ssh_client}, NULL, "Ssh client to use", CONFIG_DEFAULT}, {xrm_String, "ssh-command", {.str = &config.ssh_command}, NULL, "Ssh command to execute", CONFIG_DEFAULT}, {xrm_String, "run-command", {.str = &config.run_command}, NULL, "Run command to execute", CONFIG_DEFAULT}, {xrm_String, "run-list-command", {.str = &config.run_list_command}, NULL, "Command to get extra run targets", CONFIG_DEFAULT}, {xrm_String, "run-shell-command", {.str = &config.run_shell_command}, NULL, "Run command to execute that runs in shell", CONFIG_DEFAULT}, {xrm_String, "window-command", {.str = &config.window_command}, NULL, "Command to executed when -kb-accept-alt binding is hit on selected " "window ", CONFIG_DEFAULT}, {xrm_String, "window-match-fields", {.str = &config.window_match_fields}, NULL, "Window fields to match in window mode", CONFIG_DEFAULT}, {xrm_String, "icon-theme", {.str = &config.icon_theme}, NULL, "Theme to use to look for icons", CONFIG_DEFAULT}, {xrm_String, "drun-match-fields", {.str = &config.drun_match_fields}, NULL, "Desktop entry fields to match in drun", CONFIG_DEFAULT}, {xrm_String, "drun-categories", {.str = &config.drun_categories}, NULL, "Only show Desktop entry from these categories", CONFIG_DEFAULT}, {xrm_Boolean, "drun-show-actions", {.num = &config.drun_show_actions}, NULL, "Desktop entry show actions.", CONFIG_DEFAULT}, {xrm_String, "drun-display-format", {.str = &config.drun_display_format}, NULL, "DRUN format string. (Supports: generic,name,comment,exec,categories)", CONFIG_DEFAULT}, {xrm_String, "drun-url-launcher", {.str = &config.drun_url_launcher}, NULL, "Command to open a Desktop Entry that is a Link.", CONFIG_DEFAULT}, {xrm_Boolean, "disable-history", {.num = &config.disable_history}, NULL, "Disable history in run/ssh", CONFIG_DEFAULT}, {xrm_String, "ignored-prefixes", {.str = &config.ignored_prefixes}, NULL, "Programs ignored for history", CONFIG_DEFAULT}, {xrm_Boolean, "sort", {.num = &config.sort}, NULL, "Sort menu when filtered", CONFIG_DEFAULT}, {xrm_String, "sorting-method", {.str = &config.sorting_method}, NULL, "Choose sort strategy: normal (levenshtein) or fzf.", CONFIG_DEFAULT}, {xrm_Boolean, "case-sensitive", {.num = &config.case_sensitive}, NULL, "Set case-sensitivity", CONFIG_DEFAULT}, {xrm_Boolean, "cycle", {.num = &config.cycle}, NULL, "Cycle through the results list", CONFIG_DEFAULT}, {xrm_Boolean, "sidebar-mode", {.num = &config.sidebar_mode}, NULL, "Enable sidebar-mode", CONFIG_DEFAULT}, {xrm_Boolean, "hover-select", {.snum = &config.hover_select}, NULL, "Enable hover-select", CONFIG_DEFAULT}, {xrm_SNumber, "eh", {.snum = &config.element_height}, NULL, "Row height (in chars)", CONFIG_DEFAULT}, {xrm_Boolean, "auto-select", {.num = &config.auto_select}, NULL, "Enable auto select mode", CONFIG_DEFAULT}, {xrm_Boolean, "parse-hosts", {.num = &config.parse_hosts}, NULL, "Parse hosts file for ssh mode", CONFIG_DEFAULT}, {xrm_Boolean, "parse-known-hosts", {.num = &config.parse_known_hosts}, NULL, "Parse known_hosts file for ssh mode", CONFIG_DEFAULT}, {xrm_String, "combi-modi", {.str = &config.combi_modes}, NULL, "Set the modes to combine in combi mode", CONFIG_DEFAULT}, {xrm_String, "combi-modes", {.str = &config.combi_modes}, NULL, "Set the modes to combine in combi mode", CONFIG_DEFAULT}, {xrm_String, "matching", {.str = &config.matching}, NULL, "Set the matching algorithm. (normal, regex, glob, fuzzy, prefix)", CONFIG_DEFAULT}, {xrm_Boolean, "tokenize", {.num = &config.tokenize}, NULL, "Tokenize input string", CONFIG_DEFAULT}, {xrm_String, "monitor", {.str = &config.monitor}, NULL, "", CONFIG_DEFAULT}, /* Alias for dmenu compatibility. */ {xrm_String, "m", {.str = &config.monitor}, NULL, "Monitor id to show on", CONFIG_DEFAULT}, {xrm_String, "filter", {.str = &config.filter}, NULL, "Pre-set filter", CONFIG_DEFAULT}, {xrm_SNumber, "dpi", {.snum = &config.dpi}, NULL, "DPI", CONFIG_DEFAULT}, {xrm_Number, "threads", {.num = &config.threads}, NULL, "Threads to use for string matching", CONFIG_DEFAULT}, {xrm_Number, "scroll-method", {.num = &config.scroll_method}, NULL, "Scrolling method. (0: Page, 1: Centered)", CONFIG_DEFAULT}, {xrm_String, "window-format", {.str = &config.window_format}, NULL, "Window Format. w (desktop name), t (title), n (name), r (role), c " "(class)", CONFIG_DEFAULT}, {xrm_Boolean, "click-to-exit", {.snum = &config.click_to_exit}, NULL, "Click outside the window to exit", CONFIG_DEFAULT}, {xrm_String, "theme", {.str = &config.theme}, NULL, "New style theme file", CONFIG_NO_DISPLAY}, {xrm_Number, "max-history-size", {.num = &config.max_history_size}, NULL, "Max history size (WARNING: can cause slowdowns when set too high).", CONFIG_DEFAULT}, {xrm_Boolean, "combi-hide-mode-prefix", {.snum = &config.combi_hide_mode_prefix}, NULL, "Hide the prefix mode prefix on the combi view.**deprecated** use " "combi-display-format", CONFIG_DEFAULT}, {xrm_String, "combi-display-format", {.str = &config.combi_display_format}, NULL, "Combi format string. (Supports: mode, text)", CONFIG_DEFAULT}, {xrm_Char, "matching-negate-char", {.charc = &config.matching_negate_char}, NULL, "Set the character used to negate the matching. ('\\0' to disable)", CONFIG_DEFAULT}, {xrm_String, "cache-dir", {.str = &config.cache_dir}, NULL, "Directory where history and temporary files are stored.", CONFIG_DEFAULT}, {xrm_Boolean, "window-thumbnail", {.snum = &config.window_thumbnail}, NULL, "Show window thumbnail (if available) as icon in window switcher.", CONFIG_DEFAULT}, {xrm_Boolean, "drun-use-desktop-cache", {.snum = &config.drun_use_desktop_cache}, NULL, "DRUN: build and use a cache with desktop file content.", CONFIG_DEFAULT}, {xrm_Boolean, "drun-reload-desktop-cache", {.snum = &config.drun_reload_desktop_cache}, NULL, "DRUN: If enabled, reload the cache with desktop file content.", CONFIG_DEFAULT}, {xrm_Boolean, "normalize-match", {.snum = &config.normalize_match}, NULL, "Normalize string when matching (disables match highlighting).", CONFIG_DEFAULT}, {xrm_Boolean, "steal-focus", {.snum = &config.steal_focus}, NULL, "Steal focus on launch and restore to window that had it on rofi start on " "close .", CONFIG_DEFAULT}, {xrm_String, "application-fallback-icon", {.str = &(config.application_fallback_icon)}, NULL, "Fallback icon to use when the application icon is not found in run/drun.", CONFIG_DEFAULT}, {xrm_Number, "refilter-timeout-limit", {.num = &(config.refilter_timeout_limit)}, NULL, "When filtering takes more then this time (in ms) switch to delayed " "filter.", CONFIG_DEFAULT}, {xrm_Boolean, "xserver-i300-workaround", {.snum = &(config.xserver_i300_workaround)}, NULL, "Workaround for XServer issue #300 (issue #611 for rofi.)", CONFIG_DEFAULT}, {xrm_String, "completer-mode", {.str = &(config.completer_mode)}, NULL, "What completer to use for drun/run.", CONFIG_DEFAULT}, }; /** Dynamic array of extra options */ XrmOption *extra_options = NULL; /** Number of entries in extra options array */ unsigned int num_extra_options = 0; /** This is a big hack, we need to fix this. */ GList *extra_parsed_options = NULL; static gboolean __config_parser_set_property(XrmOption *option, const Property *p, char **error); void config_parser_add_option(XrmOptionType type, const char *key, void **value, const char *comment) { extra_options = g_realloc(extra_options, (num_extra_options + 1) * sizeof(XrmOption)); extra_options[num_extra_options].type = type; extra_options[num_extra_options].name = key; extra_options[num_extra_options].value.pointer = value; extra_options[num_extra_options].comment = comment; extra_options[num_extra_options].source = CONFIG_DEFAULT; switch (type) { case xrm_String: extra_options[num_extra_options].mem = ((char *)(*value)); break; default: extra_options[num_extra_options].mem = NULL; break; } for (GList *iter = g_list_first(extra_parsed_options); iter != NULL; iter = g_list_next(iter)) { if (g_strcmp0(((Property *)(iter->data))->name, key) == 0) { char *error = NULL; g_debug("Setting property from backup list: %s", key); if (__config_parser_set_property(&(extra_options[num_extra_options]), (Property *)(iter->data), &error)) { g_debug("Failed to set property on custom entry: %s", key); g_free(error); } num_extra_options++; return; } } num_extra_options++; } /** * Parse an option from the commandline vector. */ static void config_parse_cmd_option(XrmOption *option) { // Prepend a - to the option name. char *key = g_strdup_printf("-%s", option->name); switch (option->type) { case xrm_Number: if (find_arg_uint(key, option->value.num) == TRUE) { option->source = (option->source & ~3) | CONFIG_CMDLINE; } break; case xrm_SNumber: if (find_arg_int(key, option->value.snum) == TRUE) { option->source = (option->source & ~3) | CONFIG_CMDLINE; } break; case xrm_String: if (find_arg_str(key, option->value.str) == TRUE) { if (option->mem != NULL) { g_free(option->mem); option->mem = NULL; } option->source = (option->source & ~3) | CONFIG_CMDLINE; } break; case xrm_Boolean: if (find_arg(key) >= 0) { *(option->value.num) = TRUE; option->source = (option->source & ~3) | CONFIG_CMDLINE; } else { g_free(key); key = g_strdup_printf("-no-%s", option->name); if (find_arg(key) >= 0) { *(option->value.num) = FALSE; option->source = (option->source & ~3) | CONFIG_CMDLINE; } } break; case xrm_Char: if (find_arg_char(key, option->value.charc) == TRUE) { option->source = (option->source & ~3) | CONFIG_CMDLINE; } break; default: break; } g_free(key); } static gboolean config_parser_form_rasi_format(GString *str, char **tokens, int count, char *argv, gboolean string) { if (strlen(argv) > 4096) { return FALSE; } for (int j = 0; j < (count - 1); j++) { g_string_append_printf(str, "%s { ", tokens[j]); } if (string) { char *esc = g_strescape(argv, NULL); g_string_append_printf(str, "%s: \"%s\";", tokens[count - 1], esc); g_free(esc); } else { g_string_append_printf(str, "%s: %s;", tokens[count - 1], argv); } for (int j = 0; j < (count - 1); j++) { g_string_append(str, " } "); } return TRUE; } void config_parse_cmd_options(void) { for (unsigned int i = 0; i < sizeof(xrmOptions) / sizeof(XrmOption); ++i) { XrmOption *op = &(xrmOptions[i]); config_parse_cmd_option(op); } for (unsigned int i = 0; i < num_extra_options; ++i) { XrmOption *op = &(extra_options[i]); config_parse_cmd_option(op); } /** copy of the argc for use in commandline argument parser. */ extern int stored_argc; /** copy of the argv pointer for use in the commandline argument parser */ extern char **stored_argv; for (int in = 1; in < (stored_argc - 1); in++) { if (stored_argv[in][0] == '-') { if (stored_argv[in + 1][0] == '-') { continue; } /** TODO: This is a hack, and should be fixed in a nicer way. */ char **tokens = g_strsplit(stored_argv[in], "-", 3); int count = 1; for (int j = 1; tokens && tokens[j]; j++) { count++; } if (count >= 2) { if (g_str_has_prefix(tokens[1], "theme")) { g_strfreev(tokens); tokens = g_strsplit(stored_argv[in], "+", 0); count = g_strv_length(tokens); if (count > 2) { GString *str = g_string_new(""); config_parser_form_rasi_format(str, &(tokens[1]), count - 1, stored_argv[in + 1], FALSE); if (rofi_theme_parse_string(str->str) == 1) { /** Failed to parse, try again as string. */ g_strfreev(tokens); g_string_free(str, TRUE); return; } g_string_free(str, TRUE); } } else if (g_strcmp0(tokens[1], "no") != 0) { GString *str = g_string_new("configuration { "); config_parser_form_rasi_format(str, &(tokens[1]), count - 1, stored_argv[in + 1], FALSE); g_string_append(str, "}"); g_debug("str: \"%s\"\n", str->str); if (rofi_theme_parse_string(str->str) == 1) { /** Failed to parse, try again as string. */ rofi_clear_error_messages(); g_string_assign(str, "configuration { "); config_parser_form_rasi_format(str, &(tokens[1]), count - 1, stored_argv[in + 1], TRUE); g_string_append(str, "}"); g_debug("str: \"%s\"\n", str->str); if (rofi_theme_parse_string(str->str) == 1) { /** Failed to parse, try again as string. */ rofi_clear_error_messages(); } } g_string_free(str, TRUE); } in++; } g_strfreev(tokens); } } } static gboolean __config_parser_set_property(XrmOption *option, const Property *p, char **error) { if (option->type == xrm_String) { if (p->type != P_STRING && (p->type != P_LIST && p->type != P_INTEGER)) { *error = g_strdup_printf("Option: %s needs to be set with a string not a %s.", option->name, PropertyTypeName[p->type]); return TRUE; } gchar *value = NULL; if (p->type == P_LIST) { for (GList *iter = p->value.list; iter != NULL; iter = g_list_next(iter)) { Property *p2 = (Property *)iter->data; if (value == NULL) { value = g_strdup((char *)(p2->value.s)); } else { char *nv = g_strjoin(",", value, (char *)(p2->value.s), NULL); g_free(value); value = nv; } } } else if (p->type == P_INTEGER) { value = g_strdup_printf("%d", p->value.i); } else { value = g_strdup(p->value.s); } if ((option)->mem != NULL) { g_free(option->mem); option->mem = NULL; } *(option->value.str) = value; // Memory (option)->mem = *(option->value.str); option->source = (option->source & ~3) | CONFIG_FILE_THEME; } else if (option->type == xrm_Number) { if (p->type != P_INTEGER) { *error = g_strdup_printf("Option: %s needs to be set with a number not a %s.", option->name, PropertyTypeName[p->type]); return TRUE; } *(option->value.snum) = p->value.i; option->source = (option->source & ~3) | CONFIG_FILE_THEME; } else if (option->type == xrm_SNumber) { if (p->type != P_INTEGER) { *error = g_strdup_printf("Option: %s needs to be set with a number not a %s.", option->name, PropertyTypeName[p->type]); return TRUE; } *(option->value.num) = (unsigned int)(p->value.i); option->source = (option->source & ~3) | CONFIG_FILE_THEME; } else if (option->type == xrm_Boolean) { if (p->type != P_BOOLEAN) { *error = g_strdup_printf("Option: %s needs to be set with a boolean not a %s.", option->name, PropertyTypeName[p->type]); return TRUE; } *(option->value.num) = (p->value.b); option->source = (option->source & ~3) | CONFIG_FILE_THEME; } else if (option->type == xrm_Char) { if (p->type != P_CHAR) { *error = g_strdup_printf( "Option: %s needs to be set with a character not a %s.", option->name, PropertyTypeName[p->type]); return TRUE; } *(option->value.charc) = (p->value.c); option->source = (option->source & ~3) | CONFIG_FILE_THEME; } else { // TODO add type *error = g_strdup_printf("Option: %s is not of a supported type: %s.", option->name, PropertyTypeName[p->type]); return TRUE; } return FALSE; } gboolean config_parse_set_property(const Property *p, char **error) { if (g_ascii_strcasecmp(p->name, "theme") == 0) { if (p->type == P_STRING) { *error = g_strdup_printf("The option:\n\nconfiguration\n{\n\ttheme: " "\"%s\";\n}\nis deprecated. Please replace " "with: @theme \"%s\" " "after the configuration block.", p->value.s, p->value.s); } else { *error = g_strdup_printf("The option:\n\nconfiguration\n{\n\ttheme: " "\"%s\";\n}\nis deprecated. Please replace " "with: @theme \"%s\" " "after the configuration block.", "myTheme", "myTheme"); } return TRUE; } for (unsigned int i = 0; i < sizeof(xrmOptions) / sizeof(XrmOption); ++i) { XrmOption *op = &(xrmOptions[i]); if (g_strcmp0(op->name, p->name) == 0) { return __config_parser_set_property(op, p, error); } } for (unsigned int i = 0; i < num_extra_options; ++i) { XrmOption *op = &(extra_options[i]); if (g_strcmp0(op->name, p->name) == 0) { return __config_parser_set_property(op, p, error); } } //*error = g_strdup_printf("Option: %s is not found.", p->name); g_debug("Option: %s is not found.", p->name); for (GList *iter = g_list_first(extra_parsed_options); iter != NULL; iter = g_list_next(iter)) { if (g_strcmp0(((Property *)(iter->data))->name, p->name) == 0) { rofi_theme_property_free((Property *)(iter->data)); iter->data = (void *)rofi_theme_property_copy(p, NULL); return FALSE; } } g_debug("Adding option: %s to backup list.", p->name); extra_parsed_options = g_list_append(extra_parsed_options, rofi_theme_property_copy(p, NULL)); return FALSE; } void config_xresource_free(void) { for (unsigned int i = 0; i < (sizeof(xrmOptions) / sizeof(*xrmOptions)); ++i) { if (xrmOptions[i].mem != NULL) { g_free(xrmOptions[i].mem); xrmOptions[i].mem = NULL; } } for (unsigned int i = 0; i < num_extra_options; ++i) { if (extra_options[i].mem != NULL) { g_free(extra_options[i].mem); extra_options[i].mem = NULL; } } if (extra_options != NULL) { g_free(extra_options); } g_list_free_full(extra_parsed_options, (GDestroyNotify)rofi_theme_property_free); } static void config_parse_dump_config_option(FILE *out, XrmOption *option) { if (option->type == xrm_Char || (option->source & 3) == CONFIG_DEFAULT) { fprintf(out, "/*"); } fprintf(out, "\t%s: ", option->name); switch (option->type) { case xrm_Number: fprintf(out, "%u", *(option->value.num)); break; case xrm_SNumber: fprintf(out, "%i", *(option->value.snum)); break; case xrm_String: if ((*(option->value.str)) != NULL) { // TODO should this be escaped? fprintf(out, "\"%s\"", *(option->value.str)); } break; case xrm_Boolean: fprintf(out, "%s", (*(option->value.num) == TRUE) ? "true" : "false"); break; case xrm_Char: // TODO if (*(option->value.charc) > 32 && *(option->value.charc) < 127) { fprintf(out, "'%c'", *(option->value.charc)); } else { fprintf(out, "'\\x%02X'", *(option->value.charc)); } fprintf(out, " /* unsupported */"); break; default: break; } fprintf(out, ";"); if (option->type == xrm_Char || (option->source & 3) == CONFIG_DEFAULT) { fprintf(out, "*/"); } fprintf(out, "\n"); } void config_parse_dump_config_rasi_format(FILE *out, gboolean changes) { fprintf(out, "configuration {\n"); unsigned int entries = sizeof(xrmOptions) / sizeof(*xrmOptions); for (unsigned int i = 0; i < entries; ++i) { // Skip duplicates. if ((i + 1) < entries) { if (xrmOptions[i].value.str == xrmOptions[i + 1].value.str) { continue; } } if ((xrmOptions[i].source & CONFIG_NO_DISPLAY) == CONFIG_NO_DISPLAY) { continue; } if (!changes || (xrmOptions[i].source & 3) != CONFIG_DEFAULT) { config_parse_dump_config_option(out, &(xrmOptions[i])); } } for (unsigned int i = 0; i < num_extra_options; i++) { if ((extra_options[i].source & CONFIG_NO_DISPLAY) == CONFIG_NO_DISPLAY) { continue; } if (!changes || (extra_options[i].source & 3) != CONFIG_DEFAULT) { config_parse_dump_config_option(out, &(extra_options[i])); } } for (unsigned int index = 0; index < rofi_configuration->num_widgets; index++) { rofi_theme_print_index(rofi_configuration->widgets[index], 2); } fprintf(out, "}\n"); if (config.theme != NULL) { fprintf(out, "@theme \"%s\"\r\n", config.theme); } } static void print_option_string(XrmOption *xo, int is_term) { int l = strlen(xo->name); if (is_term) { printf("\t" color_bold "-%s" color_reset " [string]%-*c%s\n", xo->name, 30 - l, ' ', xo->comment); printf("\t" color_italic "%s" color_reset, (*(xo->value.str) == NULL) ? "(unset)" : (*(xo->value.str))); printf(" " color_green "(%s)" color_reset "\n", ConfigSourceStr[xo->source & 3]); } else { printf("\t-%s [string]%-*c%s\n", xo->name, 30 - l, ' ', xo->comment); printf("\t\t%s", (*(xo->value.str) == NULL) ? "(unset)" : (*(xo->value.str))); printf(" (%s)\n", ConfigSourceStr[xo->source & 3]); } } static void print_option_number(XrmOption *xo, int is_term) { int l = strlen(xo->name); if (is_term) { printf("\t" color_bold "-%s" color_reset " [number]%-*c%s\n", xo->name, 30 - l, ' ', xo->comment); printf("\t" color_italic "%u" color_reset, *(xo->value.num)); printf(" " color_green "(%s)" color_reset "\n", ConfigSourceStr[xo->source & 3]); } else { printf("\t-%s [number]%-*c%s\n", xo->name, 30 - l, ' ', xo->comment); printf("\t\t%u", *(xo->value.num)); printf(" (%s)\n", ConfigSourceStr[xo->source & 3]); } } static void print_option_snumber(XrmOption *xo, int is_term) { int l = strlen(xo->name); if (is_term) { printf("\t" color_bold "-%s" color_reset " [number]%-*c%s\n", xo->name, 30 - l, ' ', xo->comment); printf("\t" color_italic "%d" color_reset, *(xo->value.snum)); printf(" " color_green "(%s)" color_reset "\n", ConfigSourceStr[xo->source & 3]); } else { printf("\t-%s [number]%-*c%s\n", xo->name, 30 - l, ' ', xo->comment); printf("\t\t%d", *(xo->value.snum)); printf(" (%s)\n", ConfigSourceStr[xo->source & 3]); } } static void print_option_char(XrmOption *xo, int is_term) { int l = strlen(xo->name); if (is_term) { printf("\t" color_bold "-%s" color_reset " [character]%-*c%s\n", xo->name, 30 - l, ' ', xo->comment); printf("\t" color_italic "%c" color_reset, *(xo->value.charc)); printf(" " color_green "(%s)" color_reset "\n", ConfigSourceStr[xo->source & 3]); } else { printf("\t-%s [character]%-*c%s\n", xo->name, 30 - l, ' ', xo->comment); printf("\t\t%c", *(xo->value.charc)); printf(" (%s)\n", ConfigSourceStr[xo->source & 3]); } } static void print_option_boolean(XrmOption *xo, int is_term) { int l = strlen(xo->name); if (is_term) { printf("\t" color_bold "-[no-]%s" color_reset " %-*c%s\n", xo->name, 33 - l, ' ', xo->comment); printf("\t" color_italic "%s" color_reset, (*(xo->value.snum)) ? "True" : "False"); printf(" " color_green "(%s)" color_reset "\n", ConfigSourceStr[xo->source & 3]); } else { printf("\t-[no-]%s %-*c%s\n", xo->name, 33 - l, ' ', xo->comment); printf("\t\t%s", (*(xo->value.snum)) ? "True" : "False"); printf(" (%s)\n", ConfigSourceStr[xo->source & 3]); } } static void print_option(XrmOption *xo, int is_term) { if ((xo->source & CONFIG_NO_DISPLAY) == CONFIG_NO_DISPLAY) { return; } switch (xo->type) { case xrm_String: print_option_string(xo, is_term); break; case xrm_Number: print_option_number(xo, is_term); break; case xrm_SNumber: print_option_snumber(xo, is_term); break; case xrm_Boolean: print_option_boolean(xo, is_term); break; case xrm_Char: print_option_char(xo, is_term); break; default: break; } } void print_options(void) { // Check output filedescriptor int is_term = isatty(fileno(stdout)); unsigned int entries = sizeof(xrmOptions) / sizeof(*xrmOptions); for (unsigned int i = 0; i < entries; ++i) { if ((i + 1) < entries) { if (xrmOptions[i].value.str == xrmOptions[i + 1].value.str) { continue; } } print_option(&xrmOptions[i], is_term); } for (unsigned int i = 0; i < num_extra_options; i++) { print_option(&extra_options[i], is_term); } } void print_help_msg(const char *option, const char *type, const char *text, const char *def, int isatty) { int l = 37 - strlen(option) - strlen(type); if (isatty) { printf("\t%s%s%s %s %-*c%s\n", color_bold, option, color_reset, type, l, ' ', text); if (def != NULL) { printf("\t\t%s%s%s\n", color_italic, def, color_reset); } } else { printf("\t%s %s %-*c%s\n", option, type, l, ' ', text); if (def != NULL) { printf("\t\t%s\n", def); } } } static char *config_parser_return_display_help_entry(XrmOption *option, size_t l) { int ll = (int)l; switch (option->type) { case xrm_Number: return g_markup_printf_escaped( " (%u) %s", ll, option->name, *(option->value.num), option->comment); case xrm_SNumber: return g_markup_printf_escaped( " (%d) %s", ll, option->name, *(option->value.snum), option->comment); case xrm_String: return g_markup_printf_escaped( "%-*s (%s) %s", ll, option->name, (*(option->value.str) != NULL) ? *(option->value.str) : "null", option->comment); case xrm_Boolean: return g_markup_printf_escaped( "%-*s (%s) %s", ll, option->name, (*(option->value.num) == TRUE) ? "true" : "false", option->comment); case xrm_Char: if (*(option->value.charc) > 32 && *(option->value.charc) < 127) { return g_markup_printf_escaped( "%-*s (%c) %s", ll, option->name, *(option->value.charc), option->comment); } else { return g_markup_printf_escaped( " (\\x%02X) %s", ll, option->name, *(option->value.charc), option->comment); } default: break; } return g_strdup("failed"); } char **config_parser_return_display_help(unsigned int *length) { unsigned int entries = sizeof(xrmOptions) / sizeof(*xrmOptions); char **retv = NULL; /** * Get length of name */ size_t max_length = 0; for (unsigned int i = 0; i < entries; ++i) { size_t l = strlen(xrmOptions[i].name); max_length = MAX(max_length, l); } for (unsigned int i = 0; i < num_extra_options; i++) { size_t l = strlen(extra_options[i].name); max_length = MAX(max_length, l); } /** * Generate entries */ for (unsigned int i = 0; i < entries; ++i) { if ((i + 1) < entries) { if (xrmOptions[i].value.str == xrmOptions[i + 1].value.str) { continue; } } if (strncmp(xrmOptions[i].name, "kb", 2) != 0 && strncmp(xrmOptions[i].name, "ml", 2) != 0 && strncmp(xrmOptions[i].name, "me", 2) != 0) { continue; } retv = g_realloc(retv, ((*length) + 2) * sizeof(char *)); retv[(*length)] = config_parser_return_display_help_entry(&xrmOptions[i], max_length); (*length)++; } for (unsigned int i = 0; i < num_extra_options; i++) { if (strncmp(extra_options[i].name, "kb", 2) != 0 && strncmp(extra_options[i].name, "ml", 2) != 0 && strncmp(extra_options[i].name, "me", 2) != 0) { continue; } retv = g_realloc(retv, ((*length) + 2) * sizeof(char *)); retv[(*length)] = config_parser_return_display_help_entry(&extra_options[i], max_length); (*length)++; } if ((*length) > 0) { retv[(*length)] = NULL; } return retv; }