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