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