diff options
author | Sébastien Helleu <flashcode@flashtux.org> | 2023-03-29 22:38:01 +0200 |
---|---|---|
committer | Sébastien Helleu <flashcode@flashtux.org> | 2023-03-30 01:34:55 +0200 |
commit | 6dbfb638c6586905b3b29df9cf1e41d9166546ef (patch) | |
tree | c2604c334bfea2d5aa59ac3dea6a73f812e674ce /src | |
parent | 4548b25b78d0ba8fc61421bda88a5a79b09ff9af (diff) | |
download | weechat-6dbfb638c6586905b3b29df9cf1e41d9166546ef.zip |
doc: convert docgen.py to C, remove autogen files from repository, add parameter `--doc-gen`
Changes:
- build of doc now requires weechat-headless, translations and all plugins
- convert docgen.py to C
- remove `autogen_*` files from repository
- add command line parameter `--doc-gen` in `weechat-headless` to build
autogen files
- build .mo files with directories like the installed ones
(eg: "<lang>/LC_MESSAGES/weechat.mo")
- remove javascript chapter from user's guide
Diffstat (limited to 'src')
-rw-r--r-- | src/core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/core/wee-config-file.h | 2 | ||||
-rw-r--r-- | src/core/wee-doc.c | 1643 | ||||
-rw-r--r-- | src/core/wee-doc.h | 25 | ||||
-rw-r--r-- | src/core/wee-url.h | 1 | ||||
-rw-r--r-- | src/core/weechat.c | 74 |
6 files changed, 1724 insertions, 22 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e4fd70250..754c013bf 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -31,6 +31,7 @@ set(LIB_CORE_SRC wee-crypto.c wee-crypto.h wee-debug.c wee-debug.h wee-dir.c wee-dir.h + wee-doc.c wee-doc.h wee-eval.c wee-eval.h wee-hashtable.c wee-hashtable.h wee-hdata.c wee-hdata.h diff --git a/src/core/wee-config-file.h b/src/core/wee-config-file.h index ec5cf890e..9d56930b3 100644 --- a/src/core/wee-config-file.h +++ b/src/core/wee-config-file.h @@ -181,6 +181,8 @@ struct t_config_option extern struct t_config_file *config_files; extern struct t_config_file *last_config_file; +extern char *config_option_type_string[]; + extern int config_file_valid (struct t_config_file *config_file); extern struct t_config_file *config_file_search (const char *name); extern struct t_config_file *config_file_new (struct t_weechat_plugin *plugin, diff --git a/src/core/wee-doc.c b/src/core/wee-doc.c new file mode 100644 index 000000000..fa1e3ac28 --- /dev/null +++ b/src/core/wee-doc.c @@ -0,0 +1,1643 @@ +/* + * wee-doc.c - documentation generator + * + * Copyright (C) 2023 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 <stdio.h> +#include <string.h> +#include <libintl.h> +#include <locale.h> + +#include "weechat.h" +#include "wee-arraylist.h" +#include "wee-command.h" +#include "wee-config-file.h" +#include "wee-dir.h" +#include "wee-hashtable.h" +#include "wee-hdata.h" +#include "wee-hook.h" +#include "wee-infolist.h" +#include "wee-string.h" +#include "wee-url.h" +#include "wee-utf8.h" +#include "../plugins/plugin.h" + +#define ESCAPE(msg) (doc_gen_escape (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 (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]; +} + +/* + * 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", + path, DIR_SEPARATOR, doc, name, lang); + + if (weechat_debug_core >= 1) + printf ("Writing: %s\n", filename); + + file = fopen (filename, "wb"); + if (!file) + { + string_fprintf (stderr, + "doc generator: ERROR: unable to write file \"%s\"", + 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; +} + +/* + * 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 + * 0: 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; + const char *ptr_args, *pos_pipes, *pos_next; + + file = doc_gen_open_file (path, "user", "commands", lang); + if (!file) + return 0; + + 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 (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; + } + if (HOOK_COMMAND(ptr_hook, args_description) + && HOOK_COMMAND(ptr_hook, args_description[0])) + { + string_fprintf (file, + "\n%s\n", + TRANS(HOOK_COMMAND(ptr_hook, args_description))); + } + } + + string_fprintf ( + file, + "----\n" + "// end::%s_commands[]\n", + old_plugin); + + arraylist_free (list_hooks); + + fclose (file); + + return 1; +} + +/* + * 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 + * 0: 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 *name_escaped, *desc_escaped, *values, str_values[256]; + char *default_value, *tmp; + + file = doc_gen_open_file (path, "user", "options", lang); + if (!file) + return 0; + + 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"); + name_escaped = string_replace (ptr_option->name, ",", "_"); + desc_escaped = (ptr_option->description) ? + string_replace (TRANS(ptr_option->description), "]", "\\]") : + strdup (""); + string_fprintf (file, + "* [[option_%s.%s.%s]] *%s.%s.%s*\n", + ptr_option->config_file->name, + ptr_option->section->name, + name_escaped, + 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: + if (ptr_option->string_values) + { + values = string_rebuild_split_string ( + (const char **)ptr_option->string_values, ", ", 0, -1); + } + else + { + 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; + 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 (name_escaped) + free (name_escaped); + if (desc_escaped) + free (desc_escaped); + if (values) + free (values); + if (default_value) + free (default_value); + } + + string_fprintf ( + file, + "// end::%s_options[]\n", + old_config->name); + + arraylist_free (list_options); + + fclose (file); + + return 1; +} + +/* + * Generates files with default aliases. + * + * Returns: + * 1: OK + * 0: 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 0; + + string_fprintf ( + file, + "// tag::default_aliases[]\n" + "[width=\"100%\",cols=\"2m,5m,5\",options=\"header\"]\n" + "|===\n" + "| %s | %s | %s\n" + "\n", + ESCAPE(_("Alias")), + ESCAPE(_("Command")), + ESCAPE(_("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(infolist_string(ptr_infolist, "name")), + ESCAPE(infolist_string(ptr_infolist, "command")), + (ptr_completion && ptr_completion[0]) ? + ESCAPE(ptr_completion) : "-"); + } + infolist_free (ptr_infolist); + + string_fprintf (file, + "|===\n" + "// end::default_aliases[]\n"); + + fclose (file); + + return 1; +} + +/* + * Generates files with IRC colors. + * + * Returns: + * 1: OK + * 0: 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 0; + + string_fprintf ( + file, + "// tag::irc_colors[]\n" + "[width=\"50%\",cols=\"^2m,3\",options=\"header\"]\n" + "|===\n" + "| %s | %s\n" + "\n", + ESCAPE(_("IRC color")), + ESCAPE(_("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(infolist_string(ptr_infolist, "color_irc")), + ESCAPE(infolist_string(ptr_infolist, "color_weechat"))); + } + infolist_free (ptr_infolist); + + string_fprintf (file, + "|===\n" + "// end::irc_colors[]\n"); + + fclose (file); + + return 1; +} + +/* + * 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 + * 0: 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 0; + + string_fprintf ( + file, + "// tag::infos[]\n" + "[width=\"100%\",cols=\"^1,^2,6,6\",options=\"header\"]\n" + "|===\n" + "| %s | %s | %s | %s\n\n", + ESCAPE(_("Plugin")), + ESCAPE(_("Name")), + ESCAPE(_("Description")), + ESCAPE(_("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\n", + ESCAPE(PLUGIN(ptr_hook->plugin)), + ESCAPE(HOOK_INFO(ptr_hook, info_name)), + ESCAPE(TRANS(HOOK_INFO(ptr_hook, description))), + ESCAPE(TRANS_DEF(HOOK_INFO(ptr_hook, args_description), "-"))); + } + + arraylist_free (list_hooks); + + string_fprintf (file, + "|===\n" + "// end::infos[]\n"); + + fclose (file); + + return 1; +} + +/* + * 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 + * 0: 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 0; + + 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\n", + ESCAPE(_("Plugin")), + ESCAPE(_("Name")), + ESCAPE(_("Description")), + ESCAPE(_("Hashtable (input)")), + ESCAPE(_("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\n", + ESCAPE(PLUGIN(ptr_hook->plugin)), + ESCAPE(HOOK_INFO(ptr_hook, info_name)), + ESCAPE(TRANS(HOOK_INFO_HASHTABLE(ptr_hook, description))), + ESCAPE(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"); + + fclose (file); + + return 1; +} + +/* + * 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 + * 0: 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 0; + + string_fprintf ( + file, + "// tag::infolists[]\n" + "[width=\"100%\",cols=\"^1,^2,5,5,5\",options=\"header\"]\n" + "|===\n" + "| %s | %s | %s | %s | %s\n\n", + ESCAPE(_("Plugin")), + ESCAPE(_("Name")), + ESCAPE(_("Description")), + ESCAPE(_("Pointer")), + ESCAPE(_("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\n", + ESCAPE(PLUGIN(ptr_hook->plugin)), + ESCAPE(HOOK_INFOLIST(ptr_hook, infolist_name)), + ESCAPE(TRANS(HOOK_INFOLIST(ptr_hook, description))), + ESCAPE(TRANS_DEF(HOOK_INFOLIST(ptr_hook, pointer_description), "-")), + ESCAPE(TRANS_DEF(HOOK_INFOLIST(ptr_hook, args_description), "-"))); + } + + arraylist_free (list_hooks); + + string_fprintf (file, + "|===\n" + "// end::infolists[]\n"); + + fclose (file); + + return 1; +} + +/* + * 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)); + } + } + } + else + { + string_fprintf (file, "\n"); + } + 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 + * 0: 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 0; + + 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(_("Plugin")), + ESCAPE(_("Name")), + ESCAPE(_("Description")), + ESCAPE(_("Lists")), + ESCAPE(_("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(PLUGIN(ptr_hook->plugin))); + string_fprintf (file, + "| [[%s]]<<%s,%s>>\n", + ESCAPE(str_anchor), + ESCAPE(str_anchor), + ESCAPE(HOOK_HDATA(ptr_hook, hdata_name))); + string_fprintf (file, + "| %s\n", + ESCAPE(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"); + + fclose (file); + + return 1; +} + +/* + * 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 + * 0: 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 0; + + string_fprintf ( + file, + "// tag::completions[]\n" + "[width=\"100%\",cols=\"^1,^2,7\",options=\"header\"]\n" + "|===\n" + "| %s | %s | %s\n\n", + ESCAPE(_("Plugin")), + ESCAPE(_("Name")), + ESCAPE(_("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\n", + ESCAPE(PLUGIN(ptr_hook->plugin)), + ESCAPE(HOOK_COMPLETION(ptr_hook, completion_item)), + ESCAPE(TRANS(HOOK_COMPLETION(ptr_hook, description)))); + } + + arraylist_free (list_hooks); + + string_fprintf (file, + "|===\n" + "// end::completions[]\n"); + + fclose (file); + + return 1; +} + +/* + * Generates files with URL options. + * + * Returns: + * 1: OK + * 0: 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 0; + + string_fprintf ( + file, + "// tag::url_options[]\n" + "[width=\"100%\",cols=\"2,^1,7\",options=\"header\"]\n" + "|===\n" + "| %s | %s ^(1)^ | %s ^(2)^\n", + ESCAPE(_("Option")), + ESCAPE(_("Type")), + ESCAPE(_("Constants"))); + + for (i = 0; url_options[i].name; i++) + { + name = string_tolower (url_options[i].name); + string_fprintf ( + file, + "\n" + "| %s | %s |", + ESCAPE(name), + ESCAPE(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" + "|===\n" + "// end::url_options[]\n"); + + fclose (file); + + return 1; +} + +/* + * 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 + * 0: 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 0; + + string_fprintf ( + file, + "// tag::plugins_priority[]\n" + "[width=\"30%\",cols=\"1,3,2\",options=\"header\"]\n" + "|===\n" + "| %s | %s | %s\n", + ESCAPE(_("Rank")), + ESCAPE(_("Plugin")), + ESCAPE(_("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"); + + fclose (file); + + return 1; +} + +/* + * 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 + * 0: 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 0; + + string_fprintf ( + file, + "// tag::config_priority[]\n" + "[width=\"30%\",cols=\"1,3,2\",options=\"header\"]\n" + "|===\n" + "| %s | %s | %s\n", + ESCAPE(_("Rank")), + ESCAPE(_("File")), + ESCAPE(_("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"); + + fclose (file); + + return 1; +} + +/* + * 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, + 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: ERROR: plugins are not loaded\n"); + goto end; + } + + if (!dir_mkdir_parents (path, 0755)) + { + string_fprintf ( + stderr, + "doc generator: ERROR: failed to create directory \"%s\")", + 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`) + */ + localedir = getenv ("WEECHAT_DOCGEN_LOCALEDIR"); + if (localedir && localedir[0]) + bindtextdomain (PACKAGE, localedir); + + for (i = 0; locales[i]; i++) + { + setenv ("LANGUAGE", locales[i], 1); + setlocale (LC_ALL, locales[i]); + 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) + goto end; + num_files++; + } + } + + printf ("doc generator: build OK: %d files written in %s\n", + num_files, path); + + rc_doc_gen = 1; + +end: + for (i = 0; i < 32; i++) + { + if (string_escaped[i]) + free (string_escaped[i]); + } + return rc_doc_gen; +} diff --git a/src/core/wee-doc.h b/src/core/wee-doc.h new file mode 100644 index 000000000..9d3ca7788 --- /dev/null +++ b/src/core/wee-doc.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 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/>. + */ + +#ifndef WEECHAT_DOC_H +#define WEECHAT_DOC_H + +extern int doc_generate (const char *path); + +#endif /* WEECHAT_DOC_H */ diff --git a/src/core/wee-url.h b/src/core/wee-url.h index 0c62f7476..a70e29842 100644 --- a/src/core/wee-url.h +++ b/src/core/wee-url.h @@ -54,6 +54,7 @@ struct t_url_file FILE *stream; /* file stream */ }; +extern char *url_type_string[]; extern struct t_url_option url_options[]; extern int weeurl_download (const char *url, struct t_hashtable *options); diff --git a/src/core/weechat.c b/src/core/weechat.c index 982b41bea..eb24822c0 100644 --- a/src/core/weechat.c +++ b/src/core/weechat.c @@ -61,6 +61,7 @@ #include "wee-config.h" #include "wee-debug.h" #include "wee-dir.h" +#include "wee-doc.h" #include "wee-eval.h" #include "wee-hdata.h" #include "wee-hook.h" @@ -86,10 +87,11 @@ #include "../plugins/plugin.h" #include "../plugins/plugin-api.h" -/* debug command line options */ -#define OPTION_NO_DLCLOSE 1000 -#define OPTION_NO_GNUTLS 1001 -#define OPTION_NO_GCRYPT 1002 +/* some command line options */ +#define OPTION_DOCGEN 1000 +#define OPTION_NO_DLCLOSE 1001 +#define OPTION_NO_GNUTLS 1002 +#define OPTION_NO_GCRYPT 1003 int weechat_headless = 0; /* 1 if running headless (no GUI) */ int weechat_daemon = 0; /* 1 if daemonized (no foreground) */ @@ -118,6 +120,9 @@ int weechat_locale_ok = 0; /* is locale OK? */ char *weechat_local_charset = NULL; /* example: ISO-8859-1, UTF-8 */ int weechat_server_cmd_line = 0; /* at least 1 server on cmd line */ char *weechat_force_plugin_autoload = NULL; /* force load of plugins */ +int weechat_doc_gen = 0; /* doc generation */ +char *weechat_doc_gen_path = NULL; /* path for doc generation */ +int weechat_doc_gen_ok = 0; /* doc generation was successful? */ int weechat_plugin_no_dlclose = 0; /* remove calls to dlclose for libs */ /* (useful with valgrind) */ int weechat_no_gnutls = 0; /* remove init/deinit of gnutls */ @@ -168,7 +173,8 @@ weechat_display_usage () stdout, _(" -a, --no-connect disable auto-connect to servers at " "startup\n" - " -c, --colors display default colors in terminal\n" + " -c, --colors display default colors in terminal " + "and exit\n" " -d, --dir <path> force a single WeeChat home directory\n" " or 4 different directories separated " "by colons (in this order: config, data, cache, runtime)\n" @@ -177,8 +183,8 @@ weechat_display_usage () " -t, --temp-dir create a temporary WeeChat home " "directory and delete it on exit\n" " (incompatible with option \"-d\")\n" - " -h, --help display this help\n" - " -l, --license display WeeChat license\n" + " -h, --help display this help and exit\n" + " -l, --license display WeeChat license and exit\n" " -p, --no-plugin don't load any plugin at startup\n" " -P, --plugins <plugins> load only these plugins at startup\n" " (see /help weechat.plugin.autoload)\n" @@ -190,7 +196,7 @@ weechat_display_usage () " -s, --no-script don't load any script at startup\n" " --upgrade upgrade WeeChat using session files " "(see /help upgrade in WeeChat)\n" - " -v, --version display WeeChat version\n" + " -v, --version display WeeChat version and exit\n" " plugin:option option for plugin (see man weechat)\n")); string_fprintf (stdout, "\n"); @@ -200,6 +206,10 @@ weechat_display_usage () string_fprintf (stdout, _("Extra options in headless mode:\n")); string_fprintf ( stdout, + _(" --doc-gen <path> generate files to build " + "documentation and exit\n")); + string_fprintf ( + stdout, _(" --daemon run WeeChat as a daemon (fork, " "new process group, file descriptors closed);\n")); string_fprintf ( @@ -240,23 +250,24 @@ weechat_parse_args (int argc, char *argv[]) int opt; struct option long_options[] = { /* standard options */ - { "no-connect", no_argument, NULL, 'a' }, - { "colors", no_argument, NULL, 'c' }, - { "dir", required_argument, NULL, 'd' }, - { "temp-dir", no_argument, NULL, 't' }, - { "help", no_argument, NULL, 'h' }, - { "license", no_argument, NULL, 'l' }, - { "no-plugin", no_argument, NULL, 'p' }, - { "plugins", required_argument, NULL, 'P' }, - { "run-command", required_argument, NULL, 'r' }, - { "no-script", no_argument, NULL, 's' }, - { "upgrade", no_argument, NULL, 'u' }, - { "version", no_argument, NULL, 'v' }, + { "no-connect", no_argument, NULL, 'a' }, + { "colors", no_argument, NULL, 'c' }, + { "dir", required_argument, NULL, 'd' }, + { "temp-dir", no_argument, NULL, 't' }, + { "help", no_argument, NULL, 'h' }, + { "license", no_argument, NULL, 'l' }, + { "no-plugin", no_argument, NULL, 'p' }, + { "plugins", required_argument, NULL, 'P' }, + { "run-command", required_argument, NULL, 'r' }, + { "no-script", no_argument, NULL, 's' }, + { "upgrade", no_argument, NULL, 'u' }, + { "doc-gen", required_argument, NULL, OPTION_DOCGEN }, + { "version", no_argument, NULL, 'v' }, /* debug options */ { "no-dlclose", no_argument, NULL, OPTION_NO_DLCLOSE }, { "no-gnutls", no_argument, NULL, OPTION_NO_GNUTLS }, { "no-gcrypt", no_argument, NULL, OPTION_NO_GCRYPT }, - { NULL, 0, NULL, 0 }, + { NULL, 0, NULL, 0 }, }; weechat_argv0 = (argv[0]) ? strdup (argv[0]) : NULL; @@ -266,6 +277,7 @@ weechat_parse_args (int argc, char *argv[]) weechat_home_delete_on_exit = 0; weechat_server_cmd_line = 0; weechat_force_plugin_autoload = NULL; + weechat_doc_gen = 0; weechat_plugin_no_dlclose = 0; optind = 0; @@ -330,6 +342,13 @@ weechat_parse_args (int argc, char *argv[]) case 'u': /* --upgrade */ weechat_upgrading = 1; break; + case OPTION_DOCGEN: /* --doc-gen */ + if (weechat_headless) + { + weechat_doc_gen = 1; + weechat_doc_gen_path = strdup (optarg); + } + break; case 'v': /* -v / --version */ string_fprintf (stdout, version_get_version ()); fprintf (stdout, "\n"); @@ -572,9 +591,13 @@ weechat_shutdown (int return_code, int crash) free (weechat_force_plugin_autoload); if (weechat_startup_commands) weelist_free (weechat_startup_commands); + if (weechat_doc_gen_path) + free (weechat_doc_gen_path); if (crash) abort (); + else if (weechat_doc_gen) + exit ((weechat_doc_gen_ok) ? 0 : 1); else if (return_code >= 0) exit (return_code); } @@ -650,7 +673,8 @@ weechat_init (int argc, char *argv[], void (*gui_init_cb)()) else weechat_upgrading = 0; } - weechat_startup_message (); /* display WeeChat startup message */ + if (!weechat_doc_gen) + weechat_startup_message (); /* display WeeChat startup message */ gui_chat_print_lines_waiting_buffer (NULL); /* display lines waiting */ weechat_term_check (); /* warning about wrong $TERM */ weechat_locale_check (); /* warning about wrong locale */ @@ -662,6 +686,12 @@ weechat_init (int argc, char *argv[], void (*gui_init_cb)()) gui_layout_window_apply (gui_layout_current, -1); if (weechat_upgrading) upgrade_weechat_end (); /* remove .upgrade files + signal */ + + if (weechat_doc_gen) + { + weechat_doc_gen_ok = doc_generate (weechat_doc_gen_path); + weechat_quit = 1; + } } /* |