summaryrefslogtreecommitdiffstats
path: root/lib/common/iso8601.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/common/iso8601.c233
1 files changed, 190 insertions, 43 deletions
diff --git a/lib/common/iso8601.c b/lib/common/iso8601.c
index 9de018f..d24f268 100644
--- a/lib/common/iso8601.c
+++ b/lib/common/iso8601.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2022 the Pacemaker project contributors
+ * Copyright 2005-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -18,9 +18,12 @@
#include <time.h>
#include <ctype.h>
#include <inttypes.h>
+#include <limits.h> // INT_MIN, INT_MAX
#include <string.h>
#include <stdbool.h>
#include <crm/common/iso8601.h>
+#include <crm/common/iso8601_internal.h>
+#include "crmcommon_private.h"
/*
* Andrew's code was originally written for OSes whose "struct tm" contains:
@@ -125,10 +128,7 @@ crm_time_new(const char *date_time)
crm_time_t *
crm_time_new_undefined(void)
{
- crm_time_t *result = calloc(1, sizeof(crm_time_t));
-
- CRM_ASSERT(result != NULL);
- return result;
+ return (crm_time_t *) pcmk__assert_alloc(1, sizeof(crm_time_t));
}
/*!
@@ -623,7 +623,9 @@ time_as_string_common(const crm_time_t *dt, int usec, uint32_t flags,
if (pcmk_is_set(flags, crm_time_log_date)) {
if (pcmk_is_set(flags, crm_time_weeks)) { // YYYY-WW-D
- uint32_t y, w, d;
+ uint32_t y = 0;
+ uint32_t w = 0;
+ uint32_t d = 0;
if (crm_time_get_isoweek(dt, &y, &w, &d)) {
offset += snprintf(result + offset, DATE_MAX - offset,
@@ -632,7 +634,8 @@ time_as_string_common(const crm_time_t *dt, int usec, uint32_t flags,
}
} else if (pcmk_is_set(flags, crm_time_ordinal)) { // YYYY-DDD
- uint32_t y, d;
+ uint32_t y = 0;
+ uint32_t d = 0;
if (crm_time_get_ordinal(dt, &y, &d)) {
offset += snprintf(result + offset, DATE_MAX - offset,
@@ -640,7 +643,9 @@ time_as_string_common(const crm_time_t *dt, int usec, uint32_t flags,
}
} else { // YYYY-MM-DD
- uint32_t y, m, d;
+ uint32_t y = 0;
+ uint32_t m = 0;
+ uint32_t d = 0;
if (crm_time_get_gregorian(dt, &y, &m, &d)) {
offset += snprintf(result + offset, DATE_MAX - offset,
@@ -694,12 +699,9 @@ char *
crm_time_as_string(const crm_time_t *dt, int flags)
{
char result[DATE_MAX] = { '\0', };
- char *result_copy = NULL;
time_as_string_common(dt, 0, flags, result);
-
- pcmk__str_update(&result_copy, result);
- return result_copy;
+ return pcmk__str_copy(result);
}
/*!
@@ -864,7 +866,8 @@ crm_time_parse(const char *time_str, crm_time_t *a_time)
* \internal
* \brief Parse a time object from an ISO 8601 date/time specification
*
- * \param[in] date_str ISO 8601 date/time specification (or "epoch")
+ * \param[in] date_str ISO 8601 date/time specification (or
+ * \c PCMK__VALUE_EPOCH)
*
* \return New time object on success, NULL (and set errno) otherwise
*/
@@ -898,8 +901,10 @@ parse_date(const char *date_str)
dt = crm_time_new_undefined();
- if (!strncasecmp("epoch", date_str, 5)
- && ((date_str[5] == '\0') || (date_str[5] == '/') || isspace(date_str[5]))) {
+ if ((strncasecmp(PCMK__VALUE_EPOCH, date_str, 5) == 0)
+ && ((date_str[5] == '\0')
+ || (date_str[5] == '/')
+ || isspace(date_str[5]))) {
dt->days = 1;
dt->years = 1970;
crm_time_log(LOG_TRACE, "Unpacked", dt, crm_time_log_date | crm_time_log_timeofday);
@@ -1211,8 +1216,7 @@ crm_time_parse_period(const char *period_str)
}
tzset();
- period = calloc(1, sizeof(crm_time_period_t));
- CRM_ASSERT(period != NULL);
+ period = pcmk__assert_alloc(1, sizeof(crm_time_period_t));
if (period_str[0] == 'P') {
period->diff = crm_time_parse_duration(period_str);
@@ -1370,6 +1374,23 @@ crm_time_set_timet(crm_time_t *target, const time_t *source)
ha_set_tm_time(target, localtime(source));
}
+/*!
+ * \internal
+ * \brief Set one time object to another if the other is earlier
+ *
+ * \param[in,out] target Time object to set
+ * \param[in] source Time object to use if earlier
+ */
+void
+pcmk__set_time_if_earlier(crm_time_t *target, const crm_time_t *source)
+{
+ if ((target != NULL) && (source != NULL)
+ && (!crm_time_is_defined(target)
+ || (crm_time_compare(source, target) < 0))) {
+ crm_time_set(target, source);
+ }
+}
+
crm_time_t *
pcmk_copy_time(const crm_time_t *source)
{
@@ -1424,6 +1445,127 @@ crm_time_add(const crm_time_t *dt, const crm_time_t *value)
return answer;
}
+/*!
+ * \internal
+ * \brief Return the XML attribute name corresponding to a time component
+ *
+ * \param[in] component Component to check
+ *
+ * \return XML attribute name corresponding to \p component, or NULL if
+ * \p component is invalid
+ */
+const char *
+pcmk__time_component_attr(enum pcmk__time_component component)
+{
+ switch (component) {
+ case pcmk__time_years:
+ return PCMK_XA_YEARS;
+
+ case pcmk__time_months:
+ return PCMK_XA_MONTHS;
+
+ case pcmk__time_weeks:
+ return PCMK_XA_WEEKS;
+
+ case pcmk__time_days:
+ return PCMK_XA_DAYS;
+
+ case pcmk__time_hours:
+ return PCMK_XA_HOURS;
+
+ case pcmk__time_minutes:
+ return PCMK_XA_MINUTES;
+
+ case pcmk__time_seconds:
+ return PCMK_XA_SECONDS;
+
+ default:
+ return NULL;
+ }
+}
+
+typedef void (*component_fn_t)(crm_time_t *, int);
+
+/*!
+ * \internal
+ * \brief Get the addition function corresponding to a time component
+ * \param[in] component Component to check
+ *
+ * \return Addition function corresponding to \p component, or NULL if
+ * \p component is invalid
+ */
+static component_fn_t
+component_fn(enum pcmk__time_component component)
+{
+ switch (component) {
+ case pcmk__time_years:
+ return crm_time_add_years;
+
+ case pcmk__time_months:
+ return crm_time_add_months;
+
+ case pcmk__time_weeks:
+ return crm_time_add_weeks;
+
+ case pcmk__time_days:
+ return crm_time_add_days;
+
+ case pcmk__time_hours:
+ return crm_time_add_hours;
+
+ case pcmk__time_minutes:
+ return crm_time_add_minutes;
+
+ case pcmk__time_seconds:
+ return crm_time_add_seconds;
+
+ default:
+ return NULL;
+ }
+
+}
+
+/*!
+ * \internal
+ * \brief Add the value of an XML attribute to a time object
+ *
+ * \param[in,out] t Time object to add to
+ * \param[in] component Component of \p t to add to
+ * \param[in] xml XML with value to add
+ *
+ * \return Standard Pacemaker return code
+ */
+int
+pcmk__add_time_from_xml(crm_time_t *t, enum pcmk__time_component component,
+ const xmlNode *xml)
+{
+ long long value;
+ const char *attr = pcmk__time_component_attr(component);
+ component_fn_t add = component_fn(component);
+
+ if ((t == NULL) || (attr == NULL) || (add == NULL)) {
+ return EINVAL;
+ }
+
+ if (xml == NULL) {
+ return pcmk_rc_ok;
+ }
+
+ if (pcmk__scan_ll(crm_element_value(xml, attr), &value,
+ 0LL) != pcmk_rc_ok) {
+ return pcmk_rc_unpack_error;
+ }
+
+ if ((value < INT_MIN) || (value > INT_MAX)) {
+ return ERANGE;
+ }
+
+ if (value != 0LL) {
+ add(t, (int) value);
+ }
+ return pcmk_rc_ok;
+}
+
crm_time_t *
crm_time_calculate_duration(const crm_time_t *dt, const crm_time_t *value)
{
@@ -1690,8 +1832,11 @@ pcmk__time_hr_convert(pcmk__time_hr_t *target, const crm_time_t *dt)
pcmk__time_hr_t *hr_dt = NULL;
if (dt) {
- hr_dt = target?target:calloc(1, sizeof(pcmk__time_hr_t));
- CRM_ASSERT(hr_dt != NULL);
+ hr_dt = target;
+ if (hr_dt == NULL) {
+ hr_dt = pcmk__assert_alloc(1, sizeof(pcmk__time_hr_t));
+ }
+
*hr_dt = (pcmk__time_hr_t) {
.years = dt->years,
.months = dt->months,
@@ -1772,12 +1917,21 @@ pcmk__time_hr_free(pcmk__time_hr_t * hr_dt)
char *
pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt)
{
- const char *mark_s;
- int max = 128, scanned_pos = 0, printed_pos = 0, fmt_pos = 0,
- date_len = 0, nano_digits = 0;
- char nano_s[10], date_s[max+1], nanofmt_s[5] = "%", *tmp_fmt_s;
- struct tm tm;
- crm_time_t dt;
+#define DATE_LEN_MAX 128
+ const char *mark_s = NULL;
+ int scanned_pos = 0;
+ int printed_pos = 0;
+ int fmt_pos = 0;
+ size_t date_len = 0;
+ int nano_digits = 0;
+
+ char nano_s[10] = { '\0', };
+ char date_s[DATE_LEN_MAX] = { '\0', };
+ char nanofmt_s[5] = "%";
+ char *tmp_fmt_s = NULL;
+
+ struct tm tm = { 0, };
+ crm_time_t dt = { 0, };
if (!format) {
return NULL;
@@ -1818,7 +1972,8 @@ pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
- date_len += strftime(&date_s[date_len], max-date_len, tmp_fmt_s, &tm);
+ date_len += strftime(&date_s[date_len], DATE_LEN_MAX - date_len,
+ tmp_fmt_s, &tm);
#ifdef HAVE_FORMAT_NONLITERAL
#pragma GCC diagnostic pop
#endif
@@ -1829,7 +1984,7 @@ pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#endif
- date_len += snprintf(&date_s[date_len], max-date_len,
+ date_len += snprintf(&date_s[date_len], DATE_LEN_MAX - date_len,
nanofmt_s, nano_s);
#ifdef HAVE_FORMAT_NONLITERAL
#pragma GCC diagnostic pop
@@ -1839,6 +1994,7 @@ pcmk__time_format_hr(const char *format, const pcmk__time_hr_t *hr_dt)
}
return (date_len == 0)?NULL:strdup(date_s);
+#undef DATE_LEN_MAX
}
/*!
@@ -1858,22 +2014,15 @@ char *
pcmk__epoch2str(const time_t *source, uint32_t flags)
{
time_t epoch_time = (source == NULL)? time(NULL) : *source;
- char *result = NULL;
if (flags == 0) {
- const char *buf = pcmk__trim(ctime(&epoch_time));
-
- if (buf != NULL) {
- result = strdup(buf);
- CRM_ASSERT(result != NULL);
- }
+ return pcmk__str_copy(pcmk__trim(ctime(&epoch_time)));
} else {
crm_time_t dt;
crm_time_set_timet(&dt, &epoch_time);
- result = crm_time_as_string(&dt, flags);
+ return crm_time_as_string(&dt, flags);
}
- return result;
}
/*!
@@ -1899,7 +2048,6 @@ pcmk__timespec2str(const struct timespec *ts, uint32_t flags)
struct timespec tmp_ts;
crm_time_t dt;
char result[DATE_MAX] = { 0 };
- char *result_copy = NULL;
if (ts == NULL) {
qb_util_timespec_from_epoch_get(&tmp_ts);
@@ -1907,8 +2055,7 @@ pcmk__timespec2str(const struct timespec *ts, uint32_t flags)
}
crm_time_set_timet(&dt, &ts->tv_sec);
time_as_string_common(&dt, ts->tv_nsec / QB_TIME_NS_IN_USEC, flags, result);
- pcmk__str_update(&result_copy, result);
- return result_copy;
+ return pcmk__str_copy(result);
}
/*!
@@ -1934,24 +2081,24 @@ pcmk__readable_interval(guint interval_ms)
int offset = 0;
str[0] = '\0';
- if (interval_ms > MS_IN_D) {
+ if (interval_ms >= MS_IN_D) {
offset += snprintf(str + offset, MAXSTR - offset, "%ud",
interval_ms / MS_IN_D);
interval_ms -= (interval_ms / MS_IN_D) * MS_IN_D;
}
- if (interval_ms > MS_IN_H) {
+ if (interval_ms >= MS_IN_H) {
offset += snprintf(str + offset, MAXSTR - offset, "%uh",
interval_ms / MS_IN_H);
interval_ms -= (interval_ms / MS_IN_H) * MS_IN_H;
}
- if (interval_ms > MS_IN_M) {
+ if (interval_ms >= MS_IN_M) {
offset += snprintf(str + offset, MAXSTR - offset, "%um",
interval_ms / MS_IN_M);
interval_ms -= (interval_ms / MS_IN_M) * MS_IN_M;
}
// Ns, N.NNNs, or NNNms
- if (interval_ms > MS_IN_S) {
+ if (interval_ms >= MS_IN_S) {
offset += snprintf(str + offset, MAXSTR - offset, "%u",
interval_ms / MS_IN_S);
interval_ms -= (interval_ms / MS_IN_S) * MS_IN_S;