root/lib/common/output_text.c

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

DEFINITIONS

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

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

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