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. pcmk_parse_interval_spec
  9. crm_is_true
  10. crm_str_to_boolean
  11. pcmk__trim
  12. pcmk__starts_with
  13. ends_with
  14. pcmk__ends_with
  15. pcmk__ends_with_ext
  16. pcmk__str_hash
  17. pcmk__strkey_table
  18. pcmk__insert_dup
  19. pcmk__strcase_equal
  20. pcmk__strcase_hash
  21. pcmk__strikey_table
  22. copy_str_table_entry
  23. pcmk__str_table_dup
  24. pcmk__add_separated_word
  25. pcmk__compress
  26. crm_strdup_printf
  27. pcmk__parse_ll_range
  28. pcmk__str_in_list
  29. str_any_of
  30. pcmk__strcase_any_of
  31. pcmk__str_any_of
  32. pcmk__numeric_strcasecmp
  33. pcmk__strcmp
  34. pcmk__str_copy_as
  35. pcmk__str_update
  36. pcmk__g_strcat
  37. safe_str_neq
  38. crm_str_eq
  39. crm_itoa_stack
  40. g_str_hash_traditional
  41. crm_strcase_equal
  42. crm_strcase_hash
  43. crm_str_table_dup
  44. crm_parse_ll
  45. crm_parse_int
  46. crm_strip_trailing_newline
  47. pcmk_numeric_strcasecmp

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

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