Created SILC Runtime Toolkit git repository Part I.
[runtime.git] / apps / irssi / src / fe-common / core / themes.c
diff --git a/apps/irssi/src/fe-common/core/themes.c b/apps/irssi/src/fe-common/core/themes.c
deleted file mode 100644 (file)
index b4407bd..0000000
+++ /dev/null
@@ -1,1391 +0,0 @@
-/*
- themes.c : irssi
-
-    Copyright (C) 1999-2000 Timo Sirainen
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-*/
-
-#include "module.h"
-#include "module-formats.h"
-#include "signals.h"
-#include "commands.h"
-#include "levels.h"
-#include "misc.h"
-#include "special-vars.h"
-#include "lib-config/iconfig.h"
-#include "settings.h"
-
-#include "themes.h"
-#include "printtext.h"
-
-#include "default-theme.h"
-
-GSList *themes;
-THEME_REC *current_theme;
-GHashTable *default_formats;
-
-static int init_finished;
-static char *init_errors;
-static THEME_REC *internal_theme;
-
-static int theme_read(THEME_REC *theme, const char *path);
-
-THEME_REC *theme_create(const char *path, const char *name)
-{
-       THEME_REC *rec;
-
-       g_return_val_if_fail(path != NULL, NULL);
-       g_return_val_if_fail(name != NULL, NULL);
-
-       rec = g_new0(THEME_REC, 1);
-       rec->refcount = 1;
-       rec->path = g_strdup(path);
-       rec->name = g_strdup(name);
-       rec->abstracts = g_hash_table_new((GHashFunc) g_str_hash,
-                                         (GCompareFunc) g_str_equal);
-       rec->modules = g_hash_table_new((GHashFunc) g_istr_hash,
-                                       (GCompareFunc) g_istr_equal);
-       themes = g_slist_append(themes, rec);
-       signal_emit("theme created", 1, rec);
-
-       return rec;
-}
-
-static void theme_abstract_destroy(char *key, char *value)
-{
-       g_free(key);
-        g_free(value);
-}
-
-static void theme_module_destroy(const char *key, MODULE_THEME_REC *rec)
-{
-       int n;
-
-       for (n = 0; n < rec->count; n++) {
-               g_free_not_null(rec->formats[n]);
-               g_free_not_null(rec->expanded_formats[n]);
-       }
-       g_free(rec->formats);
-       g_free(rec->expanded_formats);
-
-       g_free(rec->name);
-       g_free(rec);
-}
-
-static void theme_real_destroy(THEME_REC *rec)
-{
-       g_hash_table_foreach(rec->abstracts, (GHFunc) theme_abstract_destroy, NULL);
-       g_hash_table_destroy(rec->abstracts);
-       g_hash_table_foreach(rec->modules, (GHFunc) theme_module_destroy, NULL);
-       g_hash_table_destroy(rec->modules);
-
-       g_slist_foreach(rec->replace_values, (GFunc) g_free, NULL);
-       g_slist_free(rec->replace_values);
-
-       g_free(rec->path);
-       g_free(rec->name);
-       g_free(rec);
-}
-
-static void theme_unref(THEME_REC *rec)
-{
-       if (--rec->refcount == 0)
-               theme_real_destroy(rec);
-}
-
-void theme_destroy(THEME_REC *rec)
-{
-       themes = g_slist_remove(themes, rec);
-       signal_emit("theme destroyed", 1, rec);
-
-       theme_unref(rec);
-}
-
-static char *theme_replace_expand(THEME_REC *theme, int index,
-                                 char default_fg, char default_bg,
-                                 char *last_fg, char *last_bg,
-                                 char chr, int flags)
-{
-       GSList *rec;
-       char *ret, *abstract, data[2];
-
-       rec = g_slist_nth(theme->replace_values, index);
-       g_return_val_if_fail(rec != NULL, NULL);
-
-       data[0] = chr; data[1] = '\0';
-
-       abstract = rec->data;
-       abstract = theme_format_expand_data(theme, (const char **) &abstract,
-                                           default_fg, default_bg,
-                                           last_fg, last_bg, flags);
-       ret = parse_special_string(abstract, NULL, NULL, data, NULL,
-                                  PARSE_FLAG_ONLY_ARGS);
-       g_free(abstract);
-       return ret;
-}
-
-static const char *fgcolorformats = "nkrgybmpcwKRGYBMPCW";
-static const char *bgcolorformats = "n01234567";
-
-#define IS_FGCOLOR_FORMAT(c) \
-        ((c) != '\0' && strchr(fgcolorformats, c) != NULL)
-#define IS_BGCOLOR_FORMAT(c) \
-        ((c) != '\0' && strchr(bgcolorformats, c) != NULL)
-
-/* append "variable" part in $variable, ie. not the contents of the variable */
-static void theme_format_append_variable(GString *str, const char **format)
-{
-       const char *orig;
-       char *value, *args[1] = { NULL };
-       int free_ret;
-
-       orig = *format;
-       (*format)++;
-
-       value = parse_special((char **) format, NULL, NULL,
-                             args, &free_ret, NULL, PARSE_FLAG_ONLY_ARGS);
-       if (free_ret) g_free(value);
-
-       if (**format != '\0')
-               (*format)++;
-
-       /* append the variable name */
-       value = g_strndup(orig, (int) (*format-orig));
-       g_string_append(str, value);
-       g_free(value);
-}
-
-/* append next "item", either a character, $variable or %format */
-static void theme_format_append_next(THEME_REC *theme, GString *str,
-                                    const char **format,
-                                    char default_fg, char default_bg,
-                                    char *last_fg, char *last_bg,
-                                    int flags)
-{
-       int index;
-       unsigned char chr;
-
-       chr = **format;
-       if ((chr == '$' || chr == '%') &&
-           (*format)[1] == '\0') {
-               /* last char, always append */
-               g_string_append_c(str, chr);
-               (*format)++;
-                return;
-       }
-
-       if (chr == '$') {
-               /* $variable .. we'll always need to skip this, since it
-                  may contain characters that are in replace chars. */
-               theme_format_append_variable(str, format);
-               return;
-       }
-
-       if (**format == '%') {
-               /* format */
-               (*format)++;
-               if (**format != '{' && **format != '}') {
-                        chr = **format;
-                       if (**format == 'n') {
-                               /* %n = change to default color */
-                               g_string_append(str, "%n");
-
-                               if (default_bg != 'n') {
-                                       g_string_append_c(str, '%');
-                                       g_string_append_c(str, default_bg);
-                               }
-                               if (default_fg != 'n') {
-                                       g_string_append_c(str, '%');
-                                       g_string_append_c(str, default_fg);
-                               }
-
-                               *last_fg = default_fg;
-                               *last_bg = default_bg;
-                       } else {
-                               if (IS_FGCOLOR_FORMAT(chr))
-                                       *last_fg = chr;
-                               if (IS_BGCOLOR_FORMAT(chr))
-                                       *last_bg = chr;
-                               g_string_append_c(str, '%');
-                               g_string_append_c(str, chr);
-                       }
-                       (*format)++;
-                       return;
-               }
-
-               /* %{ or %} gives us { or } char - keep the % char
-                  though to make sure {} isn't treated as abstract */
-               g_string_append_c(str, '%');
-               chr = **format;
-       }
-
-       index = (flags & EXPAND_FLAG_IGNORE_REPLACES) ? -1 :
-               theme->replace_keys[(int) (unsigned char) chr];
-       if (index == -1)
-               g_string_append_c(str, chr);
-       else {
-               char *value;
-
-               value = theme_replace_expand(theme, index,
-                                            default_fg, default_bg,
-                                            last_fg, last_bg, chr, flags);
-               g_string_append(str, value);
-               g_free(value);
-       }
-
-        (*format)++;
-}
-
-/* returns TRUE if data is empty, or the data is a $variable which is empty */
-static int data_is_empty(const char **data)
-{
-       /* since we don't know the real argument list, assume there's always
-          an argument in them */
-       static char *arglist[] = {
-               "x", "x", "x", "x", "x", "x","x", "x", "x", "x",
-               NULL
-       };
-       SERVER_REC *server;
-       const char *p;
-       char *ret;
-        int free_ret, empty;
-
-        p = *data;
-       while (*p == ' ') p++;
-
-       if (*p == '}') {
-                /* empty */
-                *data = p+1;
-                return TRUE;
-       }
-
-       if (*p != '$') {
-                /* not empty */
-               return FALSE;
-       }
-
-       /* variable - check if it's empty */
-        p++;
-
-       server = active_win == NULL ? NULL :
-               active_win->active_server != NULL ?
-               active_win->active_server : active_win->connect_server;
-
-       ret = parse_special((char **) &p, server,
-                           active_win == NULL ? NULL : active_win->active,
-                           arglist, &free_ret, NULL, 0);
-        p++;
-
-       while (*p == ' ') p++;
-       empty = *p == '}' && (ret == NULL || *ret == '\0');
-        if (free_ret) g_free(ret);
-
-       if (empty) {
-               /* empty */
-               *data = p+1;
-                return TRUE;
-       }
-
-        return FALSE;
-}
-
-/* return "data" from {abstract data} string */
-char *theme_format_expand_get(THEME_REC *theme, const char **format)
-{
-       GString *str;
-       char *ret, dummy;
-       int braces = 1; /* we start with one brace opened */
-
-       str = g_string_new(NULL);
-       while (**format != '\0' && braces != 0) {
-               if (**format == '{')
-                       braces++;
-               else if (**format == '}')
-                       braces--;
-               else if ((braces > 1) && (**format == ' ')) {
-                       g_string_append(str, "\\x20");
-                       (*format)++;
-                       continue;
-               } else {
-                       theme_format_append_next(theme, str, format,
-                                                'n', 'n',
-                                                &dummy, &dummy, 0);
-                       continue;
-               }
-               
-               if (braces == 0) {
-                       (*format)++;
-                       break;
-               }
-
-               g_string_append_c(str, **format);
-               (*format)++;
-       }
-
-       ret = str->str;
-        g_string_free(str, FALSE);
-        return ret;
-}
-
-/* expand a single {abstract ...data... } */
-static char *theme_format_expand_abstract(THEME_REC *theme,
-                                         const char **formatp,
-                                         char default_fg, char default_bg,
-                                         int flags)
-{
-       GString *str;
-       const char *p, *format;
-       char *abstract, *data, *ret;
-       int len;
-
-       format = *formatp;
-
-       /* get abstract name first */
-       p = format;
-       while (*p != '\0' && *p != ' ' &&
-              *p != '{' && *p != '}') p++;
-       if (*p == '\0' || p == format)
-               return NULL; /* error */
-
-       len = (int) (p-format);
-       abstract = g_strndup(format, len);
-
-       /* skip the following space, if there's any more spaces they're
-          treated as arguments */
-       if (*p == ' ') {
-               len++;
-               if ((flags & EXPAND_FLAG_IGNORE_EMPTY) && data_is_empty(&p)) {
-                       *formatp = p;
-                       g_free(abstract);
-                       return NULL;
-               }
-       }
-       *formatp = format+len;
-
-       /* get the abstract data */
-       data = g_hash_table_lookup(theme->abstracts, abstract);
-       g_free(abstract);
-       if (data == NULL) {
-               /* unknown abstract, just display the data */
-               data = "$0-";
-       }
-       abstract = g_strdup(data);
-
-       /* we'll need to get the data part. it may contain
-          more abstracts, they are _NOT_ expanded. */
-       data = theme_format_expand_get(theme, formatp);
-       len = strlen(data);
-
-       if (len > 1 && i_isdigit(data[len-1]) && data[len-2] == '$') {
-               /* ends with $<digit> .. this breaks things if next
-                  character is digit or '-' */
-                char digit, *tmp;
-
-               tmp = data;
-               digit = tmp[len-1];
-               tmp[len-1] = '\0';
-
-               data = g_strdup_printf("%s{%c}", tmp, digit);
-               g_free(tmp);
-       }
-
-       ret = parse_special_string(abstract, NULL, NULL, data, NULL,
-                                  PARSE_FLAG_ONLY_ARGS);
-       g_free(abstract);
-        g_free(data);
-       str = g_string_new(NULL);
-       p = ret;
-       while (*p != '\0') {
-               if (*p == '\\') {
-                       int chr;
-                       p++;
-                       chr = expand_escape(&p);
-                       g_string_append_c(str, chr != -1 ? chr : *p);
-               } else
-                       g_string_append_c(str, *p);
-               p++;
-       }
-       g_free(ret);
-       abstract = str->str;
-       g_string_free(str, FALSE);
-
-       /* abstract may itself contain abstracts or replaces */
-       p = abstract;
-       ret = theme_format_expand_data(theme, &p, default_fg, default_bg,
-                                      &default_fg, &default_bg,
-                                      flags | EXPAND_FLAG_LASTCOLOR_ARG);
-       g_free(abstract);
-       return ret;
-}
-
-/* expand the data part in {abstract data} */
-char *theme_format_expand_data(THEME_REC *theme, const char **format,
-                              char default_fg, char default_bg,
-                              char *save_last_fg, char *save_last_bg,
-                              int flags)
-{
-       GString *str;
-       char *ret, *abstract;
-       char last_fg, last_bg;
-        int recurse_flags;
-
-       last_fg = default_fg;
-       last_bg = default_bg;
-        recurse_flags = flags & EXPAND_FLAG_RECURSIVE_MASK;
-
-       str = g_string_new(NULL);
-       while (**format != '\0') {
-               if ((flags & EXPAND_FLAG_ROOT) == 0 && **format == '}') {
-                       /* ignore } if we're expanding original string */
-                       (*format)++;
-                       break;
-               }
-
-               if (**format != '{') {
-                       if ((flags & EXPAND_FLAG_LASTCOLOR_ARG) &&
-                           **format == '$' && (*format)[1] == '0') {
-                               /* save the color before $0 ..
-                                  this is for the %n replacing */
-                               if (save_last_fg != NULL) {
-                                       *save_last_fg = last_fg;
-                                       save_last_fg = NULL;
-                               }
-                               if (save_last_bg != NULL) {
-                                       *save_last_bg = last_bg;
-                                       save_last_bg = NULL;
-                               }
-                       }
-
-                       theme_format_append_next(theme, str, format,
-                                                default_fg, default_bg,
-                                                &last_fg, &last_bg,
-                                                recurse_flags);
-                       continue;
-               }
-
-               (*format)++;
-               if (**format == '\0' || **format == '}')
-                       break; /* error */
-
-               /* get a single {...} */
-               abstract = theme_format_expand_abstract(theme, format,
-                                                       last_fg, last_bg,
-                                                       recurse_flags);
-               if (abstract != NULL) {
-                       g_string_append(str, abstract);
-                       g_free(abstract);
-               }
-       }
-
-       if ((flags & EXPAND_FLAG_LASTCOLOR_ARG) == 0) {
-               /* save the last color */
-               if (save_last_fg != NULL)
-                       *save_last_fg = last_fg;
-               if (save_last_bg != NULL)
-                       *save_last_bg = last_bg;
-       }
-
-       ret = str->str;
-        g_string_free(str, FALSE);
-        return ret;
-}
-
-#define IS_OLD_FORMAT(code, last_fg, last_bg) \
-       (((code) == 'n' && (last_fg) == 'n' && (last_bg) == 'n') || \
-       ((code) != 'n' && ((code) == (last_fg) || (code) == (last_bg))))
-
-static char *theme_format_compress_colors(THEME_REC *theme, const char *format)
-{
-       GString *str;
-       char *ret, last_fg, last_bg;
-
-       str = g_string_new(NULL);
-
-       last_fg = last_bg = '\0';
-       while (*format != '\0') {
-               if (*format == '$') {
-                        /* $variable, skrip it entirely */
-                       theme_format_append_variable(str, &format);
-                        last_fg = last_bg = '\0';
-               } else if (*format != '%') {
-                       /* a normal character */
-                       g_string_append_c(str, *format);
-                       format++;
-               } else {
-                       /* %format */
-                       format++;
-                       if (IS_OLD_FORMAT(*format, last_fg, last_bg)) {
-                               /* active color set again */
-                       } else if (IS_FGCOLOR_FORMAT(*format) &&
-                                  format[1] == '%' &&
-                                  IS_FGCOLOR_FORMAT(format[2]) &&
-                                  (*format != 'n' || format[2] == 'n')) {
-                               /* two fg colors in a row. bg colors are
-                                  so rare that we don't bother checking
-                                  them */
-                       } else {
-                               /* some format, add it */
-                               g_string_append_c(str, '%');
-                               g_string_append_c(str, *format);
-
-                               if (IS_FGCOLOR_FORMAT(*format))
-                                       last_fg = *format;
-                               if (IS_BGCOLOR_FORMAT(*format))
-                                       last_bg = *format;
-                       }
-                       format++;
-               }
-       }
-
-       ret = str->str;
-        g_string_free(str, FALSE);
-        return ret;
-}
-
-char *theme_format_expand(THEME_REC *theme, const char *format)
-{
-       char *data, *ret;
-
-       g_return_val_if_fail(theme != NULL, NULL);
-       g_return_val_if_fail(format != NULL, NULL);
-
-       data = theme_format_expand_data(theme, &format, 'n', 'n', NULL, NULL,
-                                       EXPAND_FLAG_ROOT);
-       ret = theme_format_compress_colors(theme, data);
-        g_free(data);
-       return ret;
-}
-
-static MODULE_THEME_REC *theme_module_create(THEME_REC *theme, const char *module)
-{
-       MODULE_THEME_REC *rec;
-       FORMAT_REC *formats;
-
-       rec = g_hash_table_lookup(theme->modules, module);
-       if (rec != NULL) return rec;
-
-       formats = g_hash_table_lookup(default_formats, module);
-        g_return_val_if_fail(formats != NULL, NULL);
-
-       rec = g_new0(MODULE_THEME_REC, 1);
-       rec->name = g_strdup(module);
-
-       for (rec->count = 0; formats[rec->count].def != NULL; rec->count++) ;
-       rec->formats = g_new0(char *, rec->count);
-       rec->expanded_formats = g_new0(char *, rec->count);
-
-       g_hash_table_insert(theme->modules, rec->name, rec);
-       return rec;
-}
-
-static void theme_read_replaces(CONFIG_REC *config, THEME_REC *theme)
-{
-       GSList *tmp;
-       CONFIG_NODE *node;
-       const char *p;
-        int index;
-
-        /* reset replace keys */
-       for (index = 0; index < 256; index++)
-                theme->replace_keys[index] = -1;
-       index = 0;
-
-       node = config_node_traverse(config, "replaces", FALSE);
-       if (node == NULL || node->type !=  NODE_TYPE_BLOCK) return;
-
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
-               node = tmp->data;
-
-               if (node->key != NULL && node->value != NULL) {
-                       for (p = node->key; *p != '\0'; p++)
-                                theme->replace_keys[(int) (unsigned char) *p] = index;
-
-                       theme->replace_values =
-                               g_slist_append(theme->replace_values,
-                                              g_strdup(node->value));
-                        index++;
-               }
-       }
-}
-
-static void theme_read_abstracts(CONFIG_REC *config, THEME_REC *theme)
-{
-       GSList *tmp;
-       CONFIG_NODE *node;
-        gpointer oldkey, oldvalue;
-
-       node = config_node_traverse(config, "abstracts", FALSE);
-       if (node == NULL || node->type !=  NODE_TYPE_BLOCK) return;
-
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
-               node = tmp->data;
-
-               if (node->key == NULL || node->value == NULL)
-                       continue;
-
-               if (g_hash_table_lookup_extended(theme->abstracts, node->key,
-                                                &oldkey, &oldvalue)) {
-                        /* new values override old ones */
-                        g_hash_table_remove(theme->abstracts, oldkey);
-                       g_free(oldkey);
-                       g_free(oldvalue);
-               }
-
-               g_hash_table_insert(theme->abstracts, g_strdup(node->key),
-                                   g_strdup(node->value));
-       }
-}
-
-static void theme_set_format(THEME_REC *theme, MODULE_THEME_REC *rec,
-                            const char *module,
-                            const char *key, const char *value)
-{
-       int num;
-
-        num = format_find_tag(module, key);
-       if (num != -1) {
-               rec->formats[num] = g_strdup(value);
-               rec->expanded_formats[num] = theme_format_expand(theme, value);
-       }
-}
-
-static void theme_read_formats(THEME_REC *theme, const char *module,
-                              CONFIG_REC *config, MODULE_THEME_REC *rec)
-{
-       CONFIG_NODE *node;
-       GSList *tmp;
-
-       node = config_node_traverse(config, "formats", FALSE);
-       if (node == NULL) return;
-       node = config_node_section(node, module, -1);
-       if (node == NULL) return;
-
-       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
-               node = tmp->data;
-
-               if (node->key != NULL && node->value != NULL) {
-                       theme_set_format(theme, rec, module,
-                                        node->key, node->value);
-               }
-       }
-}
-
-static void theme_init_module(THEME_REC *theme, const char *module,
-                             CONFIG_REC *config)
-{
-       MODULE_THEME_REC *rec;
-       FORMAT_REC *formats;
-       int n;
-
-       formats = g_hash_table_lookup(default_formats, module);
-       g_return_if_fail(formats != NULL);
-
-       rec = theme_module_create(theme, module);
-
-       if (config != NULL)
-               theme_read_formats(theme, module, config, rec);
-
-       /* expand the remaining formats */
-       for (n = 0; n < rec->count; n++) {
-               if (rec->expanded_formats[n] == NULL) {
-                       rec->expanded_formats[n] =
-                               theme_format_expand(theme, formats[n].def);
-               }
-       }
-}
-
-static void sig_print_errors(void)
-{
-       init_finished = TRUE;
-
-       if (init_errors != NULL) {
-               signal_emit("gui dialog", 2, "error", init_errors);
-                g_free(init_errors);
-       }
-}
-
-static void theme_read_module(THEME_REC *theme, const char *module)
-{
-       CONFIG_REC *config;
-
-       config = config_open(theme->path, -1);
-       if (config != NULL)
-               config_parse(config);
-
-       theme_init_module(theme, module, config);
-
-       if (config != NULL) config_close(config);
-}
-
-static void themes_read_module(const char *module)
-{
-        g_slist_foreach(themes, (GFunc) theme_read_module, (void *) module);
-}
-
-static void theme_remove_module(THEME_REC *theme, const char *module)
-{
-       MODULE_THEME_REC *rec;
-
-       rec = g_hash_table_lookup(theme->modules, module);
-       if (rec == NULL) return;
-
-       g_hash_table_remove(theme->modules, module);
-       theme_module_destroy(module, rec);
-}
-
-static void themes_remove_module(const char *module)
-{
-        g_slist_foreach(themes, (GFunc) theme_remove_module, (void *) module);
-}
-
-void theme_register_module(const char *module, FORMAT_REC *formats)
-{
-       if (g_hash_table_lookup(default_formats, module) != NULL)
-               return;
-
-        g_hash_table_insert(default_formats, g_strdup(module), formats);
-       themes_read_module(module);
-}
-
-void theme_unregister_module(const char *module)
-{
-       gpointer key, value;
-
-       if (default_formats == NULL)
-               return; /* already uninitialized */
-
-       if (!g_hash_table_lookup_extended(default_formats, module, &key, &value))
-               return;
-
-       g_hash_table_remove(default_formats, key);
-       g_free(key);
-
-       themes_remove_module(module);
-}
-
-void theme_set_default_abstract(const char *key, const char *value)
-{
-       gpointer oldkey, oldvalue;
-
-       if (g_hash_table_lookup_extended(internal_theme->abstracts, key,
-                                        &oldkey, &oldvalue)) {
-               /* new values override old ones */
-               g_hash_table_remove(internal_theme->abstracts, oldkey);
-               g_free(oldkey);
-               g_free(oldvalue);
-       }
-
-       g_hash_table_insert(internal_theme->abstracts,
-                           g_strdup(key), g_strdup(value));
-}
-
-static THEME_REC *theme_find(const char *name)
-{
-       GSList *tmp;
-
-       for (tmp = themes; tmp != NULL; tmp = tmp->next) {
-               THEME_REC *rec = tmp->data;
-
-               if (g_strcasecmp(rec->name, name) == 0)
-                       return rec;
-       }
-
-       return NULL;
-}
-
-static void window_themes_update(void)
-{
-       GSList *tmp;
-
-       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
-               WINDOW_REC *rec = tmp->data;
-
-               if (rec->theme_name != NULL)
-                        rec->theme = theme_load(rec->theme_name);
-       }
-}
-
-THEME_REC *theme_load(const char *setname)
-{
-       THEME_REC *theme, *oldtheme;
-       struct stat statbuf;
-       char *fname, *name, *p;
-
-        name = g_strdup(setname);
-       p = strrchr(name, '.');
-       if (p != NULL && strcmp(p, ".theme") == 0) {
-               /* remove the trailing .theme */
-                *p = '\0';
-       }
-
-       theme = theme_find(name);
-
-       /* check home dir */
-       fname = g_strdup_printf("%s/%s.theme", get_irssi_dir(), name);
-       if (stat(fname, &statbuf) != 0) {
-               /* check global config dir */
-               g_free(fname);
-               fname = g_strdup_printf(THEMESDIR"/%s.theme", name);
-               if (stat(fname, &statbuf) != 0) {
-                       /* theme not found */
-                       g_free(fname);
-                       g_free(name);
-                       return theme; /* use the one in memory if possible */
-               }
-       }
-
-       if (theme != NULL && theme->last_modify == statbuf.st_mtime) {
-               /* theme not modified, use the one already in memory */
-               g_free(fname);
-               g_free(name);
-               return theme;
-       }
-
-        oldtheme = theme;
-       theme = theme_create(fname, name);
-       theme->last_modify = statbuf.st_mtime;
-       if (!theme_read(theme, theme->path)) {
-                /* error reading .theme file */
-               theme_destroy(theme);
-               theme = NULL;
-       }
-
-       if (oldtheme != NULL && theme != NULL) {
-               theme_destroy(oldtheme);
-               if (current_theme == oldtheme)
-                       current_theme = theme;
-               window_themes_update();
-       }
-
-       g_free(fname);
-       g_free(name);
-       return theme;
-}
-
-static void copy_abstract_hash(char *key, char *value, GHashTable *dest)
-{
-       g_hash_table_insert(dest, g_strdup(key), g_strdup(value));
-}
-
-static void theme_copy_abstracts(THEME_REC *dest, THEME_REC *src)
-{
-       g_hash_table_foreach(src->abstracts, (GHFunc) copy_abstract_hash,
-                            dest->abstracts);
-}
-
-typedef struct {
-        THEME_REC *theme;
-       CONFIG_REC *config;
-} THEME_READ_REC;
-
-static void theme_read_modules(const char *module, void *value,
-                              THEME_READ_REC *rec)
-{
-       theme_init_module(rec->theme, module, rec->config);
-}
-
-static void read_error(const char *str)
-{
-       char *old;
-
-       if (init_finished)
-                printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
-       else if (init_errors == NULL)
-               init_errors = g_strdup(str);
-       else {
-                old = init_errors;
-               init_errors = g_strconcat(init_errors, "\n", str, NULL);
-                g_free(old);
-       }
-}
-
-static int theme_read(THEME_REC *theme, const char *path)
-{
-       CONFIG_REC *config;
-       THEME_READ_REC rec;
-        char *str;
-
-       config = config_open(path, -1) ;
-       if (config == NULL) {
-               /* didn't exist or no access? */
-               str = g_strdup_printf("Error reading theme file %s: %s",
-                                     path, g_strerror(errno));
-               read_error(str);
-               g_free(str);
-               return FALSE;
-       }
-
-       if (path == NULL)
-               config_parse_data(config, default_theme, "internal");
-        else
-               config_parse(config);
-
-       if (config_last_error(config) != NULL) {
-               str = g_strdup_printf("Ignored errors in theme %s:\n%s",
-                                     theme->name, config_last_error(config));
-               read_error(str);
-                g_free(str);
-       }
-
-       theme->default_color =
-               config_get_int(config, NULL, "default_color", -1);
-       theme->info_eol = config_get_bool(config, NULL, "info_eol", FALSE);
-
-       /* FIXME: remove after 0.7.99 */
-       if (theme->default_color == 0 &&
-           config_get_int(config, NULL, "default_real_color", -1) != -1)
-                theme->default_color = -1;
-       theme_read_replaces(config, theme);
-
-       if (path != NULL)
-               theme_copy_abstracts(theme, internal_theme);
-       theme_read_abstracts(config, theme);
-
-       rec.theme = theme;
-       rec.config = config;
-       g_hash_table_foreach(default_formats,
-                            (GHFunc) theme_read_modules, &rec);
-       config_close(config);
-
-        return TRUE;
-}
-
-typedef struct {
-       char *name;
-       char *short_name;
-} THEME_SEARCH_REC;
-
-static int theme_search_equal(THEME_SEARCH_REC *r1, THEME_SEARCH_REC *r2)
-{
-       return g_strcasecmp(r1->short_name, r2->short_name);
-}
-
-static void theme_get_modules(char *module, FORMAT_REC *formats, GSList **list)
-{
-       THEME_SEARCH_REC *rec;
-
-       rec = g_new(THEME_SEARCH_REC, 1);
-       rec->name = module;
-       rec->short_name = strrchr(module, '/');
-       if (rec->short_name != NULL)
-               rec->short_name++; else rec->short_name = module;
-       *list = g_slist_insert_sorted(*list, rec, (GCompareFunc) theme_search_equal);
-}
-
-static GSList *get_sorted_modules(void)
-{
-       GSList *list;
-
-       list = NULL;
-       g_hash_table_foreach(default_formats, (GHFunc) theme_get_modules, &list);
-       return list;
-}
-
-static THEME_SEARCH_REC *theme_search(GSList *list, const char *module)
-{
-       THEME_SEARCH_REC *rec;
-
-       while (list != NULL) {
-               rec = list->data;
-
-               if (g_strcasecmp(rec->short_name, module) == 0)
-                       return rec;
-               list = list->next;
-       }
-
-       return NULL;
-}
-
-static void theme_show(THEME_SEARCH_REC *rec, const char *key, const char *value, int reset)
-{
-       MODULE_THEME_REC *theme;
-       FORMAT_REC *formats;
-       const char *text, *last_title;
-       int n, first;
-
-       formats = g_hash_table_lookup(default_formats, rec->name);
-       theme = g_hash_table_lookup(current_theme->modules, rec->name);
-
-       last_title = NULL; first = TRUE;
-       for (n = 1; formats[n].def != NULL; n++) {
-               text = theme != NULL && theme->formats[n] != NULL ?
-                       theme->formats[n] : formats[n].def;
-
-               if (formats[n].tag == NULL)
-                       last_title = text;
-               else if ((value != NULL && key != NULL && g_strcasecmp(formats[n].tag, key) == 0) ||
-                        (value == NULL && (key == NULL || stristr(formats[n].tag, key) != NULL))) {
-                       if (first) {
-                               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_TITLE, rec->short_name, formats[0].def);
-                               first = FALSE;
-                       }
-                       if (last_title != NULL)
-                               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_SUBTITLE, last_title);
-                       if (reset || value != NULL) {
-                               theme = theme_module_create(current_theme, rec->name);
-                                g_free_not_null(theme->formats[n]);
-                                g_free_not_null(theme->expanded_formats[n]);
-
-                               text = reset ? formats[n].def : value;
-                               theme->formats[n] = reset ? NULL : g_strdup(value);
-                               theme->expanded_formats[n] = theme_format_expand(current_theme, text);
-                       }
-                       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_ITEM, formats[n].tag, text);
-                       last_title = NULL;
-               }
-       }
-}
-
-/* SYNTAX: FORMAT [-delete | -reset] [<module>] [<key> [<value>]] */
-static void cmd_format(const char *data)
-{
-        GHashTable *optlist;
-       GSList *tmp, *modules;
-       char *module, *key, *value;
-       void *free_arg;
-       int reset;
-
-       if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS,
-                           "format", &optlist, &module, &key, &value))
-               return;
-
-       modules = get_sorted_modules();
-       if (*module == '\0')
-               module = NULL;
-       else if (theme_search(modules, module) == NULL) {
-               /* first argument isn't module.. */
-               cmd_params_free(free_arg);
-               if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS,
-                                   "format", &optlist, &key, &value))
-                       return;
-               module = NULL;
-       }
-
-       reset = FALSE;
-       if (*key == '\0') key = NULL;
-       if (g_hash_table_lookup(optlist, "reset"))
-               reset = TRUE;
-       else if (g_hash_table_lookup(optlist, "delete"))
-               value = "";
-       else if (*value == '\0')
-               value = NULL;
-
-       for (tmp = modules; tmp != NULL; tmp = tmp->next) {
-               THEME_SEARCH_REC *rec = tmp->data;
-
-               if (module == NULL || g_strcasecmp(rec->short_name, module) == 0)
-                       theme_show(rec, key, value, reset);
-       }
-       g_slist_foreach(modules, (GFunc) g_free, NULL);
-       g_slist_free(modules);
-
-        cmd_params_free(free_arg);
-}
-
-typedef struct {
-       CONFIG_REC *config;
-        int save_all;
-} THEME_SAVE_REC;
-
-static void module_save(const char *module, MODULE_THEME_REC *rec,
-                        THEME_SAVE_REC *data)
-{
-       CONFIG_NODE *fnode, *node;
-       FORMAT_REC *formats;
-       int n;
-
-        formats = g_hash_table_lookup(default_formats, rec->name);
-       if (formats == NULL) return;
-
-       fnode = config_node_traverse(data->config, "formats", TRUE);
-
-       node = config_node_section(fnode, rec->name, NODE_TYPE_BLOCK);
-       for (n = 1; formats[n].def != NULL; n++) {
-                if (rec->formats[n] != NULL) {
-                        config_node_set_str(data->config, node, formats[n].tag,
-                                            rec->formats[n]);
-               } else if (data->save_all && formats[n].tag != NULL) {
-                        config_node_set_str(data->config, node, formats[n].tag,
-                                            formats[n].def);
-               }
-        }
-
-        if (node->value == NULL) {
-                /* not modified, don't keep the empty section */
-                config_node_remove(data->config, fnode, node);
-               if (fnode->value == NULL) {
-                       config_node_remove(data->config,
-                                          data->config->mainnode, fnode);
-               }
-        }
-}
-
-static void theme_save(THEME_REC *theme, int save_all)
-{
-       CONFIG_REC *config;
-       THEME_SAVE_REC data;
-       char *path;
-       int ok;
-
-       config = config_open(theme->path, -1);
-        if (config != NULL)
-                config_parse(config);
-        else {
-                if (g_strcasecmp(theme->name, "default") == 0) {
-                        config = config_open(NULL, -1);
-                        config_parse_data(config, default_theme, "internal");
-                        config_change_file_name(config, theme->path, 0660);
-                } else {
-                        config = config_open(theme->path, 0660);
-                        if (config == NULL)
-                                return;
-                        config_parse(config);
-                }
-        }
-
-       data.config = config;
-        data.save_all = save_all;
-       g_hash_table_foreach(theme->modules, (GHFunc) module_save, &data);
-
-        /* always save the theme to ~/.irssi/ */
-       path = g_strdup_printf("%s/%s", get_irssi_dir(),
-                              g_basename(theme->path));
-       ok = config_write(config, path, 0660) == 0;
-
-       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                   ok ? TXT_THEME_SAVED : TXT_THEME_SAVE_FAILED,
-                   path, config_last_error(config));
-
-       g_free(path);
-       config_close(config);
-}
-
-/* save changed formats, -format saves all */
-static void cmd_save(const char *data)
-{
-       GSList *tmp;
-        GHashTable *optlist;
-        void *free_arg;
-       char *fname;
-       int saveall;
-
-       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
-                           "save", &optlist, &fname))
-               return;
-
-        saveall = g_hash_table_lookup(optlist, "formats") != NULL;
-       for (tmp = themes; tmp != NULL; tmp = tmp->next) {
-               THEME_REC *theme = tmp->data;
-
-               theme_save(theme, saveall);
-       }
-
-       cmd_params_free(free_arg);
-}
-
-static void complete_format_list(THEME_SEARCH_REC *rec, const char *key, GList **list)
-{
-       FORMAT_REC *formats;
-       int n, len;
-
-       formats = g_hash_table_lookup(default_formats, rec->name);
-
-       len = strlen(key);
-       for (n = 1; formats[n].def != NULL; n++) {
-               const char *item = formats[n].tag;
-
-               if (item != NULL && g_strncasecmp(item, key, len) == 0)
-                        *list = g_list_append(*list, g_strdup(item));
-       }
-}
-
-static GList *completion_get_formats(const char *module, const char *key)
-{
-       GSList *modules, *tmp;
-       GList *list;
-
-       g_return_val_if_fail(key != NULL, NULL);
-
-       list = NULL;
-
-       modules = get_sorted_modules();
-       if (*module == '\0' || theme_search(modules, module) != NULL) {
-               for (tmp = modules; tmp != NULL; tmp = tmp->next) {
-                       THEME_SEARCH_REC *rec = tmp->data;
-
-                       if (*module == '\0' || g_strcasecmp(rec->short_name, module) == 0)
-                               complete_format_list(rec, key, &list);
-               }
-       }
-       g_slist_foreach(modules, (GFunc) g_free, NULL);
-       g_slist_free(modules);
-
-       return list;
-}
-
-static void sig_complete_format(GList **list, WINDOW_REC *window,
-                               const char *word, const char *line, int *want_space)
-{
-       const char *ptr;
-       int words;
-
-       g_return_if_fail(list != NULL);
-       g_return_if_fail(word != NULL);
-       g_return_if_fail(line != NULL);
-
-        ptr = line;
-
-       words = 0;
-       if (*ptr != '\0') {
-               do {
-                       ptr++;
-                       words++;
-                       ptr = strchr(ptr, ' ');
-               } while (ptr != NULL);
-       }
-
-       if (words > 2)
-               return;
-
-       *list = completion_get_formats(line, word);
-       if (*list != NULL) signal_stop();
-}
-
-static void change_theme(const char *name, int verbose)
-{
-       THEME_REC *rec;
-
-       rec = theme_load(name);
-       if (rec != NULL) {
-               current_theme = rec;
-                signal_emit("theme changed", 1, rec);
-
-               if (verbose) {
-                       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
-                                   TXT_THEME_CHANGED,
-                                   rec->name, rec->path);
-               }
-       } else if (verbose) {
-               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
-                           TXT_THEME_NOT_FOUND, name);
-       }
-}
-
-static void read_settings(void)
-{
-       const char *theme;
-        int len;
-
-       theme = settings_get_str("theme");
-       len = strlen(current_theme->name);
-       if (strcmp(current_theme->name, theme) != 0 &&
-           (strncmp(current_theme->name, theme, len) != 0 ||
-            strcmp(theme+len, ".theme") != 0))
-               change_theme(theme, TRUE);
-}
-
-void themes_reload(void)
-{
-       GSList *refs;
-       char *fname;
-
-       /* increase every theme's refcount, and destroy them. this way if
-          we want to use the theme before it's reloaded we don't crash. */
-       refs = NULL;
-       while (themes != NULL) {
-               THEME_REC *theme = themes->data;
-
-               refs = g_slist_prepend(refs, theme);
-
-               theme->refcount++;
-               theme_destroy(theme);
-       }
-
-       /* first there's default theme.. */
-       current_theme = theme_load("default");
-       if (current_theme == NULL) {
-               fname = g_strdup_printf("%s/default.theme", get_irssi_dir());
-               current_theme = theme_create(fname, "default");
-               current_theme->default_color = -1;
-                theme_read(current_theme, NULL);
-               g_free(fname);
-       }
-
-        window_themes_update();
-       change_theme(settings_get_str("theme"), FALSE);
-
-       while (refs != NULL) {
-               theme_unref(refs->data);
-               refs = g_slist_remove(refs, refs->data);
-       }
-}
-
-static THEME_REC *read_internal_theme(void)
-{
-       CONFIG_REC *config;
-       THEME_REC *theme;
-
-       theme = theme_create("internal", "_internal");
-
-       config = config_open(NULL, -1);
-       config_parse_data(config, default_theme, "internal");
-       theme_read_abstracts(config, theme);
-       config_close(config);
-
-       return theme;
-}
-
-void themes_init(void)
-{
-       settings_add_str("lookandfeel", "theme", "default");
-
-       default_formats = g_hash_table_new((GHashFunc) g_str_hash,
-                                          (GCompareFunc) g_str_equal);
-       internal_theme = read_internal_theme();
-
-        init_finished = FALSE;
-        init_errors = NULL;
-
-       themes = NULL;
-       themes_reload();
-
-       command_bind("format", NULL, (SIGNAL_FUNC) cmd_format);
-       command_bind("save", NULL, (SIGNAL_FUNC) cmd_save);
-       signal_add("complete command format", (SIGNAL_FUNC) sig_complete_format);
-       signal_add("irssi init finished", (SIGNAL_FUNC) sig_print_errors);
-        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
-       signal_add("setup reread", (SIGNAL_FUNC) themes_reload);
-
-       command_set_options("format", "delete reset");
-       command_set_options("save", "formats");
-}
-
-void themes_deinit(void)
-{
-       while (themes != NULL)
-               theme_destroy(themes->data);
-       theme_destroy(internal_theme);
-
-       g_hash_table_destroy(default_formats);
-       default_formats = NULL;
-
-       command_unbind("format", (SIGNAL_FUNC) cmd_format);
-       command_unbind("save", (SIGNAL_FUNC) cmd_save);
-       signal_remove("complete command format", (SIGNAL_FUNC) sig_complete_format);
-       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_print_errors);
-        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
-        signal_remove("setup reread", (SIGNAL_FUNC) themes_reload);
-}