LCOV - code coverage report
Current view: top level - common - output_text.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 247 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 26 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2019-2024 the Pacemaker project contributors
       3             :  *
       4             :  * The version control history for this file may have further details.
       5             :  *
       6             :  * This source code is licensed under the GNU Lesser General Public License
       7             :  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
       8             :  */
       9             : 
      10             : #include <crm_internal.h>
      11             : #include <crm/common/cmdline_internal.h>
      12             : 
      13             : #include <stdarg.h>
      14             : #include <stdlib.h>
      15             : #include <glib.h>
      16             : #include <termios.h>
      17             : 
      18             : #include "crmcommon_private.h"
      19             : 
      20             : // @COMPAT Drop at 3.0.0
      21             : static gboolean fancy = FALSE;
      22             : 
      23             : // @COMPAT Drop at 3.0.0
      24             : GOptionEntry pcmk__text_output_entries[] = {
      25             :     { "text-fancy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &fancy,
      26             :       "Use more highly formatted output (requires --output-as=text)",
      27             :       NULL },
      28             : 
      29             :     { NULL }
      30             : };
      31             : 
      32             : typedef struct text_list_data_s {
      33             :     unsigned int len;
      34             :     char *singular_noun;
      35             :     char *plural_noun;
      36             : } text_list_data_t;
      37             : 
      38             : typedef struct private_data_s {
      39             :     GQueue *parent_q;
      40             :     bool fancy;
      41             : } private_data_t;
      42             : 
      43             : static void
      44           0 : free_list_data(gpointer data) {
      45           0 :     text_list_data_t *list_data = data;
      46             : 
      47           0 :     free(list_data->singular_noun);
      48           0 :     free(list_data->plural_noun);
      49           0 : }
      50             : 
      51             : static void
      52           0 : text_free_priv(pcmk__output_t *out) {
      53           0 :     private_data_t *priv = NULL;
      54             : 
      55           0 :     if (out == NULL || out->priv == NULL) {
      56           0 :         return;
      57             :     }
      58             : 
      59           0 :     priv = out->priv;
      60             : 
      61           0 :     g_queue_free_full(priv->parent_q, free_list_data);
      62           0 :     free(priv);
      63           0 :     out->priv = NULL;
      64             : }
      65             : 
      66             : static bool
      67           0 : text_init(pcmk__output_t *out) {
      68           0 :     private_data_t *priv = NULL;
      69             : 
      70           0 :     CRM_ASSERT(out != NULL);
      71             : 
      72             :     /* If text_init was previously called on this output struct, just return. */
      73           0 :     if (out->priv != NULL) {
      74           0 :         return true;
      75             :     }
      76             : 
      77           0 :     out->priv = calloc(1, sizeof(private_data_t));
      78           0 :     if (out->priv == NULL) {
      79           0 :         return false;
      80             :     }
      81             : 
      82           0 :     priv = out->priv;
      83           0 :     priv->parent_q = g_queue_new();
      84           0 :     return true;
      85             : }
      86             : 
      87             : static void
      88           0 : text_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
      89           0 :     CRM_ASSERT(out != NULL && out->dest != NULL);
      90           0 :     fflush(out->dest);
      91           0 : }
      92             : 
      93             : static void
      94           0 : text_reset(pcmk__output_t *out) {
      95           0 :     private_data_t *priv = NULL;
      96           0 :     bool old_fancy = false;
      97             : 
      98           0 :     CRM_ASSERT(out != NULL);
      99             : 
     100           0 :     if (out->dest != stdout) {
     101           0 :         out->dest = freopen(NULL, "w", out->dest);
     102             :     }
     103             : 
     104           0 :     CRM_ASSERT(out->dest != NULL);
     105             : 
     106             :     // Save priv->fancy before free/init sequence overwrites it
     107           0 :     priv = out->priv;
     108           0 :     old_fancy = priv->fancy;
     109             : 
     110           0 :     text_free_priv(out);
     111           0 :     text_init(out);
     112             : 
     113           0 :     priv = out->priv;
     114           0 :     priv->fancy = old_fancy;
     115           0 : }
     116             : 
     117             : static void
     118           0 : text_subprocess_output(pcmk__output_t *out, int exit_status,
     119             :                        const char *proc_stdout, const char *proc_stderr) {
     120           0 :     CRM_ASSERT(out != NULL);
     121             : 
     122           0 :     if (proc_stdout != NULL) {
     123           0 :         fprintf(out->dest, "%s\n", proc_stdout);
     124             :     }
     125             : 
     126           0 :     if (proc_stderr != NULL) {
     127           0 :         fprintf(out->dest, "%s\n", proc_stderr);
     128             :     }
     129           0 : }
     130             : 
     131             : static void
     132           0 : text_version(pcmk__output_t *out, bool extended) {
     133           0 :     CRM_ASSERT(out != NULL && out->dest != NULL);
     134             : 
     135           0 :     if (extended) {
     136           0 :         fprintf(out->dest, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
     137             :     } else {
     138           0 :         fprintf(out->dest, "Pacemaker %s\n", PACEMAKER_VERSION);
     139           0 :         fprintf(out->dest, "Written by Andrew Beekhof and "
     140             :                            "the Pacemaker project contributors\n");
     141             :     }
     142           0 : }
     143             : 
     144             : G_GNUC_PRINTF(2, 3)
     145             : static void
     146           0 : text_err(pcmk__output_t *out, const char *format, ...) {
     147             :     va_list ap;
     148           0 :     int len = 0;
     149             : 
     150           0 :     CRM_ASSERT(out != NULL);
     151             : 
     152           0 :     va_start(ap, format);
     153             : 
     154             :     /* Informational output does not get indented, to separate it from other
     155             :      * potentially indented list output.
     156             :      */
     157           0 :     len = vfprintf(stderr, format, ap);
     158           0 :     CRM_ASSERT(len >= 0);
     159           0 :     va_end(ap);
     160             : 
     161             :     /* Add a newline. */
     162           0 :     fprintf(stderr, "\n");
     163           0 : }
     164             : 
     165             : G_GNUC_PRINTF(2, 3)
     166             : static int
     167           0 : text_info(pcmk__output_t *out, const char *format, ...) {
     168             :     va_list ap;
     169           0 :     int len = 0;
     170             : 
     171           0 :     CRM_ASSERT(out != NULL);
     172             : 
     173           0 :     if (out->is_quiet(out)) {
     174           0 :         return pcmk_rc_no_output;
     175             :     }
     176             : 
     177           0 :     va_start(ap, format);
     178             : 
     179             :     /* Informational output does not get indented, to separate it from other
     180             :      * potentially indented list output.
     181             :      */
     182           0 :     len = vfprintf(out->dest, format, ap);
     183           0 :     CRM_ASSERT(len >= 0);
     184           0 :     va_end(ap);
     185             : 
     186             :     /* Add a newline. */
     187           0 :     fprintf(out->dest, "\n");
     188           0 :     return pcmk_rc_ok;
     189             : }
     190             : 
     191             : G_GNUC_PRINTF(2, 3)
     192             : static int
     193           0 : text_transient(pcmk__output_t *out, const char *format, ...)
     194             : {
     195           0 :     return pcmk_rc_no_output;
     196             : }
     197             : 
     198             : static void
     199           0 : text_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
     200           0 :     CRM_ASSERT(out != NULL);
     201           0 :     pcmk__indented_printf(out, "%s", buf);
     202           0 : }
     203             : 
     204             : G_GNUC_PRINTF(4, 5)
     205             : static void
     206           0 : text_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
     207             :                 const char *format, ...) {
     208           0 :     private_data_t *priv = NULL;
     209           0 :     text_list_data_t *new_list = NULL;
     210             :     va_list ap;
     211             : 
     212           0 :     CRM_ASSERT(out != NULL && out->priv != NULL);
     213           0 :     priv = out->priv;
     214             : 
     215           0 :     va_start(ap, format);
     216             : 
     217           0 :     if ((fancy || priv->fancy) && (format != NULL)) {
     218           0 :         pcmk__indented_vprintf(out, format, ap);
     219           0 :         fprintf(out->dest, ":\n");
     220             :     }
     221             : 
     222           0 :     va_end(ap);
     223             : 
     224           0 :     new_list = pcmk__assert_alloc(1, sizeof(text_list_data_t));
     225           0 :     new_list->len = 0;
     226           0 :     new_list->singular_noun = pcmk__str_copy(singular_noun);
     227           0 :     new_list->plural_noun = pcmk__str_copy(plural_noun);
     228             : 
     229           0 :     g_queue_push_tail(priv->parent_q, new_list);
     230           0 : }
     231             : 
     232             : G_GNUC_PRINTF(3, 4)
     233             : static void
     234           0 : text_list_item(pcmk__output_t *out, const char *id, const char *format, ...) {
     235           0 :     private_data_t *priv = NULL;
     236             :     va_list ap;
     237             : 
     238           0 :     CRM_ASSERT(out != NULL);
     239             : 
     240           0 :     priv = out->priv;
     241           0 :     va_start(ap, format);
     242             : 
     243           0 :     if (fancy || priv->fancy) {
     244           0 :         if (id != NULL) {
     245             :             /* Not really a good way to do this all in one call, so make it two.
     246             :              * The first handles the indentation and list styling.  The second
     247             :              * just prints right after that one.
     248             :              */
     249           0 :             pcmk__indented_printf(out, "%s: ", id);
     250           0 :             vfprintf(out->dest, format, ap);
     251             :         } else {
     252           0 :             pcmk__indented_vprintf(out, format, ap);
     253             :         }
     254             :     } else {
     255           0 :         pcmk__indented_vprintf(out, format, ap);
     256             :     }
     257             : 
     258           0 :     fputc('\n', out->dest);
     259           0 :     fflush(out->dest);
     260           0 :     va_end(ap);
     261             : 
     262           0 :     out->increment_list(out);
     263           0 : }
     264             : 
     265             : static void
     266           0 : text_increment_list(pcmk__output_t *out) {
     267           0 :     private_data_t *priv = NULL;
     268             :     gpointer tail;
     269             : 
     270           0 :     CRM_ASSERT(out != NULL && out->priv != NULL);
     271           0 :     priv = out->priv;
     272             : 
     273           0 :     tail = g_queue_peek_tail(priv->parent_q);
     274           0 :     CRM_ASSERT(tail != NULL);
     275           0 :     ((text_list_data_t *) tail)->len++;
     276           0 : }
     277             : 
     278             : static void
     279           0 : text_end_list(pcmk__output_t *out) {
     280           0 :     private_data_t *priv = NULL;
     281           0 :     text_list_data_t *node = NULL;
     282             : 
     283           0 :     CRM_ASSERT(out != NULL && out->priv != NULL);
     284           0 :     priv = out->priv;
     285             : 
     286           0 :     node = g_queue_pop_tail(priv->parent_q);
     287             : 
     288           0 :     if (node->singular_noun != NULL && node->plural_noun != NULL) {
     289           0 :         if (node->len == 1) {
     290           0 :             pcmk__indented_printf(out, "%d %s found\n", node->len, node->singular_noun);
     291             :         } else {
     292           0 :             pcmk__indented_printf(out, "%d %s found\n", node->len, node->plural_noun);
     293             :         }
     294             :     }
     295             : 
     296           0 :     free_list_data(node);
     297           0 : }
     298             : 
     299             : static bool
     300           0 : text_is_quiet(pcmk__output_t *out) {
     301           0 :     CRM_ASSERT(out != NULL);
     302           0 :     return out->quiet;
     303             : }
     304             : 
     305             : static void
     306           0 : text_spacer(pcmk__output_t *out) {
     307           0 :     CRM_ASSERT(out != NULL);
     308           0 :     fprintf(out->dest, "\n");
     309           0 : }
     310             : 
     311             : static void
     312           0 : text_progress(pcmk__output_t *out, bool end) {
     313           0 :     CRM_ASSERT(out != NULL);
     314             : 
     315           0 :     if (out->dest == stdout) {
     316           0 :         fprintf(out->dest, ".");
     317             : 
     318           0 :         if (end) {
     319           0 :             fprintf(out->dest, "\n");
     320             :         }
     321             :     }
     322           0 : }
     323             : 
     324             : pcmk__output_t *
     325           0 : pcmk__mk_text_output(char **argv) {
     326           0 :     pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
     327             : 
     328           0 :     if (retval == NULL) {
     329           0 :         return NULL;
     330             :     }
     331             : 
     332           0 :     retval->fmt_name = "text";
     333           0 :     retval->request = pcmk__quote_cmdline(argv);
     334             : 
     335           0 :     retval->init = text_init;
     336           0 :     retval->free_priv = text_free_priv;
     337           0 :     retval->finish = text_finish;
     338           0 :     retval->reset = text_reset;
     339             : 
     340           0 :     retval->register_message = pcmk__register_message;
     341           0 :     retval->message = pcmk__call_message;
     342             : 
     343           0 :     retval->subprocess_output = text_subprocess_output;
     344           0 :     retval->version = text_version;
     345           0 :     retval->info = text_info;
     346           0 :     retval->transient = text_transient;
     347           0 :     retval->err = text_err;
     348           0 :     retval->output_xml = text_output_xml;
     349             : 
     350           0 :     retval->begin_list = text_begin_list;
     351           0 :     retval->list_item = text_list_item;
     352           0 :     retval->increment_list = text_increment_list;
     353           0 :     retval->end_list = text_end_list;
     354             : 
     355           0 :     retval->is_quiet = text_is_quiet;
     356           0 :     retval->spacer = text_spacer;
     357           0 :     retval->progress = text_progress;
     358           0 :     retval->prompt = pcmk__text_prompt;
     359             : 
     360           0 :     return retval;
     361             : }
     362             : 
     363             : /*!
     364             :  * \internal
     365             :  * \brief Check whether fancy output is enabled for a text output object
     366             :  *
     367             :  * This returns \c false if the output object is not of text format.
     368             :  *
     369             :  * \param[in] out  Output object
     370             :  *
     371             :  * \return \c true if \p out has fancy output enabled, or \c false otherwise
     372             :  */
     373             : bool
     374           0 : pcmk__output_text_get_fancy(pcmk__output_t *out)
     375             : {
     376           0 :     CRM_ASSERT(out != NULL);
     377             : 
     378           0 :     if (pcmk__str_eq(out->fmt_name, "text", pcmk__str_none)) {
     379           0 :         private_data_t *priv = out->priv;
     380             : 
     381           0 :         CRM_ASSERT(priv != NULL);
     382           0 :         return priv->fancy;
     383             :     }
     384           0 :     return false;
     385             : }
     386             : 
     387             : /*!
     388             :  * \internal
     389             :  * \brief Enable or disable fancy output for a text output object
     390             :  *
     391             :  * This does nothing if the output object is not of text format.
     392             :  *
     393             :  * \param[in,out] out      Output object
     394             :  * \param[in]     enabled  Whether fancy output should be enabled for \p out
     395             :  */
     396             : void
     397           0 : pcmk__output_text_set_fancy(pcmk__output_t *out, bool enabled)
     398             : {
     399           0 :     CRM_ASSERT(out != NULL);
     400             : 
     401           0 :     if (pcmk__str_eq(out->fmt_name, "text", pcmk__str_none)) {
     402           0 :         private_data_t *priv = out->priv;
     403             : 
     404           0 :         CRM_ASSERT(priv != NULL);
     405           0 :         priv->fancy = enabled;
     406             :     }
     407           0 : }
     408             : 
     409             : G_GNUC_PRINTF(2, 0)
     410             : void
     411           0 : pcmk__formatted_vprintf(pcmk__output_t *out, const char *format, va_list args) {
     412           0 :     int len = 0;
     413             : 
     414           0 :     CRM_ASSERT(out != NULL);
     415           0 :     CRM_CHECK(pcmk__str_eq(out->fmt_name, "text", pcmk__str_none), return);
     416             : 
     417           0 :     len = vfprintf(out->dest, format, args);
     418           0 :     CRM_ASSERT(len >= 0);
     419             : }
     420             : 
     421             : G_GNUC_PRINTF(2, 3)
     422             : void
     423           0 : pcmk__formatted_printf(pcmk__output_t *out, const char *format, ...) {
     424             :     va_list ap;
     425             : 
     426           0 :     CRM_ASSERT(out != NULL);
     427             : 
     428           0 :     va_start(ap, format);
     429           0 :     pcmk__formatted_vprintf(out, format, ap);
     430           0 :     va_end(ap);
     431           0 : }
     432             : 
     433             : G_GNUC_PRINTF(2, 0)
     434             : void
     435           0 : pcmk__indented_vprintf(pcmk__output_t *out, const char *format, va_list args) {
     436           0 :     private_data_t *priv = NULL;
     437             : 
     438           0 :     CRM_ASSERT(out != NULL);
     439           0 :     CRM_CHECK(pcmk__str_eq(out->fmt_name, "text", pcmk__str_none), return);
     440             : 
     441           0 :     priv = out->priv;
     442             : 
     443           0 :     if (fancy || priv->fancy) {
     444           0 :         int level = 0;
     445           0 :         private_data_t *priv = out->priv;
     446             : 
     447           0 :         CRM_ASSERT(priv != NULL);
     448             : 
     449           0 :         level = g_queue_get_length(priv->parent_q);
     450             : 
     451           0 :         for (int i = 0; i < level; i++) {
     452           0 :             fprintf(out->dest, "  ");
     453             :         }
     454             : 
     455           0 :         if (level > 0) {
     456           0 :             fprintf(out->dest, "* ");
     457             :         }
     458             :     }
     459             : 
     460           0 :     pcmk__formatted_vprintf(out, format, args);
     461             : }
     462             : 
     463             : G_GNUC_PRINTF(2, 3)
     464             : void
     465           0 : pcmk__indented_printf(pcmk__output_t *out, const char *format, ...) {
     466             :     va_list ap;
     467             : 
     468           0 :     CRM_ASSERT(out != NULL);
     469             : 
     470           0 :     va_start(ap, format);
     471           0 :     pcmk__indented_vprintf(out, format, ap);
     472           0 :     va_end(ap);
     473           0 : }
     474             : 
     475             : void
     476           0 : pcmk__text_prompt(const char *prompt, bool echo, char **dest)
     477             : {
     478           0 :     int rc = 0;
     479             :     struct termios settings;
     480           0 :     tcflag_t orig_c_lflag = 0;
     481             : 
     482           0 :     CRM_ASSERT(prompt != NULL);
     483           0 :     CRM_ASSERT(dest != NULL);
     484             : 
     485           0 :     if (!echo) {
     486           0 :         rc = tcgetattr(0, &settings);
     487           0 :         if (rc == 0) {
     488           0 :             orig_c_lflag = settings.c_lflag;
     489           0 :             settings.c_lflag &= ~ECHO;
     490           0 :             rc = tcsetattr(0, TCSANOW, &settings);
     491             :         }
     492             :     }
     493             : 
     494           0 :     if (rc == 0) {
     495           0 :         fprintf(stderr, "%s: ", prompt);
     496             : 
     497           0 :         if (*dest != NULL) {
     498           0 :             free(*dest);
     499           0 :             *dest = NULL;
     500             :         }
     501             : 
     502             : #if HAVE_SSCANF_M
     503           0 :         rc = scanf("%ms", dest);
     504             : #else
     505             :         *dest = pcmk__assert_alloc(1, 1024);
     506             :         rc = scanf("%1023s", *dest);
     507             : #endif
     508           0 :         fprintf(stderr, "\n");
     509             :     }
     510             : 
     511           0 :     if (rc < 1) {
     512           0 :         free(*dest);
     513           0 :         *dest = NULL;
     514             :     }
     515             : 
     516           0 :     if (orig_c_lflag != 0) {
     517           0 :         settings.c_lflag = orig_c_lflag;
     518           0 :         /* rc = */ tcsetattr(0, TCSANOW, &settings);
     519             :     }
     520           0 : }

Generated by: LCOV version 1.14