pacemaker 2.1.4-dc6eb4362e
Scalable High-Availability cluster resource manager
strings.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2022 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11
12#ifndef _GNU_SOURCE
13# define _GNU_SOURCE
14#endif
15
16#include <regex.h>
17#include <stdio.h>
18#include <string.h>
19#include <stdlib.h>
20#include <ctype.h>
21#include <float.h> // DBL_MIN
22#include <limits.h>
23#include <math.h> // fabs()
24#include <bzlib.h>
25#include <sys/types.h>
26
42static int
43scan_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
96int
97pcmk__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
126int
127pcmk__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
160int
161pcmk__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
198int
199pcmk__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);
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 (fabs(*result) > DBL_MIN) {
231 rc = EOVERFLOW;
232 over_under = "over";
233 } else {
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
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;
254
255 crm_debug("Could not parse floating-point value from '%s' (using "
256 "%.1f instead): No digits found", text,
258
259 } else if (fabs(*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) {
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
310int
311pcmk__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
363long long
364crm_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) {
376 }
377
378 num_start = input + strspn(input, WHITESPACE);
379 num_len = strspn(num_start, NUMCHARS);
380 if (num_len < 1) {
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')) {
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
415gboolean
416crm_is_true(const char *s)
417{
418 gboolean ret = FALSE;
419
420 if (s != NULL) {
421 crm_str_to_boolean(s, &ret);
422 }
423 return ret;
424}
425
426int
427crm_str_to_boolean(const char *s, int *ret)
428{
429 if (s == NULL) {
430 return -1;
431
432 } else if (strcasecmp(s, "true") == 0
433 || strcasecmp(s, "on") == 0
434 || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
435 *ret = TRUE;
436 return 1;
437
438 } else if (strcasecmp(s, "false") == 0
439 || strcasecmp(s, "off") == 0
440 || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) {
441 *ret = FALSE;
442 return 1;
443 }
444 return -1;
445}
446
455char *
456pcmk__trim(char *str)
457{
458 int len;
459
460 if (str == NULL) {
461 return str;
462 }
463
464 for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
465 str[len] = '\0';
466 }
467
468 return str;
469}
470
483bool
484pcmk__starts_with(const char *str, const char *prefix)
485{
486 const char *s = str;
487 const char *p = prefix;
488
489 if (!s || !p) {
490 return false;
491 }
492 while (*s && *p) {
493 if (*s++ != *p++) {
494 return false;
495 }
496 }
497 return (*p == 0);
498}
499
500static inline bool
501ends_with(const char *s, const char *match, bool as_extension)
502{
503 if (pcmk__str_empty(match)) {
504 return true;
505 } else if (s == NULL) {
506 return false;
507 } else {
508 size_t slen, mlen;
509
510 /* Besides as_extension, we could also check
511 !strchr(&match[1], match[0]) but that would be inefficient.
512 */
513 if (as_extension) {
514 s = strrchr(s, match[0]);
515 return (s == NULL)? false : !strcmp(s, match);
516 }
517
518 mlen = strlen(match);
519 slen = strlen(s);
520 return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
521 }
522}
523
535bool
536pcmk__ends_with(const char *s, const char *match)
537{
538 return ends_with(s, match, false);
539}
540
562bool
563pcmk__ends_with_ext(const char *s, const char *match)
564{
565 return ends_with(s, match, true);
566}
567
587static guint
588pcmk__str_hash(gconstpointer v)
589{
590 const signed char *p;
591 guint32 h = 0;
592
593 for (p = v; *p != '\0'; p++)
594 h = (h << 5) - h + *p;
595
596 return h;
597}
598
610GHashTable *
611pcmk__strkey_table(GDestroyNotify key_destroy_func,
612 GDestroyNotify value_destroy_func)
613{
614 return g_hash_table_new_full(pcmk__str_hash, g_str_equal,
615 key_destroy_func, value_destroy_func);
616}
617
618/* used with hash tables where case does not matter */
619static gboolean
620pcmk__strcase_equal(gconstpointer a, gconstpointer b)
621{
622 return pcmk__str_eq((const char *)a, (const char *)b, pcmk__str_casei);
623}
624
625static guint
626pcmk__strcase_hash(gconstpointer v)
627{
628 const signed char *p;
629 guint32 h = 0;
630
631 for (p = v; *p != '\0'; p++)
632 h = (h << 5) - h + g_ascii_tolower(*p);
633
634 return h;
635}
636
648GHashTable *
649pcmk__strikey_table(GDestroyNotify key_destroy_func,
650 GDestroyNotify value_destroy_func)
651{
652 return g_hash_table_new_full(pcmk__strcase_hash, pcmk__strcase_equal,
653 key_destroy_func, value_destroy_func);
654}
655
656static void
657copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
658{
659 if (key && value && user_data) {
660 g_hash_table_insert((GHashTable*)user_data, strdup(key), strdup(value));
661 }
662}
663
674GHashTable *
675pcmk__str_table_dup(GHashTable *old_table)
676{
677 GHashTable *new_table = NULL;
678
679 if (old_table) {
680 new_table = pcmk__strkey_table(free, free);
681 g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
682 }
683 return new_table;
684}
685
702void
703pcmk__add_separated_word(char **list, size_t *len, const char *word,
704 const char *separator)
705{
706 size_t orig_len, new_len;
707
708 CRM_ASSERT(list != NULL);
709
710 if (pcmk__str_empty(word)) {
711 return;
712 }
713
714 // Use provided length, or calculate it if not available
715 orig_len = (len != NULL)? *len : ((*list == NULL)? 0 : strlen(*list));
716
717 // Don't add a separator before the first word in the list
718 if (orig_len == 0) {
719 separator = "";
720
721 // Default to space-separated
722 } else if (separator == NULL) {
723 separator = " ";
724 }
725
726 new_len = orig_len + strlen(separator) + strlen(word);
727 if (len != NULL) {
728 *len = new_len;
729 }
730
731 // +1 for null terminator
732 *list = pcmk__realloc(*list, new_len + 1);
733 sprintf(*list + orig_len, "%s%s", separator, word);
734}
735
748int
749pcmk__compress(const char *data, unsigned int length, unsigned int max,
750 char **result, unsigned int *result_len)
751{
752 int rc;
753 char *compressed = NULL;
754 char *uncompressed = strdup(data);
755#ifdef CLOCK_MONOTONIC
756 struct timespec after_t;
757 struct timespec before_t;
758#endif
759
760 if (max == 0) {
761 max = (length * 1.01) + 601; // Size guaranteed to hold result
762 }
763
764#ifdef CLOCK_MONOTONIC
765 clock_gettime(CLOCK_MONOTONIC, &before_t);
766#endif
767
768 compressed = calloc((size_t) max, sizeof(char));
769 CRM_ASSERT(compressed);
770
771 *result_len = max;
772 rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length,
774 free(uncompressed);
775 if (rc != BZ_OK) {
776 crm_err("Compression of %d bytes failed: %s " CRM_XS " bzerror=%d",
777 length, bz2_strerror(rc), rc);
778 free(compressed);
779 return pcmk_rc_error;
780 }
781
782#ifdef CLOCK_MONOTONIC
783 clock_gettime(CLOCK_MONOTONIC, &after_t);
784
785 crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
786 length, *result_len, length / (*result_len),
787 (after_t.tv_sec - before_t.tv_sec) * 1000 +
788 (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
789#else
790 crm_trace("Compressed %d bytes into %d (ratio %d:1)",
791 length, *result_len, length / (*result_len));
792#endif
793
794 *result = compressed;
795 return pcmk_rc_ok;
796}
797
798char *
799crm_strdup_printf(char const *format, ...)
800{
801 va_list ap;
802 int len = 0;
803 char *string = NULL;
804
805 va_start(ap, format);
806 len = vasprintf (&string, format, ap);
807 CRM_ASSERT(len > 0);
808 va_end(ap);
809 return string;
810}
811
812int
813pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
814{
815 char *remainder = NULL;
816
817 CRM_ASSERT(start != NULL && end != NULL);
818
821
822 crm_trace("Attempting to decode: [%s]", srcstring);
823 if (pcmk__str_empty(srcstring) || !strcmp(srcstring, "-")) {
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') {
835 } else {
836 return pcmk_rc_ok;
837 }
838 }
839
840 if (scan_ll(srcstring, start, PCMK__PARSE_INT_DEFAULT,
841 &remainder) != pcmk_rc_ok) {
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 || *more_remainder != '\0') {
853 }
854 }
855 } else if (*remainder && *remainder != '-') {
858 } else {
859 /* The input string contained only one number. Set start and end
860 * to the same value and return pcmk_rc_ok. This gives the caller
861 * a way to tell this condition apart from a range with no end.
862 */
863 *end = *start;
864 }
865
866 return pcmk_rc_ok;
867}
868
885gboolean
886pcmk__str_in_list(const gchar *s, GList *lst, uint32_t flags)
887{
888 for (GList *ele = lst; ele != NULL; ele = ele->next) {
889 if (pcmk__str_eq(s, ele->data, flags)) {
890 return TRUE;
891 }
892 }
893
894 return FALSE;
895}
896
897static bool
898str_any_of(const char *s, va_list args, uint32_t flags)
899{
900 if (s == NULL) {
902 }
903
904 while (1) {
905 const char *ele = va_arg(args, const char *);
906
907 if (ele == NULL) {
908 break;
909 } else if (pcmk__str_eq(s, ele, flags)) {
910 return true;
911 }
912 }
913
914 return false;
915}
916
930bool
931pcmk__strcase_any_of(const char *s, ...)
932{
933 va_list ap;
934 bool rc;
935
936 va_start(ap, s);
937 rc = str_any_of(s, ap, pcmk__str_casei);
938 va_end(ap);
939 return rc;
940}
941
954bool
955pcmk__str_any_of(const char *s, ...)
956{
957 va_list ap;
958 bool rc;
959
960 va_start(ap, s);
961 rc = str_any_of(s, ap, pcmk__str_none);
962 va_end(ap);
963 return rc;
964}
965
977bool
979{
980 bool rc = false;
981 va_list ap;
982
983 /*
984 * Passing a char to va_start() can generate compiler warnings,
985 * so ch is declared as an int.
986 */
987 va_start(ap, ch);
988
989 while (1) {
990 const char *ele = va_arg(ap, const char *);
991
992 if (ele == NULL) {
993 break;
994 } else if (strchr(ele, ch) != NULL) {
995 rc = true;
996 break;
997 }
998 }
999
1000 va_end(ap);
1001 return rc;
1002}
1003
1020int
1021pcmk__numeric_strcasecmp(const char *s1, const char *s2)
1022{
1023 while (*s1 && *s2) {
1024 if (isdigit(*s1) && isdigit(*s2)) {
1025 // If node names contain a number, sort numerically
1026
1027 char *end1 = NULL;
1028 char *end2 = NULL;
1029 long num1 = strtol(s1, &end1, 10);
1030 long num2 = strtol(s2, &end2, 10);
1031
1032 // allow ordering e.g. 007 > 7
1033 size_t len1 = end1 - s1;
1034 size_t len2 = end2 - s2;
1035
1036 if (num1 < num2) {
1037 return -1;
1038 } else if (num1 > num2) {
1039 return 1;
1040 } else if (len1 < len2) {
1041 return -1;
1042 } else if (len1 > len2) {
1043 return 1;
1044 }
1045 s1 = end1;
1046 s2 = end2;
1047 } else {
1048 // Compare non-digits case-insensitively
1049 int lower1 = tolower(*s1);
1050 int lower2 = tolower(*s2);
1051
1052 if (lower1 < lower2) {
1053 return -1;
1054 } else if (lower1 > lower2) {
1055 return 1;
1056 }
1057 ++s1;
1058 ++s2;
1059 }
1060 }
1061 if (!*s1 && *s2) {
1062 return -1;
1063 } else if (*s1 && !*s2) {
1064 return 1;
1065 }
1066 return 0;
1067}
1068
1069/*
1070 * \brief Sort strings.
1071 *
1072 * This is your one-stop function for string comparison. By default, this
1073 * function works like g_strcmp0. That is, like strcmp but a NULL string
1074 * sorts before a non-NULL string.
1075 *
1076 * Behavior can be changed with various flags:
1077 *
1078 * - pcmk__str_regex - The second string is a regular expression that the
1079 * first string will be matched against.
1080 * - pcmk__str_casei - By default, comparisons are done taking case into
1081 * account. This flag makes comparisons case-insensitive.
1082 * This can be combined with pcmk__str_regex.
1083 * - pcmk__str_null_matches - If one string is NULL and the other is not,
1084 * still return 0.
1085 * - pcmk__str_star_matches - If one string is "*" and the other is not, still
1086 * return 0.
1087 *
1088 * \param[in] s1 First string to compare
1089 * \param[in] s2 Second string to compare, or a regular expression to
1090 * match if pcmk__str_regex is set
1091 * \param[in] flags A bitfield of pcmk__str_flags to modify operation
1092 *
1093 * \retval negative \p s1 is NULL or comes before \p s2
1094 * \retval 0 \p s1 and \p s2 are equal, or \p s1 is found in \p s2 if
1095 * pcmk__str_regex is set
1096 * \retval positive \p s2 is NULL or \p s1 comes after \p s2, or \p s2
1097 * is an invalid regular expression, or \p s1 was not found
1098 * in \p s2 if pcmk__str_regex is set.
1099 */
1100int
1101pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
1102{
1103 /* If this flag is set, the second string is a regex. */
1105 regex_t r_patt;
1106 int reg_flags = REG_EXTENDED | REG_NOSUB;
1107 int regcomp_rc = 0;
1108 int rc = 0;
1109
1110 if (s1 == NULL || s2 == NULL) {
1111 return 1;
1112 }
1113
1115 reg_flags |= REG_ICASE;
1116 }
1117 regcomp_rc = regcomp(&r_patt, s2, reg_flags);
1118 if (regcomp_rc != 0) {
1119 rc = 1;
1120 crm_err("Bad regex '%s' for update: %s", s2, strerror(regcomp_rc));
1121 } else {
1122 rc = regexec(&r_patt, s1, 0, NULL, 0);
1123
1124 if (rc != 0) {
1125 rc = 1;
1126 }
1127 }
1128
1129 regfree(&r_patt);
1130 return rc;
1131 }
1132
1133 /* If the strings are the same pointer, return 0 immediately. */
1134 if (s1 == s2) {
1135 return 0;
1136 }
1137
1138 /* If this flag is set, return 0 if either (or both) of the input strings
1139 * are NULL. If neither one is NULL, we need to continue and compare
1140 * them normally.
1141 */
1143 if (s1 == NULL || s2 == NULL) {
1144 return 0;
1145 }
1146 }
1147
1148 /* Handle the cases where one is NULL and the str_null_matches flag is not set.
1149 * A NULL string always sorts to the beginning.
1150 */
1151 if (s1 == NULL) {
1152 return -1;
1153 } else if (s2 == NULL) {
1154 return 1;
1155 }
1156
1157 /* If this flag is set, return 0 if either (or both) of the input strings
1158 * are "*". If neither one is, we need to continue and compare them
1159 * normally.
1160 */
1162 if (strcmp(s1, "*") == 0 || strcmp(s2, "*") == 0) {
1163 return 0;
1164 }
1165 }
1166
1168 return strcasecmp(s1, s2);
1169 } else {
1170 return strcmp(s1, s2);
1171 }
1172}
1173
1187void
1188pcmk__str_update(char **str, const char *value)
1189{
1190 if ((str != NULL) && !pcmk__str_eq(*str, value, pcmk__str_none)) {
1191 free(*str);
1192 if (value == NULL) {
1193 *str = NULL;
1194 } else {
1195 *str = strdup(value);
1196 CRM_ASSERT(*str != NULL);
1197 }
1198 }
1199}
1200
1201// Deprecated functions kept only for backward API compatibility
1202// LCOV_EXCL_START
1203
1204#include <crm/common/util_compat.h>
1205
1206gboolean
1207safe_str_neq(const char *a, const char *b)
1208{
1209 if (a == b) {
1210 return FALSE;
1211
1212 } else if (a == NULL || b == NULL) {
1213 return TRUE;
1214
1215 } else if (strcasecmp(a, b) == 0) {
1216 return FALSE;
1217 }
1218 return TRUE;
1219}
1220
1221gboolean
1222crm_str_eq(const char *a, const char *b, gboolean use_case)
1223{
1224 if (use_case) {
1225 return g_strcmp0(a, b) == 0;
1226
1227 /* TODO - Figure out which calls, if any, really need to be case independent */
1228 } else if (a == b) {
1229 return TRUE;
1230
1231 } else if (a == NULL || b == NULL) {
1232 /* shouldn't be comparing NULLs */
1233 return FALSE;
1234
1235 } else if (strcasecmp(a, b) == 0) {
1236 return TRUE;
1237 }
1238 return FALSE;
1239}
1240
1241char *
1242crm_itoa_stack(int an_int, char *buffer, size_t len)
1243{
1244 if (buffer != NULL) {
1245 snprintf(buffer, len, "%d", an_int);
1246 }
1247 return buffer;
1248}
1249
1250guint
1252{
1253 return pcmk__str_hash(v);
1254}
1255
1256gboolean
1257crm_strcase_equal(gconstpointer a, gconstpointer b)
1258{
1259 return pcmk__strcase_equal(a, b);
1260}
1261
1262guint
1263crm_strcase_hash(gconstpointer v)
1264{
1265 return pcmk__strcase_hash(v);
1266}
1267
1268GHashTable *
1269crm_str_table_dup(GHashTable *old_table)
1270{
1271 return pcmk__str_table_dup(old_table);
1272}
1273
1274long long
1275crm_parse_ll(const char *text, const char *default_text)
1276{
1277 long long result;
1278
1279 if (text == NULL) {
1280 text = default_text;
1281 if (text == NULL) {
1282 crm_err("No default conversion value supplied");
1283 errno = EINVAL;
1285 }
1286 }
1287 scan_ll(text, &result, PCMK__PARSE_INT_DEFAULT, NULL);
1288 return result;
1289}
1290
1291int
1292crm_parse_int(const char *text, const char *default_text)
1293{
1294 long long result = crm_parse_ll(text, default_text);
1295
1296 if (result < INT_MIN) {
1297 // If errno is ERANGE, crm_parse_ll() has already logged a message
1298 if (errno != ERANGE) {
1299 crm_err("Conversion of %s was clipped: %lld", text, result);
1300 errno = ERANGE;
1301 }
1302 return INT_MIN;
1303
1304 } else if (result > INT_MAX) {
1305 // If errno is ERANGE, crm_parse_ll() has already logged a message
1306 if (errno != ERANGE) {
1307 crm_err("Conversion of %s was clipped: %lld", text, result);
1308 errno = ERANGE;
1309 }
1310 return INT_MAX;
1311 }
1312
1313 return (int) result;
1314}
1315
1316char *
1318{
1319 return pcmk__trim(str);
1320}
1321
1322int
1323pcmk_numeric_strcasecmp(const char *s1, const char *s2)
1324{
1325 return pcmk__numeric_strcasecmp(s1, s2);
1326}
1327
1328// LCOV_EXCL_STOP
1329// End deprecated API
uint64_t flags
Definition: remote.c:3
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:122
Deprecated Pacemaker utilities.
char data[0]
Definition: cpg.c:10
#define crm_warn(fmt, args...)
Definition: logging.h:359
#define CRM_XS
Definition: logging.h:55
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
#define crm_debug(fmt, args...)
Definition: logging.h:363
#define crm_err(fmt, args...)
Definition: logging.h:358
#define crm_trace(fmt, args...)
Definition: logging.h:364
xmlNode * input
pcmk__action_result_t result
Definition: pcmk_fence.c:34
char * strerror(int errnum)
const char * bz2_strerror(int rc)
Definition: results.c:742
#define CRM_ASSERT(expr)
Definition: results.h:42
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:370
@ pcmk_rc_before_range
Definition: results.h:119
@ pcmk_rc_ok
Definition: results.h:146
@ pcmk_rc_unknown_format
Definition: results.h:140
@ pcmk_rc_after_range
Definition: results.h:117
@ pcmk_rc_error
Definition: results.h:142
@ pcmk_rc_underflow
Definition: results.h:114
guint g_str_hash_traditional(gconstpointer v)
Definition: strings.c:1251
void pcmk__add_separated_word(char **list, size_t *len, const char *word, const char *separator)
Definition: strings.c:703
gboolean crm_strcase_equal(gconstpointer a, gconstpointer b)
Definition: strings.c:1257
bool pcmk__strcase_any_of(const char *s,...)
Definition: strings.c:931
GHashTable * pcmk__str_table_dup(GHashTable *old_table)
Definition: strings.c:675
int pcmk__numeric_strcasecmp(const char *s1, const char *s2)
Definition: strings.c:1021
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:1222
char * crm_itoa_stack(int an_int, char *buffer, size_t len)
Definition: strings.c:1242
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:127
GHashTable * crm_str_table_dup(GHashTable *old_table)
Definition: strings.c:1269
bool pcmk__char_in_any_str(int ch,...)
Definition: strings.c:978
int pcmk__scan_port(const char *text, int *port)
Definition: strings.c:161
char * crm_strdup_printf(char const *format,...)
Definition: strings.c:799
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:1207
guint crm_strcase_hash(gconstpointer v)
Definition: strings.c:1263
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:611
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition: strings.c:364
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:563
char * pcmk__trim(char *str)
Definition: strings.c:456
int pcmk__scan_double(const char *text, double *result, const char *default_text, char **end_text)
Definition: strings.c:199
bool pcmk__starts_with(const char *str, const char *prefix)
Check whether a string starts with a certain sequence.
Definition: strings.c:484
int pcmk__compress(const char *data, unsigned int length, unsigned int max, char **result, unsigned int *result_len)
Definition: strings.c:749
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition: strings.c:97
void pcmk__str_update(char **str, const char *value)
Definition: strings.c:1188
int pcmk_numeric_strcasecmp(const char *s1, const char *s2)
Definition: strings.c:1323
char * crm_strip_trailing_newline(char *str)
Definition: strings.c:1317
#define WHITESPACE
Definition: strings.c:350
gboolean pcmk__str_in_list(const gchar *s, GList *lst, uint32_t flags)
Definition: strings.c:886
bool pcmk__ends_with(const char *s, const char *match)
Definition: strings.c:536
int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, guint *result)
Definition: strings.c:311
int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
Definition: strings.c:1101
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition: strings.c:813
#define NUMCHARS
Definition: strings.c:346
gboolean crm_is_true(const char *s)
Definition: strings.c:416
long long crm_parse_ll(const char *text, const char *default_text)
Definition: strings.c:1275
bool pcmk__str_any_of(const char *s,...)
Definition: strings.c:955
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:427
GHashTable * pcmk__strikey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:649
int crm_parse_int(const char *text, const char *default_text)
Definition: strings.c:1292
@ pcmk__str_regex
@ pcmk__str_none
@ pcmk__str_null_matches
@ pcmk__str_star_matches
@ pcmk__str_casei
#define PCMK__PARSE_DBL_DEFAULT
#define PCMK__PARSE_INT_DEFAULT
#define CRM_BZ2_WORK
Definition: xml.h:47
#define CRM_BZ2_BLOCKS
Definition: xml.h:46