summaryrefslogtreecommitdiffstats
path: root/include/crm/common/output_internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/crm/common/output_internal.h')
-rw-r--r--include/crm/common/output_internal.h989
1 files changed, 989 insertions, 0 deletions
diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
new file mode 100644
index 0000000..e7b631e
--- /dev/null
+++ b/include/crm/common/output_internal.h
@@ -0,0 +1,989 @@
+/*
+ * Copyright 2019-2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#ifndef PCMK__OUTPUT_INTERNAL__H
+# define PCMK__OUTPUT_INTERNAL__H
+
+# include <stdbool.h>
+# include <stdint.h>
+# include <stdio.h>
+# include <libxml/tree.h>
+# include <libxml/HTMLtree.h>
+
+# include <glib.h>
+# include <crm/common/results.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ * \brief Formatted output for pacemaker tools
+ */
+
+#if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
+# define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
+#else
+# define PCMK__OUTPUT_ARGS(ARGS...)
+#endif
+
+typedef struct pcmk__output_s pcmk__output_t;
+
+/*!
+ * \internal
+ * \brief The type of a function that creates a ::pcmk__output_t.
+ *
+ * Instances of this type are passed to pcmk__register_format(), stored in an
+ * internal data structure, and later accessed by pcmk__output_new(). For
+ * examples, see pcmk__mk_xml_output() and pcmk__mk_text_output().
+ *
+ * \param[in] argv The list of command line arguments.
+ */
+typedef pcmk__output_t * (*pcmk__output_factory_t)(char **argv);
+
+/*!
+ * \internal
+ * \brief The type of a custom message formatting function.
+ *
+ * These functions are defined by various libraries to support formatting of
+ * types aside from the basic types provided by a ::pcmk__output_t.
+ *
+ * The meaning of the return value will be different for each message.
+ * In general, however, 0 should be returned on success and a positive value
+ * on error.
+ *
+ * \param[in,out] out Output object to use to display message
+ * \param[in,out] args Message-specific arguments needed
+ *
+ * \note These functions must not call va_start or va_end - that is done
+ * automatically before the custom formatting function is called.
+ */
+typedef int (*pcmk__message_fn_t)(pcmk__output_t *out, va_list args);
+
+/*!
+ * \internal
+ * \brief Internal type for tracking custom messages.
+ *
+ * Each library can register functions that format custom message types. These
+ * are commonly used to handle some library-specific type. Registration is
+ * done by first defining a table of ::pcmk__message_entry_t structures and
+ * then passing that table to pcmk__register_messages(). Separate handlers
+ * can be defined for the same message, but for different formats (xml vs.
+ * text). Unknown formats will be ignored.
+ *
+ * Additionally, a "default" value for fmt_table can be used. In this case,
+ * fn will be registered for all supported formats. It is also possible to
+ * register a default and then override that registration with a format-specific
+ * function if necessary.
+ *
+ * \note The ::pcmk__message_entry_t table is processed in one pass, in order,
+ * from top to bottom. This means later entries with the same message_id will
+ * override previous ones. Thus, any default entry must come before any
+ * format-specific entries for the same message_id.
+ */
+typedef struct pcmk__message_entry_s {
+ /*!
+ * \brief The message to be handled.
+ *
+ * This must be the same ID that is passed to the message function of
+ * a ::pcmk__output_t. Unknown message IDs will be ignored.
+ */
+ const char *message_id;
+
+ /*!
+ * \brief The format type this handler is for.
+ *
+ * This name must match the fmt_name of the currently active formatter in
+ * order for the registered function to be called. It is valid to have
+ * multiple entries for the same message_id but with different fmt_name
+ * values.
+ */
+ const char *fmt_name;
+
+ /*!
+ * \brief The function to be called for message_id given a match on
+ * fmt_name. See comments on ::pcmk__message_fn_t.
+ */
+ pcmk__message_fn_t fn;
+} pcmk__message_entry_t;
+
+/*!
+ * \internal
+ * \brief This structure contains everything needed to add support for a
+ * single output formatter to a command line program.
+ */
+typedef struct pcmk__supported_format_s {
+ /*!
+ * \brief The name of this output formatter, which should match the
+ * fmt_name parameter in some ::pcmk__output_t structure.
+ */
+ const char *name;
+
+ /*!
+ * \brief A function that creates a ::pcmk__output_t.
+ */
+ pcmk__output_factory_t create;
+
+ /*!
+ * \brief Format-specific command line options. This can be NULL if
+ * no command line options should be supported.
+ */
+ GOptionEntry *options;
+} pcmk__supported_format_t;
+
+/* The following three blocks need to be updated each time a new base formatter
+ * is added.
+ */
+
+extern GOptionEntry pcmk__html_output_entries[];
+extern GOptionEntry pcmk__log_output_entries[];
+extern GOptionEntry pcmk__none_output_entries[];
+extern GOptionEntry pcmk__text_output_entries[];
+extern GOptionEntry pcmk__xml_output_entries[];
+
+pcmk__output_t *pcmk__mk_html_output(char **argv);
+pcmk__output_t *pcmk__mk_log_output(char **argv);
+pcmk__output_t *pcmk__mk_none_output(char **argv);
+pcmk__output_t *pcmk__mk_text_output(char **argv);
+pcmk__output_t *pcmk__mk_xml_output(char **argv);
+
+#define PCMK__SUPPORTED_FORMAT_HTML { "html", pcmk__mk_html_output, pcmk__html_output_entries }
+#define PCMK__SUPPORTED_FORMAT_LOG { "log", pcmk__mk_log_output, pcmk__log_output_entries }
+#define PCMK__SUPPORTED_FORMAT_NONE { PCMK__VALUE_NONE, pcmk__mk_none_output, \
+ pcmk__none_output_entries }
+#define PCMK__SUPPORTED_FORMAT_TEXT { "text", pcmk__mk_text_output, pcmk__text_output_entries }
+#define PCMK__SUPPORTED_FORMAT_XML { "xml", pcmk__mk_xml_output, pcmk__xml_output_entries }
+
+/*!
+ * \brief This structure contains everything that makes up a single output
+ * formatter.
+ *
+ * Instances of this structure may be created by calling pcmk__output_new()
+ * with the name of the desired formatter. They should later be freed with
+ * pcmk__output_free().
+ */
+struct pcmk__output_s {
+ /*!
+ * \brief The name of this output formatter.
+ */
+ const char *fmt_name;
+
+ /*!
+ * \brief Should this formatter supress most output?
+ *
+ * \note This setting is not respected by all formatters. In general,
+ * machine-readable output formats will not support this while
+ * user-oriented formats will. Callers should use is_quiet()
+ * to test whether to print or not.
+ */
+ bool quiet;
+
+ /*!
+ * \brief A copy of the request that generated this output.
+ *
+ * In the case of command line usage, this would be the command line
+ * arguments. For other use cases, it could be different.
+ */
+ gchar *request;
+
+ /*!
+ * \brief Where output should be written.
+ *
+ * This could be a file handle, or stdout or stderr. This is really only
+ * useful internally.
+ */
+ FILE *dest;
+
+ /*!
+ * \brief Custom messages that are currently registered on this formatter.
+ *
+ * Keys are the string message IDs, values are ::pcmk__message_fn_t function
+ * pointers.
+ */
+ GHashTable *messages;
+
+ /*!
+ * \brief Implementation-specific private data.
+ *
+ * Each individual formatter may have some private data useful in its
+ * implementation. This points to that data. Callers should not rely on
+ * its contents or structure.
+ */
+ void *priv;
+
+ /*!
+ * \internal
+ * \brief Take whatever actions are necessary to prepare out for use. This is
+ * called by pcmk__output_new(). End users should not need to call this.
+ *
+ * \note For formatted output implementers - This function should be written in
+ * such a way that it can be called repeatedly on an already initialized
+ * object without causing problems, or on a previously finished object
+ * without crashing.
+ *
+ * \param[in,out] out The output functions structure.
+ *
+ * \return true on success, false on error.
+ */
+ bool (*init) (pcmk__output_t *out);
+
+ /*!
+ * \internal
+ * \brief Free the private formatter-specific data.
+ *
+ * This is called from pcmk__output_free() and does not typically need to be
+ * called directly.
+ *
+ * \param[in,out] out The output functions structure.
+ */
+ void (*free_priv) (pcmk__output_t *out);
+
+ /*!
+ * \internal
+ * \brief Take whatever actions are necessary to end formatted output.
+ *
+ * This could include flushing output to a file, but does not include freeing
+ * anything. The finish method can potentially be fairly complicated, adding
+ * additional information to the internal data structures or doing whatever
+ * else. It is therefore suggested that finish only be called once.
+ *
+ * \note The print parameter will only affect those formatters that do all
+ * their output at the end. Console-oriented formatters typically print
+ * a line at a time as they go, so this parameter will not affect them.
+ * Structured formatters will honor it, however.
+ *
+ * \note The copy_dest parameter does not apply to all formatters. Console-
+ * oriented formatters do not build up a structure as they go, and thus
+ * do not have anything to return. Structured formatters will honor it,
+ * however. Note that each type of formatter will return a different
+ * type of value in this parameter. To use this parameter, call this
+ * function like so:
+ *
+ * \code
+ * xmlNode *dest = NULL;
+ * out->finish(out, exit_code, false, (void **) &dest);
+ * \endcode
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] exit_status The exit value of the whole program.
+ * \param[in] print Whether this function should write any output.
+ * \param[out] copy_dest A destination to store a copy of the internal
+ * data structure for this output, or NULL if no
+ * copy is required. The caller should free this
+ * memory when done with it.
+ */
+ void (*finish) (pcmk__output_t *out, crm_exit_t exit_status, bool print,
+ void **copy_dest);
+
+ /*!
+ * \internal
+ * \brief Finalize output and then immediately set back up to start a new set
+ * of output.
+ *
+ * This is conceptually the same as calling finish and then init, though in
+ * practice more be happening behind the scenes.
+ *
+ * \note This function differs from finish in that no exit_status is added.
+ * The idea is that the program is not shutting down, so there is not
+ * yet a final exit code. Call finish on the last time through if this
+ * is needed.
+ *
+ * \param[in,out] out The output functions structure.
+ */
+ void (*reset) (pcmk__output_t *out);
+
+ /*!
+ * \internal
+ * \brief Register a custom message.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] message_id The name of the message to register. This name
+ * will be used as the message_id parameter to the
+ * message function in order to call the custom
+ * format function.
+ * \param[in] fn The custom format function to call for message_id.
+ */
+ void (*register_message) (pcmk__output_t *out, const char *message_id,
+ pcmk__message_fn_t fn);
+
+ /*!
+ * \internal
+ * \brief Call a previously registered custom message.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] message_id The name of the message to call. This name must
+ * be the same as the message_id parameter of some
+ * previous call to register_message.
+ * \param[in] ... Arguments to be passed to the registered function.
+ *
+ * \return A standard Pacemaker return code. Generally: 0 if a function was
+ * registered for the message, that function was called, and returned
+ * successfully; EINVAL if no function was registered; or pcmk_rc_no_output
+ * if a function was called but produced no output.
+ */
+ int (*message) (pcmk__output_t *out, const char *message_id, ...);
+
+ /*!
+ * \internal
+ * \brief Format the output of a completed subprocess.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] exit_status The exit value of the subprocess.
+ * \param[in] proc_stdout stdout from the completed subprocess.
+ * \param[in] proc_stderr stderr from the completed subprocess.
+ */
+ void (*subprocess_output) (pcmk__output_t *out, int exit_status,
+ const char *proc_stdout, const char *proc_stderr);
+
+ /*!
+ * \internal
+ * \brief Format version information. This is useful for the --version
+ * argument of command line tools.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] extended Add additional version information.
+ */
+ void (*version) (pcmk__output_t *out, bool extended);
+
+ /*!
+ * \internal
+ * \brief Format an informational message that should be shown to
+ * to an interactive user. Not all formatters will do this.
+ *
+ * \note A newline will automatically be added to the end of the format
+ * string, so callers should not include a newline.
+ *
+ * \note It is possible for a formatter that supports this method to
+ * still not print anything out if is_quiet returns true.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] buf The message to be printed.
+ * \param[in] ... Arguments to be formatted.
+ *
+ * \return A standard Pacemaker return code. Generally: pcmk_rc_ok
+ * if output was produced and pcmk_rc_no_output if it was not.
+ * As not all formatters implement this function, those that
+ * do not will always just return pcmk_rc_no_output.
+ */
+ int (*info) (pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3);
+
+ /*!
+ * \internal
+ * \brief Like \p info() but for messages that should appear only
+ * transiently. Not all formatters will do this.
+ *
+ * The originally envisioned use case is for console output, where a
+ * transient status-related message may be quickly overwritten by a refresh.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] format The format string of the message to be printed.
+ * \param[in] ... Arguments to be formatted.
+ *
+ * \return A standard Pacemaker return code. Generally: \p pcmk_rc_ok if
+ * output was produced and \p pcmk_rc_no_output if it was not. As
+ * not all formatters implement this function, those that do not
+ * will always just return \p pcmk_rc_no_output.
+ */
+ int (*transient) (pcmk__output_t *out, const char *format, ...)
+ G_GNUC_PRINTF(2, 3);
+
+ /*!
+ * \internal
+ * \brief Format an error message that should be shown to an interactive
+ * user. Not all formatters will do this.
+ *
+ * \note A newline will automatically be added to the end of the format
+ * string, so callers should not include a newline.
+ *
+ * \note Formatters that support this method should always generate output,
+ * even if is_quiet returns true.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] buf The message to be printed.
+ * \param[in] ... Arguments to be formatted.
+ */
+ void (*err) (pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3);
+
+ /*!
+ * \internal
+ * \brief Format already formatted XML.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] name A name to associate with the XML.
+ * \param[in] buf The XML in a string.
+ */
+ void (*output_xml) (pcmk__output_t *out, const char *name, const char *buf);
+
+ /*!
+ * \internal
+ * \brief Start a new list of items.
+ *
+ * \note For text output, this corresponds to another level of indentation. For
+ * XML output, this corresponds to wrapping any following output in another
+ * layer of tags.
+ *
+ * \note If singular_noun and plural_noun are non-NULL, calling end_list will
+ * result in a summary being added.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] singular_noun When outputting the summary for a list with
+ * one item, the noun to use.
+ * \param[in] plural_noun When outputting the summary for a list with
+ * more than one item, the noun to use.
+ * \param[in] format The format string.
+ * \param[in] ... Arguments to be formatted.
+ */
+ void (*begin_list) (pcmk__output_t *out, const char *singular_noun,
+ const char *plural_noun, const char *format, ...)
+ G_GNUC_PRINTF(4, 5);
+
+ /*!
+ * \internal
+ * \brief Format a single item in a list.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] name A name to associate with this item.
+ * \param[in] format The format string.
+ * \param[in] ... Arguments to be formatted.
+ */
+ void (*list_item) (pcmk__output_t *out, const char *name, const char *format, ...)
+ G_GNUC_PRINTF(3, 4);
+
+ /*!
+ * \internal
+ * \brief Increment the internal counter of the current list's length.
+ *
+ * Typically, this counter is maintained behind the scenes as a side effect
+ * of calling list_item(). However, custom functions that maintain lists
+ * some other way will need to manage this counter manually. This is
+ * useful for implementing custom message functions and should not be
+ * needed otherwise.
+ *
+ * \param[in,out] out The output functions structure.
+ */
+ void (*increment_list) (pcmk__output_t *out);
+
+ /*!
+ * \internal
+ * \brief Conclude a list.
+ *
+ * \note If begin_list was called with non-NULL for both the singular_noun
+ * and plural_noun arguments, this function will output a summary.
+ * Otherwise, no summary will be added.
+ *
+ * \param[in,out] out The output functions structure.
+ */
+ void (*end_list) (pcmk__output_t *out);
+
+ /*!
+ * \internal
+ * \brief Should anything be printed to the user?
+ *
+ * \note This takes into account both the \p quiet value as well as the
+ * current formatter.
+ *
+ * \param[in,out] out The output functions structure.
+ *
+ * \return true if output should be supressed, false otherwise.
+ */
+ bool (*is_quiet) (pcmk__output_t *out);
+
+ /*!
+ * \internal
+ * \brief Output a spacer. Not all formatters will do this.
+ *
+ * \param[in,out] out The output functions structure.
+ */
+ void (*spacer) (pcmk__output_t *out);
+
+ /*!
+ * \internal
+ * \brief Output a progress indicator. This is likely only useful for
+ * plain text, console based formatters.
+ *
+ * \param[in,out] out The output functions structure
+ * \param[in] end If true, output a newline afterwards (this should
+ * only be used the last time this function is called)
+ *
+ */
+ void (*progress) (pcmk__output_t *out, bool end);
+
+ /*!
+ * \internal
+ * \brief Prompt the user for input. Not all formatters will do this.
+ *
+ * \note This function is part of pcmk__output_t, but unlike all other
+ * function it does not take that as an argument. In general, a
+ * prompt will go directly to the screen and therefore bypass any
+ * need to use the formatted output code to decide where and how
+ * to display.
+ *
+ * \param[in] prompt The prompt to display. This is required.
+ * \param[in] echo If true, echo the user's input to the screen. Set
+ * to false for password entry.
+ * \param[out] dest Where to store the user's response. This is
+ * required.
+ */
+ void (*prompt) (const char *prompt, bool echo, char **dest);
+};
+
+/*!
+ * \internal
+ * \brief Call a formatting function for a previously registered message.
+ *
+ * \note This function is for implementing custom formatters. It should not
+ * be called directly. Instead, call out->message.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] message_id The message to be handled. Unknown messages
+ * will be ignored.
+ * \param[in] ... Arguments to be passed to the registered function.
+ */
+int
+pcmk__call_message(pcmk__output_t *out, const char *message_id, ...);
+
+/*!
+ * \internal
+ * \brief Free a ::pcmk__output_t structure that was previously created by
+ * pcmk__output_new().
+ *
+ * \note While the create and finish functions are designed in such a way that
+ * they can be called repeatedly, this function will completely free the
+ * memory of the object. Once this function has been called, producing
+ * more output requires starting over from pcmk__output_new().
+ *
+ * \param[in,out] out The output structure.
+ */
+void pcmk__output_free(pcmk__output_t *out);
+
+/*!
+ * \internal
+ * \brief Create a new ::pcmk__output_t structure.
+ *
+ * This also registers message functions from libcrmcommon.
+ *
+ * \param[in,out] out The destination of the new ::pcmk__output_t.
+ * \param[in] fmt_name How should output be formatted?
+ * \param[in] filename Where should formatted output be written to? This
+ * can be a filename (which will be overwritten if it
+ * already exists), or NULL or "-" for stdout. For no
+ * output, pass a filename of "/dev/null".
+ * \param[in] argv The list of command line arguments.
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk__output_new(pcmk__output_t **out, const char *fmt_name,
+ const char *filename, char **argv);
+
+/*!
+ * \internal
+ * \brief Register a new output formatter, making it available for use
+ * the same as a base formatter.
+ *
+ * \param[in,out] group A ::GOptionGroup that formatted output related command
+ * line arguments should be added to. This can be NULL
+ * for use outside of command line programs.
+ * \param[in] name The name of the format. This will be used to select a
+ * format from command line options and for displaying help.
+ * \param[in] create A function that creates a ::pcmk__output_t.
+ * \param[in] options Format-specific command line options. These will be
+ * added to the context. This argument can also be NULL.
+ *
+ * \return Standard Pacemaker return code
+ */
+int
+pcmk__register_format(GOptionGroup *group, const char *name,
+ pcmk__output_factory_t create,
+ const GOptionEntry *options);
+
+/*!
+ * \internal
+ * \brief Register an entire table of output formatters at once.
+ *
+ * \param[in,out] group A ::GOptionGroup that formatted output related command
+ * line arguments should be added to. This can be NULL
+ * for use outside of command line programs.
+ * \param[in] table An array of ::pcmk__supported_format_t which should
+ * all be registered. This array must be NULL-terminated.
+ *
+ */
+void
+pcmk__register_formats(GOptionGroup *group,
+ const pcmk__supported_format_t *table);
+
+/*!
+ * \internal
+ * \brief Unregister a previously registered table of custom formatting
+ * functions and destroy the internal data structures associated with them.
+ */
+void
+pcmk__unregister_formats(void);
+
+/*!
+ * \internal
+ * \brief Register a function to handle a custom message.
+ *
+ * \note This function is for implementing custom formatters. It should not
+ * be called directly. Instead, call out->register_message.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] message_id The message to be handled.
+ * \param[in] fn The custom format function to call for message_id.
+ */
+void
+pcmk__register_message(pcmk__output_t *out, const char *message_id,
+ pcmk__message_fn_t fn);
+
+/*!
+ * \internal
+ * \brief Register an entire table of custom formatting functions at once.
+ *
+ * This table can contain multiple formatting functions for the same message ID
+ * if they are for different format types.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] table An array of ::pcmk__message_entry_t values which should
+ * all be registered. This array must be NULL-terminated.
+ */
+void
+pcmk__register_messages(pcmk__output_t *out,
+ const pcmk__message_entry_t *table);
+
+/* Functions that are useful for implementing custom message formatters */
+
+/*!
+ * \internal
+ * \brief A printf-like function.
+ *
+ * This function writes to out->dest and indents the text to the current level
+ * of the text formatter's nesting. This function should be used when implementing
+ * custom message functions for the text output format. It should not be used
+ * for any other purpose.
+ *
+ * Typically, this function should be used instead of printf.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] format The format string.
+ * \param[in] ... Arguments to be passed to the format string.
+ */
+void
+pcmk__indented_printf(pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3);
+
+/*!
+ * \internal
+ * \brief A vprintf-like function.
+ *
+ * This function is like pcmk__indented_printf(), except it takes a va_list instead
+ * of a list of arguments. This function should be used when implementing custom
+ * functions for the text output format. It should not be used for any other purpose.
+ *
+ * Typically, this function should be used instead of vprintf.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] format The format string.
+ * \param[in] args A list of arguments to apply to the format string.
+ */
+void
+pcmk__indented_vprintf(pcmk__output_t *out, const char *format, va_list args) G_GNUC_PRINTF(2, 0);
+
+
+/*!
+ * \internal
+ * \brief A printf-like function.
+ *
+ * This function writes to out->dest without indenting the text. This function
+ * should be used when implementing custom message functions for the text output
+ * format. It should not be used for any other purpose.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] format The format string.
+ * \param[in] ... Arguments to be passed to the format string.
+ */
+void
+pcmk__formatted_printf(pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3);
+
+/*!
+ * \internal
+ * \brief A vprintf-like function.
+ *
+ * This function is like pcmk__formatted_printf(), except it takes a va_list instead
+ * of a list of arguments. This function should be used when implementing custom
+ * message functions for the text output format. It should not be used for any
+ * other purpose.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] format The format string.
+ * \param[in] args A list of arguments to apply to the format string.
+ */
+void
+pcmk__formatted_vprintf(pcmk__output_t *out, const char *format, va_list args) G_GNUC_PRINTF(2, 0);
+
+/*!
+ * \internal
+ * \brief Prompt the user for input.
+ *
+ * \param[in] prompt The prompt to display
+ * \param[in] echo If true, echo the user's input to the screen. Set
+ * to false for password entry.
+ * \param[out] dest Where to store the user's response.
+ */
+void
+pcmk__text_prompt(const char *prompt, bool echo, char **dest);
+
+/*!
+ * \internal
+ * \brief Get the log level used by the formatted output logger
+ *
+ * \param[in] out Output object
+ *
+ * \return Log level used by \p out
+ */
+uint8_t
+pcmk__output_get_log_level(const pcmk__output_t *out);
+
+/*!
+ * \internal
+ * \brief Set the log level used by the formatted output logger.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] log_level The log level constant (LOG_INFO, LOG_ERR, etc.)
+ * to use.
+ *
+ * \note By default, LOG_INFO is used.
+ * \note Almost all formatted output messages will respect this setting.
+ * However, out->err will always log at LOG_ERR.
+ */
+void
+pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level);
+
+/*!
+ * \internal
+ * \brief Create and return a new XML node with the given name, as a child of the
+ * current list parent. The new node is then added as the new list parent,
+ * meaning all subsequent nodes will be its children. This is used when
+ * implementing custom functions.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] name The name of the node to be created.
+ * \param[in] ... Name/value pairs to set as XML properties.
+ */
+xmlNodePtr
+pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name, ...)
+G_GNUC_NULL_TERMINATED;
+
+/*!
+ * \internal
+ * \brief Add a copy of the given node as a child of the current list parent.
+ * This is used when implementing custom message functions.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] node An XML node to copy as a child.
+ */
+void
+pcmk__output_xml_add_node_copy(pcmk__output_t *out, xmlNodePtr node);
+
+/*!
+ * \internal
+ * \brief Create and return a new XML node with the given name, as a child of the
+ * current list parent. This is used when implementing custom functions.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] name The name of the node to be created.
+ * \param[in] ... Name/value pairs to set as XML properties.
+ */
+xmlNodePtr
+pcmk__output_create_xml_node(pcmk__output_t *out, const char *name, ...)
+G_GNUC_NULL_TERMINATED;
+
+/*!
+ * \internal
+ * \brief Like pcmk__output_create_xml_node(), but add the given text content to the
+ * new node.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] name The name of the node to be created.
+ * \param[in] content The text content of the node.
+ */
+xmlNodePtr
+pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content);
+
+/*!
+ * \internal
+ * \brief Push a parent XML node onto the stack. This is used when implementing
+ * custom message functions.
+ *
+ * The XML output formatter maintains an internal stack to keep track of which nodes
+ * are parents in order to build up the tree structure. This function can be used
+ * to temporarily push a new node onto the stack. After calling this function, any
+ * other formatting functions will have their nodes added as children of this new
+ * parent.
+ *
+ * \param[in,out] out The output functions structure
+ * \param[in] parent XML node to add
+ */
+void
+pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent);
+
+/*!
+ * \internal
+ * \brief Pop a parent XML node onto the stack. This is used when implementing
+ * custom message functions.
+ *
+ * This function removes a parent node from the stack. See pcmk__xml_push_parent()
+ * for more details.
+ *
+ * \note Little checking is done with this function. Be sure you only pop parents
+ * that were previously pushed. In general, it is best to keep the code between
+ * push and pop simple.
+ *
+ * \param[in,out] out The output functions structure.
+ */
+void
+pcmk__output_xml_pop_parent(pcmk__output_t *out);
+
+/*!
+ * \internal
+ * \brief Peek a parent XML node onto the stack. This is used when implementing
+ * custom message functions.
+ *
+ * This function peeks a parent node on stack. See pcmk__xml_push_parent()
+ * for more details. It has no side-effect and can be called for an empty stack.
+ *
+ * \note Little checking is done with this function.
+ *
+ * \param[in,out] out The output functions structure.
+ *
+ * \return NULL if stack is empty, otherwise the parent of the stack.
+ */
+xmlNodePtr
+pcmk__output_xml_peek_parent(pcmk__output_t *out);
+
+/*!
+ * \internal
+ * \brief Create a new XML node consisting of the provided text inside an HTML
+ * element node of the given name.
+ *
+ * \param[in,out] out The output functions structure.
+ * \param[in] element_name The name of the new HTML element.
+ * \param[in] id The CSS ID selector to apply to this element.
+ * If NULL, no ID is added.
+ * \param[in] class_name The CSS class selector to apply to this element.
+ * If NULL, no class is added.
+ * \param[in] text The text content of the node.
+ */
+xmlNodePtr
+pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, const char *id,
+ const char *class_name, const char *text);
+
+/*!
+ * \internal
+ * \brief Add an HTML tag to the <head> section.
+ *
+ * The arguments after name are a NULL-terminated list of keys and values,
+ * all of which will be added as attributes to the given tag. For instance,
+ * the following code would generate the tag "<meta http-equiv='refresh' content='19'>":
+ *
+ * \code
+ * pcmk__html_add_header("meta", "http-equiv", "refresh", "content", "19", NULL);
+ * \endcode
+ *
+ * \param[in] name The HTML tag for the new node.
+ * \param[in] ... A NULL-terminated key/value list of attributes.
+ */
+void
+pcmk__html_add_header(const char *name, ...)
+G_GNUC_NULL_TERMINATED;
+
+/*!
+ * \internal
+ * \brief Handle end-of-program error reporting
+ *
+ * \param[in,out] error A GError object potentially containing some error.
+ * If NULL, do nothing.
+ * \param[in,out] out The output functions structure. If NULL, any errors
+ * will simply be printed to stderr.
+ */
+void pcmk__output_and_clear_error(GError **error, pcmk__output_t *out);
+
+int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml);
+void pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml);
+int pcmk__log_output_new(pcmk__output_t **out);
+int pcmk__text_output_new(pcmk__output_t **out, const char *filename);
+
+/*!
+ * \internal
+ * \brief Select an updated return code for an operation on a \p pcmk__output_t
+ *
+ * This function helps to keep an up-to-date record of the most relevant return
+ * code from a series of operations on a \p pcmk__output_t object. For example,
+ * suppose the object has already produced some output, and we've saved a
+ * \p pcmk_rc_ok return code. A new operation did not produce any output and
+ * returned \p pcmk_rc_no_output. We can ignore the new \p pcmk_rc_no_output
+ * return code and keep the previous \p pcmk_rc_ok return code.
+ *
+ * It prioritizes return codes as follows (from highest to lowest priority):
+ * 1. Other return codes (unexpected errors)
+ * 2. \p pcmk_rc_ok
+ * 3. \p pcmk_rc_no_output
+ *
+ * \param[in] old_rc Saved return code from \p pcmk__output_t operations
+ * \param[in] new_rc New return code from a \p pcmk__output_t operation
+ *
+ * \retval \p old_rc \p new_rc is \p pcmk_rc_no_output, or \p new_rc is
+ * \p pcmk_rc_ok and \p old_rc is not \p pcmk_rc_no_output
+ * \retval \p new_rc Otherwise
+ */
+static inline int
+pcmk__output_select_rc(int old_rc, int new_rc)
+{
+ switch (new_rc) {
+ case pcmk_rc_no_output:
+ return old_rc;
+ case pcmk_rc_ok:
+ switch (old_rc) {
+ case pcmk_rc_no_output:
+ return new_rc;
+ default:
+ return old_rc;
+ }
+ default:
+ return new_rc;
+ }
+}
+
+#if defined(PCMK__UNIT_TESTING)
+/* If we are building libcrmcommon_test.a, add this accessor function so we can
+ * inspect the internal formatters hash table.
+ */
+GHashTable *pcmk__output_formatters(void);
+#endif
+
+#define PCMK__OUTPUT_SPACER_IF(out_obj, cond) \
+ if (cond) { \
+ out->spacer(out); \
+ }
+
+#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...) \
+ if (retcode == pcmk_rc_no_output) { \
+ PCMK__OUTPUT_SPACER_IF(out_obj, cond); \
+ retcode = pcmk_rc_ok; \
+ out_obj->begin_list(out_obj, NULL, NULL, title); \
+ }
+
+#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode) \
+ if (retcode == pcmk_rc_ok) { \
+ out_obj->end_list(out_obj); \
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif