1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-30 14:56:39 +02:00
Files
weechat/src/core/core-config-file.c
T
Aaron Jones f5038bccbc Fix function prototypes for list of arguments
At the moment, building WeeChat triggers several thousand -Wstrict-prototypes
diagnostics.  This is due to its source code using an empty argument list for
functions and function pointers that take no arguments, instead of explicitly
declaring that they take no arguments by using a void list.

This commit replaces all empty argument lists with a void list.

Note that Ruby's headers also suffer the same problem, which WeeChat can't
do anything to fix.  Thus, building WeeChat with the Ruby plugin enabled
will still issue approximately 30 such diagnostics.
2025-03-10 08:16:52 +01:00

4645 lines
150 KiB
C

/*
* core-config-file.c - configuration files/sections/options management
*
* Copyright (C) 2003-2025 Sébastien Helleu <flashcode@flashtux.org>
* Copyright (C) 2005-2006 Emmanuel Bouthenot <kolter@openics.org>
*
* This file is part of WeeChat, the extensible chat client.
*
* WeeChat 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 3 of the License, or
* (at your option) any later version.
*
* WeeChat 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 WeeChat. If not, see <https://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <limits.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include "weechat.h"
#include "core-config-file.h"
#include "core-arraylist.h"
#include "core-config.h"
#include "core-dir.h"
#include "core-hashtable.h"
#include "core-hdata.h"
#include "core-hook.h"
#include "core-infolist.h"
#include "core-log.h"
#include "core-string.h"
#include "core-version.h"
#include "../gui/gui-color.h"
#include "../gui/gui-chat.h"
#include "../plugins/plugin.h"
struct t_config_file *config_files = NULL;
struct t_config_file *last_config_file = NULL;
char *config_option_type_string[CONFIG_NUM_OPTION_TYPES] =
{ N_("boolean"), N_("integer"), N_("string"), N_("color"), N_("enum") };
char *config_boolean_true[] = { "on", "yes", "y", "true", "t", "1", NULL };
char *config_boolean_false[] = { "off", "no", "n", "false", "f", "0", NULL };
void config_file_option_free_data (struct t_config_option *option);
/*
* Checks if a configuration file pointer is valid.
*
* Returns:
* 1: configuration file exists
* 0: configuration file does not exist
*/
int
config_file_valid (struct t_config_file *config_file)
{
struct t_config_file *ptr_config;
if (!config_file)
return 0;
for (ptr_config = config_files; ptr_config;
ptr_config = ptr_config->next_config)
{
if (ptr_config == config_file)
return 1;
}
/* configuration file not found */
return 0;
}
/*
* Searches for a configuration file.
*/
struct t_config_file *
config_file_search (const char *name)
{
struct t_config_file *ptr_config;
int rc;
if (!name)
return NULL;
for (ptr_config = last_config_file; ptr_config;
ptr_config = ptr_config->prev_config)
{
rc = strcmp (ptr_config->name, name);
if (rc == 0)
return ptr_config;
else if (rc < 0)
break;
}
/* configuration file not found */
return NULL;
}
/*
* Searches for position of configuration file (to keep configuration files
* sorted by name).
*/
struct t_config_file *
config_file_find_pos (const char *name)
{
struct t_config_file *ptr_config;
if (!name)
return NULL;
for (ptr_config = config_files; ptr_config;
ptr_config = ptr_config->next_config)
{
if (string_strcmp (name, ptr_config->name) < 0)
return ptr_config;
}
/* position not found (we will add to the end of list) */
return NULL;
}
/*
* Inserts a configuration file (keeping files sorted by name).
*/
void
config_file_config_insert (struct t_config_file *config_file)
{
struct t_config_file *pos_config;
if (!config_file)
return;
if (config_files)
{
pos_config = config_file_find_pos (config_file->name);
if (pos_config)
{
/* insert config into the list (before config found) */
config_file->prev_config = pos_config->prev_config;
config_file->next_config = pos_config;
if (pos_config->prev_config)
(pos_config->prev_config)->next_config = config_file;
else
config_files = config_file;
pos_config->prev_config = config_file;
}
else
{
/* add config to the end */
config_file->prev_config = last_config_file;
config_file->next_config = NULL;
last_config_file->next_config = config_file;
last_config_file = config_file;
}
}
else
{
/* first config */
config_file->prev_config = NULL;
config_file->next_config = NULL;
config_files = config_file;
last_config_file = config_file;
}
}
/*
* Creates a new configuration file.
*
* Returns pointer to new configuration file, NULL if error.
*/
struct t_config_file *
config_file_new (struct t_weechat_plugin *plugin, const char *name,
int (*callback_reload)(const void *pointer,
void *data,
struct t_config_file *config_file),
const void *callback_reload_pointer,
void *callback_reload_data)
{
struct t_config_file *new_config_file;
const char *ptr_name;
int priority;
string_get_priority_and_name (name, &priority, &ptr_name,
CONFIG_PRIORITY_DEFAULT);
if (!ptr_name || !ptr_name[0])
return NULL;
/* two configuration files cannot have same name */
if (config_file_search (ptr_name))
return NULL;
new_config_file = malloc (sizeof (*new_config_file));
if (new_config_file)
{
new_config_file->plugin = plugin;
new_config_file->priority = priority;
new_config_file->name = strdup (ptr_name);
if (!new_config_file->name)
{
free (new_config_file);
return NULL;
}
if (string_asprintf (&new_config_file->filename, "%s.conf", ptr_name) < 0)
{
free (new_config_file->name);
free (new_config_file);
return NULL;
}
new_config_file->file = NULL;
new_config_file->version = 1;
new_config_file->callback_update = NULL;
new_config_file->callback_update_pointer = NULL;
new_config_file->callback_update_data = NULL;
new_config_file->callback_reload = callback_reload;
new_config_file->callback_reload_pointer = callback_reload_pointer;
new_config_file->callback_reload_data = callback_reload_data;
new_config_file->sections = NULL;
new_config_file->last_section = NULL;
config_file_config_insert (new_config_file);
}
return new_config_file;
}
/*
* Sets configuration file version and a callback to update config
* sections/options on-the-fly when the config is read.
*
* Returns:
* 1: OK
* 0: error
*/
int
config_file_set_version (struct t_config_file *config_file,
int version,
struct t_hashtable *(*callback_update)(const void *pointer,
void *data,
struct t_config_file *config_file,
int version_read,
struct t_hashtable *data_read),
const void *callback_update_pointer,
void *callback_update_data)
{
if (version < 1)
return 0;
config_file->version = version;
config_file->callback_update = callback_update;
config_file->callback_update_pointer = callback_update_pointer;
config_file->callback_update_data = callback_update_data;
return 1;
}
/*
* Compares two configuration files to sort them by priority (highest priority
* at beginning of list).
*
* Returns:
* -1: config1 has higher priority than config2
* 1: config1 has same or lower priority than config2
*/
int
config_file_arraylist_cmp_config_cb (void *data,
struct t_arraylist *arraylist,
void *pointer1, void *pointer2)
{
struct t_config_file *ptr_config1, *ptr_config2;
/* make C compiler happy */
(void) data;
(void) arraylist;
ptr_config1 = (struct t_config_file *)pointer1;
ptr_config2 = (struct t_config_file *)pointer2;
return (ptr_config1->priority > ptr_config2->priority) ? -1 : 1;
}
/*
* Returns an arraylist with pointers to configuration files, sorted by
* priority (from highest to lowest).
*/
struct t_arraylist *
config_file_get_configs_by_priority (void)
{
struct t_arraylist *list;
struct t_config_file *ptr_config;
/*
* build a list of pointers to configs sorted by priority,
* so that configs with high priority are reloaded first
*/
list = arraylist_new (
32, 1, 1,
&config_file_arraylist_cmp_config_cb, NULL,
NULL, NULL);
if (!list)
return NULL;
for (ptr_config = config_files; ptr_config;
ptr_config = ptr_config->next_config)
{
arraylist_add (list, ptr_config);
}
return list;
}
/*
* Searches for position of section in configuration file (to keep sections
* sorted by name).
*/
struct t_config_section *
config_file_section_find_pos (struct t_config_file *config_file,
const char *name)
{
struct t_config_section *ptr_section;
if (!config_file || !name)
return NULL;
for (ptr_section = config_file->sections; ptr_section;
ptr_section = ptr_section->next_section)
{
if (string_strcmp (name, ptr_section->name) < 0)
return ptr_section;
}
/* position not found (we will add to the end of list) */
return NULL;
}
/*
* Creates a new section in a configuration file.
*
* Returns pointer to new section, NULL if error.
*/
struct t_config_section *
config_file_new_section (struct t_config_file *config_file, const char *name,
int user_can_add_options, int user_can_delete_options,
int (*callback_read)(const void *pointer,
void *data,
struct t_config_file *config_file,
struct t_config_section *section,
const char *option_name,
const char *value),
const void *callback_read_pointer,
void *callback_read_data,
int (*callback_write)(const void *pointer,
void *data,
struct t_config_file *config_file,
const char *section_name),
const void *callback_write_pointer,
void *callback_write_data,
int (*callback_write_default)(const void *pointer,
void *data,
struct t_config_file *config_file,
const char *section_name),
const void *callback_write_default_pointer,
void *callback_write_default_data,
int (*callback_create_option)(const void *pointer,
void *data,
struct t_config_file *config_file,
struct t_config_section *section,
const char *option_name,
const char *value),
const void *callback_create_option_pointer,
void *callback_create_option_data,
int (*callback_delete_option)(const void *pointer,
void *data,
struct t_config_file *config_file,
struct t_config_section *section,
struct t_config_option *option),
const void *callback_delete_option_pointer,
void *callback_delete_option_data)
{
struct t_config_section *new_section;
if (!config_file || !name)
return NULL;
if (config_file_search_section (config_file, name))
return NULL;
new_section = malloc (sizeof (*new_section));
if (new_section)
{
new_section->config_file = config_file;
new_section->name = strdup (name);
if (!new_section->name)
{
free (new_section);
return NULL;
}
new_section->user_can_add_options = user_can_add_options;
new_section->user_can_delete_options = user_can_delete_options;
new_section->callback_read = callback_read;
new_section->callback_read_pointer = callback_read_pointer;
new_section->callback_read_data = callback_read_data;
new_section->callback_write = callback_write;
new_section->callback_write_pointer = callback_write_pointer;
new_section->callback_write_data = callback_write_data;
new_section->callback_write_default = callback_write_default;
new_section->callback_write_default_pointer = callback_write_default_pointer;
new_section->callback_write_default_data = callback_write_default_data;
new_section->callback_create_option = callback_create_option;
new_section->callback_create_option_pointer = callback_create_option_pointer;
new_section->callback_create_option_data = callback_create_option_data;
new_section->callback_delete_option = callback_delete_option;
new_section->callback_delete_option_pointer = callback_delete_option_pointer;
new_section->callback_delete_option_data = callback_delete_option_data;
new_section->options = NULL;
new_section->last_option = NULL;
new_section->prev_section = config_file->last_section;
new_section->next_section = NULL;
if (config_file->last_section)
config_file->last_section->next_section = new_section;
else
config_file->sections = new_section;
config_file->last_section = new_section;
}
return new_section;
}
/*
* Searches for a section in a configuration file.
*
* Returns pointer to section found, NULL if not found.
*/
struct t_config_section *
config_file_search_section (struct t_config_file *config_file,
const char *name)
{
struct t_config_section *ptr_section;
if (!config_file || !name)
return NULL;
for (ptr_section = config_file->sections; ptr_section;
ptr_section = ptr_section->next_section)
{
if (strcmp (ptr_section->name, name) == 0)
return ptr_section;
}
/* section not found */
return NULL;
}
/*
* Builds full name for an option, using format: "file.section.option".
*
* Note: result must be freed after use.
*/
char *
config_file_option_full_name (struct t_config_option *option)
{
char *option_full_name;
if (!option)
return NULL;
string_asprintf (&option_full_name,
"%s.%s.%s",
option->config_file->name,
option->section->name,
option->name);
return option_full_name;
}
/*
* Executes hook_config for modified option.
*/
void
config_file_hook_config_exec (struct t_config_option *option)
{
char *option_full_name, str_value[256];
if (!option || !option->config_file || !option->section)
return;
option_full_name = config_file_option_full_name (option);
if (!option_full_name)
return;
if (option->value)
{
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
hook_config_exec (option_full_name,
(CONFIG_BOOLEAN(option) == CONFIG_BOOLEAN_TRUE) ?
"on" : "off");
break;
case CONFIG_OPTION_TYPE_INTEGER:
snprintf (str_value, sizeof (str_value),
"%d", CONFIG_INTEGER(option));
hook_config_exec (option_full_name, str_value);
break;
case CONFIG_OPTION_TYPE_STRING:
hook_config_exec (option_full_name, (char *)option->value);
break;
case CONFIG_OPTION_TYPE_COLOR:
hook_config_exec (option_full_name,
gui_color_get_name (CONFIG_COLOR(option)));
break;
case CONFIG_OPTION_TYPE_ENUM:
hook_config_exec (option_full_name,
option->string_values[CONFIG_ENUM(option)]);
break;
case CONFIG_NUM_OPTION_TYPES:
break;
}
}
else
{
hook_config_exec (option_full_name, NULL);
}
free (option_full_name);
}
/*
* Searches for position of option in section (to keep options sorted by name).
*/
struct t_config_option *
config_file_option_find_pos (struct t_config_section *section, const char *name)
{
struct t_config_option *ptr_option;
if (!section || !name)
return NULL;
for (ptr_option = section->last_option; ptr_option;
ptr_option = ptr_option->prev_option)
{
if (string_strcmp (name, ptr_option->name) >= 0)
return ptr_option->next_option;
}
return section->options;
}
/*
* Inserts an option in section (keeping options sorted by name).
*/
void
config_file_option_insert_in_section (struct t_config_option *option)
{
struct t_config_option *pos_option;
if (!option || !option->section)
return;
if (option->section->options)
{
pos_option = config_file_option_find_pos (option->section,
option->name);
if (pos_option)
{
/* insert option into the list (before option found) */
option->prev_option = pos_option->prev_option;
option->next_option = pos_option;
if (pos_option->prev_option)
(pos_option->prev_option)->next_option = option;
else
(option->section)->options = option;
pos_option->prev_option = option;
}
else
{
/* add option to end of section */
option->prev_option = (option->section)->last_option;
option->next_option = NULL;
(option->section)->last_option->next_option = option;
(option->section)->last_option = option;
}
}
else
{
/* first option for section */
option->prev_option = NULL;
option->next_option = NULL;
(option->section)->options = option;
(option->section)->last_option = option;
}
}
/*
* Allocates memory for a new option and initializes it.
*
* Returns pointer to new option, NULL if error.
*/
struct t_config_option *
config_file_option_malloc (void)
{
struct t_config_option *new_option;
new_option = malloc (sizeof (*new_option));
if (new_option)
{
new_option->config_file = NULL;
new_option->section = NULL;
new_option->name = NULL;
new_option->parent_name = NULL;
new_option->type = 0;
new_option->description = NULL;
new_option->string_values = NULL;
new_option->min = 0;
new_option->max = 0;
new_option->default_value = NULL;
new_option->value = NULL;
new_option->null_value_allowed = 0;
new_option->callback_check_value = NULL;
new_option->callback_check_value_pointer = NULL;
new_option->callback_check_value_data = NULL;
new_option->callback_change = NULL;
new_option->callback_change_pointer = NULL;
new_option->callback_change_data = NULL;
new_option->callback_delete = NULL;
new_option->callback_delete_pointer = NULL;
new_option->callback_delete_data = NULL;
new_option->loaded = 0;
new_option->prev_option = NULL;
new_option->next_option = NULL;
}
return new_option;
}
/*
* Creates a new option.
*
* Returns pointer to new option, NULL if error.
*/
struct t_config_option *
config_file_new_option (struct t_config_file *config_file,
struct t_config_section *section, const char *name,
const char *type, const char *description,
const char *string_values, int min, int max,
const char *default_value,
const char *value,
int null_value_allowed,
int (*callback_check_value)(const void *pointer,
void *data,
struct t_config_option *option,
const char *value),
const void *callback_check_value_pointer,
void *callback_check_value_data,
void (*callback_change)(const void *pointer,
void *data,
struct t_config_option *option),
const void *callback_change_pointer,
void *callback_change_data,
void (*callback_delete)(const void *pointer,
void *data,
struct t_config_option *option),
const void *callback_delete_pointer,
void *callback_delete_data)
{
struct t_config_option *new_option;
int var_type, int_value, argc, i, index_value;
long number;
char *error, *pos, *option_name, *parent_name;
new_option = NULL;
option_name = NULL;
parent_name = NULL;
if (!name || !type)
goto error;
pos = strstr (name, " << ");
if (pos)
{
option_name = string_strndup (name, pos - name);
parent_name = strdup (pos + 4);
}
else
{
option_name = strdup (name);
}
if (config_file && section
&& config_file_search_option (config_file, section, option_name))
{
goto error;
}
var_type = -1;
for (i = 0; i < CONFIG_NUM_OPTION_TYPES; i++)
{
if (strcmp (type, config_option_type_string[i]) == 0)
{
var_type = i;
break;
}
}
if (var_type < 0)
{
gui_chat_printf (NULL, "%sUnknown option type \"%s\"",
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
type);
goto error;
}
/*
* compatibility with versions < 4.1.0: force enum type for an integer
* with string values
*/
if ((var_type == CONFIG_OPTION_TYPE_INTEGER)
&& string_values && string_values[0])
{
var_type = CONFIG_OPTION_TYPE_ENUM;
}
if ((var_type == CONFIG_OPTION_TYPE_ENUM)
&& (!string_values || !string_values[0]))
{
goto error;
}
if (!null_value_allowed)
{
if (default_value && !value)
value = default_value;
else if (!default_value && value)
default_value = value;
if (!default_value || !value)
goto error;
}
new_option = config_file_option_malloc ();
if (new_option)
{
new_option->config_file = config_file;
new_option->section = section;
new_option->name = strdup (option_name);
if (!new_option->name)
goto error;
new_option->parent_name = (parent_name) ? strdup (parent_name) : NULL;
new_option->type = var_type;
if (description)
{
new_option->description = strdup (description);
if (!new_option->description)
goto error;
}
argc = 0;
switch (var_type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
new_option->min = CONFIG_BOOLEAN_FALSE;
new_option->max = CONFIG_BOOLEAN_TRUE;
if (default_value)
{
int_value = config_file_string_to_boolean (default_value);
new_option->default_value = malloc (sizeof (int));
if (!new_option->default_value)
goto error;
CONFIG_BOOLEAN_DEFAULT(new_option) = int_value;
}
if (value)
{
int_value = config_file_string_to_boolean (value);
new_option->value = malloc (sizeof (int));
if (!new_option->value)
goto error;
CONFIG_BOOLEAN(new_option) = int_value;
}
break;
case CONFIG_OPTION_TYPE_INTEGER:
new_option->min = min;
new_option->max = max;
if (default_value)
{
error = NULL;
number = strtol (default_value, &error, 10);
if (!error || error[0])
number = 0;
if (number < min)
number = min;
else if (number > max)
number = max;
new_option->default_value = malloc (sizeof (int));
if (!new_option->default_value)
goto error;
CONFIG_INTEGER_DEFAULT(new_option) = number;
}
if (value)
{
error = NULL;
number = strtol (value, &error, 10);
if (!error || error[0])
number = 0;
if (number < min)
number = min;
else if (number > max)
number = max;
new_option->value = malloc (sizeof (int));
if (!new_option->value)
goto error;
CONFIG_INTEGER(new_option) = number;
}
break;
case CONFIG_OPTION_TYPE_STRING:
new_option->min = min;
new_option->max = max;
if (default_value)
{
new_option->default_value = strdup (default_value);
if (!new_option->default_value)
goto error;
}
if (value)
{
new_option->value = strdup (value);
if (!new_option->value)
goto error;
}
break;
case CONFIG_OPTION_TYPE_COLOR:
new_option->min = min;
new_option->max = gui_color_get_weechat_colors_number () - 1;
if (default_value)
{
new_option->default_value = malloc (sizeof (int));
if (!new_option->default_value)
goto error;
if (!gui_color_assign (new_option->default_value, default_value))
CONFIG_COLOR_DEFAULT(new_option) = 0;
}
if (value)
{
new_option->value = malloc (sizeof (int));
if (!new_option->value)
goto error;
if (!gui_color_assign (new_option->value, value))
CONFIG_COLOR(new_option) = 0;
}
break;
case CONFIG_OPTION_TYPE_ENUM:
new_option->string_values = string_split (
string_values,
"|",
NULL,
WEECHAT_STRING_SPLIT_STRIP_LEFT
| WEECHAT_STRING_SPLIT_STRIP_RIGHT
| WEECHAT_STRING_SPLIT_COLLAPSE_SEPS,
0,
&argc);
if (!new_option->string_values)
goto error;
new_option->min = 0;
new_option->max = (argc == 0) ? 0 : argc - 1;
if (default_value)
{
index_value = 0;
for (i = 0; i < argc; i++)
{
if (strcmp (new_option->string_values[i],
default_value) == 0)
{
index_value = i;
break;
}
}
new_option->default_value = malloc (sizeof (int));
if (!new_option->default_value)
goto error;
CONFIG_ENUM_DEFAULT(new_option) = index_value;
}
if (value)
{
index_value = 0;
for (i = 0; i < argc; i++)
{
if (strcmp (new_option->string_values[i],
value) == 0)
{
index_value = i;
break;
}
}
new_option->value = malloc (sizeof (int));
if (!new_option->value)
goto error;
CONFIG_ENUM(new_option) = index_value;
}
break;
case CONFIG_NUM_OPTION_TYPES:
break;
}
new_option->null_value_allowed = null_value_allowed;
new_option->callback_check_value = callback_check_value;
new_option->callback_check_value_pointer = callback_check_value_pointer;
new_option->callback_check_value_data = callback_check_value_data;
new_option->callback_change = callback_change;
new_option->callback_change_pointer = callback_change_pointer;
new_option->callback_change_data = callback_change_data;
new_option->callback_delete = callback_delete;
new_option->callback_delete_pointer = callback_delete_pointer;
new_option->callback_delete_data = callback_delete_data;
new_option->loaded = 1;
if (section)
{
config_file_option_insert_in_section (new_option);
}
else
{
new_option->prev_option = NULL;
new_option->next_option = NULL;
}
config_file_hook_config_exec (new_option);
}
goto end;
error:
if (new_option)
{
config_file_option_free_data (new_option);
free (new_option);
new_option = NULL;
}
end:
free (option_name);
free (parent_name);
return new_option;
}
/*
* Searches for an option in a configuration file or section.
*
* Returns pointer to option found, NULL if error.
*/
struct t_config_option *
config_file_search_option (struct t_config_file *config_file,
struct t_config_section *section,
const char *option_name)
{
struct t_config_section *ptr_section;
struct t_config_option *ptr_option;
int rc;
if (!option_name)
return NULL;
if (section)
{
for (ptr_option = section->last_option; ptr_option;
ptr_option = ptr_option->prev_option)
{
rc = strcmp (ptr_option->name, option_name);
if (rc == 0)
return ptr_option;
else if (rc < 0)
break;
}
}
else if (config_file)
{
for (ptr_section = config_file->sections; ptr_section;
ptr_section = ptr_section->next_section)
{
for (ptr_option = ptr_section->last_option; ptr_option;
ptr_option = ptr_option->prev_option)
{
rc = strcmp (ptr_option->name, option_name);
if (rc == 0)
return ptr_option;
else if (rc < 0)
break;
}
}
}
/* option not found */
return NULL;
}
/*
* Searches for an option in a configuration file or section.
*
* Returns section/option found (in section_found/option_found), NULL if not
* found.
*/
void
config_file_search_section_option (struct t_config_file *config_file,
struct t_config_section *section,
const char *option_name,
struct t_config_section **section_found,
struct t_config_option **option_found)
{
struct t_config_section *ptr_section;
struct t_config_option *ptr_option;
int rc;
*section_found = NULL;
*option_found = NULL;
if (!option_name)
return;
if (section)
{
for (ptr_option = section->last_option; ptr_option;
ptr_option = ptr_option->prev_option)
{
rc = strcmp (ptr_option->name, option_name);
if (rc == 0)
{
*section_found = section;
*option_found = ptr_option;
return;
}
else if (rc < 0)
break;
}
}
else if (config_file)
{
for (ptr_section = config_file->sections; ptr_section;
ptr_section = ptr_section->next_section)
{
for (ptr_option = ptr_section->last_option; ptr_option;
ptr_option = ptr_option->prev_option)
{
rc = strcmp (ptr_option->name, option_name);
if (rc == 0)
{
*section_found = ptr_section;
*option_found = ptr_option;
return;
}
else if (rc < 0)
break;
}
}
}
}
/*
* Searches for a file/section/option using a full name of option (format:
* "file.section.option").
*/
void
config_file_search_with_string (const char *option_name,
struct t_config_file **config_file,
struct t_config_section **section,
struct t_config_option **option,
char **pos_option_name)
{
struct t_config_file *ptr_config;
struct t_config_section *ptr_section;
struct t_config_option *ptr_option;
char *file_name, *pos_section, *section_name, *pos_option;
if (config_file)
*config_file = NULL;
if (section)
*section = NULL;
if (option)
*option = NULL;
if (pos_option_name)
*pos_option_name = NULL;
if (!option_name)
return;
ptr_config = NULL;
ptr_section = NULL;
ptr_option = NULL;
file_name = NULL;
section_name = NULL;
pos_option = NULL;
pos_section = strchr (option_name, '.');
pos_option = (pos_section) ? strchr (pos_section + 1, '.') : NULL;
if (pos_section && pos_option)
{
file_name = string_strndup (option_name, pos_section - option_name);
section_name = string_strndup (pos_section + 1,
pos_option - pos_section - 1);
pos_option++;
}
if (file_name && section_name && pos_option)
{
if (pos_option_name)
*pos_option_name = pos_option;
ptr_config = config_file_search (file_name);
if (ptr_config)
{
ptr_section = config_file_search_section (ptr_config,
section_name);
if (ptr_section)
{
ptr_option = config_file_search_option (ptr_config,
ptr_section,
pos_option);
}
}
}
free (file_name);
free (section_name);
if (config_file)
*config_file = ptr_config;
if (section)
*section = ptr_section;
if (option)
*option = ptr_option;
}
/*
* Gets pointer to parent option, NULL if the option has no parent.
*/
struct t_config_option *
config_file_get_parent_option (struct t_config_option *option)
{
struct t_config_option *ptr_parent_option;
if (!option || !option->parent_name)
return NULL;
config_file_search_with_string (
option->parent_name,
NULL, /* config_file */
NULL, /* section */
&ptr_parent_option,
NULL); /* pos_option_name */
return ptr_parent_option;
}
/*
* Checks if a string with boolean value is valid.
*
* Returns:
* 1: boolean value is valid
* 0: boolean value is NOT valid
*/
int
config_file_string_boolean_is_valid (const char *text)
{
int i;
if (!text)
return 0;
for (i = 0; config_boolean_true[i]; i++)
{
if (strcmp (text, config_boolean_true[i]) == 0)
return 1;
}
for (i = 0; config_boolean_false[i]; i++)
{
if (strcmp (text, config_boolean_false[i]) == 0)
return 1;
}
/* text is not a boolean */
return 0;
}
/*
* Converts string to boolean value.
*
* Returns:
* 1: boolean value is true
* 0: boolean value is false
*/
int
config_file_string_to_boolean (const char *text)
{
int i;
if (!text)
return CONFIG_BOOLEAN_FALSE;
for (i = 0; config_boolean_true[i]; i++)
{
if (strcmp (text, config_boolean_true[i]) == 0)
return CONFIG_BOOLEAN_TRUE;
}
return CONFIG_BOOLEAN_FALSE;
}
/*
* Resets an option to its default value.
*
* Returns:
* WEECHAT_CONFIG_OPTION_SET_OK_CHANGED: OK, value has been changed
* WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE: OK, value not changed
* WEECHAT_CONFIG_OPTION_SET_ERROR: error
*/
int
config_file_option_reset (struct t_config_option *option, int run_callback)
{
int rc, old_value_was_null;
if (!option)
return WEECHAT_CONFIG_OPTION_SET_ERROR;
rc = WEECHAT_CONFIG_OPTION_SET_ERROR;
if (option->default_value)
{
old_value_was_null = (option->value == NULL);
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
if (!option->value)
{
option->value = malloc (sizeof (int));
if (option->value)
{
CONFIG_BOOLEAN(option) = CONFIG_BOOLEAN_DEFAULT(option);
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
}
else
{
if (CONFIG_BOOLEAN(option) == CONFIG_BOOLEAN_DEFAULT(option))
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
else
{
CONFIG_BOOLEAN(option) = CONFIG_BOOLEAN_DEFAULT(option);
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
}
break;
case CONFIG_OPTION_TYPE_INTEGER:
if (!option->value)
{
option->value = malloc (sizeof (int));
if (option->value)
CONFIG_INTEGER(option) = 0;
else
break;
}
if (CONFIG_INTEGER(option) == CONFIG_INTEGER_DEFAULT(option))
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
else
{
CONFIG_INTEGER(option) = CONFIG_INTEGER_DEFAULT(option);
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
break;
case CONFIG_OPTION_TYPE_STRING:
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
if (!option->value
|| (strcmp ((char *)option->value,
(char *)option->default_value) != 0))
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
if (option->value)
{
free (option->value);
option->value = NULL;
}
option->value = strdup ((char *)option->default_value);
if (!option->value)
rc = WEECHAT_CONFIG_OPTION_SET_ERROR;
break;
case CONFIG_OPTION_TYPE_COLOR:
if (!option->value)
{
option->value = malloc (sizeof (int));
if (option->value)
CONFIG_COLOR(option) = 0;
else
break;
}
if (CONFIG_COLOR(option) == CONFIG_COLOR_DEFAULT(option))
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
else
{
CONFIG_COLOR(option) = CONFIG_COLOR_DEFAULT(option);
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
break;
case CONFIG_OPTION_TYPE_ENUM:
if (!option->value)
{
option->value = malloc (sizeof (int));
if (option->value)
CONFIG_ENUM(option) = 0;
else
break;
}
if (CONFIG_ENUM(option) == CONFIG_ENUM_DEFAULT(option))
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
else
{
CONFIG_ENUM(option) = CONFIG_ENUM_DEFAULT(option);
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
break;
case CONFIG_NUM_OPTION_TYPES:
break;
}
if (old_value_was_null && option->value)
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
{
if (option->null_value_allowed)
{
if (option->value)
{
free (option->value);
option->value = NULL;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
}
}
/* run callback and config hook(s) if value was changed */
if (rc == WEECHAT_CONFIG_OPTION_SET_OK_CHANGED)
{
if (run_callback && option->callback_change)
{
(void) (option->callback_change) (
option->callback_change_pointer,
option->callback_change_data,
option);
}
config_file_hook_config_exec (option);
}
return rc;
}
/*
* Sets the value for an option.
*
* Returns:
* WEECHAT_CONFIG_OPTION_SET_OK_CHANGED: OK, value has been changed
* WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE: OK, value not changed
* WEECHAT_CONFIG_OPTION_SET_ERROR: error
*/
int
config_file_option_set (struct t_config_option *option, const char *value,
int run_callback)
{
int value_int, i, rc, new_value_ok, old_value_was_null, old_value;
long number;
char *error;
if (!option)
return WEECHAT_CONFIG_OPTION_SET_ERROR;
rc = WEECHAT_CONFIG_OPTION_SET_ERROR;
if (option->callback_check_value)
{
if (!(int)(option->callback_check_value) (
option->callback_check_value_pointer,
option->callback_check_value_data,
option,
value))
{
return WEECHAT_CONFIG_OPTION_SET_ERROR;
}
}
if (value)
{
old_value_was_null = (option->value == NULL);
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
if (!option->value)
{
option->value = malloc (sizeof (int));
if (option->value)
{
if (strcmp (value, "toggle") == 0)
{
CONFIG_BOOLEAN(option) = CONFIG_BOOLEAN_TRUE;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
{
if (config_file_string_boolean_is_valid (value))
{
value_int = config_file_string_to_boolean (value);
CONFIG_BOOLEAN(option) = value_int;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
{
free (option->value);
option->value = NULL;
}
}
}
}
else
{
if (strcmp (value, "toggle") == 0)
{
CONFIG_BOOLEAN(option) =
(CONFIG_BOOLEAN(option) == CONFIG_BOOLEAN_TRUE) ?
CONFIG_BOOLEAN_FALSE : CONFIG_BOOLEAN_TRUE;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
{
if (config_file_string_boolean_is_valid (value))
{
value_int = config_file_string_to_boolean (value);
if (value_int == CONFIG_BOOLEAN(option))
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
else
{
CONFIG_BOOLEAN(option) = value_int;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
}
}
}
break;
case CONFIG_OPTION_TYPE_INTEGER:
old_value = 0;
if (!option->value)
option->value = malloc (sizeof (int));
else
old_value = CONFIG_INTEGER(option);
if (option->value)
{
new_value_ok = 0;
if (strncmp (value, "++", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0]
&& (long)old_value + number <= (long)(option->max))
{
value_int = old_value + number;
new_value_ok = 1;
}
}
else if (strncmp (value, "--", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0]
&& (long)old_value - number >= (long)(option->min))
{
value_int = old_value - number;
new_value_ok = 1;
}
}
else
{
error = NULL;
number = strtol (value, &error, 10);
if (error && !error[0])
{
value_int = number;
if ((value_int >= option->min)
&& (value_int <= option->max))
new_value_ok = 1;
}
}
if (new_value_ok)
{
if (old_value_was_null
|| (value_int != old_value))
{
CONFIG_INTEGER(option) = value_int;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
}
else
{
if (old_value_was_null)
{
free (option->value);
option->value = NULL;
}
}
}
break;
case CONFIG_OPTION_TYPE_STRING:
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
if (!option->value
|| (strcmp (CONFIG_STRING(option), value) != 0))
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
if (option->value)
{
free (option->value);
option->value = NULL;
}
option->value = strdup (value);
if (!option->value)
rc = WEECHAT_CONFIG_OPTION_SET_ERROR;
break;
case CONFIG_OPTION_TYPE_COLOR:
old_value = 0;
if (!option->value)
option->value = malloc (sizeof (int));
else
old_value = CONFIG_COLOR(option);
if (option->value)
{
value_int = -1;
new_value_ok = 0;
if (strncmp (value, "++", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0])
{
if (gui_color_assign_by_diff (&value_int,
gui_color_get_name (old_value),
number))
new_value_ok = 1;
}
}
else if (strncmp (value, "--", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0])
{
if (gui_color_assign_by_diff (&value_int,
gui_color_get_name (old_value),
-1 * number))
new_value_ok = 1;
}
}
else
{
if (gui_color_assign (&value_int, value))
new_value_ok = 1;
}
if (new_value_ok)
{
if (old_value_was_null
|| (value_int != old_value))
{
CONFIG_COLOR(option) = value_int;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
}
else
{
if (old_value_was_null)
{
free (option->value);
option->value = NULL;
}
}
}
break;
case CONFIG_OPTION_TYPE_ENUM:
old_value = 0;
if (!option->value)
option->value = malloc (sizeof (int));
else
old_value = CONFIG_ENUM(option);
if (option->value)
{
value_int = -1;
if (strncmp (value, "++", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0])
{
number = number % (option->max + 1);
value_int = (old_value + number) %
(option->max + 1);
}
}
else if (strncmp (value, "--", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0])
{
number = number % (option->max + 1);
value_int = (old_value + (option->max + 1) - number) %
(option->max + 1);
}
}
else
{
for (i = 0; option->string_values[i]; i++)
{
if (strcmp (option->string_values[i], value) == 0)
{
value_int = i;
break;
}
}
}
if (value_int >= 0)
{
if (old_value_was_null
|| (value_int != old_value))
{
CONFIG_ENUM(option) = value_int;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
}
else
{
if (old_value_was_null)
{
free (option->value);
option->value = NULL;
}
}
}
break;
case CONFIG_NUM_OPTION_TYPES:
break;
}
if (old_value_was_null && option->value)
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
{
if (option->null_value_allowed && option->value)
{
free (option->value);
option->value = NULL;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
}
/* run callback and config hook(s) if value was changed */
if (rc == WEECHAT_CONFIG_OPTION_SET_OK_CHANGED)
{
if (run_callback && option->callback_change)
{
(void) (option->callback_change) (
option->callback_change_pointer,
option->callback_change_data,
option);
}
config_file_hook_config_exec (option);
}
return rc;
}
/*
* Toggles value of an option.
*
* Returns:
* WEECHAT_CONFIG_OPTION_SET_OK_CHANGED: OK, value has been changed
* WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE: OK, value not changed
* WEECHAT_CONFIG_OPTION_SET_ERROR: error
*/
int
config_file_option_toggle (struct t_config_option *option,
const char **values, int num_values,
int run_callback)
{
char *current_value;
const char *ptr_new_value, *empty_string = "";
int i, rc, index_found, value_is_null, reset_value;
if (!option || (num_values < 0))
return WEECHAT_CONFIG_OPTION_SET_ERROR;
rc = WEECHAT_CONFIG_OPTION_SET_ERROR;
ptr_new_value = NULL;
reset_value = 0;
value_is_null = (option->value == NULL);
current_value = config_file_option_value_to_string (option, 0, 0, 0);
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
if (!values)
{
ptr_new_value = (option->value && CONFIG_BOOLEAN(option)) ?
config_boolean_false[0] : config_boolean_true[0];
}
break;
case CONFIG_OPTION_TYPE_INTEGER:
if (!values)
goto end;
break;
case CONFIG_OPTION_TYPE_STRING:
if (!values)
{
if (option->value && (strcmp (CONFIG_STRING(option), "") == 0))
ptr_new_value = CONFIG_STRING_DEFAULT(option);
else
ptr_new_value = empty_string;
}
break;
case CONFIG_OPTION_TYPE_COLOR:
if (!values)
goto end;
break;
case CONFIG_OPTION_TYPE_ENUM:
if (!values)
goto end;
break;
case CONFIG_NUM_OPTION_TYPES:
/* make C compiler happy */
break;
}
/* search new value to use with the provided list of values */
if (!ptr_new_value && values)
{
index_found = -1;
for (i = 0; i < num_values; i++)
{
if ((value_is_null && !values[i])
|| (!value_is_null && current_value && values[i]
&& strcmp (current_value, values[i]) == 0))
{
index_found = i;
break;
}
}
if (index_found >= 0)
{
if (index_found + 1 < num_values)
{
ptr_new_value = values[index_found + 1];
}
else
{
if (num_values < 2)
reset_value = 1;
else
ptr_new_value = values[0];
}
}
else
{
ptr_new_value = values[0];
}
}
if (reset_value)
rc = config_file_option_reset (option, run_callback);
else
rc = config_file_option_set (option, ptr_new_value, run_callback);
end:
free (current_value);
return rc;
}
/*
* Sets null (undefined) value for an option.
*
* Returns:
* WEECHAT_CONFIG_OPTION_SET_OK_CHANGED: OK, value has been changed
* WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE: OK, value not changed
* WEECHAT_CONFIG_OPTION_SET_ERROR: error
*/
int
config_file_option_set_null (struct t_config_option *option, int run_callback)
{
int rc;
if (!option)
return WEECHAT_CONFIG_OPTION_SET_ERROR;
rc = WEECHAT_CONFIG_OPTION_SET_ERROR;
/* null value is authorized only if it's allowed in option */
if (option->null_value_allowed)
{
/* option was already null: do nothing */
if (!option->value)
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
else
{
/* set option to null */
free (option->value);
option->value = NULL;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
}
/* run callback and config hook(s) if value was changed */
if (rc == WEECHAT_CONFIG_OPTION_SET_OK_CHANGED)
{
if (run_callback && option->callback_change)
{
(void) (option->callback_change) (
option->callback_change_pointer,
option->callback_change_data,
option);
}
config_file_hook_config_exec (option);
}
return rc;
}
/*
* Sets the default value for an option.
*
* Returns:
* WEECHAT_CONFIG_OPTION_SET_OK_CHANGED: OK, default value has been changed
* WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE: OK, default value not changed
* WEECHAT_CONFIG_OPTION_SET_ERROR: error
*/
int
config_file_option_set_default (struct t_config_option *option,
const char *value,
int run_callback)
{
int value_int, i, rc, new_value_ok, old_value_was_null, old_value;
long number;
char *error;
if (!option)
return WEECHAT_CONFIG_OPTION_SET_ERROR;
rc = WEECHAT_CONFIG_OPTION_SET_ERROR;
if (value)
{
old_value_was_null = (option->default_value == NULL);
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
if (!option->default_value)
{
option->default_value = malloc (sizeof (int));
if (option->default_value)
{
if (strcmp (value, "toggle") == 0)
{
CONFIG_BOOLEAN_DEFAULT(option) = CONFIG_BOOLEAN_TRUE;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
{
if (config_file_string_boolean_is_valid (value))
{
value_int = config_file_string_to_boolean (value);
CONFIG_BOOLEAN_DEFAULT(option) = value_int;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
{
free (option->default_value);
option->default_value = NULL;
}
}
}
}
else
{
if (strcmp (value, "toggle") == 0)
{
CONFIG_BOOLEAN_DEFAULT(option) =
(CONFIG_BOOLEAN_DEFAULT(option) == CONFIG_BOOLEAN_TRUE) ?
CONFIG_BOOLEAN_FALSE : CONFIG_BOOLEAN_TRUE;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
{
if (config_file_string_boolean_is_valid (value))
{
value_int = config_file_string_to_boolean (value);
if (value_int == CONFIG_BOOLEAN_DEFAULT(option))
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
else
{
CONFIG_BOOLEAN_DEFAULT(option) = value_int;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
}
}
}
break;
case CONFIG_OPTION_TYPE_INTEGER:
old_value = 0;
if (!option->default_value)
option->default_value = malloc (sizeof (int));
else
old_value = CONFIG_INTEGER_DEFAULT(option);
if (option->default_value)
{
new_value_ok = 0;
if (strncmp (value, "++", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0])
{
value_int = old_value + number;
if (value_int <= option->max)
new_value_ok = 1;
}
}
else if (strncmp (value, "--", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0])
{
value_int = old_value - number;
if (value_int >= option->min)
new_value_ok = 1;
}
}
else
{
error = NULL;
number = strtol (value, &error, 10);
if (error && !error[0])
{
value_int = number;
if ((value_int >= option->min)
&& (value_int <= option->max))
new_value_ok = 1;
}
}
if (new_value_ok)
{
if (old_value_was_null
|| (value_int != old_value))
{
CONFIG_INTEGER_DEFAULT(option) = value_int;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
}
else
{
if (old_value_was_null)
{
free (option->default_value);
option->default_value = NULL;
}
}
}
break;
case CONFIG_OPTION_TYPE_STRING:
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
if (!option->default_value
|| (strcmp (CONFIG_STRING_DEFAULT(option), value) != 0))
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
if (option->default_value)
{
free (option->default_value);
option->default_value = NULL;
}
option->default_value = strdup (value);
if (!option->default_value)
rc = WEECHAT_CONFIG_OPTION_SET_ERROR;
break;
case CONFIG_OPTION_TYPE_COLOR:
old_value = 0;
if (!option->default_value)
option->default_value = malloc (sizeof (int));
else
old_value = CONFIG_COLOR_DEFAULT(option);
if (option->default_value)
{
value_int = -1;
new_value_ok = 0;
if (strncmp (value, "++", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0])
{
if (gui_color_assign_by_diff (&value_int,
gui_color_get_name (old_value),
number))
new_value_ok = 1;
}
}
else if (strncmp (value, "--", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0])
{
if (gui_color_assign_by_diff (&value_int,
gui_color_get_name (old_value),
-1 * number))
new_value_ok = 1;
}
}
else
{
if (gui_color_assign (&value_int, value))
new_value_ok = 1;
}
if (new_value_ok)
{
if (old_value_was_null
|| (value_int != old_value))
{
CONFIG_COLOR_DEFAULT(option) = value_int;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
}
else
{
if (old_value_was_null)
{
free (option->default_value);
option->default_value = NULL;
}
}
}
break;
case CONFIG_OPTION_TYPE_ENUM:
old_value = 0;
if (!option->default_value)
option->default_value = malloc (sizeof (int));
else
old_value = CONFIG_ENUM_DEFAULT(option);
if (option->default_value)
{
value_int = -1;
if (strncmp (value, "++", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0])
{
number = number % (option->max + 1);
value_int = (old_value + number) %
(option->max + 1);
}
}
else if (strncmp (value, "--", 2) == 0)
{
error = NULL;
number = strtol (value + 2, &error, 10);
if (error && !error[0])
{
number = number % (option->max + 1);
value_int = (old_value + (option->max + 1) - number) %
(option->max + 1);
}
}
else
{
for (i = 0; option->string_values[i]; i++)
{
if (strcmp (option->string_values[i], value) == 0)
{
value_int = i;
break;
}
}
}
if (value_int >= 0)
{
if (old_value_was_null
|| (value_int != old_value))
{
CONFIG_ENUM_DEFAULT(option) = value_int;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
}
else
{
if (old_value_was_null)
{
free (option->default_value);
option->default_value = NULL;
}
}
}
break;
case CONFIG_NUM_OPTION_TYPES:
break;
}
if (old_value_was_null && option->default_value)
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
{
if (option->null_value_allowed && option->default_value)
{
free (option->default_value);
option->default_value = NULL;
rc = WEECHAT_CONFIG_OPTION_SET_OK_CHANGED;
}
else
rc = WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE;
}
/* run callback and config hook(s) if default value was changed */
if (rc == WEECHAT_CONFIG_OPTION_SET_OK_CHANGED)
{
if (run_callback && option->callback_change)
{
(void) (option->callback_change) (
option->callback_change_pointer,
option->callback_change_data,
option);
}
config_file_hook_config_exec (option);
}
return rc;
}
/*
* Unsets/resets an option.
*
* Returns:
* WEECHAT_CONFIG_OPTION_UNSET_OK_NO_RESET: OK, value has not been reset
* WEECHAT_CONFIG_OPTION_UNSET_OK_RESET: OK, value has been reset
* WEECHAT_CONFIG_OPTION_UNSET_OK_REMOVED: OK, value has been removed
* WEECHAT_CONFIG_OPTION_UNSET_ERROR: error
*/
int
config_file_option_unset (struct t_config_option *option)
{
int rc;
char *option_full_name;
if (!option)
return WEECHAT_CONFIG_OPTION_UNSET_ERROR;
rc = WEECHAT_CONFIG_OPTION_UNSET_OK_NO_RESET;
if (option->section && option->section->user_can_delete_options)
{
/* delete option */
if (option->callback_delete)
{
(void) (option->callback_delete) (
option->callback_delete_pointer,
option->callback_delete_data,
option);
}
option_full_name = config_file_option_full_name (option);
if (option->section->callback_delete_option)
{
rc = (int) (option->section->callback_delete_option) (
option->section->callback_delete_option_pointer,
option->section->callback_delete_option_data,
option->config_file,
option->section,
option);
}
else
{
config_file_option_free (option, 0);
rc = WEECHAT_CONFIG_OPTION_UNSET_OK_REMOVED;
}
if (option_full_name)
{
hook_config_exec (option_full_name, NULL);
free (option_full_name);
}
}
else
{
/* reset value */
switch (config_file_option_reset (option, 1))
{
case WEECHAT_CONFIG_OPTION_SET_ERROR:
rc = WEECHAT_CONFIG_OPTION_UNSET_ERROR;
break;
case WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE:
rc = WEECHAT_CONFIG_OPTION_UNSET_OK_NO_RESET;
break;
case WEECHAT_CONFIG_OPTION_SET_OK_CHANGED:
rc = WEECHAT_CONFIG_OPTION_UNSET_OK_RESET;
break;
}
}
return rc;
}
/*
* Renames an option.
*/
void
config_file_option_rename (struct t_config_option *option,
const char *new_name)
{
char *str_new_name, *full_old_name, *full_new_name;
struct t_config_file *ptr_config;
struct t_config_section *ptr_section;
struct t_config_option *ptr_option;
if (!option || !new_name || !new_name[0]
|| config_file_search_option (option->config_file, option->section, new_name))
return;
full_old_name = config_file_option_full_name (option);
str_new_name = strdup (new_name);
if (str_new_name)
{
/* remove option from list */
if (option->section)
{
if (option->prev_option)
(option->prev_option)->next_option = option->next_option;
if (option->next_option)
(option->next_option)->prev_option = option->prev_option;
if (option->section->options == option)
(option->section)->options = option->next_option;
if (option->section->last_option == option)
(option->section)->last_option = option->prev_option;
}
/* rename option */
free (option->name);
option->name = str_new_name;
/* re-insert option in section */
if (option->section)
config_file_option_insert_in_section (option);
}
full_new_name = config_file_option_full_name (option);
/* rename "parent_name" in any option using the old option name */
if (full_old_name && full_new_name)
{
for (ptr_config = config_files; ptr_config;
ptr_config = ptr_config->next_config)
{
for (ptr_section = ptr_config->sections; ptr_section;
ptr_section = ptr_section->next_section)
{
for (ptr_option = ptr_section->options; ptr_option;
ptr_option = ptr_option->next_option)
{
if (ptr_option->parent_name
&& (strcmp (ptr_option->parent_name, full_old_name) == 0))
{
free (ptr_option->parent_name);
ptr_option->parent_name = strdup (full_new_name);
}
}
}
}
}
free (full_old_name);
free (full_new_name);
config_file_hook_config_exec (option);
}
/*
* Builds a string with the value or default value of option,
* depending on the type of option.
*
* According to default_value:
* 0: value of option is returned
* 1: default value of option is returned
*
* Note: result must be freed after use.
*/
char *
config_file_option_value_to_string (struct t_config_option *option,
int default_value,
int use_colors,
int use_delimiters)
{
char *value;
const char *ptr_value;
int enabled;
if (!option)
return NULL;
if ((default_value && !option->default_value)
|| (!default_value && !option->value))
{
string_asprintf (
&value,
"%s%s",
(use_colors) ? GUI_COLOR(GUI_COLOR_CHAT_VALUE_NULL) : "",
"null");
return value;
}
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
enabled = (default_value) ?
CONFIG_BOOLEAN_DEFAULT(option) : CONFIG_BOOLEAN(option);
string_asprintf (
&value,
"%s%s",
(use_colors) ? GUI_COLOR(GUI_COLOR_CHAT_VALUE) : "",
(enabled) ? "on" : "off");
return value;
case CONFIG_OPTION_TYPE_INTEGER:
string_asprintf (
&value,
"%s%d",
(use_colors) ? GUI_COLOR(GUI_COLOR_CHAT_VALUE) : "",
(default_value) ? CONFIG_INTEGER_DEFAULT(option) : CONFIG_INTEGER(option));
return value;
case CONFIG_OPTION_TYPE_STRING:
ptr_value = (default_value) ? CONFIG_STRING_DEFAULT(option) : CONFIG_STRING(option);
string_asprintf (
&value,
"%s%s%s%s%s%s",
(use_colors && use_delimiters) ? GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS) : "",
(use_delimiters) ? "\"" : "",
(use_colors) ? GUI_COLOR(GUI_COLOR_CHAT_VALUE) : "",
ptr_value,
(use_colors && use_delimiters) ? GUI_COLOR(GUI_COLOR_CHAT_DELIMITERS) : "",
(use_delimiters) ? "\"" : "");
return value;
case CONFIG_OPTION_TYPE_COLOR:
ptr_value = gui_color_get_name (
(default_value) ? CONFIG_COLOR_DEFAULT(option) : CONFIG_COLOR(option));
if (!ptr_value)
return NULL;
string_asprintf (
&value,
"%s%s",
(use_colors) ? GUI_COLOR(GUI_COLOR_CHAT_VALUE) : "",
ptr_value);
return value;
case CONFIG_OPTION_TYPE_ENUM:
ptr_value = (default_value) ?
option->string_values[CONFIG_ENUM_DEFAULT(option)] :
option->string_values[CONFIG_ENUM(option)];
string_asprintf (
&value,
"%s%s",
(use_colors) ? GUI_COLOR(GUI_COLOR_CHAT_VALUE) : "",
ptr_value);
return value;
case CONFIG_NUM_OPTION_TYPES:
/* make C compiler happy */
break;
}
/* make C static analyzer happy (never executed) */
return NULL;
}
/*
* Gets a string value of an option property.
*/
const char *
config_file_option_get_string (struct t_config_option *option,
const char *property)
{
if (!option || !property)
return NULL;
if (strcmp (property, "config_name") == 0)
return option->config_file->name;
else if (strcmp (property, "section_name") == 0)
return option->section->name;
else if (strcmp (property, "name") == 0)
return option->name;
else if (strcmp (property, "parent_name") == 0)
return option->parent_name;
else if (strcmp (property, "type") == 0)
return config_option_type_string[option->type];
else if (strcmp (property, "description") == 0)
return option->description;
return NULL;
}
/*
* Gets a pointer on an option property.
*/
void *
config_file_option_get_pointer (struct t_config_option *option,
const char *property)
{
if (!option || !property)
return NULL;
if (strcmp (property, "config_file") == 0)
return option->config_file;
else if (strcmp (property, "section") == 0)
return option->section;
else if (strcmp (property, "name") == 0)
return option->name;
else if (strcmp (property, "parent_name") == 0)
return option->parent_name;
else if (strcmp (property, "type") == 0)
return &option->type;
else if (strcmp (property, "description") == 0)
return option->description;
else if (strcmp (property, "string_values") == 0)
return option->string_values;
else if (strcmp (property, "min") == 0)
return &option->min;
else if (strcmp (property, "max") == 0)
return &option->max;
else if (strcmp (property, "default_value") == 0)
return option->default_value;
else if (strcmp (property, "value") == 0)
return option->value;
else if (strcmp (property, "prev_option") == 0)
return option->prev_option;
else if (strcmp (property, "next_option") == 0)
return option->next_option;
return NULL;
}
/*
* Checks if an option has a null value.
*
* Returns:
* 1: value of option is null
* 0: value of option is not null
*/
int
config_file_option_is_null (struct t_config_option *option)
{
if (!option)
return 1;
return (option->value) ? 0 : 1;
}
/*
* Checks if an option has a null default value.
*
* Returns:
* 1: default value of option is null
* 0: default value of option is not null
*/
int
config_file_option_default_is_null (struct t_config_option *option)
{
if (!option)
return 1;
return (option->default_value) ? 0 : 1;
}
/*
* Checks if an option has changed (current value different from default value).
*
* Returns:
* 1: option has changed
* 0: option has default value
*/
int config_file_option_has_changed (struct t_config_option *option)
{
/* both default and current value are null => not changed */
if (!option->default_value && !option->value)
return 0;
/* default is null and current value is not null => changed! */
if (!option->default_value && option->value)
return 1;
/* default is not null and current value is null => changed! */
if (option->default_value && !option->value)
return 1;
/* both default and current value are not null, compare their values */
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
return CONFIG_BOOLEAN(option) != CONFIG_BOOLEAN_DEFAULT(option);
case CONFIG_OPTION_TYPE_INTEGER:
return CONFIG_INTEGER(option) != CONFIG_INTEGER_DEFAULT(option);
case CONFIG_OPTION_TYPE_STRING:
return strcmp (CONFIG_STRING(option), CONFIG_STRING_DEFAULT(option)) != 0;
case CONFIG_OPTION_TYPE_COLOR:
return CONFIG_COLOR(option) != CONFIG_COLOR_DEFAULT(option);
case CONFIG_OPTION_TYPE_ENUM:
return CONFIG_ENUM(option) != CONFIG_ENUM_DEFAULT(option);
case CONFIG_NUM_OPTION_TYPES:
/* make C compiler happy */
break;
}
return 0;
}
/*
* Sets the value for an option using a full name of option (format:
* "file.section.option").
*
* Returns:
* WEECHAT_CONFIG_OPTION_SET_OK_CHANGED: OK, value has been changed
* WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE: OK, value not changed
* WEECHAT_CONFIG_OPTION_SET_ERROR: error
* WEECHAT_CONFIG_OPTION_SET_OPTION_NOT_FOUND: option not found
*/
int
config_file_option_set_with_string (const char *option_name, const char *value)
{
int rc;
struct t_config_file *ptr_config;
struct t_config_section *ptr_section;
struct t_config_option *ptr_option;
char *pos_option;
rc = WEECHAT_CONFIG_OPTION_SET_OPTION_NOT_FOUND;
config_file_search_with_string (option_name, &ptr_config, &ptr_section,
&ptr_option, &pos_option);
if (ptr_config && ptr_section)
{
if (ptr_option)
{
rc = (value) ?
config_file_option_set (ptr_option, value, 1) :
config_file_option_set_null (ptr_option, 1);
}
else
{
if (ptr_section->user_can_add_options
&& ptr_section->callback_create_option)
{
rc = (int) (ptr_section->callback_create_option) (
ptr_section->callback_create_option_pointer,
ptr_section->callback_create_option_data,
ptr_config,
ptr_section,
pos_option,
value);
}
}
}
return rc;
}
/*
* Returns boolean value of an option.
*
* Returns 1 if value is true, 0 if it is false.
*/
int
config_file_option_boolean (struct t_config_option *option)
{
if (!option || !option->value || (option->type != CONFIG_OPTION_TYPE_BOOLEAN))
return 0;
return CONFIG_BOOLEAN(option);
}
/*
* Returns default boolean value of an option.
*
* Returns 1 if default value is true, 0 if it is false.
*/
int
config_file_option_boolean_default (struct t_config_option *option)
{
if (!option || !option->default_value || (option->type != CONFIG_OPTION_TYPE_BOOLEAN))
return 0;
return CONFIG_BOOLEAN_DEFAULT(option);
}
/*
* Returns inherited boolean value of an option: value of option if not NULL,
* or value of the parent option (if option inherits from another option).
*
* If the parent option is not found, returns the default value of the option.
* If the parent value is NULL, returns the default value of the parent option.
*/
int
config_file_option_boolean_inherited (struct t_config_option *option)
{
struct t_config_option *ptr_parent_option;
if (option && option->value)
{
return config_file_option_boolean (option);
}
else
{
ptr_parent_option = config_file_get_parent_option (option);
if (!ptr_parent_option)
return config_file_option_boolean_default (option);
if (!ptr_parent_option->value)
return config_file_option_boolean_default (ptr_parent_option);
return config_file_option_boolean (ptr_parent_option);
}
}
/*
* Returns integer value of an option.
*/
int
config_file_option_integer (struct t_config_option *option)
{
if (!option || !option->value)
return 0;
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
if (CONFIG_BOOLEAN(option) == CONFIG_BOOLEAN_TRUE)
return 1;
else
return 0;
case CONFIG_OPTION_TYPE_INTEGER:
return CONFIG_INTEGER(option);
case CONFIG_OPTION_TYPE_STRING:
return 0;
case CONFIG_OPTION_TYPE_COLOR:
return CONFIG_COLOR(option);
case CONFIG_OPTION_TYPE_ENUM:
return CONFIG_ENUM(option);
case CONFIG_NUM_OPTION_TYPES:
break;
}
return 0;
}
/*
* Returns default integer value of an option.
*/
int
config_file_option_integer_default (struct t_config_option *option)
{
if (!option || !option->default_value)
return 0;
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
if (CONFIG_BOOLEAN_DEFAULT(option) == CONFIG_BOOLEAN_TRUE)
return 1;
else
return 0;
case CONFIG_OPTION_TYPE_INTEGER:
return CONFIG_INTEGER_DEFAULT(option);
case CONFIG_OPTION_TYPE_STRING:
return 0;
case CONFIG_OPTION_TYPE_COLOR:
return CONFIG_COLOR_DEFAULT(option);
case CONFIG_OPTION_TYPE_ENUM:
return CONFIG_ENUM_DEFAULT(option);
case CONFIG_NUM_OPTION_TYPES:
break;
}
return 0;
}
/*
* Returns inherited integer value of an option: value of option if not NULL,
* or value of the parent option (if option inherits from another option).
*
* If the parent option is not found, returns the default value of the option.
* If the parent value is NULL, returns the default value of the parent option.
*/
int
config_file_option_integer_inherited (struct t_config_option *option)
{
struct t_config_option *ptr_parent_option;
if (option && option->value)
{
return config_file_option_integer (option);
}
else
{
ptr_parent_option = config_file_get_parent_option (option);
if (!ptr_parent_option)
return config_file_option_integer_default (option);
if (!ptr_parent_option->value)
return config_file_option_integer_default (ptr_parent_option);
return config_file_option_integer (ptr_parent_option);
}
}
/*
* Returns string value of an option.
*/
const char *
config_file_option_string (struct t_config_option *option)
{
if (!option || !option->value)
return NULL;
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
if (CONFIG_BOOLEAN(option))
return config_boolean_true[0];
else
return config_boolean_false[0];
case CONFIG_OPTION_TYPE_INTEGER:
return NULL;
case CONFIG_OPTION_TYPE_STRING:
return CONFIG_STRING(option);
case CONFIG_OPTION_TYPE_COLOR:
return gui_color_get_name (CONFIG_COLOR(option));
case CONFIG_OPTION_TYPE_ENUM:
return option->string_values[CONFIG_ENUM(option)];
case CONFIG_NUM_OPTION_TYPES:
return NULL;
}
return NULL;
}
/*
* Returns default string value of an option.
*/
const char *
config_file_option_string_default (struct t_config_option *option)
{
if (!option || !option->default_value)
return NULL;
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
if (CONFIG_BOOLEAN_DEFAULT(option))
return config_boolean_true[0];
else
return config_boolean_false[0];
case CONFIG_OPTION_TYPE_INTEGER:
return NULL;
case CONFIG_OPTION_TYPE_STRING:
return CONFIG_STRING_DEFAULT(option);
case CONFIG_OPTION_TYPE_COLOR:
return gui_color_get_name (CONFIG_COLOR_DEFAULT(option));
case CONFIG_OPTION_TYPE_ENUM:
return option->string_values[CONFIG_ENUM_DEFAULT(option)];
case CONFIG_NUM_OPTION_TYPES:
return NULL;
}
return NULL;
}
/*
* Returns inherited string value of an option: value of option if not NULL,
* or value of the parent option (if option inherits from another option).
*
* If the parent option is not found, returns the default value of the option.
* If the parent value is NULL, returns the default value of the parent option.
*/
const char *
config_file_option_string_inherited (struct t_config_option *option)
{
struct t_config_option *ptr_parent_option;
if (option && option->value)
{
return config_file_option_string (option);
}
else
{
ptr_parent_option = config_file_get_parent_option (option);
if (!ptr_parent_option)
return config_file_option_string_default (option);
if (!ptr_parent_option->value)
return config_file_option_string_default (ptr_parent_option);
return config_file_option_string (ptr_parent_option);
}
}
/*
* Returns color value of an option.
*/
const char *
config_file_option_color (struct t_config_option *option)
{
if (!option || !option->value || (option->type != CONFIG_OPTION_TYPE_COLOR))
return NULL;
return gui_color_get_name (CONFIG_COLOR(option));
}
/*
* Returns default color value of an option.
*/
const char *
config_file_option_color_default (struct t_config_option *option)
{
if (!option || !option->default_value || (option->type != CONFIG_OPTION_TYPE_COLOR))
return NULL;
return gui_color_get_name (CONFIG_COLOR_DEFAULT(option));
}
/*
* Returns inherited color value of an option: value of option if not NULL,
* or value of the parent option (if option inherits from another option).
*
* If the parent option is not found, returns the default value of the option.
* If the parent value is NULL, returns the default value of the parent option.
*/
const char *
config_file_option_color_inherited (struct t_config_option *option)
{
struct t_config_option *ptr_parent_option;
if (option && option->value)
return config_file_option_color (option);
else
{
ptr_parent_option = config_file_get_parent_option (option);
if (!ptr_parent_option)
return config_file_option_color_default (option);
if (!ptr_parent_option->value)
return config_file_option_color_default (ptr_parent_option);
return config_file_option_color (ptr_parent_option);
}
}
/*
* Returns enum value of an option.
*/
int
config_file_option_enum (struct t_config_option *option)
{
if (!option || !option->value)
return 0;
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
if (CONFIG_BOOLEAN(option) == CONFIG_BOOLEAN_TRUE)
return 1;
else
return 0;
case CONFIG_OPTION_TYPE_INTEGER:
return CONFIG_INTEGER(option);
case CONFIG_OPTION_TYPE_STRING:
return 0;
case CONFIG_OPTION_TYPE_COLOR:
return CONFIG_COLOR(option);
case CONFIG_OPTION_TYPE_ENUM:
return CONFIG_ENUM(option);
case CONFIG_NUM_OPTION_TYPES:
break;
}
return 0;
}
/*
* Returns default enum value of an option.
*/
int
config_file_option_enum_default (struct t_config_option *option)
{
if (!option || !option->default_value)
return 0;
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
if (CONFIG_BOOLEAN_DEFAULT(option) == CONFIG_BOOLEAN_TRUE)
return 1;
else
return 0;
case CONFIG_OPTION_TYPE_INTEGER:
return CONFIG_INTEGER_DEFAULT(option);
case CONFIG_OPTION_TYPE_STRING:
return 0;
case CONFIG_OPTION_TYPE_COLOR:
return CONFIG_COLOR_DEFAULT(option);
case CONFIG_OPTION_TYPE_ENUM:
return CONFIG_ENUM_DEFAULT(option);
case CONFIG_NUM_OPTION_TYPES:
break;
}
return 0;
}
/*
* Returns inherited enum value of an option: value of option if not NULL,
* or value of the parent option (if option inherits from another option).
*
* If the parent option is not found, returns the default value of the option.
* If the parent value is NULL, returns the default value of the parent option.
*/
int
config_file_option_enum_inherited (struct t_config_option *option)
{
struct t_config_option *ptr_parent_option;
if (option && option->value)
{
return config_file_option_enum (option);
}
else
{
ptr_parent_option = config_file_get_parent_option (option);
if (!ptr_parent_option)
return config_file_option_enum_default (option);
if (!ptr_parent_option->value)
return config_file_option_enum_default (ptr_parent_option);
return config_file_option_enum (ptr_parent_option);
}
}
/*
* Returns a char to add before the name of option to escape it.
*
* Returns:
* "\": name must be escaped with "\" (if names begins with # [ \)
* "": name must not be escaped
*/
const char *
config_file_option_escape (const char *name)
{
static char str_escaped[2] = "\\", str_not_escaped[1] = { '\0' };
if (!name)
return str_escaped;
if ((name[0] == '#') || (name[0] == '[') || (name[0] == '\\'))
return str_escaped;
return str_not_escaped;
}
/*
* Writes an option in a configuration file.
*
* Returns:
* 1: OK
* 0: error
*/
int
config_file_write_option (struct t_config_file *config_file,
struct t_config_option *option)
{
int rc;
if (!config_file || !config_file->file || !option)
return 0;
rc = 1;
if (option->value)
{
switch (option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
rc = string_fprintf (config_file->file, "%s%s = %s\n",
config_file_option_escape (option->name),
option->name,
(CONFIG_BOOLEAN(option) == CONFIG_BOOLEAN_TRUE) ?
"on" : "off");
break;
case CONFIG_OPTION_TYPE_INTEGER:
rc = string_fprintf (config_file->file, "%s%s = %d\n",
config_file_option_escape (option->name),
option->name,
CONFIG_INTEGER(option));
break;
case CONFIG_OPTION_TYPE_STRING:
rc = string_fprintf (config_file->file, "%s%s = \"%s\"\n",
config_file_option_escape (option->name),
option->name,
(char *)option->value);
break;
case CONFIG_OPTION_TYPE_COLOR:
rc = string_fprintf (config_file->file, "%s%s = %s\n",
config_file_option_escape (option->name),
option->name,
gui_color_get_name (CONFIG_COLOR(option)));
break;
case CONFIG_OPTION_TYPE_ENUM:
rc = string_fprintf (config_file->file, "%s%s = %s\n",
config_file_option_escape (option->name),
option->name,
option->string_values[CONFIG_ENUM(option)]);
break;
case CONFIG_NUM_OPTION_TYPES:
break;
}
}
else
{
rc = string_fprintf (config_file->file, "%s%s\n",
config_file_option_escape (option->name),
option->name);
}
return rc;
}
/*
* Writes a line in a configuration file.
*
* If value is NULL, then writes a section with [ ] around.
*
* Returns:
* 1: OK
* 0: error
*/
int
config_file_write_line (struct t_config_file *config_file,
const char *option_name, const char *value, ...)
{
int rc;
if (!config_file || !option_name)
return 0;
if (value && value[0])
{
weechat_va_format (value);
if (vbuffer)
{
if (vbuffer[0])
{
rc = string_fprintf (config_file->file, "%s%s = %s\n",
config_file_option_escape (option_name),
option_name, vbuffer);
free (vbuffer);
return rc;
}
free (vbuffer);
}
}
return (string_fprintf (config_file->file, "\n[%s]\n",
option_name));
}
/*
* Writes a configuration file (this function must not be called directly).
*
* Returns:
* WEECHAT_CONFIG_WRITE_OK: OK
* WEECHAT_CONFIG_WRITE_ERROR: error
* WEECHAT_CONFIG_WRITE_MEMORY_ERROR: not enough memory
*/
int
config_file_write_internal (struct t_config_file *config_file,
int default_options)
{
int rc;
long file_perms;
char *filename, *filename2, resolved_path[PATH_MAX], *error;
struct t_config_section *ptr_section;
struct t_config_option *ptr_option;
if (!config_file)
return WEECHAT_CONFIG_WRITE_ERROR;
/* build filename */
if (string_asprintf (&filename,
"%s%s%s",
weechat_config_dir,
DIR_SEPARATOR,
config_file->filename) < 0)
{
return WEECHAT_CONFIG_WRITE_MEMORY_ERROR;
}
/*
* build temporary filename, this temp file will be renamed to filename
* after write
*/
if (string_asprintf (&filename2, "%s.weechattmp", filename) < 0)
{
free (filename);
return WEECHAT_CONFIG_WRITE_MEMORY_ERROR;
}
/* if filename is a symbolic link, use target as filename */
if (realpath (filename, resolved_path))
{
if (strcmp (filename, resolved_path) != 0)
{
free (filename);
filename = strdup (resolved_path);
if (!filename)
{
free (filename2);
return WEECHAT_CONFIG_WRITE_MEMORY_ERROR;
}
}
}
log_printf (_("Writing configuration file %s%s%s"),
config_file->filename,
(default_options) ? " " : "",
(default_options) ? _("(default options)") : "");
/* open temp file in write mode */
config_file->file = fopen (filename2, "wb");
if (!config_file->file)
{
gui_chat_printf (NULL,
_("%sCannot create file \"%s\""),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename2);
goto error;
}
/* write header with name of config file and WeeChat version */
if (!string_fprintf (
config_file->file,
"#\n"
"# %s -- %s\n"
"#\n"
"# WARNING: It is NOT recommended to edit this file by hand,\n"
"# especially if WeeChat is running.\n"
"#\n"
"# Use commands like /set or /fset to change settings in WeeChat.\n"
"#\n"
"# For more info, see: https://weechat.org/doc/weechat/quickstart/\n"
"#\n",
version_get_name (),
config_file->filename))
{
goto error;
}
/* write config version (if different from 1) */
if (config_file->version > 1)
{
if (!string_fprintf (config_file->file,
"\nconfig_version = %d\n",
config_file->version))
{
goto error;
}
}
/* write all sections */
for (ptr_section = config_file->sections; ptr_section;
ptr_section = ptr_section->next_section)
{
/* call write callback if defined for section */
if (default_options && ptr_section->callback_write_default)
{
if ((ptr_section->callback_write_default) (
ptr_section->callback_write_default_pointer,
ptr_section->callback_write_default_data,
config_file,
ptr_section->name) != WEECHAT_CONFIG_WRITE_OK)
goto error;
}
else if (!default_options && ptr_section->callback_write)
{
if ((ptr_section->callback_write) (
ptr_section->callback_write_pointer,
ptr_section->callback_write_data,
config_file,
ptr_section->name) != WEECHAT_CONFIG_WRITE_OK)
goto error;
}
else
{
/* write all options for section */
if (!string_fprintf (config_file->file,
"\n[%s]\n", ptr_section->name))
goto error;
for (ptr_option = ptr_section->options; ptr_option;
ptr_option = ptr_option->next_option)
{
if (!config_file_write_option (config_file, ptr_option))
goto error;
}
}
}
if (fflush (config_file->file) != 0)
goto error;
/*
* ensure the file is really written on the storage device;
* this is disabled by default because it is really slow
* (about 20 to 200x slower)
*/
if (CONFIG_BOOLEAN(config_look_save_config_with_fsync))
{
if (fsync (fileno (config_file->file)) != 0)
goto error;
}
/* close temp file */
fclose (config_file->file);
config_file->file = NULL;
/* update file mode */
error = NULL;
file_perms = strtol (CONFIG_STRING(config_look_config_permissions), &error, 8);
if (!error || error[0])
file_perms = 0600;
if (chmod (filename2, file_perms) < 0)
{
gui_chat_printf (
NULL,
_("%sWARNING: failed to set permissions on configuration file "
"\"%s\" (%s)"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename2,
strerror (errno));
}
/* rename temp file to target file */
rc = rename (filename2, filename);
free (filename);
free (filename2);
if (rc != 0)
return WEECHAT_CONFIG_WRITE_ERROR;
return WEECHAT_CONFIG_WRITE_OK;
error:
gui_chat_printf (NULL,
_("%sError writing configuration file \"%s\""),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename);
log_printf (_("%sError writing configuration file \"%s\""),
"", config_file->filename);
if (config_file->file)
{
fclose (config_file->file);
config_file->file = NULL;
}
unlink (filename2);
free (filename);
free (filename2);
return WEECHAT_CONFIG_WRITE_ERROR;
}
/*
* Writes a configuration file.
*
* Returns:
* WEECHAT_CONFIG_WRITE_OK: OK
* WEECHAT_CONFIG_WRITE_ERROR: error
* WEECHAT_CONFIG_WRITE_MEMORY_ERROR: not enough memory
*/
int
config_file_write (struct t_config_file *config_file)
{
return config_file_write_internal (config_file, 0);
}
/*
* Parses configuration version.
*
* Returns:
* >= 1: configuration version
* -1: error
*/
int
config_file_parse_version (const char *version)
{
long number;
char *error;
if (!version)
return -1;
error = NULL;
number = strtoll (version, &error, 10);
if (!error || error[0])
return -1;
return (number < 1) ? -1 : (int)number;
}
/*
* Backups a configuration file if its version is unsupported and cannot be
* loaded.
*/
void
config_file_backup (const char *filename)
{
char *filename_backup, str_time[32], str_index[32];
int length, index;
struct tm *local_time;
time_t date;
if (!filename)
return;
length = strlen (filename) + 128;
filename_backup = malloc (length);
if (!filename_backup)
return;
date = time (NULL);
local_time = localtime (&date);
if (strftime (str_time, sizeof (str_time), ".%Y%m%d.%H%M%S", local_time) == 0)
str_time[0] = '\0';
index = 1;
while (1)
{
if (index == 1)
str_index[0] = '\0';
else
snprintf (str_index, sizeof (str_index), ".%d", index);
snprintf (filename_backup, length,
"%s.backup%s%s",
filename, str_time, str_index);
if (access (filename_backup, F_OK) != 0)
break;
index++;
}
if (dir_file_copy (filename, filename_backup))
{
gui_chat_printf (NULL,
_("%sFile %s has been backed up as %s"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename, filename_backup);
}
else
{
gui_chat_printf (NULL,
_("%sError: unable to backup file %s"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename);
}
free (filename_backup);
}
/*
* Updates data read from config file: either section or option + value.
* The update callback (if defined in config) is called if the config version
* read in file is less than to the current config version.
*
* Parameters "section", "option" and "value" are updated in place: if the
* callback gives a new value, they are first freed and allocated again with
* the new value (or set to NULL for the value if the callback returns
* special key "value_null").
*
* Section can be updated only if option and value are NULL (ie if we are
* reading a section line like "[section]").
*
* Integer warning_update_displayed is set to 1 if a warning is displayed,
* when the file is updated to a newer version (then it's not compatible any
* more with previous versions).
*/
void
config_file_update_data_read (struct t_config_file *config_file,
const char *filename,
const char *section,
const char *option,
const char *value,
char **ret_section,
char **ret_option,
char **ret_value,
int *warning_update_displayed)
{
struct t_hashtable *data_read, *hashtable;
const char *ptr_section, *ptr_option, *ptr_value;
int value_null;
/* do nothing if config is already the latest version */
if (config_file->version_read >= config_file->version)
return;
if (!*warning_update_displayed
&& (config_file->version_read < config_file->version))
{
gui_chat_printf (
NULL,
_("%sImportant: file %s has been updated from version %d to %d, "
"it is not compatible and cannot be loaded anymore with any "
"older version"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename,
config_file->version_read,
config_file->version);
*warning_update_displayed = 1;
}
/* do nothing if there's no update callback */
if (!config_file->callback_update)
return;
value_null = 0;
data_read = hashtable_new (
32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_STRING,
NULL, NULL);
if (!data_read)
return;
hashtable_set (data_read, "config", config_file->name);
if (section)
hashtable_set (data_read, "section", section);
if (option)
{
hashtable_set (data_read, "option", option);
if (value)
{
hashtable_set (data_read, "value", value);
}
else
{
hashtable_set (data_read, "value_null", "1");
value_null = 1;
}
}
hashtable = (config_file->callback_update)
(config_file->callback_update_pointer,
config_file->callback_update_data,
config_file,
config_file->version_read,
data_read);
if (hashtable)
{
/* if reading a section line, we can update its name */
if (section && !option && ret_section)
{
ptr_section = hashtable_get (hashtable, "section");
if (ptr_section && ptr_section[0])
{
free (*ret_section);
*ret_section = strdup (ptr_section);
}
}
/* if reading an option line, we can update its name and value */
if (section && option)
{
/* option name */
if (ret_option)
{
ptr_option = hashtable_get (hashtable, "option");
if (ptr_option)
{
free (*ret_option);
*ret_option = strdup (ptr_option);
}
}
/* value */
if (ret_value)
{
ptr_value = hashtable_get (hashtable, "value");
if (!value_null && hashtable_has_key (hashtable, "value_null"))
ptr_value = NULL;
free (*ret_value);
*ret_value = (ptr_value) ? strdup (ptr_value) : NULL;
}
}
}
if (hashtable && (hashtable != data_read))
hashtable_free (hashtable);
hashtable_free (data_read);
}
/*
* Reads a configuration file (this function must not be called directly).
*
* Returns:
* WEECHAT_CONFIG_READ_OK: OK
* WEECHAT_CONFIG_READ_MEMORY_ERROR: not enough memory
* WEECHAT_CONFIG_READ_FILE_NOT_FOUND: file not found
*/
int
config_file_read_internal (struct t_config_file *config_file, int reload)
{
int line_number, rc, length, version, warning_update_displayed;
char *filename, *section, *option, *value;
struct t_config_section *ptr_section;
struct t_config_option *ptr_option;
char line[16384], *ptr_line, *ptr_line2, *pos, *pos2;
if (!config_file)
return WEECHAT_CONFIG_READ_FILE_NOT_FOUND;
config_file->version_read = 1;
warning_update_displayed = 0;
/* build filename */
if (string_asprintf (&filename,
"%s%s%s",
weechat_config_dir,
DIR_SEPARATOR,
config_file->filename) < 0)
{
return WEECHAT_CONFIG_READ_MEMORY_ERROR;
}
/* create file with default options if it does not exist */
if (access (filename, F_OK) != 0)
{
if (strcmp (config_file->name, WEECHAT_CONFIG_NAME) == 0)
weechat_first_start = 1;
config_file_write_internal (config_file, 1);
}
/* read config file */
config_file->file = fopen (filename, "r");
if (!config_file->file)
{
gui_chat_printf (NULL,
_("%sWARNING: failed to read configuration file "
"\"%s\" (%s)"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename,
strerror (errno));
gui_chat_printf (NULL,
_("%sWARNING: file \"%s\" will be overwritten on exit "
"with default values (it is HIGHLY recommended to "
"backup this file now)"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename);
free (filename);
return WEECHAT_CONFIG_READ_FILE_NOT_FOUND;
}
if (!reload)
log_printf (_("Reading configuration file %s"), config_file->filename);
/* read all lines */
ptr_section = NULL;
line_number = 0;
while (!feof (config_file->file))
{
line_number++;
option = NULL;
value = NULL;
ptr_line = fgets (line, sizeof (line) - 1, config_file->file);
if (!ptr_line)
goto end_line;
/* encode line to internal charset */
ptr_line2 = string_iconv_to_internal (NULL, ptr_line);
if (ptr_line2)
{
snprintf (line, sizeof (line), "%s", ptr_line2);
free (ptr_line2);
}
/* skip spaces */
while (ptr_line[0] == ' ')
{
ptr_line++;
}
/* remove CR/LF */
pos = strchr (ptr_line, '\r');
if (pos)
pos[0] = '\0';
pos = strchr (ptr_line, '\n');
if (pos)
pos[0] = '\0';
/* ignore empty line or comment */
if (!ptr_line[0] || (ptr_line[0] == '#'))
goto end_line;
/* beginning of section */
if ((ptr_line[0] == '[') && !strchr (ptr_line, '='))
{
pos = strchr (ptr_line, ']');
if (!pos)
{
gui_chat_printf (NULL,
_("%sWarning: %s, line %d: invalid "
"syntax, missing \"]\""),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename, line_number);
}
else
{
section = string_strndup (ptr_line + 1, pos - ptr_line - 1);
if (section)
{
config_file_update_data_read (config_file, filename,
section, NULL, NULL,
&section, NULL, NULL,
&warning_update_displayed);
ptr_section = config_file_search_section (config_file,
section);
if (!ptr_section)
{
gui_chat_printf (
NULL,
_("%sWarning: %s, line %d: ignoring unknown "
"section identifier (\"%s\")"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename, line_number, section);
}
free (section);
}
}
goto end_line;
}
/* skip escape char */
if (ptr_line[0] == '\\')
ptr_line++;
pos = strstr (ptr_line, " =");
if (pos)
{
/* skip spaces before '=' */
pos2 = pos - 1;
while ((pos2 > ptr_line) && (pos2[0] == ' '))
{
pos2--;
}
option = string_strndup (ptr_line, pos2 + 1 - ptr_line);
/* skip spaces after '=' */
pos += 2;
while (pos[0] == ' ')
{
pos++;
}
if (strcmp (pos, WEECHAT_CONFIG_OPTION_NULL) != 0)
{
length = strlen (pos);
if (length > 1)
{
/* remove simple or double quotes and spaces at the end */
pos2 = pos + length - 1;
while ((pos2 > pos) && (pos2[0] == ' '))
{
pos2--;
}
if (((pos[0] == '\'') && (pos2[0] == '\''))
|| ((pos[0] == '"') && (pos2[0] == '"')))
{
value = string_strndup (pos + 1, pos2 - pos - 1);
}
else
{
value = string_strndup (pos, pos2 + 1 - pos);
}
}
else
{
value = strdup (pos);
}
}
}
else
{
option = strdup (ptr_line);
}
if (!ptr_section && (strcmp (option, CONFIG_VERSION_OPTION) == 0))
{
version = config_file_parse_version (pos);
if (version < 0)
{
gui_chat_printf (
NULL,
_("%sError: %s, line %d: invalid config "
"version: \"%s\" => "
"rest of file is IGNORED, default options are used"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename, line_number,
line);
config_file_backup (filename);
free (option);
free (value);
goto end_file;
}
else
{
config_file->version_read = version;
if (config_file->version_read > config_file->version)
{
gui_chat_printf (
NULL,
_("%sError: %s, version read (%d) is newer than "
"supported version (%d) => "
"rest of file is IGNORED, default options are used"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename,
config_file->version_read,
config_file->version);
config_file_backup (filename);
free (option);
free (value);
goto end_file;
}
}
goto end_line;
}
if (!ptr_section)
{
gui_chat_printf (NULL,
_("%sWarning: %s, line %d: "
"ignoring option outside section: %s"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename, line_number,
line);
goto end_line;
}
config_file_update_data_read (config_file, filename,
ptr_section->name, option, value,
NULL, &option, &value,
&warning_update_displayed);
/* option has been ignored by the update callback? */
if (!option || !option[0])
goto end_line;
if (ptr_section->callback_read)
{
ptr_option = NULL;
rc = (ptr_section->callback_read)
(ptr_section->callback_read_pointer,
ptr_section->callback_read_data,
config_file,
ptr_section,
option,
value);
}
else
{
rc = WEECHAT_CONFIG_OPTION_SET_OPTION_NOT_FOUND;
ptr_option = config_file_search_option (config_file,
ptr_section,
option);
if (ptr_option)
{
rc = config_file_option_set (ptr_option, value, 1);
ptr_option->loaded = 1;
}
else
{
if (ptr_section->callback_create_option)
{
rc = (int) (ptr_section->callback_create_option) (
ptr_section->callback_create_option_pointer,
ptr_section->callback_create_option_data,
config_file,
ptr_section,
option,
value);
}
}
}
switch (rc)
{
case WEECHAT_CONFIG_OPTION_SET_OPTION_NOT_FOUND:
gui_chat_printf (
NULL,
_("%sWarning: %s, line %d: "
"ignoring unknown option for section \"%s\": %s"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename, line_number,
ptr_section->name,
line);
break;
case WEECHAT_CONFIG_OPTION_SET_ERROR:
gui_chat_printf (
NULL,
_("%sWarning: %s, line %d: "
"ignoring invalid value for option in section \"%s\": %s"),
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
filename, line_number,
ptr_section->name,
line);
break;
}
end_line:
free (option);
free (value);
}
end_file:
fclose (config_file->file);
config_file->file = NULL;
free (filename);
return WEECHAT_CONFIG_READ_OK;
}
/*
* Reads a configuration file.
*
* Returns:
* WEECHAT_CONFIG_READ_OK: OK
* WEECHAT_CONFIG_READ_MEMORY_ERROR: not enough memory
* WEECHAT_CONFIG_READ_FILE_NOT_FOUND: file not found
*/
int
config_file_read (struct t_config_file *config_file)
{
return config_file_read_internal (config_file, 0);
}
/*
* Reloads a configuration file.
*
* Returns:
* WEECHAT_CONFIG_READ_OK: OK
* WEECHAT_CONFIG_READ_MEMORY_ERROR: not enough memory
* WEECHAT_CONFIG_READ_FILE_NOT_FOUND: file not found
*/
int
config_file_reload (struct t_config_file *config_file)
{
struct t_config_section *ptr_section;
struct t_config_option *ptr_option;
int rc;
if (!config_file)
return WEECHAT_CONFIG_READ_FILE_NOT_FOUND;
log_printf (_("Reloading configuration file %s"), config_file->filename);
/* init "loaded" flag for all options */
for (ptr_section = config_file->sections; ptr_section;
ptr_section = ptr_section->next_section)
{
if (!ptr_section->callback_read)
{
for (ptr_option = ptr_section->options; ptr_option;
ptr_option = ptr_option->next_option)
{
ptr_option->loaded = 0;
}
}
}
/* read configuration file */
rc = config_file_read_internal (config_file, 1);
/* reset options not found in configuration file */
for (ptr_section = config_file->sections; ptr_section;
ptr_section = ptr_section->next_section)
{
if (!ptr_section->callback_read)
{
for (ptr_option = ptr_section->options; ptr_option;
ptr_option = ptr_option->next_option)
{
if (!ptr_option->loaded)
config_file_option_reset (ptr_option, 1);
}
}
}
return rc;
}
/*
* Frees data in an option.
*/
void
config_file_option_free_data (struct t_config_option *option)
{
free (option->name);
free (option->parent_name);
free (option->description);
string_free_split (option->string_values);
free (option->default_value);
free (option->value);
free (option->callback_check_value_data);
free (option->callback_change_data);
free (option->callback_delete_data);
}
/*
* Frees an option.
*/
void
config_file_option_free (struct t_config_option *option, int run_callback)
{
struct t_config_section *ptr_section;
struct t_config_option *new_options;
char *option_full_name;
if (!option)
return;
option_full_name = (run_callback) ?
config_file_option_full_name (option) : NULL;
ptr_section = option->section;
/* free data */
config_file_option_free_data (option);
/* remove option from section */
if (ptr_section)
{
if (ptr_section->last_option == option)
ptr_section->last_option = option->prev_option;
if (option->prev_option)
{
(option->prev_option)->next_option = option->next_option;
new_options = ptr_section->options;
}
else
new_options = option->next_option;
if (option->next_option)
(option->next_option)->prev_option = option->prev_option;
ptr_section->options = new_options;
}
free (option);
if (option_full_name)
{
hook_config_exec (option_full_name, NULL);
free (option_full_name);
}
}
/*
* Frees options in a section.
*/
void
config_file_section_free_options (struct t_config_section *section)
{
if (!section)
return;
while (section->options)
{
config_file_option_free (section->options, 1);
}
}
/*
* Frees a section.
*/
void
config_file_section_free (struct t_config_section *section)
{
struct t_config_file *ptr_config;
struct t_config_section *new_sections;
if (!section)
return;
ptr_config = section->config_file;
/* free data */
config_file_section_free_options (section);
free (section->name);
free (section->callback_read_data);
free (section->callback_write_data);
free (section->callback_write_default_data);
free (section->callback_create_option_data);
free (section->callback_delete_option_data);
/* remove section from list */
if (ptr_config->last_section == section)
ptr_config->last_section = section->prev_section;
if (section->prev_section)
{
(section->prev_section)->next_section = section->next_section;
new_sections = ptr_config->sections;
}
else
new_sections = section->next_section;
if (section->next_section)
(section->next_section)->prev_section = section->prev_section;
free (section);
ptr_config->sections = new_sections;
}
/*
* Frees a configuration file.
*/
void
config_file_free (struct t_config_file *config_file)
{
struct t_config_file *new_config_files;
if (!config_file)
return;
/* free data */
while (config_file->sections)
{
config_file_section_free (config_file->sections);
}
free (config_file->name);
free (config_file->filename);
/* remove configuration file from list */
if (last_config_file == config_file)
last_config_file = config_file->prev_config;
if (config_file->prev_config)
{
(config_file->prev_config)->next_config = config_file->next_config;
new_config_files = config_files;
}
else
new_config_files = config_file->next_config;
if (config_file->next_config)
(config_file->next_config)->prev_config = config_file->prev_config;
/* free data */
free (config_file->callback_update_data);
free (config_file->callback_reload_data);
free (config_file);
config_files = new_config_files;
}
/*
* Frees all configuration files.
*/
void
config_file_free_all (void)
{
while (config_files)
{
config_file_free (config_files);
}
}
/*
* Frees all configuration files for a plugin.
*/
void
config_file_free_all_plugin (struct t_weechat_plugin *plugin)
{
struct t_config_file *ptr_config, *next_config;
ptr_config = config_files;
while (ptr_config)
{
next_config = ptr_config->next_config;
if (ptr_config->plugin == plugin)
config_file_free (ptr_config);
ptr_config = next_config;
}
}
/*
* Returns hdata for structure t_config_file.
*/
struct t_hdata *
config_file_hdata_config_file_cb (const void *pointer, void *data,
const char *hdata_name)
{
struct t_hdata *hdata;
/* make C compiler happy */
(void) pointer;
(void) data;
hdata = hdata_new (NULL, hdata_name, "prev_config", "next_config",
0, 0, NULL, NULL);
if (hdata)
{
HDATA_VAR(struct t_config_file, plugin, POINTER, 0, NULL, "plugin");
HDATA_VAR(struct t_config_file, priority, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_config_file, name, STRING, 0, NULL, NULL);
HDATA_VAR(struct t_config_file, filename, STRING, 0, NULL, NULL);
HDATA_VAR(struct t_config_file, file, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_file, version, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_config_file, callback_reload, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_file, callback_reload_pointer, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_file, callback_reload_data, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_file, sections, POINTER, 0, NULL, "config_section");
HDATA_VAR(struct t_config_file, last_section, POINTER, 0, NULL, "config_section");
HDATA_VAR(struct t_config_file, prev_config, POINTER, 0, NULL, hdata_name);
HDATA_VAR(struct t_config_file, next_config, POINTER, 0, NULL, hdata_name);
HDATA_LIST(config_files, WEECHAT_HDATA_LIST_CHECK_POINTERS);
HDATA_LIST(last_config_file, 0);
}
return hdata;
}
/*
* Returns hdata for structure t_config_section.
*/
struct t_hdata *
config_file_hdata_config_section_cb (const void *pointer, void *data,
const char *hdata_name)
{
struct t_hdata *hdata;
/* make C compiler happy */
(void) pointer;
(void) data;
hdata = hdata_new (NULL, hdata_name, "prev_section", "next_section",
0, 0, NULL, NULL);
if (hdata)
{
HDATA_VAR(struct t_config_section, config_file, POINTER, 0, NULL, "config_file");
HDATA_VAR(struct t_config_section, name, STRING, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, user_can_add_options, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, user_can_delete_options, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_read, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_read_pointer, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_read_data, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_write, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_write_pointer, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_write_data, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_write_default, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_write_default_pointer, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_write_default_data, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_create_option, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_create_option_pointer, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_create_option_data, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_delete_option, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_delete_option_pointer, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, callback_delete_option_data, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_section, options, POINTER, 0, NULL, "config_option");
HDATA_VAR(struct t_config_section, last_option, POINTER, 0, NULL, "config_option");
HDATA_VAR(struct t_config_section, prev_section, POINTER, 0, NULL, hdata_name);
HDATA_VAR(struct t_config_section, next_section, POINTER, 0, NULL, hdata_name);
}
return hdata;
}
/*
* Returns hdata for structure t_config_option.
*/
struct t_hdata *
config_file_hdata_config_option_cb (const void *pointer, void *data,
const char *hdata_name)
{
struct t_hdata *hdata;
/* make C compiler happy */
(void) pointer;
(void) data;
hdata = hdata_new (NULL, hdata_name, "prev_option", "next_option",
0, 0, NULL, NULL);
if (hdata)
{
HDATA_VAR(struct t_config_option, config_file, POINTER, 0, NULL, "config_file");
HDATA_VAR(struct t_config_option, section, POINTER, 0, NULL, "config_section");
HDATA_VAR(struct t_config_option, name, STRING, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, parent_name, STRING, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, type, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, description, STRING, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, string_values, STRING, 0, "*,*", NULL);
HDATA_VAR(struct t_config_option, min, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, max, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, default_value, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, value, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, null_value_allowed, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, callback_check_value, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, callback_check_value_pointer, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, callback_check_value_data, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, callback_change, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, callback_change_pointer, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, callback_change_data, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, callback_delete, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, callback_delete_pointer, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, callback_delete_data, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, loaded, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_config_option, prev_option, POINTER, 0, NULL, hdata_name);
HDATA_VAR(struct t_config_option, next_option, POINTER, 0, NULL, hdata_name);
}
return hdata;
}
/*
* Adds a configuration option in an infolist.
*
* Returns:
* 1: OK
* 0: error
*/
int
config_file_add_option_to_infolist (struct t_infolist *infolist,
struct t_config_file *config_file,
struct t_config_section *section,
struct t_config_option *option,
const char *option_name)
{
char *option_full_name, *value, *string_values;
struct t_config_option *ptr_parent_option;
struct t_infolist_item *ptr_item;
int rc;
rc = 1;
option_full_name = config_file_option_full_name (option);
if (!option_full_name)
goto error;
if (option_name && option_name[0]
&& (!string_match (option_full_name, option_name, 1)))
{
goto end;
}
ptr_item = infolist_new_item (infolist);
if (!ptr_item)
goto error;
if (!infolist_new_var_string (ptr_item, "full_name", option_full_name))
goto error;
if (!infolist_new_var_string (ptr_item, "config_name", config_file->name))
goto error;
if (!infolist_new_var_string (ptr_item, "section_name", section->name))
goto error;
if (!infolist_new_var_string (ptr_item, "option_name", option->name))
goto error;
if (!infolist_new_var_string (ptr_item, "parent_name", option->parent_name))
goto error;
if (!infolist_new_var_string (ptr_item, "description", option->description))
goto error;
if (!infolist_new_var_string (ptr_item, "description_nls",
(option->description
&& option->description[0]) ?
_(option->description) : ""))
{
goto error;
}
string_values = string_rebuild_split_string (
(const char **)option->string_values, "|", 0, -1);
if (!infolist_new_var_string (ptr_item, "string_values", string_values))
{
free (string_values);
goto error;
}
free (string_values);
if (!infolist_new_var_integer (ptr_item, "min", option->min))
goto error;
if (!infolist_new_var_integer (ptr_item, "max", option->max))
goto error;
if (!infolist_new_var_integer (ptr_item, "null_value_allowed",
option->null_value_allowed))
{
goto error;
}
if (!infolist_new_var_integer (ptr_item, "value_is_null",
(option->value) ? 0 : 1))
{
goto error;
}
if (!infolist_new_var_integer (ptr_item,
"default_value_is_null",
(option->default_value) ?
0 : 1))
{
goto error;
}
if (!infolist_new_var_string (ptr_item, "type",
config_option_type_string[option->type]))
{
goto error;
}
if (option->value)
{
value = config_file_option_value_to_string (option, 0, 0, 0);
if (!value)
goto error;
if (!infolist_new_var_string (ptr_item, "value", value))
{
free (value);
goto error;
}
free (value);
}
if (option->default_value)
{
value = config_file_option_value_to_string (option, 1, 0, 0);
if (!value)
goto error;
if (!infolist_new_var_string (ptr_item, "default_value", value))
{
free (value);
goto error;
}
free (value);
}
if (option->parent_name)
{
config_file_search_with_string (option->parent_name,
NULL, NULL, &ptr_parent_option, NULL);
if (ptr_parent_option && ptr_parent_option->value)
{
value = config_file_option_value_to_string (ptr_parent_option,
0, 0, 0);
if (!value)
goto error;
if (!infolist_new_var_string (ptr_item, "parent_value", value))
{
free (value);
goto error;
}
free (value);
}
}
goto end;
error:
rc = 0;
end:
free (option_full_name);
return rc;
}
/*
* Adds configuration options in an infolist.
*
* Returns:
* 1: OK
* 0: error
*/
int
config_file_add_to_infolist (struct t_infolist *infolist,
const char *option_name)
{
struct t_config_file *ptr_config;
struct t_config_section *ptr_section;
struct t_config_option *ptr_option;
if (!infolist)
return 0;
for (ptr_config = config_files; ptr_config;
ptr_config = ptr_config->next_config)
{
for (ptr_section = ptr_config->sections; ptr_section;
ptr_section = ptr_section->next_section)
{
for (ptr_option = ptr_section->options; ptr_option;
ptr_option = ptr_option->next_option)
{
if (!config_file_add_option_to_infolist (infolist,
ptr_config,
ptr_section,
ptr_option,
option_name))
{
return 0;
}
}
}
}
return 1;
}
/*
* Prints configuration file in WeeChat log file (usually for crash dump).
*/
void
config_file_print_log (void)
{
struct t_config_file *ptr_config_file;
struct t_config_section *ptr_section;
struct t_config_option *ptr_option;
for (ptr_config_file = config_files; ptr_config_file;
ptr_config_file = ptr_config_file->next_config)
{
log_printf ("");
log_printf ("[config (addr:%p)]", ptr_config_file);
log_printf (" plugin . . . . . . . . : %p ('%s')",
ptr_config_file->plugin,
plugin_get_name (ptr_config_file->plugin));
log_printf (" priority . . . . . . . : %d", ptr_config_file->priority);
log_printf (" name . . . . . . . . . : '%s'", ptr_config_file->name);
log_printf (" filename . . . . . . . : '%s'", ptr_config_file->filename);
log_printf (" file . . . . . . . . . : %p", ptr_config_file->file);
log_printf (" callback_reload. . . . : %p", ptr_config_file->callback_reload);
log_printf (" callback_reload_pointer: %p", ptr_config_file->callback_reload_pointer);
log_printf (" callback_reload_data . : %p", ptr_config_file->callback_reload_data);
log_printf (" sections . . . . . . . : %p", ptr_config_file->sections);
log_printf (" last_section . . . . . : %p", ptr_config_file->last_section);
log_printf (" prev_config. . . . . . : %p", ptr_config_file->prev_config);
log_printf (" next_config. . . . . . : %p", ptr_config_file->next_config);
for (ptr_section = ptr_config_file->sections; ptr_section;
ptr_section = ptr_section->next_section)
{
log_printf ("");
log_printf (" [section (addr:%p)]", ptr_section);
log_printf (" config_file . . . . . . . . . : %p", ptr_section->config_file);
log_printf (" name. . . . . . . . . . . . . : '%s'", ptr_section->name);
log_printf (" callback_read . . . . . . . . : %p", ptr_section->callback_read);
log_printf (" callback_read_pointer . . . . : %p", ptr_section->callback_read_pointer);
log_printf (" callback_read_data. . . . . . : %p", ptr_section->callback_read_data);
log_printf (" callback_write. . . . . . . . : %p", ptr_section->callback_write);
log_printf (" callback_write_pointer. . . . : %p", ptr_section->callback_write_pointer);
log_printf (" callback_write_data . . . . . : %p", ptr_section->callback_write_data);
log_printf (" callback_write_default. . . . : %p", ptr_section->callback_write_default);
log_printf (" callback_write_default_pointer: %p", ptr_section->callback_write_default_pointer);
log_printf (" callback_write_default_data . : %p", ptr_section->callback_write_default_data);
log_printf (" callback_create_option. . . . : %p", ptr_section->callback_create_option);
log_printf (" callback_create_option_pointer: %p", ptr_section->callback_create_option_pointer);
log_printf (" callback_create_option_data . : %p", ptr_section->callback_create_option_data);
log_printf (" callback_delete_option. . . . : %p", ptr_section->callback_delete_option);
log_printf (" callback_delete_option_pointer: %p", ptr_section->callback_delete_option_pointer);
log_printf (" callback_delete_option_data . : %p", ptr_section->callback_delete_option_data);
log_printf (" options . . . . . . . . . . . : %p", ptr_section->options);
log_printf (" last_option . . . . . . . . . : %p", ptr_section->last_option);
log_printf (" prev_section. . . . . . . . . : %p", ptr_section->prev_section);
log_printf (" next_section. . . . . . . . . : %p", ptr_section->next_section);
for (ptr_option = ptr_section->options; ptr_option;
ptr_option = ptr_option->next_option)
{
log_printf ("");
log_printf (" [option (addr:%p)]", ptr_option);
log_printf (" config_file. . . . . . . . . : %p", ptr_option->config_file);
log_printf (" section. . . . . . . . . . . : %p", ptr_option->section);
log_printf (" name . . . . . . . . . . . . : '%s'", ptr_option->name);
log_printf (" parent_name. . . . . . . . . : '%s'", ptr_option->parent_name);
log_printf (" type . . . . . . . . . . . . : %d", ptr_option->type);
log_printf (" description. . . . . . . . . : '%s'", ptr_option->description);
log_printf (" string_values. . . . . . . . : %p", ptr_option->string_values);
log_printf (" min. . . . . . . . . . . . . : %d", ptr_option->min);
log_printf (" max. . . . . . . . . . . . . : %d", ptr_option->max);
switch (ptr_option->type)
{
case CONFIG_OPTION_TYPE_BOOLEAN:
log_printf (" default value. . . . . . . . : %s",
(ptr_option->default_value) ?
((CONFIG_BOOLEAN_DEFAULT(ptr_option) == CONFIG_BOOLEAN_TRUE) ?
"on" : "off") : "null");
log_printf (" value (boolean). . . . . . . : %s",
(ptr_option->value) ?
((CONFIG_BOOLEAN(ptr_option) == CONFIG_BOOLEAN_TRUE) ?
"on" : "off") : "null");
break;
case CONFIG_OPTION_TYPE_INTEGER:
if (ptr_option->default_value)
log_printf (" default value. . . . . . . . : %d",
CONFIG_INTEGER_DEFAULT(ptr_option));
else
log_printf (" default value. . . . . . . . : null");
if (ptr_option->value)
log_printf (" value (integer). . . . . . . : %d",
CONFIG_INTEGER(ptr_option));
else
log_printf (" value (integer). . . . . . . : null");
break;
case CONFIG_OPTION_TYPE_STRING:
if (ptr_option->default_value)
log_printf (" default value. . . . . . . . : '%s'",
CONFIG_STRING_DEFAULT(ptr_option));
else
log_printf (" default value. . . . . . . . : null");
if (ptr_option->value)
log_printf (" value (string) . . . . . . . : '%s'",
CONFIG_STRING(ptr_option));
else
log_printf (" value (string) . . . . . . . : null");
break;
case CONFIG_OPTION_TYPE_COLOR:
if (ptr_option->default_value)
log_printf (" default value. . . . . . . . : %d ('%s')",
CONFIG_COLOR_DEFAULT(ptr_option),
gui_color_get_name (CONFIG_COLOR_DEFAULT(ptr_option)));
else
log_printf (" default value. . . . . . . . : null");
if (ptr_option->value)
log_printf (" value (color). . . . . . . . : %d ('%s')",
CONFIG_COLOR(ptr_option),
gui_color_get_name (CONFIG_COLOR(ptr_option)));
else
log_printf (" value (color). . . . . . . . : null");
break;
case CONFIG_OPTION_TYPE_ENUM:
log_printf (" default value. . . . . . . . : '%s'",
(ptr_option->default_value) ?
ptr_option->string_values[CONFIG_ENUM_DEFAULT(ptr_option)] : "null");
log_printf (" value (integer/str). . . . . : '%s'",
(ptr_option->value) ?
ptr_option->string_values[CONFIG_ENUM(ptr_option)] : "null");
break;
case CONFIG_NUM_OPTION_TYPES:
break;
}
log_printf (" null_value_allowed . . . . . : %d", ptr_option->null_value_allowed);
log_printf (" callback_check_value . . . . : %p", ptr_option->callback_check_value);
log_printf (" callback_check_value_pointer : %p", ptr_option->callback_check_value_pointer);
log_printf (" callback_check_value_data. . : %p", ptr_option->callback_check_value_data);
log_printf (" callback_change. . . . . . . : %p", ptr_option->callback_change);
log_printf (" callback_change_pointer. . . : %p", ptr_option->callback_change_pointer);
log_printf (" callback_change_data . . . . : %p", ptr_option->callback_change_data);
log_printf (" callback_delete. . . . . . . : %p", ptr_option->callback_delete);
log_printf (" callback_delete_pointer. . . : %p", ptr_option->callback_delete_pointer);
log_printf (" callback_delete_data . . . . : %p", ptr_option->callback_delete_data);
log_printf (" loaded . . . . . . . . . . . : %d", ptr_option->loaded);
log_printf (" prev_option. . . . . . . . . : %p", ptr_option->prev_option);
log_printf (" next_option. . . . . . . . . : %p", ptr_option->next_option);
}
}
}
}