mirror of
https://github.com/weechat/weechat.git
synced 2026-06-25 04:16:38 +02:00
1781 lines
50 KiB
C
1781 lines
50 KiB
C
/*
|
||
* core-doc.c - documentation generator
|
||
*
|
||
* Copyright (C) 2023-2025 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 <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;
|
||
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 (®ex, "[,@*():&|]+", REG_EXTENDED) != 0)
|
||
return NULL;
|
||
|
||
index_string_escaped = (index_string_escaped + 1) % 32;
|
||
free (string_escaped[index_string_escaped]);
|
||
string_escaped[index_string_escaped] = string_replace_regex (
|
||
message, ®ex, "-", '$', NULL, NULL);
|
||
regfree (®ex);
|
||
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 are documented
|
||
* except the default aliases (that create commands).
|
||
*/
|
||
|
||
int
|
||
doc_gen_check_command (const char *plugin, const char *command)
|
||
{
|
||
return ((strcmp (plugin, "alias") != 0) || (strcmp (plugin, command) == 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, "\"", "\\\"");
|
||
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) ? "\"" : "");
|
||
free (desc_escaped);
|
||
free (values);
|
||
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]));
|
||
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);
|
||
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];
|
||
#if ENABLE_NLS == 1
|
||
char *localedir;
|
||
#endif /* ENABLE_NLS == 1 */
|
||
|
||
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`)
|
||
*/
|
||
#if ENABLE_NLS == 1
|
||
localedir = getenv ("WEECHAT_DOCGEN_LOCALEDIR");
|
||
if (localedir && localedir[0])
|
||
bindtextdomain (PACKAGE, localedir);
|
||
#endif /* ENABLE_NLS == 1 */
|
||
|
||
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++)
|
||
{
|
||
free (string_escaped[i]);
|
||
}
|
||
return rc_doc_gen;
|
||
}
|