pacemaker 2.1.1-77db578727
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
strings.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2021 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);
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 (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
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,
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
362long long
363crm_get_msec(const char *input)
364{
365 const char *num_start = NULL;
366 const char *units;
367 long long multiplier = 1000;
368 long long divisor = 1;
369 long long msec = PCMK__PARSE_INT_DEFAULT;
370 size_t num_len = 0;
371 char *end_text = NULL;
372
373 if (input == NULL) {
375 }
376
377 num_start = input + strspn(input, WHITESPACE);
378 num_len = strspn(num_start, NUMCHARS);
379 if (num_len < 1) {
381 }
382 units = num_start + num_len;
383 units += strspn(units, WHITESPACE);
384
385 if (!strncasecmp(units, "ms", 2) || !strncasecmp(units, "msec", 4)) {
386 multiplier = 1;
387 divisor = 1;
388 } else if (!strncasecmp(units, "us", 2) || !strncasecmp(units, "usec", 4)) {
389 multiplier = 1;
390 divisor = 1000;
391 } else if (!strncasecmp(units, "s", 1) || !strncasecmp(units, "sec", 3)) {
392 multiplier = 1000;
393 divisor = 1;
394 } else if (!strncasecmp(units, "m", 1) || !strncasecmp(units, "min", 3)) {
395 multiplier = 60 * 1000;
396 divisor = 1;
397 } else if (!strncasecmp(units, "h", 1) || !strncasecmp(units, "hr", 2)) {
398 multiplier = 60 * 60 * 1000;
399 divisor = 1;
400 } else if ((*units != '\0') && (*units != '\n') && (*units != '\r')) {
402 }
403
404 scan_ll(num_start, &msec, PCMK__PARSE_INT_DEFAULT, &end_text);
405 if (msec > (LLONG_MAX / multiplier)) {
406 // Arithmetics overflow while multiplier/divisor mutually exclusive
407 return LLONG_MAX;
408 }
409 msec *= multiplier;
410 msec /= divisor;
411 return msec;
412}
413
414gboolean
415crm_is_true(const char *s)
416{
417 gboolean ret = FALSE;
418
419 if (s != NULL) {
420 crm_str_to_boolean(s, &ret);
421 }
422 return ret;
423}
424
425int
426crm_str_to_boolean(const char *s, int *ret)
427{
428 if (s == NULL) {
429 return -1;
430
431 } else if (strcasecmp(s, "true") == 0
432 || strcasecmp(s, "on") == 0
433 || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
434 *ret = TRUE;
435 return 1;
436
437 } else if (strcasecmp(s, "false") == 0
438 || strcasecmp(s, "off") == 0
439 || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) {
440 *ret = FALSE;
441 return 1;
442 }
443 return -1;
444}
445
454char *
455pcmk__trim(char *str)
456{
457 int len;
458
459 if (str == NULL) {
460 return str;
461 }
462
463 for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
464 str[len] = '\0';
465 }
466
467 return str;
468}
469
482bool
483pcmk__starts_with(const char *str, const char *prefix)
484{
485 const char *s = str;
486 const char *p = prefix;
487
488 if (!s || !p) {
489 return false;
490 }
491 while (*s && *p) {
492 if (*s++ != *p++) {
493 return false;
494 }
495 }
496 return (*p == 0);
497}
498
499static inline bool
500ends_with(const char *s, const char *match, bool as_extension)
501{
502 if (pcmk__str_empty(match)) {
503 return true;
504 } else if (s == NULL) {
505 return false;
506 } else {
507 size_t slen, mlen;
508
509 /* Besides as_extension, we could also check
510 !strchr(&match[1], match[0]) but that would be inefficient.
511 */
512 if (as_extension) {
513 s = strrchr(s, match[0]);
514 return (s == NULL)? false : !strcmp(s, match);
515 }
516
517 mlen = strlen(match);
518 slen = strlen(s);
519 return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
520 }
521}
522
534bool
535pcmk__ends_with(const char *s, const char *match)
536{
537 return ends_with(s, match, false);
538}
539
561bool
562pcmk__ends_with_ext(const char *s, const char *match)
563{
564 return ends_with(s, match, true);
565}
566
586static guint
587pcmk__str_hash(gconstpointer v)
588{
589 const signed char *p;
590 guint32 h = 0;
591
592 for (p = v; *p != '\0'; p++)
593 h = (h << 5) - h + *p;
594
595 return h;
596}
597
609GHashTable *
610pcmk__strkey_table(GDestroyNotify key_destroy_func,
611 GDestroyNotify value_destroy_func)
612{
613 return g_hash_table_new_full(pcmk__str_hash, g_str_equal,
614 key_destroy_func, value_destroy_func);
615}
616
617/* used with hash tables where case does not matter */
618static gboolean
619pcmk__strcase_equal(gconstpointer a, gconstpointer b)
620{
621 return pcmk__str_eq((const char *)a, (const char *)b, pcmk__str_casei);
622}
623
624static guint
625pcmk__strcase_hash(gconstpointer v)
626{
627 const signed char *p;
628 guint32 h = 0;
629
630 for (p = v; *p != '\0'; p++)
631 h = (h << 5) - h + g_ascii_tolower(*p);
632
633 return h;
634}
635
647GHashTable *
648pcmk__strikey_table(GDestroyNotify key_destroy_func,
649 GDestroyNotify value_destroy_func)
650{
651 return g_hash_table_new_full(pcmk__strcase_hash, pcmk__strcase_equal,
652 key_destroy_func, value_destroy_func);
653}
654
655static void
656copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
657{
658 if (key && value && user_data) {
659 g_hash_table_insert((GHashTable*)user_data, strdup(key), strdup(value));
660 }
661}
662
673GHashTable *
674pcmk__str_table_dup(GHashTable *old_table)
675{
676 GHashTable *new_table = NULL;
677
678 if (old_table) {
679 new_table = pcmk__strkey_table(free, free);
680 g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
681 }
682 return new_table;
683}
684
701void
702pcmk__add_separated_word(char **list, size_t *len, const char *word,
703 const char *separator)
704{
705 size_t orig_len, new_len;
706
707 CRM_ASSERT(list != NULL);
708
709 if (pcmk__str_empty(word)) {
710 return;
711 }
712
713 // Use provided length, or calculate it if not available
714 orig_len = (len != NULL)? *len : ((*list == NULL)? 0 : strlen(*list));
715
716 // Don't add a separator before the first word in the list
717 if (orig_len == 0) {
718 separator = "";
719
720 // Default to space-separated
721 } else if (separator == NULL) {
722 separator = " ";
723 }
724
725 new_len = orig_len + strlen(separator) + strlen(word);
726 if (len != NULL) {
727 *len = new_len;
728 }
729
730 // +1 for null terminator
731 *list = pcmk__realloc(*list, new_len + 1);
732 sprintf(*list + orig_len, "%s%s", separator, word);
733}
734
747int
748pcmk__compress(const char *data, unsigned int length, unsigned int max,
749 char **result, unsigned int *result_len)
750{
751 int rc;
752 char *compressed = NULL;
753 char *uncompressed = strdup(data);
754#ifdef CLOCK_MONOTONIC
755 struct timespec after_t;
756 struct timespec before_t;
757#endif
758
759 if (max == 0) {
760 max = (length * 1.01) + 601; // Size guaranteed to hold result
761 }
762
763#ifdef CLOCK_MONOTONIC
764 clock_gettime(CLOCK_MONOTONIC, &before_t);
765#endif
766
767 compressed = calloc((size_t) max, sizeof(char));
768 CRM_ASSERT(compressed);
769
770 *result_len = max;
771 rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length,
773 free(uncompressed);
774 if (rc != BZ_OK) {
775 crm_err("Compression of %d bytes failed: %s " CRM_XS " bzerror=%d",
776 length, bz2_strerror(rc), rc);
777 free(compressed);
778 return pcmk_rc_error;
779 }
780
781#ifdef CLOCK_MONOTONIC
782 clock_gettime(CLOCK_MONOTONIC, &after_t);
783
784 crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
785 length, *result_len, length / (*result_len),
786 (after_t.tv_sec - before_t.tv_sec) * 1000 +
787 (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
788#else
789 crm_trace("Compressed %d bytes into %d (ratio %d:1)",
790 length, *result_len, length / (*result_len));
791#endif
792
793 *result = compressed;
794 return pcmk_rc_ok;
795}
796
797char *
798crm_strdup_printf(char const *format, ...)
799{
800 va_list ap;
801 int len = 0;
802 char *string = NULL;
803
804 va_start(ap, format);
805 len = vasprintf (&string, format, ap);
806 CRM_ASSERT(len > 0);
807 va_end(ap);
808 return string;
809}
810
811int
812pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
813{
814 char *remainder = NULL;
815
816 CRM_ASSERT(start != NULL && end != NULL);
817
820
821 crm_trace("Attempting to decode: [%s]", srcstring);
822 if (pcmk__str_empty(srcstring) || !strcmp(srcstring, "-")) {
824 }
825
826 /* String starts with a dash, so this is either a range with
827 * no beginning or garbage.
828 * */
829 if (*srcstring == '-') {
830 int rc = scan_ll(srcstring+1, end, PCMK__PARSE_INT_DEFAULT, &remainder);
831
832 if (rc != pcmk_rc_ok || *remainder != '\0') {
834 } else {
835 return pcmk_rc_ok;
836 }
837 }
838
839 if (scan_ll(srcstring, start, PCMK__PARSE_INT_DEFAULT,
840 &remainder) != pcmk_rc_ok) {
842 }
843
844 if (*remainder && *remainder == '-') {
845 if (*(remainder+1)) {
846 char *more_remainder = NULL;
847 int rc = scan_ll(remainder+1, end, PCMK__PARSE_INT_DEFAULT,
848 &more_remainder);
849
850 if (rc != pcmk_rc_ok || *more_remainder != '\0') {
852 }
853 }
854 } else if (*remainder && *remainder != '-') {
857 } else {
858 /* The input string contained only one number. Set start and end
859 * to the same value and return pcmk_rc_ok. This gives the caller
860 * a way to tell this condition apart from a range with no end.
861 */
862 *end = *start;
863 }
864
865 return pcmk_rc_ok;
866}
867
894gboolean
895pcmk__str_in_list(GList *lst, const gchar *s, uint32_t flags)
896{
897 GCompareFunc fn;
898
899 if (lst == NULL) {
900 return FALSE;
901 }
902
903 if (strcmp(lst->data, "*") == 0 && lst->next == NULL) {
904 return TRUE;
905 }
906
907 if (s == NULL) {
909 }
910
912 fn = (GCompareFunc) strcasecmp;
913 } else {
914 fn = (GCompareFunc) strcmp;
915 }
916
917 return g_list_find_custom(lst, s, fn) != NULL;
918}
919
920static bool
921str_any_of(bool casei, const char *s, va_list args)
922{
923 bool rc = false;
924
925 if (s != NULL) {
926 while (1) {
927 const char *ele = va_arg(args, const char *);
928
929 if (ele == NULL) {
930 break;
931 } else if (pcmk__str_eq(s, ele,
933 rc = true;
934 break;
935 }
936 }
937 }
938 return rc;
939}
940
954bool
955pcmk__strcase_any_of(const char *s, ...)
956{
957 va_list ap;
958 bool rc;
959
960 va_start(ap, s);
961 rc = str_any_of(true, s, ap);
962 va_end(ap);
963 return rc;
964}
965
978bool
979pcmk__str_any_of(const char *s, ...)
980{
981 va_list ap;
982 bool rc;
983
984 va_start(ap, s);
985 rc = str_any_of(false, s, ap);
986 va_end(ap);
987 return rc;
988}
989
1001bool
1003{
1004 bool rc = false;
1005 va_list ap;
1006
1007 /*
1008 * Passing a char to va_start() can generate compiler warnings,
1009 * so ch is declared as an int.
1010 */
1011 va_start(ap, ch);
1012
1013 while (1) {
1014 const char *ele = va_arg(ap, const char *);
1015
1016 if (ele == NULL) {
1017 break;
1018 } else if (strchr(ele, ch) != NULL) {
1019 rc = true;
1020 break;
1021 }
1022 }
1023
1024 va_end(ap);
1025 return rc;
1026}
1027
1044int
1045pcmk__numeric_strcasecmp(const char *s1, const char *s2)
1046{
1047 while (*s1 && *s2) {
1048 if (isdigit(*s1) && isdigit(*s2)) {
1049 // If node names contain a number, sort numerically
1050
1051 char *end1 = NULL;
1052 char *end2 = NULL;
1053 long num1 = strtol(s1, &end1, 10);
1054 long num2 = strtol(s2, &end2, 10);
1055
1056 // allow ordering e.g. 007 > 7
1057 size_t len1 = end1 - s1;
1058 size_t len2 = end2 - s2;
1059
1060 if (num1 < num2) {
1061 return -1;
1062 } else if (num1 > num2) {
1063 return 1;
1064 } else if (len1 < len2) {
1065 return -1;
1066 } else if (len1 > len2) {
1067 return 1;
1068 }
1069 s1 = end1;
1070 s2 = end2;
1071 } else {
1072 // Compare non-digits case-insensitively
1073 int lower1 = tolower(*s1);
1074 int lower2 = tolower(*s2);
1075
1076 if (lower1 < lower2) {
1077 return -1;
1078 } else if (lower1 > lower2) {
1079 return 1;
1080 }
1081 ++s1;
1082 ++s2;
1083 }
1084 }
1085 if (!*s1 && *s2) {
1086 return -1;
1087 } else if (*s1 && !*s2) {
1088 return 1;
1089 }
1090 return 0;
1091}
1092
1093/*
1094 * \brief Sort strings.
1095 *
1096 * This is your one-stop function for string comparison. By default, this
1097 * function works like g_strcmp0. That is, like strcmp but a NULL string
1098 * sorts before a non-NULL string.
1099 *
1100 * Behavior can be changed with various flags:
1101 *
1102 * - pcmk__str_regex - The second string is a regular expression that the
1103 * first string will be matched against.
1104 * - pcmk__str_casei - By default, comparisons are done taking case into
1105 * account. This flag makes comparisons case-insensitive.
1106 * This can be combined with pcmk__str_regex.
1107 * - pcmk__str_null_matches - If one string is NULL and the other is not,
1108 * still return 0.
1109 *
1110 * \param[in] s1 First string to compare
1111 * \param[in] s2 Second string to compare, or a regular expression to
1112 * match if pcmk__str_regex is set
1113 * \param[in] flags A bitfield of pcmk__str_flags to modify operation
1114 *
1115 * \retval -1 \p s1 is NULL or comes before \p s2
1116 * \retval 0 \p s1 and \p s2 are equal, or \p s1 is found in \p s2 if
1117 * pcmk__str_regex is set
1118 * \retval 1 \p s2 is NULL or \p s1 comes after \p s2, or if \p s2
1119 * is an invalid regular expression, or \p s1 was not found
1120 * in \p s2 if pcmk__str_regex is set.
1121 */
1122int
1123pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
1124{
1125 /* If this flag is set, the second string is a regex. */
1127 regex_t *r_patt = calloc(1, sizeof(regex_t));
1128 int reg_flags = REG_EXTENDED | REG_NOSUB;
1129 int regcomp_rc = 0;
1130 int rc = 0;
1131
1132 if (s1 == NULL || s2 == NULL) {
1133 free(r_patt);
1134 return 1;
1135 }
1136
1138 reg_flags |= REG_ICASE;
1139 }
1140 regcomp_rc = regcomp(r_patt, s2, reg_flags);
1141 if (regcomp_rc != 0) {
1142 rc = 1;
1143 crm_err("Bad regex '%s' for update: %s", s2, strerror(regcomp_rc));
1144 } else {
1145 rc = regexec(r_patt, s1, 0, NULL, 0);
1146
1147 if (rc != 0) {
1148 rc = 1;
1149 }
1150 }
1151
1152 regfree(r_patt);
1153 free(r_patt);
1154 return rc;
1155 }
1156
1157 /* If the strings are the same pointer, return 0 immediately. */
1158 if (s1 == s2) {
1159 return 0;
1160 }
1161
1162 /* If this flag is set, return 0 if either (or both) of the input strings
1163 * are NULL. If neither one is NULL, we need to continue and compare
1164 * them normally.
1165 */
1167 if (s1 == NULL || s2 == NULL) {
1168 return 0;
1169 }
1170 }
1171
1172 /* Handle the cases where one is NULL and the str_null_matches flag is not set.
1173 * A NULL string always sorts to the beginning.
1174 */
1175 if (s1 == NULL) {
1176 return -1;
1177 } else if (s2 == NULL) {
1178 return 1;
1179 }
1180
1182 return strcasecmp(s1, s2);
1183 } else {
1184 return strcmp(s1, s2);
1185 }
1186}
1187
1188// Deprecated functions kept only for backward API compatibility
1189
1190#include <crm/common/util_compat.h>
1191
1192gboolean
1193safe_str_neq(const char *a, const char *b)
1194{
1195 if (a == b) {
1196 return FALSE;
1197
1198 } else if (a == NULL || b == NULL) {
1199 return TRUE;
1200
1201 } else if (strcasecmp(a, b) == 0) {
1202 return FALSE;
1203 }
1204 return TRUE;
1205}
1206
1207gboolean
1208crm_str_eq(const char *a, const char *b, gboolean use_case)
1209{
1210 if (use_case) {
1211 return g_strcmp0(a, b) == 0;
1212
1213 /* TODO - Figure out which calls, if any, really need to be case independent */
1214 } else if (a == b) {
1215 return TRUE;
1216
1217 } else if (a == NULL || b == NULL) {
1218 /* shouldn't be comparing NULLs */
1219 return FALSE;
1220
1221 } else if (strcasecmp(a, b) == 0) {
1222 return TRUE;
1223 }
1224 return FALSE;
1225}
1226
1227char *
1228crm_itoa_stack(int an_int, char *buffer, size_t len)
1229{
1230 if (buffer != NULL) {
1231 snprintf(buffer, len, "%d", an_int);
1232 }
1233 return buffer;
1234}
1235
1236guint
1238{
1239 return pcmk__str_hash(v);
1240}
1241
1242gboolean
1243crm_strcase_equal(gconstpointer a, gconstpointer b)
1244{
1245 return pcmk__strcase_equal(a, b);
1246}
1247
1248guint
1249crm_strcase_hash(gconstpointer v)
1250{
1251 return pcmk__strcase_hash(v);
1252}
1253
1254GHashTable *
1255crm_str_table_dup(GHashTable *old_table)
1256{
1257 return pcmk__str_table_dup(old_table);
1258}
1259
1260long long
1261crm_parse_ll(const char *text, const char *default_text)
1262{
1263 long long result;
1264
1265 if (text == NULL) {
1266 text = default_text;
1267 if (text == NULL) {
1268 crm_err("No default conversion value supplied");
1269 errno = EINVAL;
1271 }
1272 }
1273 scan_ll(text, &result, PCMK__PARSE_INT_DEFAULT, NULL);
1274 return result;
1275}
1276
1277int
1278crm_parse_int(const char *text, const char *default_text)
1279{
1280 long long result = crm_parse_ll(text, default_text);
1281
1282 if (result < INT_MIN) {
1283 // If errno is ERANGE, crm_parse_ll() has already logged a message
1284 if (errno != ERANGE) {
1285 crm_err("Conversion of %s was clipped: %lld", text, result);
1286 errno = ERANGE;
1287 }
1288 return INT_MIN;
1289
1290 } else if (result > INT_MAX) {
1291 // If errno is ERANGE, crm_parse_ll() has already logged a message
1292 if (errno != ERANGE) {
1293 crm_err("Conversion of %s was clipped: %lld", text, result);
1294 errno = ERANGE;
1295 }
1296 return INT_MAX;
1297 }
1298
1299 return (int) result;
1300}
1301
1302char *
1304{
1305 return pcmk__trim(str);
1306}
1307
1308int
1309pcmk_numeric_strcasecmp(const char *s1, const char *s2)
1310{
1311 return pcmk__numeric_strcasecmp(s1, s2);
1312}
1313
1314// 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:114
char data[0]
Definition cpg.c:10
#define crm_warn(fmt, args...)
Definition logging.h:351
#define CRM_XS
Definition logging.h:54
#define CRM_CHECK(expr, failure_action)
Definition logging.h:218
#define crm_debug(fmt, args...)
Definition logging.h:355
#define crm_err(fmt, args...)
Definition logging.h:350
#define crm_trace(fmt, args...)
Definition logging.h:356
int rc
Definition pcmk_fence.c:35
char * strerror(int errnum)
const char * bz2_strerror(int rc)
Definition results.c:726
#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:420
@ pcmk_rc_before_range
Definition results.h:115
@ pcmk_rc_ok
Definition results.h:142
@ pcmk_rc_unknown_format
Definition results.h:136
@ pcmk_rc_after_range
Definition results.h:113
@ pcmk_rc_error
Definition results.h:138
@ pcmk_rc_underflow
Definition results.h:110
guint g_str_hash_traditional(gconstpointer v)
Definition strings.c:1237
void pcmk__add_separated_word(char **list, size_t *len, const char *word, const char *separator)
Definition strings.c:702
gboolean crm_strcase_equal(gconstpointer a, gconstpointer b)
Definition strings.c:1243
bool pcmk__strcase_any_of(const char *s,...)
Definition strings.c:955
GHashTable * pcmk__str_table_dup(GHashTable *old_table)
Definition strings.c:674
int pcmk__numeric_strcasecmp(const char *s1, const char *s2)
Definition strings.c:1045
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition strings.c:1208
char * crm_itoa_stack(int an_int, char *buffer, size_t len)
Definition strings.c:1228
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:1255
bool pcmk__char_in_any_str(int ch,...)
Definition strings.c:1002
int pcmk__scan_port(const char *text, int *port)
Definition strings.c:161
char * crm_strdup_printf(char const *format,...)
Definition strings.c:798
gboolean safe_str_neq(const char *a, const char *b)
Definition strings.c:1193
guint crm_strcase_hash(gconstpointer v)
Definition strings.c:1249
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:610
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition strings.c:363
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition strings.c:562
char * pcmk__trim(char *str)
Definition strings.c:455
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:483
int pcmk__compress(const char *data, unsigned int length, unsigned int max, char **result, unsigned int *result_len)
Definition strings.c:748
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition strings.c:97
int pcmk_numeric_strcasecmp(const char *s1, const char *s2)
Definition strings.c:1309
char * crm_strip_trailing_newline(char *str)
Definition strings.c:1303
#define WHITESPACE
Definition strings.c:350
bool pcmk__ends_with(const char *s, const char *match)
Definition strings.c:535
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:1123
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition strings.c:812
#define NUMCHARS
Definition strings.c:346
gboolean crm_is_true(const char *s)
Definition strings.c:415
long long crm_parse_ll(const char *text, const char *default_text)
Definition strings.c:1261
bool pcmk__str_any_of(const char *s,...)
Definition strings.c:979
gboolean pcmk__str_in_list(GList *lst, const gchar *s, uint32_t flags)
Definition strings.c:895
int crm_str_to_boolean(const char *s, int *ret)
Definition strings.c:426
GHashTable * pcmk__strikey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:648
int crm_parse_int(const char *text, const char *default_text)
Definition strings.c:1278
@ pcmk__str_regex
@ pcmk__str_none
@ pcmk__str_null_matches
@ pcmk__str_casei
#define PCMK__PARSE_DBL_DEFAULT
#define PCMK__PARSE_INT_DEFAULT
Deprecated Pacemaker utilities.
#define CRM_BZ2_WORK
Definition xml.h:46
#define CRM_BZ2_BLOCKS
Definition xml.h:45