root/lib/common/output_text.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. free_list_data
  2. text_free_priv
  3. text_init
  4. text_finish
  5. text_reset
  6. text_subprocess_output
  7. text_version
  8. G_GNUC_PRINTF
  9. G_GNUC_PRINTF
  10. G_GNUC_PRINTF
  11. text_output_xml
  12. G_GNUC_PRINTF
  13. G_GNUC_PRINTF
  14. text_increment_list
  15. text_end_list
  16. text_is_quiet
  17. text_spacer
  18. text_progress
  19. pcmk__mk_text_output
  20. pcmk__output_text_get_fancy
  21. pcmk__output_text_set_fancy
  22. G_GNUC_PRINTF
  23. G_GNUC_PRINTF
  24. G_GNUC_PRINTF
  25. G_GNUC_PRINTF
  26. pcmk__text_prompt

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

/* [previous][next][first][last][top][bottom][index][help] */