summaryrefslogtreecommitdiff
path: root/src/core/core-doc.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-doc.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-doc.c')
-rw-r--r--src/core/core-doc.c1806
1 files changed, 1806 insertions, 0 deletions
diff --git a/src/core/core-doc.c b/src/core/core-doc.c
new file mode 100644
index 000000000..2a0ce8f54
--- /dev/null
+++ b/src/core/core-doc.c
@@ -0,0 +1,1806 @@
+/*
+ * core-doc.c - documentation generator
+ *
+ * Copyright (C) 2023-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 <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libintl.h>
+#include <locale.h>
+#include <gcrypt.h>
+#include <regex.h>
+
+#include "weechat.h"
+#include "core-arraylist.h"
+#include "core-command.h"
+#include "core-config-file.h"
+#include "core-crypto.h"
+#include "core-dir.h"
+#include "core-hashtable.h"
+#include "core-hdata.h"
+#include "core-hook.h"
+#include "core-infolist.h"
+#include "core-string.h"
+#include "core-url.h"
+#include "core-utf8.h"
+#include "../plugins/plugin.h"
+
+#define ESCAPE_TABLE(msg) (doc_gen_escape_table (msg))
+#define ESCAPE_ANCHOR(msg) (doc_gen_escape_anchor_link (msg))
+#define TRANS(msg) ((msg && msg[0]) ? _(msg) : msg)
+#define TRANS_DEF(msg, def) ((msg && msg[0]) ? _(msg) : def)
+#define PLUGIN(plugin) ((plugin) ? plugin->name : "weechat")
+
+typedef int (t_doc_gen_func)(const char *path, const char *lang);
+
+int index_string_escaped;
+char *string_escaped[32];
+
+
+/*
+ * Escapes a string to display in a table: replace "|" by "\|".
+ */
+
+char *
+doc_gen_escape_table (const char *message)
+{
+ index_string_escaped = (index_string_escaped + 1) % 32;
+
+ if (string_escaped[index_string_escaped])
+ free (string_escaped[index_string_escaped]);
+
+ string_escaped[index_string_escaped] = string_replace (message, "|", "\\|");
+
+ return string_escaped[index_string_escaped];
+}
+
+/*
+ * Escapes a string to be used as anchor link: replace ",", "@" and "*" by "-".
+ */
+
+char *
+doc_gen_escape_anchor_link (const char *message)
+{
+ regex_t regex;
+
+ if (string_regcomp (&regex, "[,@*():&|]+", REG_EXTENDED) != 0)
+ return NULL;
+
+ index_string_escaped = (index_string_escaped + 1) % 32;
+
+ if (string_escaped[index_string_escaped])
+ free (string_escaped[index_string_escaped]);
+
+ string_escaped[index_string_escaped] = string_replace_regex (
+ message, &regex, "-", '$', NULL, NULL);
+
+ regfree (&regex);
+
+ return string_escaped[index_string_escaped];
+}
+
+/*
+ * Opens a file for write using:
+ * - path
+ * - doc: "api" or "user"
+ * - name
+ * - language (eg: "fr")
+ *
+ * Returns the file opened, NULL if error.
+ */
+
+FILE *
+doc_gen_open_file (const char *path, const char *doc, const char *name,
+ const char *lang)
+{
+ char filename[PATH_MAX];
+ FILE *file;
+
+ snprintf (filename, sizeof (filename),
+ "%s%s" "autogen_%s_%s.%s.adoc.temp",
+ path, DIR_SEPARATOR, doc, name, lang);
+
+ file = fopen (filename, "wb");
+ if (!file)
+ {
+ string_fprintf (stderr,
+ "doc generator: ERROR: unable to write file \"%s\"\n",
+ filename);
+ return NULL;
+ }
+
+ string_fprintf (
+ file,
+ "//\n"
+ "// This file is auto-generated by WeeChat.\n"
+ "// DO NOT EDIT BY HAND!\n"
+ "//\n"
+ "\n");
+
+ return file;
+}
+
+/*
+ * Closes the file and renames it without ".temp" suffix, if the target name
+ * does not exist or if it exists with a different (obsolete) content.
+ *
+ * If the target name exists with same content it's kept as-is (so the
+ * timestamp does not change) and the temporary file is just deleted.
+ *
+ * Returns:
+ * 1: target file has been updated
+ * 0: target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_close_file (const char *path, const char *doc, const char *name,
+ const char *lang, FILE *file)
+{
+ char filename_temp[PATH_MAX], filename[PATH_MAX];
+ char hash_temp[512 / 8], hash[512 / 8];
+ int rc_temp, rc;
+
+ fclose (file);
+
+ snprintf (filename_temp, sizeof (filename_temp),
+ "%s%s" "autogen_%s_%s.%s.adoc.temp",
+ path, DIR_SEPARATOR, doc, name, lang);
+
+ snprintf (filename, sizeof (filename),
+ "%s%s" "autogen_%s_%s.%s.adoc",
+ path, DIR_SEPARATOR, doc, name, lang);
+
+ rc_temp = weecrypto_hash_file (filename_temp, GCRY_MD_SHA512,
+ hash_temp, NULL);
+ if (!rc_temp)
+ return -1;
+
+ rc = weecrypto_hash_file (filename, GCRY_MD_SHA512, hash, NULL);
+
+ if (!rc || (memcmp (hash_temp, hash, sizeof (hash)) != 0))
+ {
+ rename (filename_temp, filename);
+ return 1;
+ }
+
+ unlink (filename_temp);
+ return 0;
+}
+
+/*
+ * Checks if a command must be documented or not.
+ *
+ * All commands whose name == plugin name are documented, and all commands for
+ * these plugins are documented as well:
+ * - weechat (core)
+ * - irc
+ * - xfer
+ */
+
+int
+doc_gen_check_command (const char *plugin, const char *command)
+{
+ /* command name is the same as plugin: to document! */
+ if (strcmp (plugin, command) == 0)
+ return 1;
+
+ /* document other options only for weechat, irc, xfer */
+ return ((strcmp (plugin, "weechat") == 0)
+ || (strcmp (plugin, "irc") == 0)
+ || (strcmp (plugin, "xfer") == 0)) ?
+ 1 : 0;
+}
+
+/*
+ * Compares two hooks "command" to sort by plugin / command.
+ */
+
+int
+doc_gen_hook_command_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ struct t_hook *ptr_hook1, *ptr_hook2;
+ int rc;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ ptr_hook1 = (struct t_hook *)pointer1;
+ ptr_hook2 = (struct t_hook *)pointer2;
+
+ rc = strcmp (PLUGIN(ptr_hook1->plugin), PLUGIN(ptr_hook2->plugin));
+ if (rc != 0)
+ return rc;
+
+ return strcmp (HOOK_COMMAND(ptr_hook1, command),
+ HOOK_COMMAND(ptr_hook2, command));
+}
+
+/*
+ * Generates files with commands.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_user_commands (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_hook *ptr_hook;
+ struct t_arraylist *list_hooks;
+ int i, list_size, length, first_cmd_plugin, first_line;
+ char old_plugin[1024], format[32], *value, *args_desc;
+ const char *ptr_args, *pos_pipes, *pos_next;
+
+ file = doc_gen_open_file (path, "user", "commands", lang);
+ if (!file)
+ return -1;
+
+ list_hooks = arraylist_new (64, 1, 0,
+ &doc_gen_hook_command_cmp_cb, NULL,
+ NULL, NULL);
+ for (ptr_hook = weechat_hooks[HOOK_TYPE_COMMAND]; ptr_hook;
+ ptr_hook = ptr_hook->next_hook)
+ {
+ if (doc_gen_check_command (PLUGIN(ptr_hook->plugin),
+ HOOK_COMMAND(ptr_hook, command)))
+ {
+ arraylist_add (list_hooks, ptr_hook);
+ }
+ }
+
+ old_plugin[0] = '\0';
+ first_cmd_plugin = 0;
+ list_size = arraylist_size (list_hooks);
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_hook = (struct t_hook *)arraylist_get (list_hooks, i);
+ if (strcmp (PLUGIN(ptr_hook->plugin), old_plugin) != 0)
+ {
+ if (i > 0)
+ {
+ string_fprintf (
+ file,
+ "----\n"
+ "// end::%s_commands[]\n"
+ "\n",
+ old_plugin);
+ }
+ string_fprintf (
+ file,
+ "// tag::%s_commands[]\n",
+ PLUGIN(ptr_hook->plugin));
+ strcpy (old_plugin, PLUGIN(ptr_hook->plugin));
+ first_cmd_plugin = 1;
+ }
+ else
+ {
+ first_cmd_plugin = 0;
+ }
+ if (!first_cmd_plugin)
+ string_fprintf (file, "----\n\n");
+ string_fprintf (
+ file,
+ "[[command_%s_%s]]\n"
+ "* `+%s+`: %s\n"
+ "\n"
+ "----\n",
+ PLUGIN(ptr_hook->plugin),
+ HOOK_COMMAND(ptr_hook, command),
+ HOOK_COMMAND(ptr_hook, command),
+ TRANS(HOOK_COMMAND(ptr_hook, description)));
+
+ length = 1 + utf8_strlen_screen (HOOK_COMMAND(ptr_hook, command)) + 2;
+ snprintf (format, sizeof (format), "%%-%ds%%s\n", length);
+ ptr_args = TRANS(HOOK_COMMAND(ptr_hook, args));
+ first_line = 1;
+ while (ptr_args && ptr_args[0])
+ {
+ value = NULL;
+ pos_pipes = strstr (ptr_args, "||");
+ if (pos_pipes)
+ {
+ pos_next = pos_pipes + 2;
+ while (pos_next[0] == ' ')
+ {
+ pos_next++;
+ }
+ if (pos_pipes > ptr_args)
+ {
+ pos_pipes--;
+ while ((pos_pipes > ptr_args) && (pos_pipes[0] == ' '))
+ {
+ pos_pipes--;
+ }
+ value = strndup (ptr_args, pos_pipes - ptr_args + 1);
+ }
+ }
+ else
+ {
+ value = strdup (ptr_args);
+ pos_next = NULL;
+ }
+ if (value)
+ {
+ if (first_line)
+ {
+ string_fprintf (file,
+ "/%s %s\n",
+ HOOK_COMMAND(ptr_hook, command),
+ value);
+ }
+ else
+ {
+ string_fprintf (file, format, " ", value);
+ }
+ first_line = 0;
+ free (value);
+ }
+ ptr_args = pos_next;
+ }
+ args_desc = hook_command_format_args_description (
+ HOOK_COMMAND(ptr_hook, args_description));
+ if (args_desc)
+ {
+ string_fprintf (file, "\n%s\n", args_desc);
+ free (args_desc);
+ }
+ }
+
+ string_fprintf (
+ file,
+ "----\n"
+ "// end::%s_commands[]\n",
+ old_plugin);
+
+ arraylist_free (list_hooks);
+
+ return doc_gen_close_file (path, "user", "commands", lang, file);
+}
+
+/*
+ * Checks if an option must be documented or not.
+ */
+
+int
+doc_gen_check_option (struct t_config_option *option)
+{
+ if (option->config_file->plugin
+ && (strcmp (option->config_file->plugin->name, "alias") == 0))
+ {
+ return 0;
+ }
+
+ if (option->config_file->plugin
+ && (strcmp (option->config_file->plugin->name, "trigger") == 0)
+ && (strcmp (option->section->name, "trigger") == 0))
+ {
+ return 0;
+ }
+
+ if (!option->config_file->plugin
+ && (strcmp (option->section->name, "bar") == 0))
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Compares two options to sort by plugin / command.
+ */
+
+int
+doc_gen_option_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ struct t_config_option *ptr_option1, *ptr_option2;
+ int rc;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ ptr_option1 = (struct t_config_option *)pointer1;
+ ptr_option2 = (struct t_config_option *)pointer2;
+
+ rc = strcmp (ptr_option1->config_file->name,
+ ptr_option2->config_file->name);
+ if (rc != 0)
+ return rc;
+
+ rc = strcmp (ptr_option1->section->name, ptr_option2->section->name);
+ if (rc != 0)
+ return rc;
+
+ return strcmp (ptr_option1->name, ptr_option2->name);
+}
+
+/*
+ * Generates files with commands.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_user_options (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_config_file *ptr_config, *old_config;
+ struct t_config_section *ptr_section;
+ struct t_config_option *ptr_option;
+ struct t_arraylist *list_options;
+ int i, list_size, index_option;
+ char *desc_escaped, *values, str_values[256];
+ char *default_value, *tmp;
+
+ file = doc_gen_open_file (path, "user", "options", lang);
+ if (!file)
+ return -1;
+
+ list_options = arraylist_new (64, 1, 0,
+ &doc_gen_option_cmp_cb, NULL,
+ NULL, NULL);
+ 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 (doc_gen_check_option (ptr_option))
+ arraylist_add (list_options, ptr_option);
+ }
+ }
+ }
+
+ old_config = NULL;
+ index_option = 0;
+ list_size = arraylist_size (list_options);
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_option = (struct t_config_option *)arraylist_get (list_options, i);
+ if (ptr_option->config_file != old_config)
+ {
+ if (old_config)
+ {
+ string_fprintf (
+ file,
+ "// end::%s_options[]\n"
+ "\n",
+ old_config->name);
+ }
+ string_fprintf (
+ file,
+ "// tag::%s_options[]\n",
+ ptr_option->config_file->name);
+ old_config = ptr_option->config_file;
+ index_option = 0;
+ }
+ else
+ {
+ index_option++;
+ }
+ if (index_option > 0)
+ string_fprintf (file, "\n");
+ desc_escaped = (ptr_option->description) ?
+ string_replace (TRANS(ptr_option->description), "]", "\\]") :
+ strdup ("");
+ string_fprintf (file,
+ "* [[option_%s.%s.%s]] *pass:none[%s.%s.%s]*\n",
+ ptr_option->config_file->name,
+ ptr_option->section->name,
+ ESCAPE_ANCHOR(ptr_option->name),
+ ptr_option->config_file->name,
+ ptr_option->section->name,
+ ptr_option->name);
+ string_fprintf (file,
+ "** %s: pass:none[%s]\n",
+ _("description"),
+ desc_escaped);
+ string_fprintf (file,
+ "** %s: %s\n",
+ _("type"),
+ TRANS(config_option_type_string[ptr_option->type]));
+ switch (ptr_option->type)
+ {
+ case CONFIG_OPTION_TYPE_BOOLEAN:
+ values = strdup ("on, off");
+ break;
+ case CONFIG_OPTION_TYPE_INTEGER:
+ snprintf (str_values, sizeof (str_values),
+ "%d .. %d",
+ ptr_option->min,
+ ptr_option->max);
+ values = strdup (str_values);
+ break;
+ case CONFIG_OPTION_TYPE_STRING:
+ if (ptr_option->max <= 0)
+ values = strdup (_("any string"));
+ else if (ptr_option->max == 1)
+ values = strdup (_("any char"));
+ else
+ {
+ snprintf (str_values, sizeof (str_values),
+ "%s (%s: %d)",
+ _("any string"),
+ _("max chars"),
+ ptr_option->max);
+ values = strdup (str_values);
+ }
+ break;
+ case CONFIG_OPTION_TYPE_COLOR:
+ values = strdup (command_help_option_color_values ());
+ break;
+ case CONFIG_OPTION_TYPE_ENUM:
+ values = string_rebuild_split_string (
+ (const char **)ptr_option->string_values, ", ", 0, -1);
+ break;
+ default:
+ values = NULL;
+ break;
+ }
+ string_fprintf (file, "** %s: %s\n", _("values"), values);
+ default_value = config_file_option_value_to_string (ptr_option,
+ 1, 0, 0);
+ if (ptr_option->type == CONFIG_OPTION_TYPE_STRING)
+ {
+ tmp = string_replace (default_value, "\"", "\\\"");
+ if (default_value)
+ free (default_value);
+ default_value = tmp;
+ }
+ string_fprintf (
+ file,
+ "** %s: `+%s%s%s+`\n",
+ _("default value"),
+ (ptr_option->type == CONFIG_OPTION_TYPE_STRING) ? "\"" : "",
+ default_value,
+ (ptr_option->type == CONFIG_OPTION_TYPE_STRING) ? "\"" : "");
+ if (desc_escaped)
+ free (desc_escaped);
+ if (values)
+ free (values);
+ if (default_value)
+ free (default_value);
+ }
+
+ if (old_config)
+ {
+ string_fprintf (
+ file,
+ "// end::%s_options[]\n",
+ old_config->name);
+ }
+
+ arraylist_free (list_options);
+
+ return doc_gen_close_file (path, "user", "options", lang, file);
+}
+
+/*
+ * Generates files with default aliases.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_user_default_aliases (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_infolist *ptr_infolist;
+ const char *ptr_completion;
+
+ file = doc_gen_open_file (path, "user", "default_aliases", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (
+ file,
+ "// tag::default_aliases[]\n"
+ "[width=\"100%\",cols=\"2m,5m,5\",options=\"header\"]\n"
+ "|===\n"
+ "| %s | %s | %s\n",
+ ESCAPE_TABLE(_("Alias")),
+ ESCAPE_TABLE(_("Command")),
+ ESCAPE_TABLE(_("Completion")));
+
+ ptr_infolist = hook_infolist_get (NULL, "alias_default", NULL, NULL);
+ while (infolist_next (ptr_infolist))
+ {
+ ptr_completion = infolist_string (ptr_infolist, "completion");
+ string_fprintf (file,
+ "| /%s | /%s | %s\n",
+ ESCAPE_TABLE(infolist_string (ptr_infolist, "name")),
+ ESCAPE_TABLE(infolist_string (ptr_infolist, "command")),
+ (ptr_completion && ptr_completion[0]) ?
+ ESCAPE_TABLE(ptr_completion) : "-");
+ }
+ infolist_free (ptr_infolist);
+
+ string_fprintf (file,
+ "|===\n"
+ "// end::default_aliases[]\n");
+
+ return doc_gen_close_file (path, "user", "default_aliases", lang, file);
+}
+
+/*
+ * Generates files with IRC colors.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_user_irc_colors (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_infolist *ptr_infolist;
+
+ file = doc_gen_open_file (path, "user", "irc_colors", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (
+ file,
+ "// tag::irc_colors[]\n"
+ "[width=\"50%\",cols=\"^2m,3\",options=\"header\"]\n"
+ "|===\n"
+ "| %s | %s\n",
+ ESCAPE_TABLE(_("IRC color")),
+ ESCAPE_TABLE(_("WeeChat color")));
+
+ ptr_infolist = hook_infolist_get (NULL, "irc_color_weechat", NULL, NULL);
+ while (infolist_next (ptr_infolist))
+ {
+ string_fprintf (
+ file,
+ "| %s | %s\n",
+ ESCAPE_TABLE(infolist_string (ptr_infolist, "color_irc")),
+ ESCAPE_TABLE(infolist_string (ptr_infolist, "color_weechat")));
+ }
+ infolist_free (ptr_infolist);
+
+ string_fprintf (file,
+ "|===\n"
+ "// end::irc_colors[]\n");
+
+ return doc_gen_close_file (path, "user", "irc_colors", lang, file);
+}
+
+/*
+ * Compares two hooks "info" to sort by plugin / info.
+ */
+
+int
+doc_gen_hook_info_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ struct t_hook *ptr_hook1, *ptr_hook2;
+ int rc;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ ptr_hook1 = (struct t_hook *)pointer1;
+ ptr_hook2 = (struct t_hook *)pointer2;
+
+ rc = strcmp (PLUGIN(ptr_hook1->plugin), PLUGIN(ptr_hook2->plugin));
+ if (rc != 0)
+ return rc;
+
+ return strcmp (HOOK_INFO(ptr_hook1, info_name),
+ HOOK_INFO(ptr_hook2, info_name));
+}
+
+/*
+ * Generates files with infos.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_api_infos (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_hook *ptr_hook;
+ struct t_arraylist *list_hooks;
+ int i, list_size;
+
+ file = doc_gen_open_file (path, "api", "infos", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (
+ file,
+ "// tag::infos[]\n"
+ "[width=\"100%\",cols=\"^1,^2,6,6\",options=\"header\"]\n"
+ "|===\n"
+ "| %s | %s | %s | %s\n",
+ ESCAPE_TABLE(_("Plugin")),
+ ESCAPE_TABLE(_("Name")),
+ ESCAPE_TABLE(_("Description")),
+ ESCAPE_TABLE(_("Arguments")));
+
+ list_hooks = arraylist_new (64, 1, 0,
+ &doc_gen_hook_info_cmp_cb, NULL,
+ NULL, NULL);
+ for (ptr_hook = weechat_hooks[HOOK_TYPE_INFO]; ptr_hook;
+ ptr_hook = ptr_hook->next_hook)
+ {
+ arraylist_add (list_hooks, ptr_hook);
+ }
+
+ list_size = arraylist_size (list_hooks);
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_hook = (struct t_hook *)arraylist_get (list_hooks, i);
+ string_fprintf (
+ file,
+ "| %s | %s | %s | %s\n",
+ ESCAPE_TABLE(PLUGIN(ptr_hook->plugin)),
+ ESCAPE_TABLE(HOOK_INFO(ptr_hook, info_name)),
+ ESCAPE_TABLE(TRANS(HOOK_INFO(ptr_hook, description))),
+ ESCAPE_TABLE(TRANS_DEF(HOOK_INFO(ptr_hook, args_description), "-")));
+ }
+
+ arraylist_free (list_hooks);
+
+ string_fprintf (file,
+ "|===\n"
+ "// end::infos[]\n");
+
+ return doc_gen_close_file (path, "api", "infos", lang, file);
+}
+
+/*
+ * Compares two hooks "info_hashtable" to sort by plugin / info.
+ */
+
+int
+doc_gen_hook_info_hashtable_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ struct t_hook *ptr_hook1, *ptr_hook2;
+ int rc;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ ptr_hook1 = (struct t_hook *)pointer1;
+ ptr_hook2 = (struct t_hook *)pointer2;
+
+ rc = strcmp (PLUGIN(ptr_hook1->plugin), PLUGIN(ptr_hook2->plugin));
+ if (rc != 0)
+ return rc;
+
+ return strcmp (HOOK_INFO_HASHTABLE(ptr_hook1, info_name),
+ HOOK_INFO_HASHTABLE(ptr_hook2, info_name));
+}
+
+/*
+ * Generates files with infos_hashtable.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_api_infos_hashtable (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_hook *ptr_hook;
+ struct t_arraylist *list_hooks;
+ int i, list_size;
+
+ file = doc_gen_open_file (path, "api", "infos_hashtable", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (
+ file,
+ "// tag::infos_hashtable[]\n"
+ "[width=\"100%\",cols=\"^1,^2,6,6,8\",options=\"header\"]\n"
+ "|===\n"
+ "| %s | %s | %s | %s | %s\n",
+ ESCAPE_TABLE(_("Plugin")),
+ ESCAPE_TABLE(_("Name")),
+ ESCAPE_TABLE(_("Description")),
+ ESCAPE_TABLE(_("Hashtable (input)")),
+ ESCAPE_TABLE(_("Hashtable (output)")));
+
+ list_hooks = arraylist_new (64, 1, 0,
+ &doc_gen_hook_info_hashtable_cmp_cb, NULL,
+ NULL, NULL);
+ for (ptr_hook = weechat_hooks[HOOK_TYPE_INFO_HASHTABLE]; ptr_hook;
+ ptr_hook = ptr_hook->next_hook)
+ {
+ arraylist_add (list_hooks, ptr_hook);
+ }
+
+ list_size = arraylist_size (list_hooks);
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_hook = (struct t_hook *)arraylist_get (list_hooks, i);
+ string_fprintf (
+ file,
+ "| %s | %s | %s | %s | %s\n",
+ ESCAPE_TABLE(PLUGIN(ptr_hook->plugin)),
+ ESCAPE_TABLE(HOOK_INFO(ptr_hook, info_name)),
+ ESCAPE_TABLE(TRANS(HOOK_INFO_HASHTABLE(ptr_hook, description))),
+ ESCAPE_TABLE(TRANS_DEF(HOOK_INFO_HASHTABLE(ptr_hook, args_description), "-")),
+ TRANS_DEF(HOOK_INFO_HASHTABLE(ptr_hook, output_description), "-"));
+ }
+
+ arraylist_free (list_hooks);
+
+ string_fprintf (file,
+ "|===\n"
+ "// end::infos_hashtable[]\n");
+
+ return doc_gen_close_file (path, "api", "infos_hashtable", lang, file);
+}
+
+/*
+ * Compares two hooks "infolist" to sort by plugin / infolist.
+ */
+
+int
+doc_gen_hook_infolist_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ struct t_hook *ptr_hook1, *ptr_hook2;
+ int rc;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ ptr_hook1 = (struct t_hook *)pointer1;
+ ptr_hook2 = (struct t_hook *)pointer2;
+
+ rc = strcmp (PLUGIN(ptr_hook1->plugin), PLUGIN(ptr_hook2->plugin));
+ if (rc != 0)
+ return rc;
+
+ return strcmp (HOOK_INFOLIST(ptr_hook1, infolist_name),
+ HOOK_INFOLIST(ptr_hook2, infolist_name));
+}
+
+/*
+ * Generates files with infolists.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_api_infolists (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_hook *ptr_hook;
+ struct t_arraylist *list_hooks;
+ int i, list_size;
+
+ file = doc_gen_open_file (path, "api", "infolists", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (
+ file,
+ "// tag::infolists[]\n"
+ "[width=\"100%\",cols=\"^1,^2,5,5,5\",options=\"header\"]\n"
+ "|===\n"
+ "| %s | %s | %s | %s | %s\n",
+ ESCAPE_TABLE(_("Plugin")),
+ ESCAPE_TABLE(_("Name")),
+ ESCAPE_TABLE(_("Description")),
+ ESCAPE_TABLE(_("Pointer")),
+ ESCAPE_TABLE(_("Arguments")));
+
+ list_hooks = arraylist_new (64, 1, 0,
+ &doc_gen_hook_infolist_cmp_cb, NULL,
+ NULL, NULL);
+ for (ptr_hook = weechat_hooks[HOOK_TYPE_INFOLIST]; ptr_hook;
+ ptr_hook = ptr_hook->next_hook)
+ {
+ arraylist_add (list_hooks, ptr_hook);
+ }
+
+ list_size = arraylist_size (list_hooks);
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_hook = (struct t_hook *)arraylist_get (list_hooks, i);
+ string_fprintf (
+ file,
+ "| %s | %s | %s | %s | %s\n",
+ ESCAPE_TABLE(PLUGIN(ptr_hook->plugin)),
+ ESCAPE_TABLE(HOOK_INFOLIST(ptr_hook, infolist_name)),
+ ESCAPE_TABLE(TRANS(HOOK_INFOLIST(ptr_hook, description))),
+ ESCAPE_TABLE(TRANS_DEF(HOOK_INFOLIST(ptr_hook, pointer_description), "-")),
+ ESCAPE_TABLE(TRANS_DEF(HOOK_INFOLIST(ptr_hook, args_description), "-")));
+ }
+
+ arraylist_free (list_hooks);
+
+ string_fprintf (file,
+ "|===\n"
+ "// end::infolists[]\n");
+
+ return doc_gen_close_file (path, "api", "infolists", lang, file);
+}
+
+/*
+ * Compares two hooks "hdata" to sort by plugin / hdata.
+ */
+
+int
+doc_gen_hook_hdata_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ struct t_hook *ptr_hook1, *ptr_hook2;
+ int rc;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ ptr_hook1 = (struct t_hook *)pointer1;
+ ptr_hook2 = (struct t_hook *)pointer2;
+
+ rc = strcmp (PLUGIN(ptr_hook1->plugin), PLUGIN(ptr_hook2->plugin));
+ if (rc != 0)
+ return rc;
+
+ return strcmp (HOOK_HDATA(ptr_hook1, hdata_name),
+ HOOK_HDATA(ptr_hook2, hdata_name));
+}
+
+/*
+ * Compares two hooks lists to sort by name (and lists beginning with "last_"
+ * at the end).
+ */
+
+int
+doc_gen_hdata_list_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ if ((strncmp ((const char *)pointer1, "last_", 5) != 0)
+ && (strncmp ((const char *)pointer2, "last_", 5) == 0))
+ {
+ return -1;
+ }
+
+ if ((strncmp ((const char *)pointer1, "last_", 5) == 0)
+ && (strncmp ((const char *)pointer2, "last_", 5) != 0))
+ {
+ return 1;
+ }
+
+ return strcmp ((const char *)pointer1, (const char *)pointer2);
+}
+
+/*
+ * Compares two hooks hdata keys to sort by offset.
+ */
+
+int
+doc_gen_hdata_key_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ int offset1, offset2;
+
+ /* make C compiler happy */
+ (void) arraylist;
+
+ offset1 = hdata_get_var_offset ((struct t_hdata *)data,
+ (const char *)pointer1);
+ offset2 = hdata_get_var_offset ((struct t_hdata *)data,
+ (const char *)pointer2);
+ return (offset1 < offset2) ?
+ -1 : ((offset1 > offset2) ? 1 : 0);
+}
+
+/*
+ * Generates content of a hdata.
+ */
+
+void
+doc_gen_api_hdata_content (FILE *file, struct t_hdata *hdata)
+{
+ const char *ptr_lists, *ptr_keys, *var_hdata, *var_array_size, *ptr_key;
+ const char *ptr_list;
+ char **lists, **keys, str_var_hdata[1024], str_var_array_size[1024];
+ int i, num_lists, num_keys, list_size;
+ struct t_arraylist *list_lists, *list_keys, *list_vars_update;
+ struct t_hashtable *hashtable;
+
+ ptr_lists = hdata_get_string (hdata, "list_keys");
+ if (ptr_lists)
+ {
+ lists = string_split (ptr_lists, ",", NULL, 0, 0, &num_lists);
+ if (lists)
+ {
+ string_fprintf (file, "| ");
+ list_lists = arraylist_new (64, 1, 0,
+ &doc_gen_hdata_list_cmp_cb, hdata,
+ NULL, NULL);
+ for (i = 0; i < num_lists; i++)
+ {
+ arraylist_add (list_lists, lists[i]);
+ }
+ list_size = arraylist_size (list_lists);
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_list = (const char *)arraylist_get (list_lists, i);
+ string_fprintf (file, "_%s_ +\n", ptr_list);
+ }
+ arraylist_free (list_lists);
+ string_free_split (lists);
+ string_fprintf (file, "\n");
+ }
+ }
+ else
+ {
+ string_fprintf (file, "| -\n");
+ }
+
+ list_vars_update = arraylist_new (64, 0, 1, NULL, NULL, NULL, NULL);
+ hashtable = hashtable_new (16,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL, NULL);
+
+ ptr_keys = hdata_get_string (hdata, "var_keys");
+ if (ptr_keys)
+ {
+ keys = string_split (ptr_keys, ",", NULL, 0, 0, &num_keys);
+ if (keys)
+ {
+ string_fprintf (file, "| ");
+ list_keys = arraylist_new (64, 1, 0,
+ &doc_gen_hdata_key_cmp_cb, hdata,
+ NULL, NULL);
+ for (i = 0; i < num_keys; i++)
+ {
+ arraylist_add (list_keys, keys[i]);
+ }
+ list_size = arraylist_size (list_keys);
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_key = (const char *)arraylist_get (list_keys, i);
+ hashtable_set (hashtable, "__update_allowed", ptr_key);
+ if (hdata_update (hdata, NULL, hashtable))
+ arraylist_add (list_vars_update, (void *)ptr_key);
+ var_array_size = hdata_get_var_array_size_string (
+ hdata, NULL, ptr_key);
+ if (var_array_size)
+ {
+ snprintf (str_var_array_size, sizeof (str_var_array_size),
+ ", array_size: \"%s\"",
+ var_array_size);
+ }
+ else
+ {
+ str_var_array_size[0] = '\0';
+ }
+ var_hdata = hdata_get_var_hdata (hdata, ptr_key);
+ if (var_hdata)
+ {
+ snprintf (str_var_hdata, sizeof (str_var_hdata),
+ ", hdata: \"%s\"",
+ var_hdata);
+ }
+ else
+ {
+ str_var_hdata[0] = '\0';
+ }
+ string_fprintf (file,
+ "_%s_   (%s%s%s) +\n",
+ ptr_key,
+ hdata_get_var_type_string (hdata, ptr_key),
+ str_var_array_size,
+ str_var_hdata);
+ }
+ hashtable_remove_all (hashtable);
+ hashtable_set (hashtable, "__create_allowed", "");
+ if (hdata_update (hdata, NULL, hashtable))
+ arraylist_add (list_vars_update, "{hdata_update_create}");
+ hashtable_remove_all (hashtable);
+ hashtable_set (hashtable, "__delete_allowed", "");
+ if (hdata_update (hdata, NULL, hashtable))
+ arraylist_add (list_vars_update, "{hdata_update_delete}");
+ list_size = arraylist_size (list_vars_update);
+ if (list_size > 0)
+ {
+ string_fprintf (file, "\n");
+ string_fprintf (file, "*%s* +\n", _("Update allowed:"));
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_key = (const char *)arraylist_get (list_vars_update, i);
+ if (ptr_key[0] == '{')
+ {
+ string_fprintf (file, "    _%s_ +\n", ptr_key);
+ }
+ else
+ {
+ string_fprintf (
+ file,
+ "    _%s_ (%s) +\n",
+ ptr_key,
+ hdata_get_var_type_string (hdata, ptr_key));
+ }
+ }
+ }
+ arraylist_free (list_keys);
+ string_free_split (keys);
+ }
+ }
+
+ hashtable_free (hashtable);
+ arraylist_free (list_vars_update);
+
+ string_fprintf (file, "\n");
+}
+
+/*
+ * Generates files with hdata.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_api_hdata (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_hook *ptr_hook;
+ struct t_arraylist *list_hooks;
+ struct t_hdata *ptr_hdata;
+ int i, list_size;
+ char str_anchor[256];
+
+ file = doc_gen_open_file (path, "api", "hdata", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (
+ file,
+ "// tag::hdata[]\n"
+ ":hdata_update_create: __create\n"
+ ":hdata_update_delete: __delete\n"
+ "[width=\"100%\",cols=\"^1,^2,2,2,5\",options=\"header\"]\n"
+ "|===\n"
+ "| %s | %s | %s | %s | %s\n\n",
+ ESCAPE_TABLE(_("Plugin")),
+ ESCAPE_TABLE(_("Name")),
+ ESCAPE_TABLE(_("Description")),
+ ESCAPE_TABLE(_("Lists")),
+ ESCAPE_TABLE(_("Variables")));
+
+ list_hooks = arraylist_new (64, 1, 0,
+ &doc_gen_hook_hdata_cmp_cb, NULL,
+ NULL, NULL);
+ for (ptr_hook = weechat_hooks[HOOK_TYPE_HDATA]; ptr_hook;
+ ptr_hook = ptr_hook->next_hook)
+ {
+ arraylist_add (list_hooks, ptr_hook);
+ }
+
+ list_size = arraylist_size (list_hooks);
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_hook = (struct t_hook *)arraylist_get (list_hooks, i);
+ snprintf (str_anchor, sizeof (str_anchor),
+ "hdata_%s",
+ HOOK_HDATA(ptr_hook, hdata_name));
+ string_fprintf (file,
+ "| %s\n",
+ ESCAPE_TABLE(PLUGIN(ptr_hook->plugin)));
+ string_fprintf (file,
+ "| [[%s]]<<%s,%s>>\n",
+ ESCAPE_TABLE(str_anchor),
+ ESCAPE_TABLE(str_anchor),
+ ESCAPE_TABLE(HOOK_HDATA(ptr_hook, hdata_name)));
+ string_fprintf (file,
+ "| %s\n",
+ ESCAPE_TABLE(TRANS(HOOK_HDATA(ptr_hook, description))));
+ ptr_hdata = hook_hdata_get (NULL, HOOK_HDATA(ptr_hook, hdata_name));
+ if (ptr_hdata)
+ doc_gen_api_hdata_content (file, ptr_hdata);
+ }
+
+ arraylist_free (list_hooks);
+
+ string_fprintf (file,
+ "|===\n"
+ "// end::hdata[]\n");
+
+ return doc_gen_close_file (path, "api", "hdata", lang, file);
+}
+
+/*
+ * Compares two hooks "completion" to sort by plugin / completion.
+ */
+
+int
+doc_gen_hook_completion_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ struct t_hook *ptr_hook1, *ptr_hook2;
+ int rc;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ ptr_hook1 = (struct t_hook *)pointer1;
+ ptr_hook2 = (struct t_hook *)pointer2;
+
+ rc = strcmp (PLUGIN(ptr_hook1->plugin), PLUGIN(ptr_hook2->plugin));
+ if (rc != 0)
+ return rc;
+
+ return strcmp (HOOK_COMPLETION(ptr_hook1, completion_item),
+ HOOK_COMPLETION(ptr_hook2, completion_item));
+}
+
+/*
+ * Generates files with completions.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_api_completions (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_hook *ptr_hook;
+ struct t_arraylist *list_hooks;
+ int i, list_size;
+
+ file = doc_gen_open_file (path, "api", "completions", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (
+ file,
+ "// tag::completions[]\n"
+ "[width=\"100%\",cols=\"^1,^2,7\",options=\"header\"]\n"
+ "|===\n"
+ "| %s | %s | %s\n",
+ ESCAPE_TABLE(_("Plugin")),
+ ESCAPE_TABLE(_("Name")),
+ ESCAPE_TABLE(_("Description")));
+
+ list_hooks = arraylist_new (64, 1, 0,
+ &doc_gen_hook_completion_cmp_cb, NULL,
+ NULL, NULL);
+ for (ptr_hook = weechat_hooks[HOOK_TYPE_COMPLETION]; ptr_hook;
+ ptr_hook = ptr_hook->next_hook)
+ {
+ arraylist_add (list_hooks, ptr_hook);
+ }
+
+ list_size = arraylist_size (list_hooks);
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_hook = (struct t_hook *)arraylist_get (list_hooks, i);
+ string_fprintf (
+ file,
+ "| %s | %s | %s\n",
+ ESCAPE_TABLE(PLUGIN(ptr_hook->plugin)),
+ ESCAPE_TABLE(HOOK_COMPLETION(ptr_hook, completion_item)),
+ ESCAPE_TABLE(TRANS(HOOK_COMPLETION(ptr_hook, description))));
+ }
+
+ arraylist_free (list_hooks);
+
+ string_fprintf (file,
+ "|===\n"
+ "// end::completions[]\n");
+
+ return doc_gen_close_file (path, "api", "completions", lang, file);
+}
+
+/*
+ * Generates files with URL options.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_api_url_options (const char *path, const char *lang)
+{
+ FILE *file;
+ int i, j;
+ char *name, *constant;
+
+ file = doc_gen_open_file (path, "api", "url_options", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (
+ file,
+ "// tag::url_options[]\n"
+ "[width=\"100%\",cols=\"2,^1,7\",options=\"header\"]\n"
+ "|===\n"
+ "| %s | %s ^(1)^ | %s ^(2)^\n",
+ ESCAPE_TABLE(_("Option")),
+ ESCAPE_TABLE(_("Type")),
+ ESCAPE_TABLE(_("Constants")));
+
+ for (i = 0; url_options[i].name; i++)
+ {
+ name = string_tolower (url_options[i].name);
+ string_fprintf (
+ file,
+ "| %s | %s |",
+ ESCAPE_TABLE(name),
+ ESCAPE_TABLE(url_type_string[url_options[i].type]));
+ if (name)
+ free (name);
+ if (url_options[i].constants)
+ {
+ for (j = 0; url_options[i].constants[j].name; j++)
+ {
+ if (j > 0)
+ string_fprintf (file, ",");
+ constant = string_tolower (url_options[i].constants[j].name);
+ string_fprintf (file, " %s", constant);
+ if (constant)
+ free (constant);
+ }
+ }
+ string_fprintf (file, "\n");
+ }
+
+ string_fprintf (file,
+ "|===\n"
+ "// end::url_options[]\n");
+
+ return doc_gen_close_file (path, "api", "url_options", lang, file);
+}
+
+/*
+ * Compares two plugins to sort by priority (descending).
+ */
+
+int
+doc_gen_plugin_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ struct t_weechat_plugin *ptr_plugin1, *ptr_plugin2;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ ptr_plugin1 = (struct t_weechat_plugin *)pointer1;
+ ptr_plugin2 = (struct t_weechat_plugin *)pointer2;
+
+ if (ptr_plugin1->priority != ptr_plugin2->priority)
+ return (ptr_plugin1->priority > ptr_plugin2->priority) ?
+ -1 : ((ptr_plugin1->priority < ptr_plugin2->priority) ? 1 : 0);
+
+ return strcmp (ptr_plugin1->name, ptr_plugin2->name);
+}
+
+/*
+ * Generates files with plugins priority.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_api_plugins_priority (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_weechat_plugin *ptr_plugin;
+ struct t_arraylist *list_plugins;
+ int i, index, list_size;
+
+ file = doc_gen_open_file (path, "api", "plugins_priority", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (
+ file,
+ "// tag::plugins_priority[]\n"
+ "[width=\"30%\",cols=\"1,3,2\",options=\"header\"]\n"
+ "|===\n"
+ "| %s | %s | %s\n",
+ ESCAPE_TABLE(_("Rank")),
+ ESCAPE_TABLE(_("Plugin")),
+ ESCAPE_TABLE(_("Priority")));
+
+ list_plugins = arraylist_new (64, 1, 0,
+ &doc_gen_plugin_cmp_cb, NULL,
+ NULL, NULL);
+ for (ptr_plugin = weechat_plugins; ptr_plugin;
+ ptr_plugin = ptr_plugin->next_plugin)
+ {
+ arraylist_add (list_plugins, ptr_plugin);
+ }
+
+ index = 1;
+ list_size = arraylist_size (list_plugins);
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_plugin = (struct t_weechat_plugin *)arraylist_get (list_plugins, i);
+ string_fprintf (file,
+ "| %d | %s | %d\n",
+ index,
+ ptr_plugin->name,
+ ptr_plugin->priority);
+ index++;
+ }
+
+ arraylist_free (list_plugins);
+
+ string_fprintf (file,
+ "|===\n"
+ "// end::plugins_priority[]\n");
+
+ return doc_gen_close_file (path, "api", "plugins_priority", lang, file);
+}
+
+/*
+ * Compares two configurations to sort by priority (descending).
+ */
+
+int
+doc_gen_config_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ struct t_config_file *ptr_config1, *ptr_config2;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ ptr_config1 = (struct t_config_file *)pointer1;
+ ptr_config2 = (struct t_config_file *)pointer2;
+
+ if (ptr_config1->priority != ptr_config2->priority)
+ return (ptr_config1->priority > ptr_config2->priority) ?
+ -1 : ((ptr_config1->priority < ptr_config2->priority) ? 1 : 0);
+
+ return strcmp (ptr_config1->name, ptr_config2->name);
+}
+
+/*
+ * Generates files with config priority.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_api_config_priority (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_config_file *ptr_config;
+ struct t_arraylist *list_configs;
+ int i, index, list_size;
+
+ file = doc_gen_open_file (path, "api", "config_priority", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (
+ file,
+ "// tag::config_priority[]\n"
+ "[width=\"30%\",cols=\"1,3,2\",options=\"header\"]\n"
+ "|===\n"
+ "| %s | %s | %s\n",
+ ESCAPE_TABLE(_("Rank")),
+ ESCAPE_TABLE(_("File")),
+ ESCAPE_TABLE(_("Priority")));
+
+ list_configs = arraylist_new (64, 1, 0,
+ &doc_gen_config_cmp_cb, NULL,
+ NULL, NULL);
+ for (ptr_config = config_files; ptr_config;
+ ptr_config = ptr_config->next_config)
+ {
+ arraylist_add (list_configs, ptr_config);
+ }
+
+ index = 1;
+ list_size = arraylist_size (list_configs);
+ for (i = 0; i < list_size; i++)
+ {
+ ptr_config = (struct t_config_file *)arraylist_get (list_configs, i);
+ string_fprintf (file,
+ "| %d | %s.conf | %d\n",
+ index,
+ ptr_config->name,
+ ptr_config->priority);
+ index++;
+ }
+
+ arraylist_free (list_configs);
+
+ string_fprintf (file,
+ "|===\n"
+ "// end::config_priority[]\n");
+
+ return doc_gen_close_file (path, "api", "config_priority", lang, file);
+}
+
+/*
+ * Generates files with scripting API functions.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_scripting_functions (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_infolist *ptr_infolist;
+
+ file = doc_gen_open_file (path, "scripting", "functions", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (file, "// tag::functions[]\n");
+
+ ptr_infolist = hook_infolist_get (NULL, "python_function", NULL, NULL);
+ while (infolist_next (ptr_infolist))
+ {
+ string_fprintf (file, "* %s\n", infolist_string (ptr_infolist, "name"));
+ }
+ infolist_free (ptr_infolist);
+
+ string_fprintf (file, "// end::functions[]\n");
+
+ return doc_gen_close_file (path, "scripting", "functions", lang, file);
+}
+
+/*
+ * Generates files with scripting API constants.
+ *
+ * Returns:
+ * 1: OK, target file updated
+ * 0: OK, target file unchanged
+ * -1: error
+ */
+
+int
+doc_gen_scripting_constants (const char *path, const char *lang)
+{
+ FILE *file;
+ struct t_infolist *ptr_infolist;
+ const char *ptr_type;
+
+ file = doc_gen_open_file (path, "scripting", "constants", lang);
+ if (!file)
+ return -1;
+
+ string_fprintf (
+ file,
+ "// tag::constants[]\n"
+ "[width=\"60%\",cols=\"8,1,3m\",options=\"header\"]\n"
+ "|===\n"
+ "| %s | %s | %s\n",
+ ESCAPE_TABLE(_("Constant")),
+ ESCAPE_TABLE(_("Type")),
+ ESCAPE_TABLE(_("Value")));
+
+ ptr_infolist = hook_infolist_get (NULL, "python_constant", NULL, NULL);
+ while (infolist_next (ptr_infolist))
+ {
+ ptr_type = infolist_string (ptr_infolist, "type");
+ if (!ptr_type)
+ continue;
+ if (strcmp (ptr_type, "integer") == 0)
+ {
+ string_fprintf (file,
+ "| %s | %s | %d\n",
+ ESCAPE_TABLE(infolist_string (ptr_infolist, "name")),
+ ESCAPE_TABLE(_("integer")),
+ infolist_integer (ptr_infolist, "value_integer"));
+ }
+ else if (strcmp (ptr_type, "string") == 0)
+ {
+ string_fprintf (file,
+ "| %s | %s | %s\n",
+ ESCAPE_TABLE(infolist_string (ptr_infolist, "name")),
+ ESCAPE_TABLE(_("string")),
+ ESCAPE_TABLE(infolist_string (ptr_infolist, "value_string")));
+ }
+ }
+ infolist_free (ptr_infolist);
+
+ string_fprintf (file,
+ "|===\n"
+ "// end::constants[]\n");
+
+ return doc_gen_close_file (path, "scripting", "constants", lang, file);
+}
+
+/*
+ * Generates WeeChat files used to build documentation.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+doc_generate (const char *path)
+{
+ int i, j, rc_doc_gen, rc, num_files;
+ char *locales[] = {
+ "de_DE.UTF-8",
+ "en_US.UTF-8",
+ "fr_FR.UTF-8",
+ "it_IT.UTF-8",
+ "ja_JP.UTF-8",
+ "pl_PL.UTF-8",
+ "sr_RS.UTF-8",
+ NULL,
+ };
+ t_doc_gen_func *doc_gen_functions[] = {
+ doc_gen_user_commands,
+ doc_gen_user_options,
+ doc_gen_user_default_aliases,
+ doc_gen_user_irc_colors,
+ doc_gen_api_infos,
+ doc_gen_api_infos_hashtable,
+ doc_gen_api_infolists,
+ doc_gen_api_hdata,
+ doc_gen_api_completions,
+ doc_gen_api_url_options,
+ doc_gen_api_plugins_priority,
+ doc_gen_api_config_priority,
+ doc_gen_scripting_functions,
+ doc_gen_scripting_constants,
+ NULL,
+ };
+ char lang[3], *localedir;
+
+ rc_doc_gen = 0;
+ num_files = 0;
+
+ index_string_escaped = 0;
+ memset (string_escaped, 0, sizeof (string_escaped));
+
+ if (!weechat_plugins)
+ {
+ string_fprintf (
+ stderr,
+ "doc generator: WARNING: no plugins loaded, docs will be "
+ "incomplete!\n");
+ }
+
+ if (!dir_mkdir_parents (path, 0755))
+ {
+ string_fprintf (
+ stderr,
+ "doc generator: ERROR: failed to create directory \"%s\")\n",
+ path);
+ goto end;
+ }
+
+ /*
+ * set a specific localedir to find .mo files
+ * (this is used to generate documentation without installing WeeChat,
+ * that means no need to run `make install`)
+ */
+#ifdef ENABLE_NLS
+ localedir = getenv ("WEECHAT_DOCGEN_LOCALEDIR");
+ if (localedir && localedir[0])
+ bindtextdomain (PACKAGE, localedir);
+#endif /* ENABLE_NLS */
+
+ for (i = 0; locales[i]; i++)
+ {
+ setenv ("LANGUAGE", locales[i], 1);
+ if (!setlocale (LC_ALL, locales[i]))
+ {
+ /* warning on missing locale */
+ string_fprintf (
+ stderr,
+ "doc generator: WARNING: failed to set locale \"%s\", "
+ "docs will include auto-generated English content\n",
+ locales[i]);
+ /* fallback to English */
+ setlocale (LC_ALL, "C");
+ }
+ memcpy (lang, locales[i], 2);
+ lang[2] = '\0';
+ for (j = 0; doc_gen_functions[j]; j++)
+ {
+ rc = (int) (doc_gen_functions[j] (path, lang));
+ if (rc < 0)
+ goto end;
+ num_files += rc;
+ }
+ }
+
+ if (num_files > 0)
+ printf ("doc generator: OK, %d files updated\n", num_files);
+ else
+ printf ("doc generator: OK, no changes\n");
+
+ rc_doc_gen = 1;
+
+end:
+ for (i = 0; i < 32; i++)
+ {
+ if (string_escaped[i])
+ free (string_escaped[i]);
+ }
+ return rc_doc_gen;
+}