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