mirror of
https://github.com/weechat/weechat.git
synced 2026-07-06 01:33:12 +02:00
cf6aca1619
This pointer is the first argument received by callbacks, and the existing argument "data" is now automatically freed by WeeChat when the object containing the callback is removed. With this new pointer, the linked list of callbacks in scripts has been removed. This will improve speed of scripts (using a lot of hooks), reduce memory used by scripts and reduce time to unload scripts. Following functions are affected in the C API: * exec_on_files * config_new * config_new_section * config_new_option * hook_command * hook_command_run * hook_timer * hook_fd * hook_process * hook_process_hashtable * hook_connect * hook_print * hook_signal * hook_hsignal * hook_config * hook_completion * hook_modifier * hook_info * hook_info_hashtable * hook_infolist * hook_hdata * hook_focus * unhook_all_plugin * buffer_new * bar_item_new * upgrade_new * upgrade_read
3569 lines
121 KiB
C
3569 lines
121 KiB
C
/*
|
|
* wee-config-file.c - configuration files/sections/options management
|
|
*
|
|
* Copyright (C) 2003-2016 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 <http://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 "wee-config-file.h"
|
|
#include "wee-config.h"
|
|
#include "wee-hdata.h"
|
|
#include "wee-hook.h"
|
|
#include "wee-infolist.h"
|
|
#include "wee-log.h"
|
|
#include "wee-string.h"
|
|
#include "wee-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") };
|
|
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);
|
|
|
|
|
|
/*
|
|
* Searches for a configuration file.
|
|
*/
|
|
|
|
struct t_config_file *
|
|
config_file_search (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_strcasecmp (ptr_config->name, name) == 0)
|
|
return ptr_config;
|
|
}
|
|
|
|
/* 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_config_find_pos (const char *name)
|
|
{
|
|
struct t_config_file *ptr_config;
|
|
|
|
if (name)
|
|
{
|
|
for (ptr_config = config_files; ptr_config;
|
|
ptr_config = ptr_config->next_config)
|
|
{
|
|
if (string_strcasecmp (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 in list (keeping configuration 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_config_find_pos (config_file->name);
|
|
if (pos_config)
|
|
{
|
|
/* insert configuration file 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 configuration file to the end of list */
|
|
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 configuration file */
|
|
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;
|
|
char *filename;
|
|
int length;
|
|
|
|
if (!name)
|
|
return NULL;
|
|
|
|
/* two configuration files can not have same name */
|
|
if (config_file_search (name))
|
|
return NULL;
|
|
|
|
new_config_file = malloc (sizeof (*new_config_file));
|
|
if (new_config_file)
|
|
{
|
|
new_config_file->plugin = plugin;
|
|
new_config_file->name = strdup (name);
|
|
if (!new_config_file->name)
|
|
{
|
|
free (new_config_file);
|
|
return NULL;
|
|
}
|
|
length = strlen (name) + 8 + 1;
|
|
filename = malloc (length);
|
|
if (filename)
|
|
{
|
|
snprintf (filename, length, "%s.conf", name);
|
|
new_config_file->filename = strdup (filename);
|
|
free (filename);
|
|
}
|
|
else
|
|
new_config_file->filename = strdup (name);
|
|
if (!new_config_file->filename)
|
|
{
|
|
free (new_config_file->name);
|
|
free (new_config_file);
|
|
return NULL;
|
|
}
|
|
new_config_file->file = 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;
|
|
|
|
new_config_file->prev_config = last_config_file;
|
|
new_config_file->next_config = NULL;
|
|
if (config_files)
|
|
last_config_file->next_config = new_config_file;
|
|
else
|
|
config_files = new_config_file;
|
|
last_config_file = new_config_file;
|
|
}
|
|
|
|
return new_config_file;
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
for (ptr_section = config_file->sections; ptr_section;
|
|
ptr_section = ptr_section->next_section)
|
|
{
|
|
if (string_strcasecmp (name, ptr_section->name) < 0)
|
|
return ptr_section;
|
|
}
|
|
}
|
|
|
|
/* position not found (we will add to the end of list) */
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Inserts a section in configuration file (keeping sections sorted by name).
|
|
*/
|
|
|
|
void
|
|
config_file_section_insert_in_config (struct t_config_section *section)
|
|
{
|
|
struct t_config_section *pos_section;
|
|
|
|
if (!section || !section->config_file)
|
|
return;
|
|
|
|
if (section->config_file->sections)
|
|
{
|
|
pos_section = config_file_section_find_pos (section->config_file,
|
|
section->name);
|
|
if (pos_section)
|
|
{
|
|
/* insert section into the list (before section found) */
|
|
section->prev_section = pos_section->prev_section;
|
|
section->next_section = pos_section;
|
|
if (pos_section->prev_section)
|
|
(pos_section->prev_section)->next_section = section;
|
|
else
|
|
(section->config_file)->sections = section;
|
|
pos_section->prev_section = section;
|
|
}
|
|
else
|
|
{
|
|
/* add section to end of sections */
|
|
section->prev_section = (section->config_file)->last_section;
|
|
section->next_section = NULL;
|
|
(section->config_file)->last_section->next_section = section;
|
|
(section->config_file)->last_section = section;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* first section of file */
|
|
section->prev_section = NULL;
|
|
section->next_section = NULL;
|
|
(section->config_file)->sections = section;
|
|
(section->config_file)->last_section = section;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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->sections)
|
|
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 *section_name)
|
|
{
|
|
struct t_config_section *ptr_section;
|
|
|
|
if (!config_file || !section_name)
|
|
return NULL;
|
|
|
|
for (ptr_section = config_file->sections; ptr_section;
|
|
ptr_section = ptr_section->next_section)
|
|
{
|
|
if (string_strcasecmp (ptr_section->name, section_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)
|
|
{
|
|
int length_option;
|
|
char *option_full_name;
|
|
|
|
if (!option)
|
|
return NULL;
|
|
|
|
length_option = strlen (option->config_file->name) + 1 +
|
|
strlen (option->section->name) + 1 + strlen (option->name) + 1;
|
|
option_full_name = malloc (length_option);
|
|
if (option_full_name)
|
|
{
|
|
snprintf (option_full_name, length_option,
|
|
"%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_full_name = config_file_option_full_name (option);
|
|
if (option_full_name)
|
|
{
|
|
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:
|
|
if (option->string_values)
|
|
hook_config_exec (option_full_name,
|
|
option->string_values[CONFIG_INTEGER(option)]);
|
|
else
|
|
{
|
|
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_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)
|
|
{
|
|
for (ptr_option = section->options; ptr_option;
|
|
ptr_option = ptr_option->next_option)
|
|
{
|
|
if (string_strcasecmp (name, ptr_option->name) < 0)
|
|
return ptr_option;
|
|
}
|
|
}
|
|
|
|
/* position not found (we will add to the end of list) */
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* 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 ()
|
|
{
|
|
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)
|
|
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 (string_strcasecmp (type, config_option_type_string[i]) == 0)
|
|
{
|
|
var_type = i;
|
|
break;
|
|
}
|
|
}
|
|
if (var_type < 0)
|
|
{
|
|
gui_chat_printf (NULL, "%sError: unknown option type \"%s\"",
|
|
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
|
type);
|
|
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_INTEGER_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_INTEGER(new_option) = int_value;
|
|
}
|
|
break;
|
|
case CONFIG_OPTION_TYPE_INTEGER:
|
|
if (string_values && string_values[0])
|
|
{
|
|
new_option->string_values = string_split (string_values,
|
|
"|", 0, 0,
|
|
&argc);
|
|
if (!new_option->string_values)
|
|
goto error;
|
|
}
|
|
if (new_option->string_values)
|
|
{
|
|
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 (string_strcasecmp (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_INTEGER_DEFAULT(new_option) = index_value;
|
|
}
|
|
if (value)
|
|
{
|
|
index_value = 0;
|
|
for (i = 0; i < argc; i++)
|
|
{
|
|
if (string_strcasecmp (new_option->string_values[i],
|
|
value) == 0)
|
|
{
|
|
index_value = i;
|
|
break;
|
|
}
|
|
}
|
|
new_option->value = malloc (sizeof (int));
|
|
if (!new_option->value)
|
|
goto error;
|
|
CONFIG_INTEGER(new_option) = index_value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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_INTEGER_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_INTEGER(new_option) = 0;
|
|
}
|
|
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;
|
|
}
|
|
|
|
/* run config hook(s) */
|
|
if (new_option->config_file && new_option->section)
|
|
{
|
|
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:
|
|
if (option_name)
|
|
free (option_name);
|
|
if (parent_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;
|
|
|
|
if (section)
|
|
{
|
|
for (ptr_option = section->options; ptr_option;
|
|
ptr_option = ptr_option->next_option)
|
|
{
|
|
if (string_strcasecmp (ptr_option->name, option_name) == 0)
|
|
return ptr_option;
|
|
}
|
|
}
|
|
else if (config_file)
|
|
{
|
|
for (ptr_section = config_file->sections; ptr_section;
|
|
ptr_section = ptr_section->next_section)
|
|
{
|
|
for (ptr_option = ptr_section->options; ptr_option;
|
|
ptr_option = ptr_option->next_option)
|
|
{
|
|
if (string_strcasecmp (ptr_option->name, option_name) == 0)
|
|
return ptr_option;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
|
|
*section_found = NULL;
|
|
*option_found = NULL;
|
|
|
|
if (section)
|
|
{
|
|
for (ptr_option = section->options; ptr_option;
|
|
ptr_option = ptr_option->next_option)
|
|
{
|
|
if (string_strcasecmp (ptr_option->name, option_name) == 0)
|
|
{
|
|
*section_found = section;
|
|
*option_found = ptr_option;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if (config_file)
|
|
{
|
|
for (ptr_section = config_file->sections; ptr_section;
|
|
ptr_section = ptr_section->next_section)
|
|
{
|
|
for (ptr_option = ptr_section->options; ptr_option;
|
|
ptr_option = ptr_option->next_option)
|
|
{
|
|
if (string_strcasecmp (ptr_option->name, option_name) == 0)
|
|
{
|
|
*section_found = ptr_section;
|
|
*option_found = ptr_option;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (file_name)
|
|
free (file_name);
|
|
if (section_name)
|
|
free (section_name);
|
|
|
|
if (config_file)
|
|
*config_file = ptr_config;
|
|
if (section)
|
|
*section = ptr_section;
|
|
if (option)
|
|
*option = ptr_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 (string_strcasecmp (text, config_boolean_true[i]) == 0)
|
|
return 1;
|
|
}
|
|
|
|
for (i = 0; config_boolean_false[i]; i++)
|
|
{
|
|
if (string_strcasecmp (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 (string_strcasecmp (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_INTEGER(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_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;
|
|
}
|
|
}
|
|
|
|
if ((rc == WEECHAT_CONFIG_OPTION_SET_OK_CHANGED)
|
|
&& run_callback && option->callback_change)
|
|
{
|
|
(void) (option->callback_change) (
|
|
option->callback_change_pointer,
|
|
option->callback_change_data,
|
|
option);
|
|
}
|
|
|
|
/* run config hook(s) */
|
|
if ((rc != WEECHAT_CONFIG_OPTION_SET_ERROR)
|
|
&& option->config_file && option->section)
|
|
{
|
|
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 (string_strcasecmp (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 (string_strcasecmp (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)
|
|
{
|
|
if (option->string_values)
|
|
{
|
|
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 (string_strcasecmp (option->string_values[i],
|
|
value) == 0)
|
|
{
|
|
value_int = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (value_int >= 0)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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(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;
|
|
}
|
|
}
|
|
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 if asked and value was changed */
|
|
if ((rc == WEECHAT_CONFIG_OPTION_SET_OK_CHANGED)
|
|
&& run_callback && option->callback_change)
|
|
{
|
|
(void) (option->callback_change) (
|
|
option->callback_change_pointer,
|
|
option->callback_change_data,
|
|
option);
|
|
}
|
|
|
|
/* run config hook(s) */
|
|
if ((rc != WEECHAT_CONFIG_OPTION_SET_ERROR)
|
|
&& option->config_file && option->section)
|
|
{
|
|
config_file_hook_config_exec (option);
|
|
}
|
|
|
|
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 if asked and value was changed */
|
|
if ((rc == WEECHAT_CONFIG_OPTION_SET_OK_CHANGED)
|
|
&& run_callback && option->callback_change)
|
|
{
|
|
(void) (option->callback_change) (
|
|
option->callback_change_pointer,
|
|
option->callback_change_data,
|
|
option);
|
|
}
|
|
|
|
/* run config hook(s) */
|
|
if ((rc != WEECHAT_CONFIG_OPTION_SET_ERROR)
|
|
&& option->config_file && option->section)
|
|
{
|
|
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);
|
|
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 */
|
|
if (option->name)
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (full_old_name)
|
|
free (full_old_name);
|
|
if (full_new_name)
|
|
free (full_new_name);
|
|
}
|
|
|
|
/*
|
|
* 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, length;
|
|
|
|
if ((default_value && !option->default_value)
|
|
|| (!default_value && !option->value))
|
|
{
|
|
length = 7 + ((use_colors) ? 64 : 0) + 1;
|
|
value = malloc (length);
|
|
if (!value)
|
|
return NULL;
|
|
snprintf (value, length,
|
|
"%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);
|
|
length = 7 + ((use_colors) ? 64 : 0) + 1;
|
|
value = malloc (length);
|
|
if (!value)
|
|
return NULL;
|
|
snprintf (value, length,
|
|
"%s%s",
|
|
(use_colors) ? GUI_COLOR(GUI_COLOR_CHAT_VALUE) : "",
|
|
(enabled) ? "on" : "off");
|
|
return value;
|
|
break;
|
|
case CONFIG_OPTION_TYPE_INTEGER:
|
|
if (option->string_values)
|
|
{
|
|
ptr_value = (default_value) ?
|
|
option->string_values[CONFIG_INTEGER_DEFAULT(option)] :
|
|
option->string_values[CONFIG_INTEGER(option)];
|
|
length = strlen (ptr_value) + ((use_colors) ? 64 : 0) + 1;
|
|
value = malloc (length);
|
|
if (!value)
|
|
return NULL;
|
|
snprintf (value, length,
|
|
"%s%s",
|
|
(use_colors) ? GUI_COLOR(GUI_COLOR_CHAT_VALUE) : "",
|
|
ptr_value);
|
|
return value;
|
|
}
|
|
else
|
|
{
|
|
length = 31 + ((use_colors) ? 64 : 0) + 1;
|
|
value = malloc (length);
|
|
if (!value)
|
|
return NULL;
|
|
snprintf (value, length,
|
|
"%s%d",
|
|
(use_colors) ? GUI_COLOR(GUI_COLOR_CHAT_VALUE) : "",
|
|
(default_value) ? CONFIG_INTEGER_DEFAULT(option) : CONFIG_INTEGER(option));
|
|
return value;
|
|
}
|
|
break;
|
|
case CONFIG_OPTION_TYPE_STRING:
|
|
ptr_value = (default_value) ? CONFIG_STRING_DEFAULT(option) : CONFIG_STRING(option);
|
|
length = strlen (ptr_value) + ((use_colors) ? 64 : 0) + 1;
|
|
value = malloc (length);
|
|
if (!value)
|
|
return NULL;
|
|
snprintf (value, length,
|
|
"%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;
|
|
break;
|
|
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;
|
|
length = strlen (ptr_value) + ((use_colors) ? 64 : 0) + 1;
|
|
value = malloc (length);
|
|
if (!value)
|
|
return NULL;
|
|
snprintf (value, length,
|
|
"%s%s",
|
|
(use_colors) ? GUI_COLOR(GUI_COLOR_CHAT_VALUE) : "",
|
|
ptr_value);
|
|
return value;
|
|
break;
|
|
case CONFIG_NUM_OPTION_TYPES:
|
|
/* make C compiler happy */
|
|
break;
|
|
}
|
|
|
|
/* make C static analyzer happy (never executed) */
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Gets a pointer of an option property.
|
|
*/
|
|
|
|
void *
|
|
config_file_option_get_pointer (struct t_config_option *option,
|
|
const char *property)
|
|
{
|
|
if (!option || !property)
|
|
return NULL;
|
|
|
|
if (string_strcasecmp (property, "config_file") == 0)
|
|
return option->config_file;
|
|
else if (string_strcasecmp (property, "section") == 0)
|
|
return option->section;
|
|
else if (string_strcasecmp (property, "name") == 0)
|
|
return option->name;
|
|
else if (string_strcasecmp (property, "parent_name") == 0)
|
|
return option->parent_name;
|
|
else if (string_strcasecmp (property, "type") == 0)
|
|
return &option->type;
|
|
else if (string_strcasecmp (property, "description") == 0)
|
|
return option->description;
|
|
else if (string_strcasecmp (property, "string_values") == 0)
|
|
return option->string_values;
|
|
else if (string_strcasecmp (property, "min") == 0)
|
|
return &option->min;
|
|
else if (string_strcasecmp (property, "max") == 0)
|
|
return &option->max;
|
|
else if (string_strcasecmp (property, "default_value") == 0)
|
|
return option->default_value;
|
|
else if (string_strcasecmp (property, "value") == 0)
|
|
return option->value;
|
|
else if (string_strcasecmp (property, "prev_option") == 0)
|
|
return option->prev_option;
|
|
else if (string_strcasecmp (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_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 CONFIG_BOOLEAN(option);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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 CONFIG_BOOLEAN_DEFAULT(option);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Returns integer value of an option.
|
|
*/
|
|
|
|
int
|
|
config_file_option_integer (struct t_config_option *option)
|
|
{
|
|
if (option && option->value)
|
|
{
|
|
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:
|
|
case CONFIG_OPTION_TYPE_COLOR:
|
|
return CONFIG_INTEGER(option);
|
|
case CONFIG_OPTION_TYPE_STRING:
|
|
return 0;
|
|
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)
|
|
{
|
|
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:
|
|
case CONFIG_OPTION_TYPE_COLOR:
|
|
return CONFIG_INTEGER_DEFAULT(option);
|
|
case CONFIG_OPTION_TYPE_STRING:
|
|
return 0;
|
|
case CONFIG_NUM_OPTION_TYPES:
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Returns string value of an option.
|
|
*/
|
|
|
|
const char *
|
|
config_file_option_string (struct t_config_option *option)
|
|
{
|
|
if (option && option->value)
|
|
{
|
|
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:
|
|
if (option->string_values)
|
|
return option->string_values[CONFIG_INTEGER(option)];
|
|
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_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)
|
|
{
|
|
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:
|
|
if (option->string_values)
|
|
return option->string_values[CONFIG_INTEGER_DEFAULT(option)];
|
|
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_NUM_OPTION_TYPES:
|
|
return NULL;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* 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 gui_color_get_name (CONFIG_COLOR(option));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* 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 gui_color_get_name (CONFIG_COLOR_DEFAULT(option));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* 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_iconv_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:
|
|
if (option->string_values)
|
|
rc = string_iconv_fprintf (config_file->file, "%s%s = %s\n",
|
|
config_file_option_escape (option->name),
|
|
option->name,
|
|
option->string_values[CONFIG_INTEGER(option)]);
|
|
else
|
|
rc = string_iconv_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_iconv_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_iconv_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_NUM_OPTION_TYPES:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = string_iconv_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_iconv_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_iconv_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 filename_length, rc;
|
|
char *filename, *filename2, resolved_path[PATH_MAX];
|
|
struct t_config_section *ptr_section;
|
|
struct t_config_option *ptr_option;
|
|
|
|
if (!config_file)
|
|
return WEECHAT_CONFIG_WRITE_ERROR;
|
|
|
|
/* build filename */
|
|
filename_length = strlen (weechat_home) +
|
|
strlen (config_file->filename) + 2;
|
|
filename = malloc (filename_length);
|
|
if (!filename)
|
|
return WEECHAT_CONFIG_WRITE_MEMORY_ERROR;
|
|
snprintf (filename, filename_length, "%s%s%s",
|
|
weechat_home, DIR_SEPARATOR, config_file->filename);
|
|
|
|
/*
|
|
* build temporary filename, this temp file will be renamed to filename
|
|
* after write
|
|
*/
|
|
filename2 = malloc (filename_length + 32);
|
|
if (!filename2)
|
|
{
|
|
free (filename);
|
|
return WEECHAT_CONFIG_WRITE_MEMORY_ERROR;
|
|
}
|
|
snprintf (filename2, filename_length + 32, "%s.weechattmp", filename);
|
|
|
|
/* 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,
|
|
_("%sError: cannot 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_iconv_fprintf (config_file->file, "#\n"))
|
|
goto error;
|
|
if (!string_iconv_fprintf (config_file->file,
|
|
"# %s -- %s\n#\n",
|
|
version_get_name (),
|
|
config_file->filename))
|
|
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_iconv_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;
|
|
|
|
/* close temp file */
|
|
fclose (config_file->file);
|
|
config_file->file = NULL;
|
|
|
|
/* update file mode */
|
|
chmod (filename2, 0600);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/*
|
|
* 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 filename_length, line_number, rc, undefined_value;
|
|
char *filename;
|
|
struct t_config_section *ptr_section;
|
|
struct t_config_option *ptr_option;
|
|
char line[16384], *ptr_line, *ptr_line2, *pos, *pos2, *ptr_option_name;
|
|
|
|
if (!config_file)
|
|
return WEECHAT_CONFIG_READ_FILE_NOT_FOUND;
|
|
|
|
/* build filename */
|
|
filename_length = strlen (weechat_home) + strlen (DIR_SEPARATOR) +
|
|
strlen (config_file->filename) + 1;
|
|
filename = malloc (filename_length);
|
|
if (!filename)
|
|
return WEECHAT_CONFIG_READ_MEMORY_ERROR;
|
|
snprintf (filename, filename_length, "%s%s%s",
|
|
weechat_home, DIR_SEPARATOR, config_file->filename);
|
|
|
|
/* 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))
|
|
{
|
|
ptr_line = fgets (line, sizeof (line) - 1, config_file->file);
|
|
line_number++;
|
|
if (ptr_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);
|
|
|
|
/* skip spaces */
|
|
while (ptr_line[0] == ' ')
|
|
{
|
|
ptr_line++;
|
|
}
|
|
|
|
/* not a comment and not an empty line */
|
|
if ((ptr_line[0] != '#') && (ptr_line[0] != '\r')
|
|
&& (ptr_line[0] != '\n'))
|
|
{
|
|
/* beginning of section */
|
|
if ((ptr_line[0] == '[') && !strchr (ptr_line, '='))
|
|
{
|
|
pos = strchr (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
|
|
{
|
|
pos[0] = '\0';
|
|
pos = ptr_line + 1;
|
|
ptr_section = config_file_search_section (config_file,
|
|
pos);
|
|
if (!ptr_section)
|
|
{
|
|
gui_chat_printf (NULL,
|
|
_("%sWarning: %s, line %d: unknown "
|
|
"section identifier "
|
|
"(\"%s\")"),
|
|
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
|
filename, line_number, pos);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
undefined_value = 1;
|
|
|
|
/* remove CR/LF */
|
|
pos = strchr (line, '\r');
|
|
if (pos != NULL)
|
|
pos[0] = '\0';
|
|
pos = strchr (line, '\n');
|
|
if (pos != NULL)
|
|
pos[0] = '\0';
|
|
|
|
pos = strstr (line, " =");
|
|
if (pos)
|
|
{
|
|
pos[0] = '\0';
|
|
pos += 2;
|
|
|
|
/* remove spaces before '=' */
|
|
pos2 = pos - 3;
|
|
while ((pos2 > line) && (pos2[0] == ' '))
|
|
{
|
|
pos2[0] = '\0';
|
|
pos2--;
|
|
}
|
|
|
|
/* skip spaces after '=' */
|
|
while (pos[0] && (pos[0] == ' '))
|
|
{
|
|
pos++;
|
|
}
|
|
|
|
if (pos[0]
|
|
&& string_strcasecmp (pos, WEECHAT_CONFIG_OPTION_NULL) != 0)
|
|
{
|
|
undefined_value = 0;
|
|
/* remove simple or double quotes and spaces at the end */
|
|
if (strlen(pos) > 1)
|
|
{
|
|
pos2 = pos + strlen (pos) - 1;
|
|
while ((pos2 > pos) && (pos2[0] == ' '))
|
|
{
|
|
pos2[0] = '\0';
|
|
pos2--;
|
|
}
|
|
pos2 = pos + strlen (pos) - 1;
|
|
if (((pos[0] == '\'') &&
|
|
(pos2[0] == '\'')) ||
|
|
((pos[0] == '"') &&
|
|
(pos2[0] == '"')))
|
|
{
|
|
pos2[0] = '\0';
|
|
pos++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ptr_option_name = (line[0] == '\\') ? line + 1 : line;
|
|
|
|
if (ptr_section && 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,
|
|
ptr_option_name,
|
|
(undefined_value) ? NULL : pos);
|
|
}
|
|
else
|
|
{
|
|
rc = WEECHAT_CONFIG_OPTION_SET_OPTION_NOT_FOUND;
|
|
ptr_option = config_file_search_option (config_file,
|
|
ptr_section,
|
|
ptr_option_name);
|
|
if (ptr_option)
|
|
{
|
|
rc = config_file_option_set (ptr_option,
|
|
(undefined_value) ?
|
|
NULL : pos,
|
|
1);
|
|
ptr_option->loaded = 1;
|
|
}
|
|
else
|
|
{
|
|
if (ptr_section
|
|
&& 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,
|
|
ptr_option_name,
|
|
(undefined_value) ? NULL : pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (rc)
|
|
{
|
|
case WEECHAT_CONFIG_OPTION_SET_OPTION_NOT_FOUND:
|
|
if (ptr_section)
|
|
gui_chat_printf (NULL,
|
|
_("%sWarning: %s, line %d: "
|
|
"unknown option for section "
|
|
"\"%s\": %s"),
|
|
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
|
filename, line_number,
|
|
ptr_section->name,
|
|
ptr_line2);
|
|
else
|
|
gui_chat_printf (NULL,
|
|
_("%sWarning: %s, line %d: "
|
|
"option outside section: "
|
|
"%s"),
|
|
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
|
filename, line_number,
|
|
ptr_line2);
|
|
break;
|
|
case WEECHAT_CONFIG_OPTION_SET_ERROR:
|
|
gui_chat_printf (NULL,
|
|
_("%sWarning: %s, line %d: "
|
|
"invalid value for option: "
|
|
"%s"),
|
|
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
|
filename, line_number,
|
|
ptr_line2);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ptr_line2)
|
|
free (ptr_line2);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (option->name)
|
|
free (option->name);
|
|
if (option->parent_name)
|
|
free (option->parent_name);
|
|
if (option->description)
|
|
free (option->description);
|
|
if (option->string_values)
|
|
string_free_split (option->string_values);
|
|
if (option->default_value)
|
|
free (option->default_value);
|
|
if (option->value)
|
|
free (option->value);
|
|
if (option->callback_check_value_data)
|
|
free (option->callback_check_value_data);
|
|
if (option->callback_change_data)
|
|
free (option->callback_change_data);
|
|
if (option->callback_delete_data)
|
|
free (option->callback_delete_data);
|
|
}
|
|
|
|
/*
|
|
* Frees an option.
|
|
*/
|
|
|
|
void
|
|
config_file_option_free (struct t_config_option *option)
|
|
{
|
|
struct t_config_section *ptr_section;
|
|
struct t_config_option *new_options;
|
|
|
|
if (!option)
|
|
return;
|
|
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
if (section->name)
|
|
free (section->name);
|
|
if (section->callback_read_data)
|
|
free (section->callback_read_data);
|
|
if (section->callback_write_data)
|
|
free (section->callback_write_data);
|
|
if (section->callback_write_default_data)
|
|
free (section->callback_write_default_data);
|
|
if (section->callback_create_option_data)
|
|
free (section->callback_create_option_data);
|
|
if (section->callback_delete_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);
|
|
}
|
|
if (config_file->name)
|
|
free (config_file->name);
|
|
if (config_file->filename)
|
|
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 */
|
|
if (config_file->callback_reload_data)
|
|
free (config_file->callback_reload_data);
|
|
|
|
free (config_file);
|
|
|
|
config_files = new_config_files;
|
|
}
|
|
|
|
/*
|
|
* Frees all configuration files.
|
|
*/
|
|
|
|
void
|
|
config_file_free_all ()
|
|
{
|
|
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, 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, 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, 0)))
|
|
{
|
|
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_build_with_split_string (
|
|
(const char **)option->string_values, "|");
|
|
if (!infolist_new_var_string (ptr_item, "string_values", string_values))
|
|
{
|
|
if (string_values)
|
|
free (string_values);
|
|
goto error;
|
|
}
|
|
if (string_values)
|
|
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 ()
|
|
{
|
|
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:0x%lx)]", ptr_config_file);
|
|
log_printf (" plugin . . . . . . . . : 0x%lx ('%s')",
|
|
ptr_config_file->plugin,
|
|
plugin_get_name (ptr_config_file->plugin));
|
|
log_printf (" name . . . . . . . . . : '%s'", ptr_config_file->name);
|
|
log_printf (" filename . . . . . . . : '%s'", ptr_config_file->filename);
|
|
log_printf (" file . . . . . . . . . : 0x%lx", ptr_config_file->file);
|
|
log_printf (" callback_reload. . . . : 0x%lx", ptr_config_file->callback_reload);
|
|
log_printf (" callback_reload_pointer: 0x%lx", ptr_config_file->callback_reload_pointer);
|
|
log_printf (" callback_reload_data . : 0x%lx", ptr_config_file->callback_reload_data);
|
|
log_printf (" sections . . . . . . . : 0x%lx", ptr_config_file->sections);
|
|
log_printf (" last_section . . . . . : 0x%lx", ptr_config_file->last_section);
|
|
log_printf (" prev_config. . . . . . : 0x%lx", ptr_config_file->prev_config);
|
|
log_printf (" next_config. . . . . . : 0x%lx", 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:0x%lx)]", ptr_section);
|
|
log_printf (" config_file . . . . . . . . . : 0x%lx", ptr_section->config_file);
|
|
log_printf (" name. . . . . . . . . . . . . : '%s'", ptr_section->name);
|
|
log_printf (" callback_read . . . . . . . . : 0x%lx", ptr_section->callback_read);
|
|
log_printf (" callback_read_pointer . . . . : 0x%lx", ptr_section->callback_read_pointer);
|
|
log_printf (" callback_read_data. . . . . . : 0x%lx", ptr_section->callback_read_data);
|
|
log_printf (" callback_write. . . . . . . . : 0x%lx", ptr_section->callback_write);
|
|
log_printf (" callback_write_pointer. . . . : 0x%lx", ptr_section->callback_write_pointer);
|
|
log_printf (" callback_write_data . . . . . : 0x%lx", ptr_section->callback_write_data);
|
|
log_printf (" callback_write_default. . . . : 0x%lx", ptr_section->callback_write_default);
|
|
log_printf (" callback_write_default_pointer: 0x%lx", ptr_section->callback_write_default_pointer);
|
|
log_printf (" callback_write_default_data . : 0x%lx", ptr_section->callback_write_default_data);
|
|
log_printf (" callback_create_option. . . . : 0x%lx", ptr_section->callback_create_option);
|
|
log_printf (" callback_create_option_pointer: 0x%lx", ptr_section->callback_create_option_pointer);
|
|
log_printf (" callback_create_option_data . : 0x%lx", ptr_section->callback_create_option_data);
|
|
log_printf (" callback_delete_option. . . . : 0x%lx", ptr_section->callback_delete_option);
|
|
log_printf (" callback_delete_option_pointer: 0x%lx", ptr_section->callback_delete_option_pointer);
|
|
log_printf (" callback_delete_option_data . : 0x%lx", ptr_section->callback_delete_option_data);
|
|
log_printf (" options . . . . . . . . . . . : 0x%lx", ptr_section->options);
|
|
log_printf (" last_option . . . . . . . . . : 0x%lx", ptr_section->last_option);
|
|
log_printf (" prev_section. . . . . . . . . : 0x%lx", ptr_section->prev_section);
|
|
log_printf (" next_section. . . . . . . . . : 0x%lx", ptr_section->next_section);
|
|
|
|
for (ptr_option = ptr_section->options; ptr_option;
|
|
ptr_option = ptr_option->next_option)
|
|
{
|
|
log_printf ("");
|
|
log_printf (" [option (addr:0x%lx)]", ptr_option);
|
|
log_printf (" config_file. . . . . . . . . : 0x%lx", ptr_option->config_file);
|
|
log_printf (" section. . . . . . . . . . . : 0x%lx", 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. . . . . . . . : 0x%lx", 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->string_values)
|
|
{
|
|
log_printf (" default value. . . . . . . . : '%s'",
|
|
(ptr_option->default_value) ?
|
|
ptr_option->string_values[CONFIG_INTEGER_DEFAULT(ptr_option)] : "null");
|
|
log_printf (" value (integer/str). . . . . : '%s'",
|
|
(ptr_option->value) ?
|
|
ptr_option->string_values[CONFIG_INTEGER(ptr_option)] : "null");
|
|
}
|
|
else
|
|
{
|
|
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_NUM_OPTION_TYPES:
|
|
break;
|
|
}
|
|
log_printf (" null_value_allowed . . . . . : %d", ptr_option->null_value_allowed);
|
|
log_printf (" callback_check_value . . . . : 0x%lx", ptr_option->callback_check_value);
|
|
log_printf (" callback_check_value_pointer : 0x%lx", ptr_option->callback_check_value_pointer);
|
|
log_printf (" callback_check_value_data. . : 0x%lx", ptr_option->callback_check_value_data);
|
|
log_printf (" callback_change. . . . . . . : 0x%lx", ptr_option->callback_change);
|
|
log_printf (" callback_change_pointer. . . : 0x%lx", ptr_option->callback_change_pointer);
|
|
log_printf (" callback_change_data . . . . : 0x%lx", ptr_option->callback_change_data);
|
|
log_printf (" callback_delete. . . . . . . : 0x%lx", ptr_option->callback_delete);
|
|
log_printf (" callback_delete_pointer. . . : 0x%lx", ptr_option->callback_delete_pointer);
|
|
log_printf (" callback_delete_data . . . . : 0x%lx", ptr_option->callback_delete_data);
|
|
log_printf (" loaded . . . . . . . . . . . : %d", ptr_option->loaded);
|
|
log_printf (" prev_option. . . . . . . . . : 0x%lx", ptr_option->prev_option);
|
|
log_printf (" next_option. . . . . . . . . : 0x%lx", ptr_option->next_option);
|
|
}
|
|
}
|
|
}
|
|
}
|