root/tools/iso8601.c

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

DEFINITIONS

This source file includes following definitions.
  1. date_now_cb
  2. modifier_cb
  3. PCMK__OUTPUT_ARGS
  4. PCMK__OUTPUT_ARGS
  5. PCMK__OUTPUT_ARGS
  6. PCMK__OUTPUT_ARGS
  7. PCMK__OUTPUT_ARGS
  8. PCMK__OUTPUT_ARGS
  9. PCMK__OUTPUT_ARGS
  10. PCMK__OUTPUT_ARGS
  11. build_arg_context
  12. main

   1 /*
   2  * Copyright 2005-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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 #include <crm/crm.h>
  12 #include <crm/common/cmdline_internal.h>
  13 #include <crm/common/iso8601.h>
  14 #include <crm/common/util.h>
  15 #include <unistd.h>
  16 
  17 #define SUMMARY "Display and parse ISO 8601 dates and times"
  18 
  19 static pcmk__supported_format_t formats[] = {
  20     PCMK__SUPPORTED_FORMAT_NONE,
  21     PCMK__SUPPORTED_FORMAT_TEXT,
  22     PCMK__SUPPORTED_FORMAT_XML,
  23     { NULL, NULL, NULL }
  24 };
  25 
  26 struct {
  27     char *date_time_s;
  28     gchar *duration_s;
  29     gchar *expected_s;
  30     gchar *period_s;
  31     int print_options;
  32 } options;
  33 
  34 #define INDENT "                              "
  35 
  36 static gboolean
  37 date_now_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
  38     if (pcmk__str_any_of(option_name, "--now", "-n", NULL)) {
  39         pcmk__str_update(&options.date_time_s, "now");
  40     } else if (pcmk__str_any_of(option_name, "--date", "-d", NULL)) {
  41         pcmk__str_update(&options.date_time_s, optarg);
  42     }
  43 
  44     return TRUE;
  45 }
  46 
  47 static gboolean
  48 modifier_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
  49     if (pcmk__str_any_of(option_name, "--seconds", "-s", NULL)) {
  50         options.print_options |= crm_time_seconds;
  51     } else if (pcmk__str_any_of(option_name, "--epoch", "-S", NULL)) {
  52         options.print_options |= crm_time_epoch;
  53     } else if (pcmk__str_any_of(option_name, "--local", "-L", NULL)) {
  54         options.print_options |= crm_time_log_with_timezone;
  55     } else if (pcmk__str_any_of(option_name, "--ordinal", "-O", NULL)) {
  56         options.print_options |= crm_time_ordinal;
  57     } else if (pcmk__str_any_of(option_name, "--week", "-W", NULL)) {
  58         options.print_options |= crm_time_weeks;
  59     }
  60 
  61     return TRUE;
  62 }
  63 
  64 static GOptionEntry command_entries[] = {
  65     { "now", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, date_now_cb,
  66       "Display the current date/time",
  67       NULL },
  68 
  69     { "date", 'd', 0, G_OPTION_ARG_CALLBACK, date_now_cb,
  70       "Parse an ISO 8601 date/time (for example,\n"
  71       INDENT "'2019-09-24 00:30:00 +01:00' or '2019-040')",
  72       "DATE" },
  73 
  74     { "period", 'p', 0, G_OPTION_ARG_STRING, &options.period_s,
  75       "Parse an ISO 8601 period (interval) with start time (for example,\n"
  76       INDENT "'2005-040/2005-043')",
  77       "PERIOD" },
  78 
  79     { "duration", 'D', 0, G_OPTION_ARG_STRING, &options.duration_s,
  80       "Parse an ISO 8601 duration (for example, 'P1M')",
  81       "DURATION" },
  82 
  83     { "expected", 'E', 0, G_OPTION_ARG_STRING, &options.expected_s,
  84       "Exit with error status if result does not match this text.\n"
  85       INDENT "Requires: -n or -d",
  86       "TEXT" },
  87 
  88     { NULL }
  89 };
  90 
  91 static GOptionEntry modifier_entries[] = {
  92     { "seconds", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
  93       "Show result as a seconds since 0000-001 00:00:00Z",
  94       NULL },
  95 
  96     { "epoch", 'S', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
  97       "Show result as a seconds since EPOCH (1970-001 00:00:00Z)",
  98       NULL },
  99 
 100     { "local", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
 101       "Show result as a 'local' date/time",
 102       NULL },
 103 
 104     { "ordinal", 'O', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
 105       "Show result as an 'ordinal' date/time",
 106       NULL },
 107 
 108     { "week", 'W', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
 109       "Show result as an 'calendar week' date/time",
 110       NULL },
 111 
 112     { NULL }
 113 };
 114 
 115 PCMK__OUTPUT_ARGS("date", "const char *", "crm_time_t *", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 116 static int
 117 date_default(pcmk__output_t *out, va_list args)
 118 {
 119     const char *prefix = va_arg(args, const char *);
 120     crm_time_t *date = va_arg(args, crm_time_t *);
 121     int opts = va_arg(args, int);
 122 
 123     char *date_s = NULL;
 124 
 125     opts |= crm_time_log_date | crm_time_log_timeofday;
 126     date_s = crm_time_as_string(date, opts);
 127 
 128     out->info(out, "%s: %s", prefix, date_s);
 129 
 130     free(date_s);
 131     return pcmk_rc_ok;
 132 }
 133 
 134 PCMK__OUTPUT_ARGS("date", "const char *", "crm_time_t *", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 135 static int
 136 date_xml(pcmk__output_t *out, va_list args)
 137 {
 138     const char *prefix G_GNUC_UNUSED = va_arg(args, const char *);
 139     crm_time_t *date = va_arg(args, crm_time_t *);
 140     int opts = va_arg(args, int);
 141 
 142     char *date_s = NULL;
 143 
 144     opts |= crm_time_log_date | crm_time_log_timeofday;
 145     date_s = crm_time_as_string(date, opts);
 146 
 147     pcmk__output_create_xml_text_node(out, PCMK_XE_DATE, date_s);
 148     free(date_s);
 149     return pcmk_rc_ok;
 150 }
 151 
 152 PCMK__OUTPUT_ARGS("duration", "crm_time_t *", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 153 static int
 154 duration_default(pcmk__output_t *out, va_list args)
 155 {
 156     crm_time_t *time = va_arg(args, crm_time_t *);
 157     int opts = va_arg(args, int);
 158 
 159     char *date_s = crm_time_as_string(time, opts | crm_time_log_duration);
 160 
 161     out->info(out, "Duration: %s", date_s);
 162 
 163     free(date_s);
 164     return pcmk_rc_ok;
 165 }
 166 
 167 PCMK__OUTPUT_ARGS("duration", "crm_time_t *", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 168 static int
 169 duration_xml(pcmk__output_t *out, va_list args)
 170 {
 171     crm_time_t *time = va_arg(args, crm_time_t *);
 172     int opts = va_arg(args, int);
 173 
 174     char *date_s = crm_time_as_string(time, opts | crm_time_log_duration);
 175 
 176     pcmk__output_create_xml_text_node(out, PCMK_XE_DURATION, date_s);
 177     free(date_s);
 178     return pcmk_rc_ok;
 179 }
 180 
 181 PCMK__OUTPUT_ARGS("duration_ends", "crm_time_t *", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 182 static int
 183 duration_ends_default(pcmk__output_t *out, va_list args)
 184 {
 185     crm_time_t *time = va_arg(args, crm_time_t *);
 186     int opts = va_arg(args, int);
 187 
 188     char *date_s = NULL;
 189 
 190     opts |= crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone;
 191     date_s = crm_time_as_string(time, opts);
 192 
 193     out->info(out, "Duration ends at: %s", date_s);
 194 
 195     free(date_s);
 196     return pcmk_rc_ok;
 197 }
 198 
 199 PCMK__OUTPUT_ARGS("duration_ends", "crm_time_t *", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 200 static int
 201 duration_ends_xml(pcmk__output_t *out, va_list args)
 202 {
 203     crm_time_t *time = va_arg(args, crm_time_t *);
 204     int opts = va_arg(args, int);
 205 
 206     char *date_s = NULL;
 207 
 208     opts |= crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone;
 209     date_s = crm_time_as_string(time, opts);
 210 
 211     pcmk__output_create_xml_text_node(out, PCMK_XE_DURATION_ENDS, date_s);
 212     free(date_s);
 213     return pcmk_rc_ok;
 214 }
 215 
 216 PCMK__OUTPUT_ARGS("period", "crm_time_period_t *", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 217 static int
 218 period_default(pcmk__output_t *out, va_list args)
 219 {
 220     crm_time_period_t *period = va_arg(args, crm_time_period_t *);
 221     int opts = va_arg(args, int);
 222 
 223     char *start = NULL;
 224     char *end = NULL;
 225 
 226     opts |= crm_time_log_date | crm_time_log_timeofday;
 227 
 228     start = crm_time_as_string(period->start, opts);
 229     if (start == NULL) {
 230         return pcmk_rc_no_output;
 231     }
 232 
 233     end = crm_time_as_string(period->end, opts);
 234     if (end == NULL) {
 235         free(start);
 236         return pcmk_rc_no_output;
 237     }
 238 
 239     out->info(out, "Period: %s to %s", start, end);
 240 
 241     free(start);
 242     free(end);
 243     return pcmk_rc_ok;
 244 }
 245 
 246 PCMK__OUTPUT_ARGS("period", "crm_time_period_t *", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 247 static int
 248 period_xml(pcmk__output_t *out, va_list args)
 249 {
 250     crm_time_period_t *period = va_arg(args, crm_time_period_t *);
 251     int opts = va_arg(args, int);
 252 
 253     char *start = NULL;
 254     char *end = NULL;
 255 
 256     opts |= crm_time_log_date | crm_time_log_timeofday;
 257 
 258     start = crm_time_as_string(period->start, opts);
 259     if (start == NULL) {
 260         return pcmk_rc_no_output;
 261     }
 262 
 263     end = crm_time_as_string(period->end, opts);
 264     if (end == NULL) {
 265         free(start);
 266         return pcmk_rc_no_output;
 267     }
 268 
 269     pcmk__output_xml_create_parent(out, PCMK_XE_PERIOD, NULL);
 270     pcmk__output_create_xml_text_node(out, PCMK_XE_START, start);
 271     pcmk__output_create_xml_text_node(out, PCMK_XE_END, end);
 272 
 273     free(start);
 274     free(end);
 275     return pcmk_rc_ok;
 276 }
 277 
 278 static GOptionContext *
 279 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
     /* [previous][next][first][last][top][bottom][index][help] */
 280 {
 281     GOptionContext *context = NULL;
 282 
 283     const char *description = "For more information on the ISO 8601 standard, see " \
 284                               "https://en.wikipedia.org/wiki/ISO_8601";
 285 
 286     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 287     g_option_context_set_description(context, description);
 288 
 289     pcmk__add_arg_group(context, "commands", "Commands:",
 290                         "Show command options", command_entries);
 291     pcmk__add_arg_group(context, "modifiers", "Output modifiers:",
 292                         "Show output modifiers", modifier_entries);
 293 
 294     return context;
 295 }
 296 
 297 static pcmk__message_entry_t fmt_functions[] = {
 298     { "date", "default", date_default },
 299     { "date", "xml", date_xml },
 300     { "duration", "default", duration_default },
 301     { "duration", "xml", duration_xml },
 302     { "duration_ends", "default", duration_ends_default },
 303     { "duration_ends", "xml", duration_ends_xml },
 304     { "period", "default", period_default },
 305     { "period", "xml", period_xml },
 306 
 307     { NULL, NULL, NULL }
 308 };
 309 
 310 int
 311 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 312 {
 313     int rc = pcmk_rc_ok;
 314     crm_exit_t exit_code = CRM_EX_OK;
 315     crm_time_t *duration = NULL;
 316     crm_time_t *date_time = NULL;
 317 
 318     GError *error = NULL;
 319     pcmk__output_t *out = NULL;
 320 
 321     GOptionGroup *output_group = NULL;
 322     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 323     GOptionContext *context = build_arg_context(args, &output_group);
 324     gchar **processed_args = pcmk__cmdline_preproc(argv, "dpDE");
 325 
 326     pcmk__register_formats(output_group, formats);
 327     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 328         exit_code = CRM_EX_USAGE;
 329         goto done;
 330     }
 331 
 332     pcmk__cli_init_logging("iso8601", args->verbosity);
 333 
 334     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 335     if (rc != pcmk_rc_ok) {
 336         exit_code = pcmk_rc2exitc(rc);
 337         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 338                     "Error creating output format %s: %s", args->output_ty,
 339                     pcmk_rc_str(rc));
 340         goto done;
 341     }
 342 
 343     if (args->version) {
 344         out->version(out, false);
 345         goto done;
 346     }
 347 
 348     pcmk__register_messages(out, fmt_functions);
 349 
 350     if (pcmk__str_eq("now", options.date_time_s, pcmk__str_casei)) {
 351         date_time = crm_time_new(NULL);
 352 
 353         if (date_time == NULL) {
 354             exit_code = CRM_EX_SOFTWARE;
 355             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 356                         "Internal error: couldn't determine 'now'!");
 357             goto done;
 358         }
 359 
 360         out->message(out, "date", "Current date/time", date_time,
 361                      options.print_options);
 362 
 363     } else if (options.date_time_s) {
 364         date_time = crm_time_new(options.date_time_s);
 365 
 366         if (date_time == NULL) {
 367             exit_code = CRM_EX_INVALID_PARAM;
 368             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 369                         "Invalid date/time specified: %s", options.date_time_s);
 370             goto done;
 371         }
 372 
 373         out->message(out, "date", "Date", date_time, options.print_options);
 374     }
 375 
 376     if (options.duration_s) {
 377         duration = crm_time_parse_duration(options.duration_s);
 378 
 379         if (duration == NULL) {
 380             exit_code = CRM_EX_INVALID_PARAM;
 381             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 382                         "Invalid duration specified: %s", options.duration_s);
 383             goto done;
 384         }
 385 
 386         out->message(out, "duration", duration, options.print_options);
 387     }
 388 
 389     if (options.period_s) {
 390         crm_time_period_t *period = crm_time_parse_period(options.period_s);
 391 
 392         if (period == NULL) {
 393             exit_code = CRM_EX_INVALID_PARAM;
 394             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 395                         "Invalid interval specified: %s", options.period_s);
 396             goto done;
 397         }
 398 
 399         out->message(out, "period", period, options.print_options);
 400         crm_time_free_period(period);
 401     }
 402 
 403     if (date_time && duration) {
 404         crm_time_t *later = crm_time_add(date_time, duration);
 405 
 406         if (later == NULL) {
 407             exit_code = CRM_EX_SOFTWARE;
 408             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 409                         "Unable to calculate ending time of %s plus %s",
 410                         options.date_time_s, options.duration_s);
 411             goto done;
 412         }
 413 
 414         out->message(out, "duration_ends", later, options.print_options);
 415 
 416         if (options.expected_s) {
 417             char *dt_s = crm_time_as_string(later,
 418                                             options.print_options | crm_time_log_date |
 419                                             crm_time_log_timeofday);
 420             if (!pcmk__str_eq(options.expected_s, dt_s, pcmk__str_casei)) {
 421                 exit_code = CRM_EX_ERROR;
 422                 goto done;
 423             }
 424             free(dt_s);
 425         }
 426         crm_time_free(later);
 427 
 428     } else if (date_time && options.expected_s) {
 429         char *dt_s = crm_time_as_string(date_time,
 430                                         options.print_options | crm_time_log_date | crm_time_log_timeofday);
 431 
 432         if (!pcmk__str_eq(options.expected_s, dt_s, pcmk__str_casei)) {
 433             exit_code = CRM_EX_ERROR;
 434             goto done;
 435         }
 436         free(dt_s);
 437     }
 438 
 439 done:
 440     crm_time_free(date_time);
 441     crm_time_free(duration);
 442 
 443     g_strfreev(processed_args);
 444     pcmk__free_arg_context(context);
 445 
 446     free(options.date_time_s);
 447     g_free(options.duration_s);
 448     g_free(options.expected_s);
 449     g_free(options.period_s);
 450 
 451     pcmk__output_and_clear_error(&error, out);
 452 
 453     if (out != NULL) {
 454         out->finish(out, exit_code, true, NULL);
 455         pcmk__output_free(out);
 456     }
 457 
 458     pcmk__unregister_formats();
 459     crm_exit(exit_code);
 460 }

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