root/lib/common/strings.c

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

DEFINITIONS

This source file includes following definitions.
  1. scan_ll
  2. pcmk__scan_ll
  3. pcmk__scan_min_int
  4. pcmk__scan_port
  5. pcmk__scan_double
  6. pcmk__guint_from_hash
  7. crm_get_msec
  8. crm_is_true
  9. crm_str_to_boolean
  10. pcmk__trim
  11. pcmk__starts_with
  12. ends_with
  13. pcmk__ends_with
  14. pcmk__ends_with_ext
  15. pcmk__str_hash
  16. pcmk__strkey_table
  17. pcmk__strcase_equal
  18. pcmk__strcase_hash
  19. pcmk__strikey_table
  20. copy_str_table_entry
  21. pcmk__str_table_dup
  22. pcmk__add_separated_word
  23. pcmk__compress
  24. crm_strdup_printf
  25. pcmk__parse_ll_range
  26. pcmk__str_in_list
  27. str_any_of
  28. pcmk__strcase_any_of
  29. pcmk__str_any_of
  30. pcmk__char_in_any_str
  31. pcmk__numeric_strcasecmp
  32. pcmk__strcmp
  33. pcmk__str_update
  34. pcmk__g_strcat
  35. safe_str_neq
  36. crm_str_eq
  37. crm_itoa_stack
  38. g_str_hash_traditional
  39. crm_strcase_equal
  40. crm_strcase_hash
  41. crm_str_table_dup
  42. crm_parse_ll
  43. crm_parse_int
  44. crm_strip_trailing_newline
  45. pcmk_numeric_strcasecmp

   1 /*
   2  * Copyright 2004-2023 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/common/results.h"
  11 #include <crm_internal.h>
  12 
  13 #ifndef _GNU_SOURCE
  14 #  define _GNU_SOURCE
  15 #endif
  16 
  17 #include <regex.h>
  18 #include <stdio.h>
  19 #include <string.h>
  20 #include <stdlib.h>
  21 #include <ctype.h>
  22 #include <float.h>  // DBL_MIN
  23 #include <limits.h>
  24 #include <bzlib.h>
  25 #include <sys/types.h>
  26 
  27 /*!
  28  * \internal
  29  * \brief Scan a long long integer from a string
  30  *
  31  * \param[in]  text           String to scan
  32  * \param[out] result         If not NULL, where to store scanned value
  33  * \param[in]  default_value  Value to use if text is NULL or invalid
  34  * \param[out] end_text       If not NULL, where to store pointer to first
  35  *                            non-integer character
  36  *
  37  * \return Standard Pacemaker return code (\c pcmk_rc_ok on success,
  38  *         \c EINVAL on failed string conversion due to invalid input,
  39  *         or \c EOVERFLOW on arithmetic overflow)
  40  * \note Sets \c errno on error
  41  */
  42 static int
  43 scan_ll(const char *text, long long *result, long long default_value,
     /* [previous][next][first][last][top][bottom][index][help] */
  44         char **end_text)
  45 {
  46     long long local_result = default_value;
  47     char *local_end_text = NULL;
  48     int rc = pcmk_rc_ok;
  49 
  50     errno = 0;
  51     if (text != NULL) {
  52         local_result = strtoll(text, &local_end_text, 10);
  53         if (errno == ERANGE) {
  54             rc = EOVERFLOW;
  55             crm_warn("Integer parsed from '%s' was clipped to %lld",
  56                      text, local_result);
  57 
  58         } else if (errno != 0) {
  59             rc = errno;
  60             local_result = default_value;
  61             crm_warn("Could not parse integer from '%s' (using %lld instead): "
  62                      "%s", text, default_value, pcmk_rc_str(rc));
  63 
  64         } else if (local_end_text == text) {
  65             rc = EINVAL;
  66             local_result = default_value;
  67             crm_warn("Could not parse integer from '%s' (using %lld instead): "
  68                     "No digits found", text, default_value);
  69         }
  70 
  71         if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) {
  72             crm_warn("Characters left over after parsing '%s': '%s'",
  73                      text, local_end_text);
  74         }
  75         errno = rc;
  76     }
  77     if (end_text != NULL) {
  78         *end_text = local_end_text;
  79     }
  80     if (result != NULL) {
  81         *result = local_result;
  82     }
  83     return rc;
  84 }
  85 
  86 /*!
  87  * \internal
  88  * \brief Scan a long long integer value from a string
  89  *
  90  * \param[in]  text           The string to scan (may be NULL)
  91  * \param[out] result         Where to store result (or NULL to ignore)
  92  * \param[in]  default_value  Value to use if text is NULL or invalid
  93  *
  94  * \return Standard Pacemaker return code
  95  */
  96 int
  97 pcmk__scan_ll(const char *text, long long *result, long long default_value)
     /* [previous][next][first][last][top][bottom][index][help] */
  98 {
  99     long long local_result = default_value;
 100     int rc = pcmk_rc_ok;
 101 
 102     if (text != NULL) {
 103         rc = scan_ll(text, &local_result, default_value, NULL);
 104         if (rc != pcmk_rc_ok) {
 105             local_result = default_value;
 106         }
 107     }
 108     if (result != NULL) {
 109         *result = local_result;
 110     }
 111     return rc;
 112 }
 113 
 114 /*!
 115  * \internal
 116  * \brief Scan an integer value from a string, constrained to a minimum
 117  *
 118  * \param[in]  text           The string to scan (may be NULL)
 119  * \param[out] result         Where to store result (or NULL to ignore)
 120  * \param[in]  minimum        Value to use as default and minimum
 121  *
 122  * \return Standard Pacemaker return code
 123  * \note If the value is larger than the maximum integer, EOVERFLOW will be
 124  *       returned and \p result will be set to the maximum integer.
 125  */
 126 int
 127 pcmk__scan_min_int(const char *text, int *result, int minimum)
     /* [previous][next][first][last][top][bottom][index][help] */
 128 {
 129     int rc;
 130     long long result_ll;
 131 
 132     rc = pcmk__scan_ll(text, &result_ll, (long long) minimum);
 133 
 134     if (result_ll < (long long) minimum) {
 135         crm_warn("Clipped '%s' to minimum acceptable value %d", text, minimum);
 136         result_ll = (long long) minimum;
 137 
 138     } else if (result_ll > INT_MAX) {
 139         crm_warn("Clipped '%s' to maximum integer %d", text, INT_MAX);
 140         result_ll = (long long) INT_MAX;
 141         rc = EOVERFLOW;
 142     }
 143 
 144     if (result != NULL) {
 145         *result = (int) result_ll;
 146     }
 147     return rc;
 148 }
 149 
 150 /*!
 151  * \internal
 152  * \brief Scan a TCP port number from a string
 153  *
 154  * \param[in]  text  The string to scan
 155  * \param[out] port  Where to store result (or NULL to ignore)
 156  *
 157  * \return Standard Pacemaker return code
 158  * \note \p port will be -1 if \p text is NULL or invalid
 159  */
 160 int
 161 pcmk__scan_port(const char *text, int *port)
     /* [previous][next][first][last][top][bottom][index][help] */
 162 {
 163     long long port_ll;
 164     int rc = pcmk__scan_ll(text, &port_ll, -1LL);
 165 
 166     if ((text != NULL) && (rc == pcmk_rc_ok) // wasn't default or invalid
 167         && ((port_ll < 0LL) || (port_ll > 65535LL))) {
 168         crm_warn("Ignoring port specification '%s' "
 169                  "not in valid range (0-65535)", text);
 170         rc = (port_ll < 0LL)? pcmk_rc_before_range : pcmk_rc_after_range;
 171         port_ll = -1LL;
 172     }
 173     if (port != NULL) {
 174         *port = (int) port_ll;
 175     }
 176     return rc;
 177 }
 178 
 179 /*!
 180  * \internal
 181  * \brief Scan a double-precision floating-point value from a string
 182  *
 183  * \param[in]      text         The string to parse
 184  * \param[out]     result       Parsed value on success, or
 185  *                              \c PCMK__PARSE_DBL_DEFAULT on error
 186  * \param[in]      default_text Default string to parse if \p text is
 187  *                              \c NULL
 188  * \param[out]     end_text     If not \c NULL, where to store a pointer
 189  *                              to the position immediately after the
 190  *                              value
 191  *
 192  * \return Standard Pacemaker return code (\c pcmk_rc_ok on success,
 193  *         \c EINVAL on failed string conversion due to invalid input,
 194  *         \c EOVERFLOW on arithmetic overflow, \c pcmk_rc_underflow
 195  *         on arithmetic underflow, or \c errno from \c strtod() on
 196  *         other parse errors)
 197  */
 198 int
 199 pcmk__scan_double(const char *text, double *result, const char *default_text,
     /* [previous][next][first][last][top][bottom][index][help] */
 200                   char **end_text)
 201 {
 202     int rc = pcmk_rc_ok;
 203     char *local_end_text = NULL;
 204 
 205     CRM_ASSERT(result != NULL);
 206     *result = PCMK__PARSE_DBL_DEFAULT;
 207 
 208     text = (text != NULL) ? text : default_text;
 209 
 210     if (text == NULL) {
 211         rc = EINVAL;
 212         crm_debug("No text and no default conversion value supplied");
 213 
 214     } else {
 215         errno = 0;
 216         *result = strtod(text, &local_end_text);
 217 
 218         if (errno == ERANGE) {
 219             /*
 220              * Overflow: strtod() returns +/- HUGE_VAL and sets errno to
 221              *           ERANGE
 222              *
 223              * Underflow: strtod() returns "a value whose magnitude is
 224              *            no greater than the smallest normalized
 225              *            positive" double. Whether ERANGE is set is
 226              *            implementation-defined.
 227              */
 228             const char *over_under;
 229 
 230             if (QB_ABS(*result) > DBL_MIN) {
 231                 rc = EOVERFLOW;
 232                 over_under = "over";
 233             } else {
 234                 rc = pcmk_rc_underflow;
 235                 over_under = "under";
 236             }
 237 
 238             crm_debug("Floating-point value parsed from '%s' would %sflow "
 239                       "(using %g instead)", text, over_under, *result);
 240 
 241         } else if (errno != 0) {
 242             rc = errno;
 243             // strtod() set *result = 0 on parse failure
 244             *result = PCMK__PARSE_DBL_DEFAULT;
 245 
 246             crm_debug("Could not parse floating-point value from '%s' (using "
 247                       "%.1f instead): %s", text, PCMK__PARSE_DBL_DEFAULT,
 248                       pcmk_rc_str(rc));
 249 
 250         } else if (local_end_text == text) {
 251             // errno == 0, but nothing was parsed
 252             rc = EINVAL;
 253             *result = PCMK__PARSE_DBL_DEFAULT;
 254 
 255             crm_debug("Could not parse floating-point value from '%s' (using "
 256                       "%.1f instead): No digits found", text,
 257                       PCMK__PARSE_DBL_DEFAULT);
 258 
 259         } else if (QB_ABS(*result) <= DBL_MIN) {
 260             /*
 261              * errno == 0 and text was parsed, but value might have
 262              * underflowed.
 263              *
 264              * ERANGE might not be set for underflow. Check magnitude
 265              * of *result, but also make sure the input number is not
 266              * actually zero (0 <= DBL_MIN is not underflow).
 267              *
 268              * This check must come last. A parse failure in strtod()
 269              * also sets *result == 0, so a parse failure would match
 270              * this test condition prematurely.
 271              */
 272             for (const char *p = text; p != local_end_text; p++) {
 273                 if (strchr("0.eE", *p) == NULL) {
 274                     rc = pcmk_rc_underflow;
 275                     crm_debug("Floating-point value parsed from '%s' would "
 276                               "underflow (using %g instead)", text, *result);
 277                     break;
 278                 }
 279             }
 280 
 281         } else {
 282             crm_trace("Floating-point value parsed successfully from "
 283                       "'%s': %g", text, *result);
 284         }
 285 
 286         if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) {
 287             crm_debug("Characters left over after parsing '%s': '%s'",
 288                       text, local_end_text);
 289         }
 290     }
 291 
 292     if (end_text != NULL) {
 293         *end_text = local_end_text;
 294     }
 295 
 296     return rc;
 297 }
 298 
 299 /*!
 300  * \internal
 301  * \brief Parse a guint from a string stored in a hash table
 302  *
 303  * \param[in]     table        Hash table to search
 304  * \param[in]     key          Hash table key to use to retrieve string
 305  * \param[in]     default_val  What to use if key has no entry in table
 306  * \param[out]    result       If not NULL, where to store parsed integer
 307  *
 308  * \return Standard Pacemaker return code
 309  */
 310 int
 311 pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val,
     /* [previous][next][first][last][top][bottom][index][help] */
 312                       guint *result)
 313 {
 314     const char *value;
 315     long long value_ll;
 316     int rc = pcmk_rc_ok;
 317 
 318     CRM_CHECK((table != NULL) && (key != NULL), return EINVAL);
 319 
 320     if (result != NULL) {
 321         *result = default_val;
 322     }
 323 
 324     value = g_hash_table_lookup(table, key);
 325     if (value == NULL) {
 326         return pcmk_rc_ok;
 327     }
 328 
 329     rc = pcmk__scan_ll(value, &value_ll, 0LL);
 330     if (rc != pcmk_rc_ok) {
 331         return rc;
 332     }
 333 
 334     if ((value_ll < 0) || (value_ll > G_MAXUINT)) {
 335         crm_warn("Could not parse non-negative integer from %s", value);
 336         return ERANGE;
 337     }
 338 
 339     if (result != NULL) {
 340         *result = (guint) value_ll;
 341     }
 342     return pcmk_rc_ok;
 343 }
 344 
 345 #ifndef NUMCHARS
 346 #  define       NUMCHARS        "0123456789."
 347 #endif
 348 
 349 #ifndef WHITESPACE
 350 #  define       WHITESPACE      " \t\n\r\f"
 351 #endif
 352 
 353 /*!
 354  * \brief Parse a time+units string and return milliseconds equivalent
 355  *
 356  * \param[in] input  String with a number and optional unit (optionally
 357  *                   with whitespace before and/or after the number).  If
 358  *                   missing, the unit defaults to seconds.
 359  *
 360  * \return Milliseconds corresponding to string expression, or
 361  *         PCMK__PARSE_INT_DEFAULT on error
 362  */
 363 long long
 364 crm_get_msec(const char *input)
     /* [previous][next][first][last][top][bottom][index][help] */
 365 {
 366     const char *num_start = NULL;
 367     const char *units;
 368     long long multiplier = 1000;
 369     long long divisor = 1;
 370     long long msec = PCMK__PARSE_INT_DEFAULT;
 371     size_t num_len = 0;
 372     char *end_text = NULL;
 373 
 374     if (input == NULL) {
 375         return PCMK__PARSE_INT_DEFAULT;
 376     }
 377 
 378     num_start = input + strspn(input, WHITESPACE);
 379     num_len = strspn(num_start, NUMCHARS);
 380     if (num_len < 1) {
 381         return PCMK__PARSE_INT_DEFAULT;
 382     }
 383     units = num_start + num_len;
 384     units += strspn(units, WHITESPACE);
 385 
 386     if (!strncasecmp(units, "ms", 2) || !strncasecmp(units, "msec", 4)) {
 387         multiplier = 1;
 388         divisor = 1;
 389     } else if (!strncasecmp(units, "us", 2) || !strncasecmp(units, "usec", 4)) {
 390         multiplier = 1;
 391         divisor = 1000;
 392     } else if (!strncasecmp(units, "s", 1) || !strncasecmp(units, "sec", 3)) {
 393         multiplier = 1000;
 394         divisor = 1;
 395     } else if (!strncasecmp(units, "m", 1) || !strncasecmp(units, "min", 3)) {
 396         multiplier = 60 * 1000;
 397         divisor = 1;
 398     } else if (!strncasecmp(units, "h", 1) || !strncasecmp(units, "hr", 2)) {
 399         multiplier = 60 * 60 * 1000;
 400         divisor = 1;
 401     } else if ((*units != '\0') && (*units != '\n') && (*units != '\r')) {
 402         return PCMK__PARSE_INT_DEFAULT;
 403     }
 404 
 405     scan_ll(num_start, &msec, PCMK__PARSE_INT_DEFAULT, &end_text);
 406     if (msec > (LLONG_MAX / multiplier)) {
 407         // Arithmetics overflow while multiplier/divisor mutually exclusive
 408         return LLONG_MAX;
 409     }
 410     msec *= multiplier;
 411     msec /= divisor;
 412     return msec;
 413 }
 414 
 415 gboolean
 416 crm_is_true(const char *s)
     /* [previous][next][first][last][top][bottom][index][help] */
 417 {
 418     gboolean ret = FALSE;
 419 
 420     return (crm_str_to_boolean(s, &ret) < 0)? FALSE : ret;
 421 }
 422 
 423 int
 424 crm_str_to_boolean(const char *s, int *ret)
     /* [previous][next][first][last][top][bottom][index][help] */
 425 {
 426     if (s == NULL) {
 427         return -1;
 428 
 429     } else if (strcasecmp(s, "true") == 0
 430                || strcasecmp(s, "on") == 0
 431                || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
 432         *ret = TRUE;
 433         return 1;
 434 
 435     } else if (strcasecmp(s, "false") == 0
 436                || strcasecmp(s, "off") == 0
 437                || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) {
 438         *ret = FALSE;
 439         return 1;
 440     }
 441     return -1;
 442 }
 443 
 444 /*!
 445  * \internal
 446  * \brief Replace any trailing newlines in a string with \0's
 447  *
 448  * \param[in,out] str  String to trim
 449  *
 450  * \return \p str
 451  */
 452 char *
 453 pcmk__trim(char *str)
     /* [previous][next][first][last][top][bottom][index][help] */
 454 {
 455     int len;
 456 
 457     if (str == NULL) {
 458         return str;
 459     }
 460 
 461     for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
 462         str[len] = '\0';
 463     }
 464 
 465     return str;
 466 }
 467 
 468 /*!
 469  * \brief Check whether a string starts with a certain sequence
 470  *
 471  * \param[in] str    String to check
 472  * \param[in] prefix Sequence to match against beginning of \p str
 473  *
 474  * \return \c true if \p str begins with match, \c false otherwise
 475  * \note This is equivalent to !strncmp(s, prefix, strlen(prefix))
 476  *       but is likely less efficient when prefix is a string literal
 477  *       if the compiler optimizes away the strlen() at compile time,
 478  *       and more efficient otherwise.
 479  */
 480 bool
 481 pcmk__starts_with(const char *str, const char *prefix)
     /* [previous][next][first][last][top][bottom][index][help] */
 482 {
 483     const char *s = str;
 484     const char *p = prefix;
 485 
 486     if (!s || !p) {
 487         return false;
 488     }
 489     while (*s && *p) {
 490         if (*s++ != *p++) {
 491             return false;
 492         }
 493     }
 494     return (*p == 0);
 495 }
 496 
 497 static inline bool
 498 ends_with(const char *s, const char *match, bool as_extension)
     /* [previous][next][first][last][top][bottom][index][help] */
 499 {
 500     if (pcmk__str_empty(match)) {
 501         return true;
 502     } else if (s == NULL) {
 503         return false;
 504     } else {
 505         size_t slen, mlen;
 506 
 507         /* Besides as_extension, we could also check
 508            !strchr(&match[1], match[0]) but that would be inefficient.
 509          */
 510         if (as_extension) {
 511             s = strrchr(s, match[0]);
 512             return (s == NULL)? false : !strcmp(s, match);
 513         }
 514 
 515         mlen = strlen(match);
 516         slen = strlen(s);
 517         return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
 518     }
 519 }
 520 
 521 /*!
 522  * \internal
 523  * \brief Check whether a string ends with a certain sequence
 524  *
 525  * \param[in] s      String to check
 526  * \param[in] match  Sequence to match against end of \p s
 527  *
 528  * \return \c true if \p s ends case-sensitively with match, \c false otherwise
 529  * \note pcmk__ends_with_ext() can be used if the first character of match
 530  *       does not recur in match.
 531  */
 532 bool
 533 pcmk__ends_with(const char *s, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
 534 {
 535     return ends_with(s, match, false);
 536 }
 537 
 538 /*!
 539  * \internal
 540  * \brief Check whether a string ends with a certain "extension"
 541  *
 542  * \param[in] s      String to check
 543  * \param[in] match  Extension to match against end of \p s, that is,
 544  *                   its first character must not occur anywhere
 545  *                   in the rest of that very sequence (example: file
 546  *                   extension where the last dot is its delimiter,
 547  *                   e.g., ".html"); incorrect results may be
 548  *                   returned otherwise.
 549  *
 550  * \return \c true if \p s ends (verbatim, i.e., case sensitively)
 551  *         with "extension" designated as \p match (including empty
 552  *         string), \c false otherwise
 553  *
 554  * \note Main incentive to prefer this function over \c pcmk__ends_with()
 555  *       where possible is the efficiency (at the cost of added
 556  *       restriction on \p match as stated; the complexity class
 557  *       remains the same, though: BigO(M+N) vs. BigO(M+2N)).
 558  */
 559 bool
 560 pcmk__ends_with_ext(const char *s, const char *match)
     /* [previous][next][first][last][top][bottom][index][help] */
 561 {
 562     return ends_with(s, match, true);
 563 }
 564 
 565 /*!
 566  * \internal
 567  * \brief Create a hash of a string suitable for use with GHashTable
 568  *
 569  * \param[in] v  String to hash
 570  *
 571  * \return A hash of \p v compatible with g_str_hash() before glib 2.28
 572  * \note glib changed their hash implementation:
 573  *
 574  * https://gitlab.gnome.org/GNOME/glib/commit/354d655ba8a54b754cb5a3efb42767327775696c
 575  *
 576  * Note that the new g_str_hash is presumably a *better* hash (it's actually
 577  * a correct implementation of DJB's hash), but we need to preserve existing
 578  * behaviour, because the hash key ultimately determines the "sort" order
 579  * when iterating through GHashTables, which affects allocation of scores to
 580  * clone instances when iterating through rsc->allowed_nodes.  It (somehow)
 581  * also appears to have some minor impact on the ordering of a few
 582  * pseudo_event IDs in the transition graph.
 583  */
 584 static guint
 585 pcmk__str_hash(gconstpointer v)
     /* [previous][next][first][last][top][bottom][index][help] */
 586 {
 587     const signed char *p;
 588     guint32 h = 0;
 589 
 590     for (p = v; *p != '\0'; p++)
 591         h = (h << 5) - h + *p;
 592 
 593     return h;
 594 }
 595 
 596 /*!
 597  * \internal
 598  * \brief Create a hash table with case-sensitive strings as keys
 599  *
 600  * \param[in] key_destroy_func    Function to free a key
 601  * \param[in] value_destroy_func  Function to free a value
 602  *
 603  * \return Newly allocated hash table
 604  * \note It is the caller's responsibility to free the result, using
 605  *       g_hash_table_destroy().
 606  */
 607 GHashTable *
 608 pcmk__strkey_table(GDestroyNotify key_destroy_func,
     /* [previous][next][first][last][top][bottom][index][help] */
 609                    GDestroyNotify value_destroy_func)
 610 {
 611     return g_hash_table_new_full(pcmk__str_hash, g_str_equal,
 612                                  key_destroy_func, value_destroy_func);
 613 }
 614 
 615 /* used with hash tables where case does not matter */
 616 static gboolean
 617 pcmk__strcase_equal(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 618 {
 619     return pcmk__str_eq((const char *)a, (const char *)b, pcmk__str_casei);
 620 }
 621 
 622 static guint
 623 pcmk__strcase_hash(gconstpointer v)
     /* [previous][next][first][last][top][bottom][index][help] */
 624 {
 625     const signed char *p;
 626     guint32 h = 0;
 627 
 628     for (p = v; *p != '\0'; p++)
 629         h = (h << 5) - h + g_ascii_tolower(*p);
 630 
 631     return h;
 632 }
 633 
 634 /*!
 635  * \internal
 636  * \brief Create a hash table with case-insensitive strings as keys
 637  *
 638  * \param[in] key_destroy_func    Function to free a key
 639  * \param[in] value_destroy_func  Function to free a value
 640  *
 641  * \return Newly allocated hash table
 642  * \note It is the caller's responsibility to free the result, using
 643  *       g_hash_table_destroy().
 644  */
 645 GHashTable *
 646 pcmk__strikey_table(GDestroyNotify key_destroy_func,
     /* [previous][next][first][last][top][bottom][index][help] */
 647                     GDestroyNotify value_destroy_func)
 648 {
 649     return g_hash_table_new_full(pcmk__strcase_hash, pcmk__strcase_equal,
 650                                  key_destroy_func, value_destroy_func);
 651 }
 652 
 653 static void
 654 copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 655 {
 656     if (key && value && user_data) {
 657         g_hash_table_insert((GHashTable*)user_data, strdup(key), strdup(value));
 658     }
 659 }
 660 
 661 /*!
 662  * \internal
 663  * \brief Copy a hash table that uses dynamically allocated strings
 664  *
 665  * \param[in,out] old_table  Hash table to duplicate
 666  *
 667  * \return New hash table with copies of everything in \p old_table
 668  * \note This assumes the hash table uses dynamically allocated strings -- that
 669  *       is, both the key and value free functions are free().
 670  */
 671 GHashTable *
 672 pcmk__str_table_dup(GHashTable *old_table)
     /* [previous][next][first][last][top][bottom][index][help] */
 673 {
 674     GHashTable *new_table = NULL;
 675 
 676     if (old_table) {
 677         new_table = pcmk__strkey_table(free, free);
 678         g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
 679     }
 680     return new_table;
 681 }
 682 
 683 /*!
 684  * \internal
 685  * \brief Add a word to a string list of words
 686  *
 687  * \param[in,out] list       Pointer to current string list (may not be \p NULL)
 688  * \param[in]     init_size  \p list will be initialized to at least this size,
 689  *                           if it needs initialization (if 0, use GLib's default
 690  *                           initial string size)
 691  * \param[in]     word       String to add to \p list (\p list will be
 692  *                           unchanged if this is \p NULL or the empty string)
 693  * \param[in]     separator  String to separate words in \p list
 694  *                           (a space will be used if this is NULL)
 695  *
 696  * \note \p word may contain \p separator, though that would be a bad idea if
 697  *       the string needs to be parsed later.
 698  */
 699 void
 700 pcmk__add_separated_word(GString **list, size_t init_size, const char *word,
     /* [previous][next][first][last][top][bottom][index][help] */
 701                          const char *separator)
 702 {
 703     CRM_ASSERT(list != NULL);
 704 
 705     if (pcmk__str_empty(word)) {
 706         return;
 707     }
 708 
 709     if (*list == NULL) {
 710         if (init_size > 0) {
 711             *list = g_string_sized_new(init_size);
 712         } else {
 713             *list = g_string_new(NULL);
 714         }
 715     }
 716 
 717     if ((*list)->len == 0) {
 718         // Don't add a separator before the first word in the list
 719         separator = "";
 720 
 721     } else if (separator == NULL) {
 722         // Default to space-separated
 723         separator = " ";
 724     }
 725 
 726     g_string_append(*list, separator);
 727     g_string_append(*list, word);
 728 }
 729 
 730 /*!
 731  * \internal
 732  * \brief Compress data
 733  *
 734  * \param[in]  data        Data to compress
 735  * \param[in]  length      Number of characters of data to compress
 736  * \param[in]  max         Maximum size of compressed data (or 0 to estimate)
 737  * \param[out] result      Where to store newly allocated compressed result
 738  * \param[out] result_len  Where to store actual compressed length of result
 739  *
 740  * \return Standard Pacemaker return code
 741  */
 742 int
 743 pcmk__compress(const char *data, unsigned int length, unsigned int max,
     /* [previous][next][first][last][top][bottom][index][help] */
 744                char **result, unsigned int *result_len)
 745 {
 746     int rc;
 747     char *compressed = NULL;
 748     char *uncompressed = strdup(data);
 749 #ifdef CLOCK_MONOTONIC
 750     struct timespec after_t;
 751     struct timespec before_t;
 752 #endif
 753 
 754     if (max == 0) {
 755         max = (length * 1.01) + 601; // Size guaranteed to hold result
 756     }
 757 
 758 #ifdef CLOCK_MONOTONIC
 759     clock_gettime(CLOCK_MONOTONIC, &before_t);
 760 #endif
 761 
 762     compressed = calloc((size_t) max, sizeof(char));
 763     CRM_ASSERT(compressed);
 764 
 765     *result_len = max;
 766     rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length,
 767                                   CRM_BZ2_BLOCKS, 0, CRM_BZ2_WORK);
 768     rc = pcmk__bzlib2rc(rc);
 769 
 770     free(uncompressed);
 771 
 772     if (rc != pcmk_rc_ok) {
 773         crm_err("Compression of %d bytes failed: %s " CRM_XS " rc=%d",
 774                 length, pcmk_rc_str(rc), rc);
 775         free(compressed);
 776         return rc;
 777     }
 778 
 779 #ifdef CLOCK_MONOTONIC
 780     clock_gettime(CLOCK_MONOTONIC, &after_t);
 781 
 782     crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
 783              length, *result_len, length / (*result_len),
 784              (after_t.tv_sec - before_t.tv_sec) * 1000 +
 785              (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
 786 #else
 787     crm_trace("Compressed %d bytes into %d (ratio %d:1)",
 788              length, *result_len, length / (*result_len));
 789 #endif
 790 
 791     *result = compressed;
 792     return pcmk_rc_ok;
 793 }
 794 
 795 char *
 796 crm_strdup_printf(char const *format, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 797 {
 798     va_list ap;
 799     int len = 0;
 800     char *string = NULL;
 801 
 802     va_start(ap, format);
 803     len = vasprintf (&string, format, ap);
 804     CRM_ASSERT(len > 0);
 805     va_end(ap);
 806     return string;
 807 }
 808 
 809 int
 810 pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
     /* [previous][next][first][last][top][bottom][index][help] */
 811 {
 812     char *remainder = NULL;
 813     int rc = pcmk_rc_ok;
 814 
 815     CRM_ASSERT(start != NULL && end != NULL);
 816 
 817     *start = PCMK__PARSE_INT_DEFAULT;
 818     *end = PCMK__PARSE_INT_DEFAULT;
 819 
 820     crm_trace("Attempting to decode: [%s]", srcstring);
 821     if (pcmk__str_eq(srcstring, "", pcmk__str_null_matches)) {
 822         return ENODATA;
 823     } else if (pcmk__str_eq(srcstring, "-", pcmk__str_none)) {
 824         return pcmk_rc_bad_input;
 825     }
 826 
 827     /* String starts with a dash, so this is either a range with
 828      * no beginning or garbage.
 829      * */
 830     if (*srcstring == '-') {
 831         int rc = scan_ll(srcstring+1, end, PCMK__PARSE_INT_DEFAULT, &remainder);
 832 
 833         if (rc != pcmk_rc_ok || *remainder != '\0') {
 834             return pcmk_rc_bad_input;
 835         } else {
 836             return pcmk_rc_ok;
 837         }
 838     }
 839 
 840     rc = scan_ll(srcstring, start, PCMK__PARSE_INT_DEFAULT, &remainder);
 841     if (rc != pcmk_rc_ok) {
 842         return rc;
 843     }
 844 
 845     if (*remainder && *remainder == '-') {
 846         if (*(remainder+1)) {
 847             char *more_remainder = NULL;
 848             int rc = scan_ll(remainder+1, end, PCMK__PARSE_INT_DEFAULT,
 849                              &more_remainder);
 850 
 851             if (rc != pcmk_rc_ok) {
 852                 return rc;
 853             } else if (*more_remainder != '\0') {
 854                 return pcmk_rc_bad_input;
 855             }
 856         }
 857     } else if (*remainder && *remainder != '-') {
 858         *start = PCMK__PARSE_INT_DEFAULT;
 859         return pcmk_rc_bad_input;
 860     } else {
 861         /* The input string contained only one number.  Set start and end
 862          * to the same value and return pcmk_rc_ok.  This gives the caller
 863          * a way to tell this condition apart from a range with no end.
 864          */
 865         *end = *start;
 866     }
 867 
 868     return pcmk_rc_ok;
 869 }
 870 
 871 /*!
 872  * \internal
 873  * \brief Find a string in a list of strings
 874  *
 875  * \note This function takes the same flags and has the same behavior as
 876  *       pcmk__str_eq().
 877  *
 878  * \note No matter what input string or flags are provided, an empty
 879  *       list will always return FALSE.
 880  *
 881  * \param[in] s      String to search for
 882  * \param[in] lst    List to search
 883  * \param[in] flags  A bitfield of pcmk__str_flags to modify operation
 884  *
 885  * \return \c TRUE if \p s is in \p lst, or \c FALSE otherwise
 886  */
 887 gboolean
 888 pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 889 {
 890     for (const GList *ele = lst; ele != NULL; ele = ele->next) {
 891         if (pcmk__str_eq(s, ele->data, flags)) {
 892             return TRUE;
 893         }
 894     }
 895 
 896     return FALSE;
 897 }
 898 
 899 static bool
 900 str_any_of(const char *s, va_list args, uint32_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 901 {
 902     if (s == NULL) {
 903         return pcmk_is_set(flags, pcmk__str_null_matches);
 904     }
 905 
 906     while (1) {
 907         const char *ele = va_arg(args, const char *);
 908 
 909         if (ele == NULL) {
 910             break;
 911         } else if (pcmk__str_eq(s, ele, flags)) {
 912             return true;
 913         }
 914     }
 915 
 916     return false;
 917 }
 918 
 919 /*!
 920  * \internal
 921  * \brief Is a string a member of a list of strings?
 922  *
 923  * \param[in]  s    String to search for in \p ...
 924  * \param[in]  ...  Strings to compare \p s against.  The final string
 925  *                  must be NULL.
 926  *
 927  * \note The comparison is done case-insensitively.  The function name is
 928  *       meant to be reminiscent of strcasecmp.
 929  *
 930  * \return \c true if \p s is in \p ..., or \c false otherwise
 931  */
 932 bool
 933 pcmk__strcase_any_of(const char *s, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 934 {
 935     va_list ap;
 936     bool rc;
 937 
 938     va_start(ap, s);
 939     rc = str_any_of(s, ap, pcmk__str_casei);
 940     va_end(ap);
 941     return rc;
 942 }
 943 
 944 /*!
 945  * \internal
 946  * \brief Is a string a member of a list of strings?
 947  *
 948  * \param[in]  s    String to search for in \p ...
 949  * \param[in]  ...  Strings to compare \p s against.  The final string
 950  *                  must be NULL.
 951  *
 952  * \note The comparison is done taking case into account.
 953  *
 954  * \return \c true if \p s is in \p ..., or \c false otherwise
 955  */
 956 bool
 957 pcmk__str_any_of(const char *s, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 958 {
 959     va_list ap;
 960     bool rc;
 961 
 962     va_start(ap, s);
 963     rc = str_any_of(s, ap, pcmk__str_none);
 964     va_end(ap);
 965     return rc;
 966 }
 967 
 968 /*!
 969  * \internal
 970  * \brief Check whether a character is in any of a list of strings
 971  *
 972  * \param[in]   ch      Character (ASCII) to search for
 973  * \param[in]   ...     Strings to search. Final argument must be
 974  *                      \c NULL.
 975  *
 976  * \return  \c true if any of \p ... contain \p ch, \c false otherwise
 977  * \note    \p ... must contain at least one argument (\c NULL).
 978  */
 979 bool
 980 pcmk__char_in_any_str(int ch, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 981 {
 982     bool rc = false;
 983     va_list ap;
 984 
 985     /*
 986      * Passing a char to va_start() can generate compiler warnings,
 987      * so ch is declared as an int.
 988      */
 989     va_start(ap, ch);
 990 
 991     while (1) {
 992         const char *ele = va_arg(ap, const char *);
 993 
 994         if (ele == NULL) {
 995             break;
 996         } else if (strchr(ele, ch) != NULL) {
 997             rc = true;
 998             break;
 999         }
1000     }
1001 
1002     va_end(ap);
1003     return rc;
1004 }
1005 
1006 /*!
1007  * \internal
1008  * \brief Sort strings, with numeric portions sorted numerically
1009  *
1010  * Sort two strings case-insensitively like strcasecmp(), but with any numeric
1011  * portions of the string sorted numerically. This is particularly useful for
1012  * node names (for example, "node10" will sort higher than "node9" but lower
1013  * than "remotenode9").
1014  *
1015  * \param[in] s1  First string to compare (must not be NULL)
1016  * \param[in] s2  Second string to compare (must not be NULL)
1017  *
1018  * \retval -1 \p s1 comes before \p s2
1019  * \retval  0 \p s1 and \p s2 are equal
1020  * \retval  1 \p s1 comes after \p s2
1021  */
1022 int
1023 pcmk__numeric_strcasecmp(const char *s1, const char *s2)
     /* [previous][next][first][last][top][bottom][index][help] */
1024 {
1025     CRM_ASSERT((s1 != NULL) && (s2 != NULL));
1026 
1027     while (*s1 && *s2) {
1028         if (isdigit(*s1) && isdigit(*s2)) {
1029             // If node names contain a number, sort numerically
1030 
1031             char *end1 = NULL;
1032             char *end2 = NULL;
1033             long num1 = strtol(s1, &end1, 10);
1034             long num2 = strtol(s2, &end2, 10);
1035 
1036             // allow ordering e.g. 007 > 7
1037             size_t len1 = end1 - s1;
1038             size_t len2 = end2 - s2;
1039 
1040             if (num1 < num2) {
1041                 return -1;
1042             } else if (num1 > num2) {
1043                 return 1;
1044             } else if (len1 < len2) {
1045                 return -1;
1046             } else if (len1 > len2) {
1047                 return 1;
1048             }
1049             s1 = end1;
1050             s2 = end2;
1051         } else {
1052             // Compare non-digits case-insensitively
1053             int lower1 = tolower(*s1);
1054             int lower2 = tolower(*s2);
1055 
1056             if (lower1 < lower2) {
1057                 return -1;
1058             } else if (lower1 > lower2) {
1059                 return 1;
1060             }
1061             ++s1;
1062             ++s2;
1063         }
1064     }
1065     if (!*s1 && *s2) {
1066         return -1;
1067     } else if (*s1 && !*s2) {
1068         return 1;
1069     }
1070     return 0;
1071 }
1072 
1073 /*!
1074  * \internal
1075  * \brief Sort strings.
1076  *
1077  * This is your one-stop function for string comparison. By default, this
1078  * function works like \p g_strcmp0. That is, like \p strcmp but a \p NULL
1079  * string sorts before a non-<tt>NULL</tt> string.
1080  *
1081  * The \p pcmk__str_none flag produces the default behavior. Behavior can be
1082  * changed with various flags:
1083  *
1084  * - \p pcmk__str_regex - The second string is a regular expression that the
1085  *                        first string will be matched against.
1086  * - \p pcmk__str_casei - By default, comparisons are done taking case into
1087  *                        account. This flag makes comparisons case-
1088  *                        insensitive. This can be combined with
1089  *                        \p pcmk__str_regex.
1090  * - \p pcmk__str_null_matches - If one string is \p NULL and the other is not,
1091  *                               still return \p 0.
1092  * - \p pcmk__str_star_matches - If one string is \p "*" and the other is not,
1093  *                               still return \p 0.
1094  *
1095  * \param[in] s1     First string to compare
1096  * \param[in] s2     Second string to compare, or a regular expression to
1097  *                   match if \p pcmk__str_regex is set
1098  * \param[in] flags  A bitfield of \p pcmk__str_flags to modify operation
1099  *
1100  * \retval  negative \p s1 is \p NULL or comes before \p s2
1101  * \retval  0        \p s1 and \p s2 are equal, or \p s1 is found in \p s2 if
1102  *                   \c pcmk__str_regex is set
1103  * \retval  positive \p s2 is \p NULL or \p s1 comes after \p s2, or \p s2
1104  *                   is an invalid regular expression, or \p s1 was not found
1105  *                   in \p s2 if \p pcmk__str_regex is set.
1106  */
1107 int
1108 pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
1109 {
1110     /* If this flag is set, the second string is a regex. */
1111     if (pcmk_is_set(flags, pcmk__str_regex)) {
1112         regex_t r_patt;
1113         int reg_flags = REG_EXTENDED | REG_NOSUB;
1114         int regcomp_rc = 0;
1115         int rc = 0;
1116 
1117         if (s1 == NULL || s2 == NULL) {
1118             return 1;
1119         }
1120 
1121         if (pcmk_is_set(flags, pcmk__str_casei)) {
1122             reg_flags |= REG_ICASE;
1123         }
1124         regcomp_rc = regcomp(&r_patt, s2, reg_flags);
1125         if (regcomp_rc != 0) {
1126             rc = 1;
1127             crm_err("Bad regex '%s' for update: %s", s2, strerror(regcomp_rc));
1128         } else {
1129             rc = regexec(&r_patt, s1, 0, NULL, 0);
1130             regfree(&r_patt);
1131             if (rc != 0) {
1132                 rc = 1;
1133             }
1134         }
1135         return rc;
1136     }
1137 
1138     /* If the strings are the same pointer, return 0 immediately. */
1139     if (s1 == s2) {
1140         return 0;
1141     }
1142 
1143     /* If this flag is set, return 0 if either (or both) of the input strings
1144      * are NULL.  If neither one is NULL, we need to continue and compare
1145      * them normally.
1146      */
1147     if (pcmk_is_set(flags, pcmk__str_null_matches)) {
1148         if (s1 == NULL || s2 == NULL) {
1149             return 0;
1150         }
1151     }
1152 
1153     /* Handle the cases where one is NULL and the str_null_matches flag is not set.
1154      * A NULL string always sorts to the beginning.
1155      */
1156     if (s1 == NULL) {
1157         return -1;
1158     } else if (s2 == NULL) {
1159         return 1;
1160     }
1161 
1162     /* If this flag is set, return 0 if either (or both) of the input strings
1163      * are "*".  If neither one is, we need to continue and compare them
1164      * normally.
1165      */
1166     if (pcmk_is_set(flags, pcmk__str_star_matches)) {
1167         if (strcmp(s1, "*") == 0 || strcmp(s2, "*") == 0) {
1168             return 0;
1169         }
1170     }
1171 
1172     if (pcmk_is_set(flags, pcmk__str_casei)) {
1173         return strcasecmp(s1, s2);
1174     } else {
1175         return strcmp(s1, s2);
1176     }
1177 }
1178 
1179 /*!
1180  * \internal
1181  * \brief Update a dynamically allocated string with a new value
1182  *
1183  * Given a dynamically allocated string and a new value for it, if the string
1184  * is different from the new value, free the string and replace it with either a
1185  * newly allocated duplicate of the value or NULL as appropriate.
1186  *
1187  * \param[in,out] str    Pointer to dynamically allocated string
1188  * \param[in]     value  New value to duplicate (or NULL)
1189  *
1190  * \note The caller remains responsibile for freeing \p *str.
1191  */
1192 void
1193 pcmk__str_update(char **str, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
1194 {
1195     if ((str != NULL) && !pcmk__str_eq(*str, value, pcmk__str_none)) {
1196         free(*str);
1197         if (value == NULL) {
1198             *str = NULL;
1199         } else {
1200             *str = strdup(value);
1201             CRM_ASSERT(*str != NULL);
1202         }
1203     }
1204 }
1205 
1206 /*!
1207  * \internal
1208  * \brief Append a list of strings to a destination \p GString
1209  *
1210  * \param[in,out] buffer  Where to append the strings (must not be \p NULL)
1211  * \param[in]     ...     A <tt>NULL</tt>-terminated list of strings
1212  *
1213  * \note This tends to be more efficient than a single call to
1214  *       \p g_string_append_printf().
1215  */
1216 void
1217 pcmk__g_strcat(GString *buffer, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
1218 {
1219     va_list ap;
1220 
1221     CRM_ASSERT(buffer != NULL);
1222     va_start(ap, buffer);
1223 
1224     while (true) {
1225         const char *ele = va_arg(ap, const char *);
1226 
1227         if (ele == NULL) {
1228             break;
1229         }
1230         g_string_append(buffer, ele);
1231     }
1232     va_end(ap);
1233 }
1234 
1235 // Deprecated functions kept only for backward API compatibility
1236 // LCOV_EXCL_START
1237 
1238 #include <crm/common/util_compat.h>
1239 
1240 gboolean
1241 safe_str_neq(const char *a, const char *b)
     /* [previous][next][first][last][top][bottom][index][help] */
1242 {
1243     if (a == b) {
1244         return FALSE;
1245 
1246     } else if (a == NULL || b == NULL) {
1247         return TRUE;
1248 
1249     } else if (strcasecmp(a, b) == 0) {
1250         return FALSE;
1251     }
1252     return TRUE;
1253 }
1254 
1255 gboolean
1256 crm_str_eq(const char *a, const char *b, gboolean use_case)
     /* [previous][next][first][last][top][bottom][index][help] */
1257 {
1258     if (use_case) {
1259         return g_strcmp0(a, b) == 0;
1260 
1261         /* TODO - Figure out which calls, if any, really need to be case independent */
1262     } else if (a == b) {
1263         return TRUE;
1264 
1265     } else if (a == NULL || b == NULL) {
1266         /* shouldn't be comparing NULLs */
1267         return FALSE;
1268 
1269     } else if (strcasecmp(a, b) == 0) {
1270         return TRUE;
1271     }
1272     return FALSE;
1273 }
1274 
1275 char *
1276 crm_itoa_stack(int an_int, char *buffer, size_t len)
     /* [previous][next][first][last][top][bottom][index][help] */
1277 {
1278     if (buffer != NULL) {
1279         snprintf(buffer, len, "%d", an_int);
1280     }
1281     return buffer;
1282 }
1283 
1284 guint
1285 g_str_hash_traditional(gconstpointer v)
     /* [previous][next][first][last][top][bottom][index][help] */
1286 {
1287     return pcmk__str_hash(v);
1288 }
1289 
1290 gboolean
1291 crm_strcase_equal(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
1292 {
1293     return pcmk__strcase_equal(a, b);
1294 }
1295 
1296 guint
1297 crm_strcase_hash(gconstpointer v)
     /* [previous][next][first][last][top][bottom][index][help] */
1298 {
1299     return pcmk__strcase_hash(v);
1300 }
1301 
1302 GHashTable *
1303 crm_str_table_dup(GHashTable *old_table)
     /* [previous][next][first][last][top][bottom][index][help] */
1304 {
1305     return pcmk__str_table_dup(old_table);
1306 }
1307 
1308 long long
1309 crm_parse_ll(const char *text, const char *default_text)
     /* [previous][next][first][last][top][bottom][index][help] */
1310 {
1311     long long result;
1312 
1313     if (text == NULL) {
1314         text = default_text;
1315         if (text == NULL) {
1316             crm_err("No default conversion value supplied");
1317             errno = EINVAL;
1318             return PCMK__PARSE_INT_DEFAULT;
1319         }
1320     }
1321     scan_ll(text, &result, PCMK__PARSE_INT_DEFAULT, NULL);
1322     return result;
1323 }
1324 
1325 int
1326 crm_parse_int(const char *text, const char *default_text)
     /* [previous][next][first][last][top][bottom][index][help] */
1327 {
1328     long long result = crm_parse_ll(text, default_text);
1329 
1330     if (result < INT_MIN) {
1331         // If errno is ERANGE, crm_parse_ll() has already logged a message
1332         if (errno != ERANGE) {
1333             crm_err("Conversion of %s was clipped: %lld", text, result);
1334             errno = ERANGE;
1335         }
1336         return INT_MIN;
1337 
1338     } else if (result > INT_MAX) {
1339         // If errno is ERANGE, crm_parse_ll() has already logged a message
1340         if (errno != ERANGE) {
1341             crm_err("Conversion of %s was clipped: %lld", text, result);
1342             errno = ERANGE;
1343         }
1344         return INT_MAX;
1345     }
1346 
1347     return (int) result;
1348 }
1349 
1350 char *
1351 crm_strip_trailing_newline(char *str)
     /* [previous][next][first][last][top][bottom][index][help] */
1352 {
1353     return pcmk__trim(str);
1354 }
1355 
1356 int
1357 pcmk_numeric_strcasecmp(const char *s1, const char *s2)
     /* [previous][next][first][last][top][bottom][index][help] */
1358 {
1359     return pcmk__numeric_strcasecmp(s1, s2);
1360 }
1361 
1362 // LCOV_EXCL_STOP
1363 // End deprecated API

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