summaryrefslogtreecommitdiff
path: root/src/core/core-hdata.c
diff options
context:
space:
mode:
authorSébastien Helleu <flashcode@flashtux.org>2024-03-12 21:09:42 +0100
committerSébastien Helleu <flashcode@flashtux.org>2024-03-12 21:27:37 +0100
commit24c4029c96fa04b3cb4f90fbb36dc5248dd39810 (patch)
treeca51727f97207117f335f0309c063ffd2f168f2d /src/core/core-hdata.c
parentbb346f8c6c62655a6ef8fe4bc848d179258ce008 (diff)
downloadweechat-24c4029c96fa04b3cb4f90fbb36dc5248dd39810.zip
core: remove "wee-" prefix from source files in src/core and src/core/hook
Diffstat (limited to 'src/core/core-hdata.c')
-rw-r--r--src/core/core-hdata.c1576
1 files changed, 1576 insertions, 0 deletions
diff --git a/src/core/core-hdata.c b/src/core/core-hdata.c
new file mode 100644
index 000000000..a3f4e195d
--- /dev/null
+++ b/src/core/core-hdata.c
@@ -0,0 +1,1576 @@
+/*
+ * core-hdata.c - direct access to WeeChat data using hashtables
+ *
+ * Copyright (C) 2011-2024 Sébastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * WeeChat is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with WeeChat. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "weechat.h"
+#include "core-hdata.h"
+#include "core-hook.h"
+#include "core-eval.h"
+#include "core-hashtable.h"
+#include "core-log.h"
+#include "core-string.h"
+#include "../plugins/plugin.h"
+
+
+struct t_hashtable *weechat_hdata = NULL;
+
+char *hdata_type_string[WEECHAT_NUM_HDATA_TYPES] =
+{ "other", "char", "integer", "long", "longlong", "string", "pointer", "time",
+ "hashtable", "shared_string" };
+
+
+/*
+ * Frees a hdata variable.
+ */
+
+void
+hdata_free_var_cb (struct t_hashtable *hashtable, const void *key, void *value)
+{
+ struct t_hdata_var *var;
+
+ /* make C compiler happy */
+ (void) hashtable;
+ (void) key;
+
+ var = (struct t_hdata_var *)value;
+ if (var)
+ {
+ if (var->array_size)
+ free (var->array_size);
+ if (var->hdata_name)
+ free (var->hdata_name);
+ free (var);
+ }
+}
+
+/*
+ * Frees a hdata list.
+ */
+
+void
+hdata_free_list_cb (struct t_hashtable *hashtable, const void *key, void *value)
+{
+ /* make C compiler happy */
+ (void) hashtable;
+ (void) key;
+
+ free (value);
+}
+
+/*
+ * Creates a new hdata.
+ *
+ * Returns pointer to new hdata, NULL if error.
+ */
+
+struct t_hdata *
+hdata_new (struct t_weechat_plugin *plugin, const char *hdata_name,
+ const char *var_prev, const char *var_next,
+ int create_allowed, int delete_allowed,
+ int (*callback_update)(void *data,
+ struct t_hdata *hdata,
+ void *pointer,
+ struct t_hashtable *hashtable),
+ void *callback_update_data)
+{
+ struct t_hdata *new_hdata;
+
+ if (!hdata_name || !hdata_name[0])
+ return NULL;
+
+ new_hdata = malloc (sizeof (*new_hdata));
+ if (new_hdata)
+ {
+ new_hdata->name = strdup (hdata_name);
+ new_hdata->plugin = plugin;
+ new_hdata->var_prev = (var_prev) ? strdup (var_prev) : NULL;
+ new_hdata->var_next = (var_next) ? strdup (var_next) : NULL;
+ new_hdata->hash_var = hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_POINTER,
+ NULL,
+ NULL);
+ new_hdata->hash_var->callback_free_value = &hdata_free_var_cb;
+ new_hdata->hash_list = hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_POINTER,
+ NULL,
+ NULL);
+ new_hdata->hash_list->callback_free_value = &hdata_free_list_cb;
+ hashtable_set (weechat_hdata, hdata_name, new_hdata);
+ new_hdata->create_allowed = create_allowed;
+ new_hdata->delete_allowed = delete_allowed;
+ new_hdata->callback_update = callback_update;
+ new_hdata->callback_update_data = callback_update_data;
+ new_hdata->update_pending = 0;
+ }
+
+ return new_hdata;
+}
+
+/*
+ * Adds a new variable in a hdata.
+ */
+
+void
+hdata_new_var (struct t_hdata *hdata, const char *name, int offset, int type,
+ int update_allowed, const char *array_size,
+ const char *hdata_name)
+{
+ struct t_hdata_var *var;
+ const char *ptr_array_size;
+
+ if (!hdata || !name)
+ return;
+
+ var = malloc (sizeof (*var));
+ if (var)
+ {
+ var->offset = offset;
+ var->type = type;
+ var->update_allowed = update_allowed;
+ ptr_array_size = array_size;
+ if (ptr_array_size && (strncmp (ptr_array_size, "*,", 2) == 0))
+ {
+ var->array_pointer = 1;
+ ptr_array_size += 2;
+ }
+ else
+ {
+ var->array_pointer = 0;
+ }
+ var->array_size = (ptr_array_size && ptr_array_size[0]) ?
+ strdup (ptr_array_size) : NULL;
+ var->hdata_name = (hdata_name && hdata_name[0]) ?
+ strdup (hdata_name) : NULL;
+ hashtable_set (hdata->hash_var, name, var);
+ }
+}
+
+/*
+ * Adds a new list pointer in a hdata.
+ */
+
+void
+hdata_new_list (struct t_hdata *hdata, const char *name, void *pointer,
+ int flags)
+{
+ struct t_hdata_list *list;
+
+ if (!hdata || !name)
+ return;
+
+ list = malloc (sizeof (*list));
+ if (list)
+ {
+ list->pointer = pointer;
+ list->flags = flags;
+ hashtable_set (hdata->hash_list, name, list);
+ }
+}
+
+/*
+ * Gets offset of variable in hdata.
+ */
+
+int
+hdata_get_var_offset (struct t_hdata *hdata, const char *name)
+{
+ struct t_hdata_var *var;
+
+ if (!hdata || !name)
+ return -1;
+
+ var = hashtable_get (hdata->hash_var, name);
+ if (var)
+ return var->offset;
+
+ return -1;
+}
+
+/*
+ * Gets type of variable in hdata (as integer).
+ */
+
+int
+hdata_get_var_type (struct t_hdata *hdata, const char *name)
+{
+ struct t_hdata_var *var;
+
+ if (!hdata || !name)
+ return -1;
+
+ var = hashtable_get (hdata->hash_var, name);
+ if (var)
+ return var->type;
+
+ return -1;
+}
+
+/*
+ * Gets type of variable in hdata (as string).
+ */
+
+const char *
+hdata_get_var_type_string (struct t_hdata *hdata, const char *name)
+{
+ struct t_hdata_var *var;
+
+ if (!hdata || !name)
+ return NULL;
+
+ var = hashtable_get (hdata->hash_var, name);
+ if (var)
+ return hdata_type_string[(int)var->type];
+
+ return NULL;
+}
+
+/*
+ * Gets size of array for a variable (if variable is an array).
+ *
+ * Returns size of array, -1 if variable is not an array (or if error).
+ */
+
+int
+hdata_get_var_array_size (struct t_hdata *hdata, void *pointer,
+ const char *name)
+{
+ struct t_hdata_var *var;
+ const char *ptr_size;
+ char *error;
+ long value;
+ int i, offset;
+ void *ptr_value;
+
+ if (!hdata || !name)
+ return -1;
+
+ var = hashtable_get (hdata->hash_var, name);
+ if (!var)
+ return -1;
+
+ ptr_size = var->array_size;
+ if (!ptr_size)
+ return -1;
+
+ if (strcmp (ptr_size, "*") == 0)
+ {
+ /*
+ * automatic size: look for NULL in array
+ * (this automatic size is possible only with pointers, so with
+ * types: string, pointer, hashtable)
+ */
+ if ((var->type == WEECHAT_HDATA_STRING)
+ || (var->type == WEECHAT_HDATA_SHARED_STRING)
+ || (var->type == WEECHAT_HDATA_POINTER)
+ || (var->type == WEECHAT_HDATA_HASHTABLE))
+ {
+ if (!(*((void **)(pointer + var->offset))))
+ return 0;
+ i = 0;
+ while (1)
+ {
+ ptr_value = NULL;
+ switch (var->type)
+ {
+ case WEECHAT_HDATA_STRING:
+ case WEECHAT_HDATA_SHARED_STRING:
+ ptr_value = (*((char ***)(pointer + var->offset)))[i];
+ break;
+ case WEECHAT_HDATA_POINTER:
+ ptr_value = (*((void ***)(pointer + var->offset)))[i];
+ break;
+ case WEECHAT_HDATA_HASHTABLE:
+ ptr_value = (*((struct t_hashtable ***)(pointer + var->offset)))[i];
+ break;
+ }
+ if (!ptr_value)
+ break;
+ i++;
+ }
+ return i;
+ }
+ }
+ else
+ {
+ /* fixed size: the size can be a name of variable or integer */
+ offset = hdata_get_var_offset (hdata, ptr_size);
+ if (offset >= 0)
+ {
+ /* size is the name of a variable in hdata, read it */
+ switch (hdata_get_var_type (hdata, ptr_size))
+ {
+ case WEECHAT_HDATA_CHAR:
+ return (int)(*((char *)(pointer + offset)));
+ case WEECHAT_HDATA_INTEGER:
+ return *((int *)(pointer + offset));
+ case WEECHAT_HDATA_LONG:
+ return (int)(*((long *)(pointer + offset)));
+ case WEECHAT_HDATA_LONGLONG:
+ return (int)(*((long long *)(pointer + offset)));
+ default:
+ break;
+ }
+ }
+ else
+ {
+ /* check if the size is a valid integer */
+ error = NULL;
+ value = strtol (ptr_size, &error, 10);
+ if (error && !error[0])
+ return (int)value;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Gets size of array for variable as string.
+ */
+
+const char *
+hdata_get_var_array_size_string (struct t_hdata *hdata, void *pointer,
+ const char *name)
+{
+ struct t_hdata_var *var;
+
+ /* make C compiler happy */
+ (void) pointer;
+
+ if (!hdata || !name)
+ return NULL;
+
+ var = hashtable_get (hdata->hash_var, name);
+ if (var)
+ return (const char *)var->array_size;
+
+ return NULL;
+}
+
+/*
+ * Gets hdata name for a variable.
+ *
+ * Returns hdata name, NULL if variable has no hdata.
+ */
+
+const char *
+hdata_get_var_hdata (struct t_hdata *hdata, const char *name)
+{
+ struct t_hdata_var *var;
+
+ if (!hdata || !name)
+ return NULL;
+
+ var = hashtable_get (hdata->hash_var, name);
+ if (var)
+ return (const char *)var->hdata_name;
+
+ return NULL;
+}
+
+/*
+ * Gets pointer to content of variable using hdata variable name.
+ */
+
+void *
+hdata_get_var (struct t_hdata *hdata, void *pointer, const char *name)
+{
+ int offset;
+
+ if (!hdata || !pointer || !name)
+ return NULL;
+
+ offset = hdata_get_var_offset (hdata, name);
+ if (offset >= 0)
+ return pointer + offset;
+
+ return NULL;
+}
+
+/*
+ * Gets pointer to content of variable using hdata variable offset.
+ */
+
+void *
+hdata_get_var_at_offset (struct t_hdata *hdata, void *pointer, int offset)
+{
+ if (!hdata || !pointer)
+ return NULL;
+
+ return pointer + offset;
+}
+
+/*
+ * Gets a list pointer in hdata.
+ */
+
+void *
+hdata_get_list (struct t_hdata *hdata, const char *name)
+{
+ struct t_hdata_list *ptr_list;
+
+ if (!hdata || !name)
+ return NULL;
+
+ ptr_list = hashtable_get (hdata->hash_list, name);
+ if (ptr_list)
+ return *((void **)(ptr_list->pointer));
+
+ return NULL;
+}
+
+/*
+ * Checks if a pointer is in the list.
+ *
+ * Returns:
+ * 1: pointer exists in list
+ * 0: pointer does not exist
+ */
+
+int
+hdata_check_pointer_in_list (struct t_hdata *hdata, void *list, void *pointer)
+{
+ void *ptr_current;
+
+ if (!hdata || !pointer)
+ return 0;
+
+ if (pointer == list)
+ return 1;
+
+ ptr_current = list;
+ while (ptr_current)
+ {
+ ptr_current = hdata_move (hdata, ptr_current, 1);
+ if (ptr_current && (ptr_current == pointer))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Checks if a pointer is in a list with flag "check_pointers".
+ */
+
+void
+hdata_check_pointer_map_cb (void *data, struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ void **pointers, *pointer, **num_lists, **found;
+ struct t_hdata *ptr_hdata;
+ struct t_hdata_list *ptr_list;
+
+ /* make C compiler happy */
+ (void) hashtable;
+ (void) key;
+
+ pointers = (void **)data;
+ ptr_hdata = pointers[0];
+ pointer = pointers[1];
+ num_lists = &pointers[2];
+ found = &pointers[3];
+
+ /* pointer already found in another list? just exit */
+ if (*found)
+ return;
+
+ ptr_list = (struct t_hdata_list *)value;
+ if (!ptr_list || !(ptr_list->flags & WEECHAT_HDATA_LIST_CHECK_POINTERS))
+ return;
+
+ *found = (void *)((unsigned long)hdata_check_pointer_in_list (
+ ptr_hdata,
+ *((void **)(ptr_list->pointer)),
+ pointer));
+ (*num_lists)++;
+}
+
+/*
+ * Checks if a pointer is valid for a given hdata/list.
+ *
+ * If argument "list" is NULL, the check is made with all lists in hdata
+ * that have flag "check_pointers". If no list is defined with this flag,
+ * the pointer is considered valid (so this function returns 1); if the
+ * pointer is not found in any list, this function returns 0.
+ *
+ * Returns:
+ * 1: pointer exists in the given list (or a list with check_pointers flag)
+ * 0: pointer does not exist
+ */
+
+int
+hdata_check_pointer (struct t_hdata *hdata, void *list, void *pointer)
+{
+ void *pointers[4];
+
+ if (!hdata || !pointer)
+ return 0;
+
+ if (list)
+ {
+ /* search pointer in the given list */
+ return hdata_check_pointer_in_list (hdata, list, pointer);
+ }
+ else
+ {
+ /* search pointer in all lists with flag "check_pointers" */
+ pointers[0] = hdata;
+ pointers[1] = pointer;
+ pointers[2] = 0; /* number of lists with flag check_pointers */
+ pointers[3] = 0; /* pointer found? (0/1) */
+ hashtable_map (hdata->hash_list,
+ &hdata_check_pointer_map_cb,
+ pointers);
+ return ((pointers[2] == 0) || pointers[3]) ? 1 : 0;
+ }
+}
+
+/*
+ * Moves pointer to another element in list.
+ */
+
+void *
+hdata_move (struct t_hdata *hdata, void *pointer, int count)
+{
+ char *ptr_var;
+ int i, abs_count;
+
+ if (!hdata || !pointer || (count == 0))
+ return NULL;
+
+ ptr_var = (count < 0) ? hdata->var_prev : hdata->var_next;
+ abs_count = abs (count);
+
+ for (i = 0; i < abs_count; i++)
+ {
+ pointer = hdata_pointer (hdata, pointer, ptr_var);
+ if (!pointer)
+ break;
+ }
+
+ return pointer;
+}
+
+/*
+ * Searches for an element in list using expression.
+ *
+ * Returns pointer to element found, NULL if not found.
+ */
+
+void *
+hdata_search (struct t_hdata *hdata,
+ void *pointer,
+ const char *search,
+ struct t_hashtable *pointers,
+ struct t_hashtable *extra_vars,
+ struct t_hashtable *options,
+ int move)
+{
+ struct t_hashtable *pointers2, *options2;
+ char *result;
+ void *ret_pointer;
+ int rc;
+
+ if (!hdata || !pointer || !search || !search[0] || (move == 0))
+ return NULL;
+
+ ret_pointer = NULL;
+
+ /* duplicate or create hashtable with pointers */
+ if (pointers)
+ {
+ pointers2 = hashtable_dup (pointers);
+ }
+ else
+ {
+ pointers2 = hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_POINTER,
+ NULL,
+ NULL);
+ }
+
+ /* duplicate or create hashtable with options */
+ if (options)
+ {
+ options2 = hashtable_dup (options);
+ }
+ else
+ {
+ options2 = hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_POINTER,
+ NULL,
+ NULL);
+ }
+ if (options2)
+ hashtable_set (options2, "type", "condition");
+
+ while (pointer)
+ {
+ /* set pointer in hashtable (used for evaluating expression) */
+ if (pointers2)
+ hashtable_set (pointers2, hdata->name, pointer);
+
+ /* evaluate expression */
+ result = eval_expression (search, pointers2, extra_vars, options2);
+ rc = eval_is_true (result);
+ if (result)
+ free (result);
+ if (rc)
+ {
+ ret_pointer = pointer;
+ goto end;
+ }
+
+ pointer = hdata_move (hdata, pointer, move);
+ }
+
+end:
+ hashtable_free (pointers2);
+ hashtable_free (options2);
+ return ret_pointer;
+}
+
+/*
+ * Extracts index from name of a variable.
+ *
+ * A name can contain index with this format: "NNN|name" (where NNN is an
+ * integer >= 0).
+ * Argument "index" is set to N, "ptr_name" points to start of name in string
+ * (after the "|" if found).
+ */
+
+void
+hdata_get_index_and_name (const char *name, int *index, const char **ptr_name)
+{
+ char *pos, *str_index, *error;
+ long number;
+
+ if (index)
+ *index = -1;
+ if (ptr_name)
+ *ptr_name = name;
+
+ if (!name)
+ return;
+
+ pos = strchr (name, '|');
+ if (pos)
+ {
+ str_index = string_strndup (name, pos - name);
+ if (str_index)
+ {
+ error = NULL;
+ number = strtol (str_index, &error, 10);
+ if (error && !error[0])
+ {
+ if (index)
+ *index = number;
+ if (ptr_name)
+ *ptr_name = pos + 1;
+ }
+ free (str_index);
+ }
+ }
+}
+
+/*
+ * Gets char value of a variable in hdata.
+ */
+
+char
+hdata_char (struct t_hdata *hdata, void *pointer, const char *name)
+{
+ int index;
+ const char *ptr_name;
+ struct t_hdata_var *var;
+
+ if (!hdata || !pointer || !name)
+ return '\0';
+
+ hdata_get_index_and_name (name, &index, &ptr_name);
+ var = hashtable_get (hdata->hash_var, ptr_name);
+ if (var && (var->offset >= 0))
+ {
+ if (var->array_size && (index >= 0))
+ {
+ if (var->array_pointer)
+ return (*((char **)(pointer + var->offset)))[index];
+ else
+ return ((char *)(pointer + var->offset))[index];
+ }
+ else
+ {
+ return *((char *)(pointer + var->offset));
+ }
+ }
+
+ return '\0';
+}
+
+/*
+ * Gets integer value of a variable in hdata.
+ */
+
+int
+hdata_integer (struct t_hdata *hdata, void *pointer, const char *name)
+{
+ int index;
+ const char *ptr_name;
+ struct t_hdata_var *var;
+
+ if (!hdata || !pointer || !name)
+ return 0;
+
+ hdata_get_index_and_name (name, &index, &ptr_name);
+ var = hashtable_get (hdata->hash_var, ptr_name);
+ if (var && (var->offset >= 0))
+ {
+ if (var->array_size && (index >= 0))
+ {
+ if (var->array_pointer)
+ return (*((int **)(pointer + var->offset)))[index];
+ else
+ return ((int *)(pointer + var->offset))[index];
+ }
+ else
+ {
+ return *((int *)(pointer + var->offset));
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Gets long value of a variable in hdata.
+ */
+
+long
+hdata_long (struct t_hdata *hdata, void *pointer, const char *name)
+{
+ int index;
+ const char *ptr_name;
+ struct t_hdata_var *var;
+
+ if (!hdata || !pointer || !name)
+ return 0;
+
+ hdata_get_index_and_name (name, &index, &ptr_name);
+ var = hashtable_get (hdata->hash_var, ptr_name);
+ if (var && (var->offset >= 0))
+ {
+ if (var->array_size && (index >= 0))
+ {
+ if (var->array_pointer)
+ return (*((long **)(pointer + var->offset)))[index];
+ else
+ return ((long *)(pointer + var->offset))[index];
+ }
+ else
+ {
+ return *((long *)(pointer + var->offset));
+ }
+ }
+
+ return 0L;
+}
+
+/*
+ * Gets "long long" value of a variable in hdata.
+ */
+
+long long
+hdata_longlong (struct t_hdata *hdata, void *pointer, const char *name)
+{
+ int index;
+ const char *ptr_name;
+ struct t_hdata_var *var;
+
+ if (!hdata || !pointer || !name)
+ return 0;
+
+ hdata_get_index_and_name (name, &index, &ptr_name);
+ var = hashtable_get (hdata->hash_var, ptr_name);
+ if (var && (var->offset >= 0))
+ {
+ if (var->array_size && (index >= 0))
+ {
+ if (var->array_pointer)
+ return (*((long long **)(pointer + var->offset)))[index];
+ else
+ return ((long long *)(pointer + var->offset))[index];
+ }
+ else
+ {
+ return *((long long *)(pointer + var->offset));
+ }
+ }
+
+ return 0LL;
+}
+
+/*
+ * Gets string value of a variable in hdata.
+ */
+
+const char *
+hdata_string (struct t_hdata *hdata, void *pointer, const char *name)
+{
+ int index;
+ const char *ptr_name;
+ struct t_hdata_var *var;
+
+ if (!hdata || !pointer || !name)
+ return NULL;
+
+ hdata_get_index_and_name (name, &index, &ptr_name);
+ var = hashtable_get (hdata->hash_var, ptr_name);
+ if (var && (var->offset >= 0))
+ {
+ if (var->array_size && (index >= 0))
+ {
+ if (var->array_pointer)
+ return (*((char ***)(pointer + var->offset)))[index];
+ else
+ {
+ /* we can not index a static array of strings */
+ return NULL;
+ }
+ }
+ else
+ {
+ return *((char **)(pointer + var->offset));
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Gets pointer value of a variable in hdata.
+ */
+
+void *
+hdata_pointer (struct t_hdata *hdata, void *pointer, const char *name)
+{
+ int index;
+ const char *ptr_name;
+ struct t_hdata_var *var;
+
+ if (!hdata || !pointer || !name)
+ return NULL;
+
+ hdata_get_index_and_name (name, &index, &ptr_name);
+ var = hashtable_get (hdata->hash_var, ptr_name);
+ if (var && (var->offset >= 0))
+ {
+ if (var->array_size && (index >= 0))
+ {
+ if (var->array_pointer)
+ return (*((void ***)(pointer + var->offset)))[index];
+ else
+ return ((void **)(pointer + var->offset))[index];
+ }
+ else
+ {
+ return *((void **)(pointer + var->offset));
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Gets time value of a variable in hdata.
+ */
+
+time_t
+hdata_time (struct t_hdata *hdata, void *pointer, const char *name)
+{
+ int index;
+ const char *ptr_name;
+ struct t_hdata_var *var;
+
+ if (!hdata || !pointer || !name)
+ return 0;
+
+ hdata_get_index_and_name (name, &index, &ptr_name);
+ var = hashtable_get (hdata->hash_var, ptr_name);
+ if (var && (var->offset >= 0))
+ {
+ if (var->array_size && (index >= 0))
+ {
+ if (var->array_pointer)
+ return (*((time_t **)(pointer + var->offset)))[index];
+ else
+ return ((time_t *)(pointer + var->offset))[index];
+ }
+ else
+ {
+ return *((time_t *)(pointer + var->offset));
+ }
+ }
+
+ return (time_t)0;
+}
+
+/*
+ * Gets hashtable value of a variable in hdata.
+ */
+
+struct t_hashtable *
+hdata_hashtable (struct t_hdata *hdata, void *pointer, const char *name)
+{
+ int index;
+ const char *ptr_name;
+ struct t_hdata_var *var;
+
+ if (!hdata || !pointer || !name)
+ return NULL;
+
+ hdata_get_index_and_name (name, &index, &ptr_name);
+ var = hashtable_get (hdata->hash_var, ptr_name);
+ if (var && (var->offset >= 0))
+ {
+ if (var->array_size && (index >= 0))
+ {
+ if (var->array_pointer)
+ return (*((struct t_hashtable ***)(pointer + var->offset)))[index];
+ else
+ return ((struct t_hashtable **)(pointer + var->offset))[index];
+ }
+ else
+ {
+ return *((struct t_hashtable **)(pointer + var->offset));
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Compares a hdata variable of two objects.
+ *
+ * If case_sensitive == 1, the comparison of strings is case sensitive.
+ *
+ * Returns:
+ * -1: variable1 < variable2
+ * 0: variable1 == variable2
+ * 1: variable1 > variable2
+ */
+
+int
+hdata_compare (struct t_hdata *hdata, void *pointer1, void *pointer2,
+ const char *name, int case_sensitive)
+{
+ int rc, type, type1, type2, int_value1, int_value2;
+ long long_value1, long_value2;
+ long long longlong_value1, longlong_value2;
+ char *var_name, *property, char_value1, char_value2;
+ const char *ptr_var_name, *pos, *pos_open_paren, *hdata_name;
+ const char *str_value1, *str_value2;
+ void *ptr_value1, *ptr_value2;
+ time_t time_value1, time_value2;
+ struct t_hashtable *hashtable1, *hashtable2;
+
+ if (!hdata || !name)
+ return 0;
+
+ if (!pointer1 && pointer2)
+ return -1;
+ if (pointer1 && !pointer2)
+ return 1;
+ if (!pointer1 && !pointer2)
+ return 0;
+
+ rc = 0;
+ ptr_value1 = NULL;
+ ptr_value2 = NULL;
+
+ pos = strchr (name, '.');
+ if (pos > name)
+ var_name = string_strndup (name, pos - name);
+ else
+ var_name = strdup (name);
+
+ if (!var_name)
+ goto end;
+
+ hdata_get_index_and_name (var_name, NULL, &ptr_var_name);
+ type = hdata_get_var_type (hdata, ptr_var_name);
+ if (type < 0)
+ goto end;
+
+ switch (type)
+ {
+ case WEECHAT_HDATA_CHAR:
+ char_value1 = hdata_char (hdata, pointer1, var_name);
+ char_value2 = hdata_char (hdata, pointer2, var_name);
+ rc = (char_value1 < char_value2) ?
+ -1 : ((char_value1 > char_value2) ? 1 : 0);
+ break;
+ case WEECHAT_HDATA_INTEGER:
+ int_value1 = hdata_integer (hdata, pointer1, var_name);
+ int_value2 = hdata_integer (hdata, pointer2, var_name);
+ rc = (int_value1 < int_value2) ?
+ -1 : ((int_value1 > int_value2) ? 1 : 0);
+ break;
+ case WEECHAT_HDATA_LONG:
+ long_value1 = hdata_long (hdata, pointer1, var_name);
+ long_value2 = hdata_long (hdata, pointer2, var_name);
+ rc = (long_value1 < long_value2) ?
+ -1 : ((long_value1 > long_value2) ? 1 : 0);
+ break;
+ case WEECHAT_HDATA_LONGLONG:
+ longlong_value1 = hdata_longlong (hdata, pointer1, var_name);
+ longlong_value2 = hdata_longlong (hdata, pointer2, var_name);
+ rc = (longlong_value1 < longlong_value2) ?
+ -1 : ((longlong_value1 > longlong_value2) ? 1 : 0);
+ break;
+ case WEECHAT_HDATA_STRING:
+ case WEECHAT_HDATA_SHARED_STRING:
+ str_value1 = hdata_string (hdata, pointer1, var_name);
+ str_value2 = hdata_string (hdata, pointer2, var_name);
+ if (!str_value1 && !str_value2)
+ rc = 0;
+ else if (str_value1 && !str_value2)
+ rc = 1;
+ else if (!str_value1 && str_value2)
+ rc = -1;
+ else
+ {
+ if (case_sensitive)
+ rc = strcmp (str_value1, str_value2);
+ else
+ rc = string_strcasecmp (str_value1, str_value2);
+ if (rc < 0)
+ rc = -1;
+ else if (rc > 0)
+ rc = 1;
+ }
+ break;
+ case WEECHAT_HDATA_POINTER:
+ ptr_value1 = hdata_pointer (hdata, pointer1, var_name);
+ ptr_value2 = hdata_pointer (hdata, pointer2, var_name);
+ rc = (ptr_value1 < ptr_value2) ?
+ -1 : ((ptr_value1 > ptr_value2) ? 1 : 0);
+ break;
+ case WEECHAT_HDATA_TIME:
+ time_value1 = hdata_time (hdata, pointer1, var_name);
+ time_value2 = hdata_time (hdata, pointer2, var_name);
+ rc = (time_value1 < time_value2) ?
+ -1 : ((time_value1 > time_value2) ? 1 : 0);
+ break;
+ case WEECHAT_HDATA_HASHTABLE:
+ ptr_value1 = hdata_hashtable (hdata, pointer1, var_name);
+ ptr_value2 = hdata_hashtable (hdata, pointer2, var_name);
+ if (pos)
+ {
+ /*
+ * for a hashtable, if there is a "." after name of hdata:
+ * 1) If "()" is at the end, it is a function call to
+ * hashtable_get_string().
+ * 2) Otherwise, get the value for this key in hashtable.
+ */
+ hashtable1 = ptr_value1;
+ hashtable2 = ptr_value2;
+
+ pos_open_paren = strchr (pos, '(');
+ if (pos_open_paren
+ && (pos_open_paren > pos + 1)
+ && (pos_open_paren[1] == ')'))
+ {
+ property = string_strndup (pos + 1,
+ pos_open_paren - pos - 1);
+ ptr_value1 = (void *)hashtable_get_string (hashtable1, property);
+ ptr_value2 = (void *)hashtable_get_string (hashtable2, property);
+ if (property)
+ free (property);
+ type1 = HASHTABLE_STRING;
+ type2 = HASHTABLE_STRING;
+ }
+ else
+ {
+ ptr_value1 = hashtable_get (hashtable1, pos + 1);
+ ptr_value2 = hashtable_get (hashtable2, pos + 1);
+ type1 = hashtable1->type_values;
+ type2 = hashtable2->type_values;
+ }
+
+ if (!ptr_value1 && ptr_value2)
+ rc = -1;
+ else if (ptr_value1 && !ptr_value2)
+ rc = 1;
+ else if (!ptr_value1 && !ptr_value2)
+ rc = 0;
+ else if (type1 != type2)
+ rc = 0;
+ else
+ {
+ switch (type1)
+ {
+ case HASHTABLE_INTEGER:
+ int_value1 = *((int *)ptr_value1);
+ int_value2 = *((int *)ptr_value2);
+ rc = (int_value1 < int_value2) ?
+ -1 : ((int_value1 > int_value2) ? 1 : 0);
+ break;
+ case HASHTABLE_STRING:
+ if (case_sensitive)
+ rc = strcmp ((const char *)ptr_value1,
+ (const char *)ptr_value2);
+ else
+ rc = string_strcasecmp ((const char *)ptr_value1,
+ (const char *)ptr_value2);
+ if (rc < 0)
+ rc = -1;
+ else if (rc > 0)
+ rc = 1;
+ break;
+ case HASHTABLE_POINTER:
+ case HASHTABLE_BUFFER:
+ rc = (ptr_value1 < ptr_value2) ?
+ -1 : ((ptr_value1 > ptr_value2) ? 1 : 0);
+ break;
+ case HASHTABLE_TIME:
+ time_value1 = (long long)(*((time_t *)ptr_value1));
+ time_value2 = (long long)(*((time_t *)ptr_value2));
+ rc = (time_value1 < time_value2) ?
+ -1 : ((time_value1 > time_value2) ? 1 : 0);
+ break;
+ case HASHTABLE_NUM_TYPES:
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* compare hashtables by pointer */
+ rc = (ptr_value1 < ptr_value2) ?
+ -1 : ((ptr_value1 > ptr_value2) ? 1 : 0);
+ }
+ break;
+ case WEECHAT_HDATA_OTHER:
+ /* no comparison for other types */
+ rc = 0;
+ break;
+ }
+
+ /*
+ * if we are on a pointer and that something else is in path (after "."),
+ * go on with this pointer and remaining path
+ */
+ if ((type == WEECHAT_HDATA_POINTER) && pos)
+ {
+ hdata_name = hdata_get_var_hdata (hdata, var_name);
+ if (!hdata_name)
+ goto end;
+ hdata = hook_hdata_get (NULL, hdata_name);
+ rc = hdata_compare (hdata, ptr_value1, ptr_value2, pos + 1,
+ case_sensitive);
+ }
+
+end:
+ if (var_name)
+ free (var_name);
+ return rc;
+}
+
+/*
+ * Sets value for a variable in hdata.
+ *
+ * WARNING: this is dangerous, and only some variables can be set by this
+ * function (this depends on hdata, see API doc for more info) and this
+ * function can be called *ONLY* in an "update" callback (in hdata).
+ *
+ * Returns:
+ * 1: OK (value set)
+ * 0: error (or not allowed)
+ */
+
+int
+hdata_set (struct t_hdata *hdata, void *pointer, const char *name,
+ const char *value)
+{
+ struct t_hdata_var *var;
+ char **ptr_string, *error;
+ long number;
+ long long number_longlong;
+ unsigned long ptr;
+ int rc;
+
+ if (!hdata->update_pending)
+ return 0;
+
+ var = hashtable_get (hdata->hash_var, name);
+ if (!var)
+ return 0;
+
+ if (!var->update_allowed)
+ return 0;
+
+ switch (var->type)
+ {
+ case WEECHAT_HDATA_OTHER:
+ break;
+ case WEECHAT_HDATA_CHAR:
+ *((char *)(pointer + var->offset)) = (value) ? value[0] : '\0';
+ return 1;
+ break;
+ case WEECHAT_HDATA_INTEGER:
+ error = NULL;
+ number = strtol (value, &error, 10);
+ if (error && !error[0])
+ {
+ *((int *)(pointer + var->offset)) = (int)number;
+ return 1;
+ }
+ break;
+ case WEECHAT_HDATA_LONG:
+ error = NULL;
+ number = strtol (value, &error, 10);
+ if (error && !error[0])
+ {
+ *((long *)(pointer + var->offset)) = number;
+ return 1;
+ }
+ break;
+ case WEECHAT_HDATA_LONGLONG:
+ error = NULL;
+ number_longlong = strtoll (value, &error, 10);
+ if (error && !error[0])
+ {
+ *((long long *)(pointer + var->offset)) = number_longlong;
+ return 1;
+ }
+ break;
+ case WEECHAT_HDATA_STRING:
+ ptr_string = (char **)(pointer + var->offset);
+ if (*ptr_string)
+ free (*ptr_string);
+ *ptr_string = (value) ? strdup (value) : NULL;
+ return 1;
+ break;
+ case WEECHAT_HDATA_SHARED_STRING:
+ ptr_string = (char **)(pointer + var->offset);
+ if (*ptr_string)
+ string_shared_free (*ptr_string);
+ *ptr_string = (value) ? (char *)string_shared_get (value) : NULL;
+ return 1;
+ break;
+ case WEECHAT_HDATA_POINTER:
+ if (value)
+ {
+ rc = sscanf (value, "%lx", &ptr);
+ if ((rc != EOF) && (rc != 0))
+ {
+ *((void **)(pointer + var->offset)) = (void *)ptr;
+ return 1;
+ }
+ }
+ else
+ {
+ *((void **)(pointer + var->offset)) = NULL;
+ return 1;
+ }
+ break;
+ case WEECHAT_HDATA_TIME:
+ error = NULL;
+ number = strtol (value, &error, 10);
+ if (error && !error[0] && (number >= 0))
+ {
+ *((time_t *)(pointer + var->offset)) = (time_t)number;
+ return 1;
+ }
+ break;
+ case WEECHAT_HDATA_HASHTABLE:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Updates some data in hdata.
+ *
+ * The hashtable contains keys with new values.
+ * A special key "__delete" can be used to delete the whole structure at
+ * pointer (if allowed for this hdata).
+ *
+ * WARNING: this is dangerous, and only some data can be updated by this
+ * function (this depends on hdata, see API doc for more info).
+ *
+ * Returns number of variables updated, 0 if nothing has been updated.
+ * In case of deletion, returns 1 if OK, 0 if error.
+ */
+
+int
+hdata_update (struct t_hdata *hdata, void *pointer,
+ struct t_hashtable *hashtable)
+{
+ const char *value;
+ struct t_hdata_var *var;
+ int rc;
+
+ if (!hdata || !hashtable || !hdata->callback_update)
+ return 0;
+
+ /* check if create of structure is allowed */
+ if (hashtable_has_key (hashtable, "__create_allowed"))
+ return (int)hdata->create_allowed;
+
+ /* check if delete of structure is allowed */
+ if (hashtable_has_key (hashtable, "__delete_allowed"))
+ return (int)hdata->delete_allowed;
+
+ /* check if update of variable is allowed */
+ value = hashtable_get (hashtable, "__update_allowed");
+ if (value)
+ {
+ var = hashtable_get (hdata->hash_var, value);
+ if (!var)
+ return 0;
+ return (var->update_allowed) ? 1 : 0;
+ }
+
+ /* update data */
+ hdata->update_pending = 1;
+ rc = (hdata->callback_update) (hdata->callback_update_data,
+ hdata, pointer, hashtable);
+ hdata->update_pending = 0;
+
+ return rc;
+}
+
+/*
+ * Gets a hdata property as string.
+ */
+
+const char *
+hdata_get_string (struct t_hdata *hdata, const char *property)
+{
+ if (!hdata || !property)
+ return NULL;
+
+ if (strcmp (property, "var_keys") == 0)
+ return hashtable_get_string (hdata->hash_var, "keys");
+ else if (strcmp (property, "var_values") == 0)
+ return hashtable_get_string (hdata->hash_var, "values");
+ else if (strcmp (property, "var_keys_values") == 0)
+ return hashtable_get_string (hdata->hash_var, "keys_values");
+ else if (strcmp (property, "var_prev") == 0)
+ return hdata->var_prev;
+ else if (strcmp (property, "var_next") == 0)
+ return hdata->var_next;
+ else if (strcmp (property, "list_keys") == 0)
+ return hashtable_get_string (hdata->hash_list, "keys");
+ else if (strcmp (property, "list_values") == 0)
+ return hashtable_get_string (hdata->hash_list, "values");
+ else if (strcmp (property, "list_keys_values") == 0)
+ return hashtable_get_string (hdata->hash_list, "keys_values");
+
+ return NULL;
+}
+
+/*
+ * Frees a hdata.
+ */
+
+void
+hdata_free (struct t_hdata *hdata)
+{
+ if (!hdata)
+ return;
+
+ if (hdata->hash_var)
+ hashtable_free (hdata->hash_var);
+ if (hdata->var_prev)
+ free (hdata->var_prev);
+ if (hdata->var_next)
+ free (hdata->var_next);
+ if (hdata->hash_list)
+ hashtable_free (hdata->hash_list);
+ if (hdata->name)
+ free (hdata->name);
+
+ free (hdata);
+}
+
+/*
+ * Frees hdata for a plugin (callback called for each hdata in memory).
+ */
+
+void
+hdata_free_all_plugin_map_cb (void *data, struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ struct t_hdata *ptr_hdata;
+
+ ptr_hdata = (struct t_hdata *)value;
+
+ if (ptr_hdata->plugin == (struct t_weechat_plugin *)data)
+ hashtable_remove (hashtable, key);
+}
+
+/*
+ * Frees all hdata created by a plugin.
+ */
+
+void
+hdata_free_all_plugin (struct t_weechat_plugin *plugin)
+{
+ hashtable_map (weechat_hdata, &hdata_free_all_plugin_map_cb, plugin);
+}
+
+/*
+ * Frees all hdata.
+ */
+
+void
+hdata_free_all ()
+{
+ hashtable_remove_all (weechat_hdata);
+}
+
+/*
+ * Prints variable of a hdata in WeeChat log file (callback called for each
+ * variable in hdata).
+ */
+
+void
+hdata_print_log_var_map_cb (void *data, struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ struct t_hdata_var *var;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) hashtable;
+
+ var = (struct t_hdata_var *)value;
+
+ log_printf ("");
+ log_printf (" [hdata var '%s']", (const char *)key);
+ log_printf (" offset . . . . . . . . : %d", var->offset);
+ log_printf (" type . . . . . . . . . : %d ('%s')", var->type, hdata_type_string[(int)var->type]);
+ log_printf (" update_allowed . . . . : %d", (int)var->update_allowed);
+ log_printf (" array_size . . . . . . : '%s'", var->array_size);
+ log_printf (" hdata_name . . . . . . : '%s'", var->hdata_name);
+}
+
+/*
+ * Prints hdata in WeeChat log file (callback called for each hdata in memory).
+ */
+
+void
+hdata_print_log_map_cb (void *data, struct t_hashtable *hashtable,
+ const void *key, const void *value)
+{
+ struct t_hdata *ptr_hdata;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) hashtable;
+ (void) key;
+
+ ptr_hdata = (struct t_hdata *)value;
+
+ log_printf ("");
+ log_printf ("[hdata (addr:0x%lx)]", ptr_hdata);
+ log_printf (" name . . . . . . . . . : '%s'", ptr_hdata->name);
+ log_printf (" plugin . . . . . . . . : 0x%lx", ptr_hdata->plugin);
+ log_printf (" var_prev . . . . . . . : '%s'", ptr_hdata->var_prev);
+ log_printf (" var_next . . . . . . . : '%s'", ptr_hdata->var_next);
+ log_printf (" hash_var . . . . . . . : 0x%lx (hashtable: '%s')",
+ ptr_hdata->hash_var,
+ hashtable_get_string (ptr_hdata->hash_var, "keys_values"));
+ log_printf (" hash_list. . . . . . . : 0x%lx (hashtable: '%s')",
+ ptr_hdata->hash_list,
+ hashtable_get_string (ptr_hdata->hash_list, "keys_values"));
+ log_printf (" create_allowed . . . . : %d", (int)ptr_hdata->create_allowed);
+ log_printf (" delete_allowed . . . . : %d", (int)ptr_hdata->delete_allowed);
+ log_printf (" callback_update. . . . : 0x%lx", ptr_hdata->callback_update);
+ log_printf (" callback_update_data . : 0x%lx", ptr_hdata->callback_update_data);
+ log_printf (" update_pending . . . . : %d", (int)ptr_hdata->update_pending);
+ hashtable_map (ptr_hdata->hash_var, &hdata_print_log_var_map_cb, NULL);
+}
+
+/*
+ * Prints hdata in WeeChat log file (usually for crash dump).
+ */
+
+void
+hdata_print_log ()
+{
+ hashtable_map (weechat_hdata, &hdata_print_log_map_cb, NULL);
+}
+
+/*
+ * Frees a hdata in hashtable "weechat_hdata".
+ */
+
+void
+hdata_free_hdata_cb (struct t_hashtable *hashtable,
+ const void *key, void *value)
+{
+ /* make C compiler happy */
+ (void) hashtable;
+ (void) key;
+
+ hdata_free (value);
+}
+
+/*
+ * Initializes hdata: creates a hashtable with hdata.
+ */
+
+void
+hdata_init ()
+{
+ weechat_hdata = hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_POINTER,
+ NULL,
+ NULL);
+ weechat_hdata->callback_free_value = &hdata_free_hdata_cb;
+}
+
+/*
+ * Frees all hdata and hashtable with hdata.
+ */
+
+void
+hdata_end ()
+{
+ hdata_free_all ();
+ hashtable_free (weechat_hdata);
+ weechat_hdata = NULL;
+}