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

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