pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
strings.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2024 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11
12#include <regex.h>
13#include <stdio.h>
14#include <string.h>
15#include <stdlib.h>
16#include <ctype.h>
17#include <float.h> // DBL_MIN
18#include <limits.h>
19#include <bzlib.h>
20#include <sys/types.h>
21
37static int
38scan_ll(const char *text, long long *result, long long default_value,
39 char **end_text)
40{
41 long long local_result = default_value;
42 char *local_end_text = NULL;
43 int rc = pcmk_rc_ok;
44
45 errno = 0;
46 if (text != NULL) {
47 local_result = strtoll(text, &local_end_text, 10);
48 if (errno == ERANGE) {
49 rc = errno;
50 crm_debug("Integer parsed from '%s' was clipped to %lld",
51 text, local_result);
52
53 } else if (local_end_text == text) {
55 local_result = default_value;
56 crm_debug("Could not parse integer from '%s' (using %lld instead): "
57 "No digits found", text, default_value);
58
59 } else if (errno != 0) {
60 rc = errno;
61 local_result = default_value;
62 crm_debug("Could not parse integer from '%s' (using %lld instead): "
63 "%s", text, default_value, pcmk_rc_str(rc));
64 }
65
66 if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) {
67 crm_debug("Characters left over after parsing '%s': '%s'",
68 text, local_end_text);
69 }
70 errno = rc;
71 }
72 if (end_text != NULL) {
73 *end_text = local_end_text;
74 }
75 if (result != NULL) {
76 *result = local_result;
77 }
78 return rc;
79}
80
91int
92pcmk__scan_ll(const char *text, long long *result, long long default_value)
93{
94 long long local_result = default_value;
95 int rc = scan_ll(text, &local_result, default_value, NULL);
96
97 if (result != NULL) {
98 *result = local_result;
99 }
100 return rc;
101}
102
115int
116pcmk__scan_min_int(const char *text, int *result, int minimum)
117{
118 int rc;
119 long long result_ll;
120
121 rc = pcmk__scan_ll(text, &result_ll, (long long) minimum);
122
123 if (result_ll < (long long) minimum) {
124 crm_warn("Clipped '%s' to minimum acceptable value %d", text, minimum);
125 result_ll = (long long) minimum;
126
127 } else if (result_ll > INT_MAX) {
128 crm_warn("Clipped '%s' to maximum integer %d", text, INT_MAX);
129 result_ll = (long long) INT_MAX;
130 rc = EOVERFLOW;
131 }
132
133 if (result != NULL) {
134 *result = (int) result_ll;
135 }
136 return rc;
137}
138
149int
150pcmk__scan_port(const char *text, int *port)
151{
152 long long port_ll;
153 int rc = pcmk__scan_ll(text, &port_ll, -1LL);
154
155 if (rc != pcmk_rc_ok) {
156 crm_warn("'%s' is not a valid port: %s", text, pcmk_rc_str(rc));
157
158 } else if ((text != NULL) // wasn't default or invalid
159 && ((port_ll < 0LL) || (port_ll > 65535LL))) {
160 crm_warn("Ignoring port specification '%s' "
161 "not in valid range (0-65535)", text);
162 rc = (port_ll < 0LL)? pcmk_rc_before_range : pcmk_rc_after_range;
163 port_ll = -1LL;
164 }
165 if (port != NULL) {
166 *port = (int) port_ll;
167 }
168 return rc;
169}
170
190int
191pcmk__scan_double(const char *text, double *result, const char *default_text,
192 char **end_text)
193{
194 int rc = pcmk_rc_ok;
195 char *local_end_text = NULL;
196
197 pcmk__assert(result != NULL);
199
200 text = (text != NULL) ? text : default_text;
201
202 if (text == NULL) {
203 rc = EINVAL;
204 crm_debug("No text and no default conversion value supplied");
205
206 } else {
207 errno = 0;
208 *result = strtod(text, &local_end_text);
209
210 if (errno == ERANGE) {
211 /*
212 * Overflow: strtod() returns +/- HUGE_VAL and sets errno to
213 * ERANGE
214 *
215 * Underflow: strtod() returns "a value whose magnitude is
216 * no greater than the smallest normalized
217 * positive" double. Whether ERANGE is set is
218 * implementation-defined.
219 */
220 const char *over_under;
221
222 if (QB_ABS(*result) > DBL_MIN) {
223 rc = EOVERFLOW;
224 over_under = "over";
225 } else {
227 over_under = "under";
228 }
229
230 crm_debug("Floating-point value parsed from '%s' would %sflow "
231 "(using %g instead)", text, over_under, *result);
232
233 } else if (errno != 0) {
234 rc = errno;
235 // strtod() set *result = 0 on parse failure
237
238 crm_debug("Could not parse floating-point value from '%s' (using "
239 "%.1f instead): %s", text, PCMK__PARSE_DBL_DEFAULT,
240 pcmk_rc_str(rc));
241
242 } else if (local_end_text == text) {
243 // errno == 0, but nothing was parsed
244 rc = EINVAL;
246
247 crm_debug("Could not parse floating-point value from '%s' (using "
248 "%.1f instead): No digits found", text,
250
251 } else if (QB_ABS(*result) <= DBL_MIN) {
252 /*
253 * errno == 0 and text was parsed, but value might have
254 * underflowed.
255 *
256 * ERANGE might not be set for underflow. Check magnitude
257 * of *result, but also make sure the input number is not
258 * actually zero (0 <= DBL_MIN is not underflow).
259 *
260 * This check must come last. A parse failure in strtod()
261 * also sets *result == 0, so a parse failure would match
262 * this test condition prematurely.
263 */
264 for (const char *p = text; p != local_end_text; p++) {
265 if (strchr("0.eE", *p) == NULL) {
267 crm_debug("Floating-point value parsed from '%s' would "
268 "underflow (using %g instead)", text, *result);
269 break;
270 }
271 }
272
273 } else {
274 crm_trace("Floating-point value parsed successfully from "
275 "'%s': %g", text, *result);
276 }
277
278 if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) {
279 crm_debug("Characters left over after parsing '%s': '%s'",
280 text, local_end_text);
281 }
282 }
283
284 if (end_text != NULL) {
285 *end_text = local_end_text;
286 }
287
288 return rc;
289}
290
302int
303pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val,
304 guint *result)
305{
306 const char *value;
307 long long value_ll;
308 int rc = pcmk_rc_ok;
309
310 CRM_CHECK((table != NULL) && (key != NULL), return EINVAL);
311
312 if (result != NULL) {
313 *result = default_val;
314 }
315
316 value = g_hash_table_lookup(table, key);
317 if (value == NULL) {
318 return pcmk_rc_ok;
319 }
320
321 rc = pcmk__scan_ll(value, &value_ll, 0LL);
322 if (rc != pcmk_rc_ok) {
323 crm_warn("Using default (%u) for %s because '%s' is not a "
324 "valid integer: %s", default_val, key, value, pcmk_rc_str(rc));
325 return rc;
326 }
327
328 if ((value_ll < 0) || (value_ll > G_MAXUINT)) {
329 crm_warn("Using default (%u) for %s because '%s' is not in valid range",
330 default_val, key, value);
331 return ERANGE;
332 }
333
334 if (result != NULL) {
335 *result = (guint) value_ll;
336 }
337 return pcmk_rc_ok;
338}
339
350long long
351crm_get_msec(const char *input)
352{
353 char *units = NULL; // Do not free; will point to part of input
354 long long multiplier = 1000;
355 long long divisor = 1;
356 long long msec = PCMK__PARSE_INT_DEFAULT;
357 int rc = pcmk_rc_ok;
358
359 if (input == NULL) {
361 }
362
363 // Skip initial whitespace
364 while (isspace(*input)) {
365 input++;
366 }
367
368 rc = scan_ll(input, &msec, PCMK__PARSE_INT_DEFAULT, &units);
369
370 if ((rc == ERANGE) && (msec > 0)) {
371 crm_warn("'%s' will be clipped to %lld", input, msec);
372
373 } else if ((rc != pcmk_rc_ok) || (msec < 0)) {
374 crm_warn("'%s' is not a valid time duration: %s",
375 input, ((rc == pcmk_rc_ok)? "Negative" : pcmk_rc_str(rc)));
377 }
378
379 /* If the number is a decimal, scan_ll() reads only the integer part. Skip
380 * any remaining digits or decimal characters.
381 *
382 * @COMPAT Well-formed and malformed decimals are both accepted inputs. For
383 * example, "3.14 ms" and "3.1.4 ms" are treated the same as "3ms" and
384 * parsed successfully. At a compatibility break, decide if this is still
385 * desired.
386 */
387 while (isdigit(*units) || (*units == '.')) {
388 units++;
389 }
390
391 // Skip any additional whitespace after the number
392 while (isspace(*units)) {
393 units++;
394 }
395
396 /* @COMPAT Use exact comparisons. Currently, we match too liberally, and the
397 * second strncasecmp() in each case is redundant.
398 */
399 if ((*units == '\0')
400 || (strncasecmp(units, "s", 1) == 0)
401 || (strncasecmp(units, "sec", 3) == 0)) {
402 multiplier = 1000;
403 divisor = 1;
404
405 } else if ((strncasecmp(units, "ms", 2) == 0)
406 || (strncasecmp(units, "msec", 4) == 0)) {
407 multiplier = 1;
408 divisor = 1;
409
410 } else if ((strncasecmp(units, "us", 2) == 0)
411 || (strncasecmp(units, "usec", 4) == 0)) {
412 multiplier = 1;
413 divisor = 1000;
414
415 } else if ((strncasecmp(units, "m", 1) == 0)
416 || (strncasecmp(units, "min", 3) == 0)) {
417 multiplier = 60 * 1000;
418 divisor = 1;
419
420 } else if ((strncasecmp(units, "h", 1) == 0)
421 || (strncasecmp(units, "hr", 2) == 0)) {
422 multiplier = 60 * 60 * 1000;
423 divisor = 1;
424
425 } else {
426 // Invalid units
428 }
429
430 // Apply units, capping at LLONG_MAX
431 if (msec > (LLONG_MAX / multiplier)) {
432 return LLONG_MAX;
433 }
434 return (msec * multiplier) / divisor;
435}
436
451int
452pcmk_parse_interval_spec(const char *input, guint *result_ms)
453{
454 long long msec = PCMK__PARSE_INT_DEFAULT;
455 int rc = pcmk_rc_ok;
456
457 if (input == NULL) {
458 msec = 0;
459 goto done;
460 }
461
462 if (input[0] == 'P') {
464
465 if (period_s != NULL) {
466 msec = crm_time_get_seconds(period_s);
467 msec = QB_MIN(msec, G_MAXUINT / 1000) * 1000;
468 crm_time_free(period_s);
469 }
470
471 } else {
472 msec = crm_get_msec(input);
473 }
474
475 if (msec < 0) {
476 crm_warn("Using 0 instead of invalid interval specification '%s'",
477 input);
478 msec = 0;
479 rc = EINVAL;
480 }
481
482done:
483 if (result_ms != NULL) {
484 *result_ms = (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
485 }
486 return rc;
487}
488
489gboolean
490crm_is_true(const char *s)
491{
492 gboolean ret = FALSE;
493
494 return (crm_str_to_boolean(s, &ret) < 0)? FALSE : ret;
495}
496
497int
498crm_str_to_boolean(const char *s, int *ret)
499{
500 if (s == NULL) {
501 return -1;
502 }
503
504 if (pcmk__strcase_any_of(s, PCMK_VALUE_TRUE, "on", "yes", "y", "1", NULL)) {
505 if (ret != NULL) {
506 *ret = TRUE;
507 }
508 return 1;
509 }
510
512 "0", NULL)) {
513 if (ret != NULL) {
514 *ret = FALSE;
515 }
516 return 1;
517 }
518 return -1;
519}
520
529char *
530pcmk__trim(char *str)
531{
532 int len;
533
534 if (str == NULL) {
535 return str;
536 }
537
538 for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
539 str[len] = '\0';
540 }
541
542 return str;
543}
544
557bool
558pcmk__starts_with(const char *str, const char *prefix)
559{
560 const char *s = str;
561 const char *p = prefix;
562
563 if (!s || !p) {
564 return false;
565 }
566 while (*s && *p) {
567 if (*s++ != *p++) {
568 return false;
569 }
570 }
571 return (*p == 0);
572}
573
574static inline bool
575ends_with(const char *s, const char *match, bool as_extension)
576{
577 if (pcmk__str_empty(match)) {
578 return true;
579 } else if (s == NULL) {
580 return false;
581 } else {
582 size_t slen, mlen;
583
584 /* Besides as_extension, we could also check
585 !strchr(&match[1], match[0]) but that would be inefficient.
586 */
587 if (as_extension) {
588 s = strrchr(s, match[0]);
589 return (s == NULL)? false : !strcmp(s, match);
590 }
591
592 mlen = strlen(match);
593 slen = strlen(s);
594 return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
595 }
596}
597
609bool
610pcmk__ends_with(const char *s, const char *match)
611{
612 return ends_with(s, match, false);
613}
614
636bool
637pcmk__ends_with_ext(const char *s, const char *match)
638{
639 return ends_with(s, match, true);
640}
641
661static guint
662pcmk__str_hash(gconstpointer v)
663{
664 const signed char *p;
665 guint32 h = 0;
666
667 for (p = v; *p != '\0'; p++)
668 h = (h << 5) - h + *p;
669
670 return h;
671}
672
684GHashTable *
685pcmk__strkey_table(GDestroyNotify key_destroy_func,
686 GDestroyNotify value_destroy_func)
687{
688 return g_hash_table_new_full(pcmk__str_hash, g_str_equal,
689 key_destroy_func, value_destroy_func);
690}
691
702void
703pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
704{
705 pcmk__assert((table != NULL) && (name != NULL));
706
707 g_hash_table_insert(table, pcmk__str_copy(name), pcmk__str_copy(value));
708}
709
710/* used with hash tables where case does not matter */
711static gboolean
712pcmk__strcase_equal(gconstpointer a, gconstpointer b)
713{
714 return pcmk__str_eq((const char *)a, (const char *)b, pcmk__str_casei);
715}
716
717static guint
718pcmk__strcase_hash(gconstpointer v)
719{
720 const signed char *p;
721 guint32 h = 0;
722
723 for (p = v; *p != '\0'; p++)
724 h = (h << 5) - h + g_ascii_tolower(*p);
725
726 return h;
727}
728
740GHashTable *
741pcmk__strikey_table(GDestroyNotify key_destroy_func,
742 GDestroyNotify value_destroy_func)
743{
744 return g_hash_table_new_full(pcmk__strcase_hash, pcmk__strcase_equal,
745 key_destroy_func, value_destroy_func);
746}
747
748static void
749copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
750{
751 if (key && value && user_data) {
752 pcmk__insert_dup((GHashTable *) user_data,
753 (const char *) key, (const char *) value);
754 }
755}
756
767GHashTable *
768pcmk__str_table_dup(GHashTable *old_table)
769{
770 GHashTable *new_table = NULL;
771
772 if (old_table) {
773 new_table = pcmk__strkey_table(free, free);
774 g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
775 }
776 return new_table;
777}
778
795void
796pcmk__add_separated_word(GString **list, size_t init_size, const char *word,
797 const char *separator)
798{
799 pcmk__assert(list != NULL);
800
801 if (pcmk__str_empty(word)) {
802 return;
803 }
804
805 if (*list == NULL) {
806 if (init_size > 0) {
807 *list = g_string_sized_new(init_size);
808 } else {
809 *list = g_string_new(NULL);
810 }
811 }
812
813 if ((*list)->len == 0) {
814 // Don't add a separator before the first word in the list
815 separator = "";
816
817 } else if (separator == NULL) {
818 // Default to space-separated
819 separator = " ";
820 }
821
822 g_string_append(*list, separator);
823 g_string_append(*list, word);
824}
825
838int
839pcmk__compress(const char *data, unsigned int length, unsigned int max,
840 char **result, unsigned int *result_len)
841{
842 int rc;
843 char *compressed = NULL;
844 char *uncompressed = strdup(data);
845#ifdef CLOCK_MONOTONIC
846 struct timespec after_t;
847 struct timespec before_t;
848#endif
849
850 if (max == 0) {
851 max = (length * 1.01) + 601; // Size guaranteed to hold result
852 }
853
854#ifdef CLOCK_MONOTONIC
855 clock_gettime(CLOCK_MONOTONIC, &before_t);
856#endif
857
858 compressed = pcmk__assert_alloc((size_t) max, sizeof(char));
859
860 *result_len = max;
861 rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length,
863 rc = pcmk__bzlib2rc(rc);
864
865 free(uncompressed);
866
867 if (rc != pcmk_rc_ok) {
868 crm_err("Compression of %d bytes failed: %s " QB_XS " rc=%d",
869 length, pcmk_rc_str(rc), rc);
870 free(compressed);
871 return rc;
872 }
873
874#ifdef CLOCK_MONOTONIC
875 clock_gettime(CLOCK_MONOTONIC, &after_t);
876
877 crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
878 length, *result_len, length / (*result_len),
879 (after_t.tv_sec - before_t.tv_sec) * 1000 +
880 (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
881#else
882 crm_trace("Compressed %d bytes into %d (ratio %d:1)",
883 length, *result_len, length / (*result_len));
884#endif
885
886 *result = compressed;
887 return pcmk_rc_ok;
888}
889
890char *
891crm_strdup_printf(char const *format, ...)
892{
893 va_list ap;
894 int len = 0;
895 char *string = NULL;
896
897 va_start(ap, format);
898 len = vasprintf(&string, format, ap);
899 pcmk__assert(len > 0);
900 va_end(ap);
901 return string;
902}
903
904int
905pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
906{
907 char *remainder = NULL;
908 int rc = pcmk_rc_ok;
909
910 pcmk__assert((start != NULL) && (end != NULL));
911
913 // cppcheck doesn't understand the above pcmk__assert line
914 // cppcheck-suppress ctunullpointer
916
917 crm_trace("Attempting to decode: [%s]", srcstring);
918 if (pcmk__str_eq(srcstring, "", pcmk__str_null_matches)) {
919 return ENODATA;
920 } else if (pcmk__str_eq(srcstring, "-", pcmk__str_none)) {
921 return pcmk_rc_bad_input;
922 }
923
924 /* String starts with a dash, so this is either a range with
925 * no beginning or garbage.
926 * */
927 if (*srcstring == '-') {
928 int rc = scan_ll(srcstring+1, end, PCMK__PARSE_INT_DEFAULT, &remainder);
929
930 if ((rc == pcmk_rc_ok) && (*remainder != '\0')) {
932 }
933 return rc;
934 }
935
936 rc = scan_ll(srcstring, start, PCMK__PARSE_INT_DEFAULT, &remainder);
937 if (rc != pcmk_rc_ok) {
938 return rc;
939 }
940
941 if (*remainder && *remainder == '-') {
942 if (*(remainder+1)) {
943 char *more_remainder = NULL;
944 int rc = scan_ll(remainder+1, end, PCMK__PARSE_INT_DEFAULT,
945 &more_remainder);
946
947 if (rc != pcmk_rc_ok) {
948 return rc;
949 } else if (*more_remainder != '\0') {
950 return pcmk_rc_bad_input;
951 }
952 }
953 } else if (*remainder && *remainder != '-') {
955 return pcmk_rc_bad_input;
956 } else {
957 /* The input string contained only one number. Set start and end
958 * to the same value and return pcmk_rc_ok. This gives the caller
959 * a way to tell this condition apart from a range with no end.
960 */
961 *end = *start;
962 }
963
964 return pcmk_rc_ok;
965}
966
983gboolean
984pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
985{
986 for (const GList *ele = lst; ele != NULL; ele = ele->next) {
987 if (pcmk__str_eq(s, ele->data, flags)) {
988 return TRUE;
989 }
990 }
991
992 return FALSE;
993}
994
995static bool
996str_any_of(const char *s, va_list args, uint32_t flags)
997{
998 if (s == NULL) {
999 return false;
1000 }
1001
1002 while (1) {
1003 const char *ele = va_arg(args, const char *);
1004
1005 if (ele == NULL) {
1006 break;
1007 } else if (pcmk__str_eq(s, ele, flags)) {
1008 return true;
1009 }
1010 }
1011
1012 return false;
1013}
1014
1028bool
1029pcmk__strcase_any_of(const char *s, ...)
1030{
1031 va_list ap;
1032 bool rc;
1033
1034 va_start(ap, s);
1035 rc = str_any_of(s, ap, pcmk__str_casei);
1036 va_end(ap);
1037 return rc;
1038}
1039
1052bool
1053pcmk__str_any_of(const char *s, ...)
1054{
1055 va_list ap;
1056 bool rc;
1057
1058 va_start(ap, s);
1059 rc = str_any_of(s, ap, pcmk__str_none);
1060 va_end(ap);
1061 return rc;
1062}
1063
1080int
1081pcmk__numeric_strcasecmp(const char *s1, const char *s2)
1082{
1083 pcmk__assert((s1 != NULL) && (s2 != NULL));
1084
1085 while (*s1 && *s2) {
1086 if (isdigit(*s1) && isdigit(*s2)) {
1087 // If node names contain a number, sort numerically
1088
1089 char *end1 = NULL;
1090 char *end2 = NULL;
1091 long num1 = strtol(s1, &end1, 10);
1092 long num2 = strtol(s2, &end2, 10);
1093
1094 // allow ordering e.g. 007 > 7
1095 size_t len1 = end1 - s1;
1096 size_t len2 = end2 - s2;
1097
1098 if (num1 < num2) {
1099 return -1;
1100 } else if (num1 > num2) {
1101 return 1;
1102 } else if (len1 < len2) {
1103 return -1;
1104 } else if (len1 > len2) {
1105 return 1;
1106 }
1107 s1 = end1;
1108 s2 = end2;
1109 } else {
1110 // Compare non-digits case-insensitively
1111 int lower1 = tolower(*s1);
1112 int lower2 = tolower(*s2);
1113
1114 if (lower1 < lower2) {
1115 return -1;
1116 } else if (lower1 > lower2) {
1117 return 1;
1118 }
1119 ++s1;
1120 ++s2;
1121 }
1122 }
1123 if (!*s1 && *s2) {
1124 return -1;
1125 } else if (*s1 && !*s2) {
1126 return 1;
1127 }
1128 return 0;
1129}
1130
1165int
1166pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
1167{
1168 /* If this flag is set, the second string is a regex. */
1170 regex_t r_patt;
1171 int reg_flags = REG_EXTENDED | REG_NOSUB;
1172 int regcomp_rc = 0;
1173 int rc = 0;
1174
1175 if (s1 == NULL || s2 == NULL) {
1176 return 1;
1177 }
1178
1180 reg_flags |= REG_ICASE;
1181 }
1182 regcomp_rc = regcomp(&r_patt, s2, reg_flags);
1183 if (regcomp_rc != 0) {
1184 rc = 1;
1185 crm_err("Bad regex '%s' for update: %s", s2, strerror(regcomp_rc));
1186 } else {
1187 rc = regexec(&r_patt, s1, 0, NULL, 0);
1188 regfree(&r_patt);
1189 if (rc != 0) {
1190 rc = 1;
1191 }
1192 }
1193 return rc;
1194 }
1195
1196 /* If the strings are the same pointer, return 0 immediately. */
1197 if (s1 == s2) {
1198 return 0;
1199 }
1200
1201 /* If this flag is set, return 0 if either (or both) of the input strings
1202 * are NULL. If neither one is NULL, we need to continue and compare
1203 * them normally.
1204 */
1206 if (s1 == NULL || s2 == NULL) {
1207 return 0;
1208 }
1209 }
1210
1211 /* Handle the cases where one is NULL and the str_null_matches flag is not set.
1212 * A NULL string always sorts to the beginning.
1213 */
1214 if (s1 == NULL) {
1215 return -1;
1216 } else if (s2 == NULL) {
1217 return 1;
1218 }
1219
1220 /* If this flag is set, return 0 if either (or both) of the input strings
1221 * are "*". If neither one is, we need to continue and compare them
1222 * normally.
1223 */
1225 if (strcmp(s1, "*") == 0 || strcmp(s2, "*") == 0) {
1226 return 0;
1227 }
1228 }
1229
1231 return strcasecmp(s1, s2);
1232 } else {
1233 return strcmp(s1, s2);
1234 }
1235}
1236
1250char *
1251pcmk__str_copy_as(const char *file, const char *function, uint32_t line,
1252 const char *str)
1253{
1254 if (str != NULL) {
1255 char *result = strdup(str);
1256
1257 if (result == NULL) {
1258 crm_abort(file, function, line, "Out of memory", FALSE, TRUE);
1260 }
1261 return result;
1262 }
1263 return NULL;
1264}
1265
1279void
1280pcmk__str_update(char **str, const char *value)
1281{
1282 if ((str != NULL) && !pcmk__str_eq(*str, value, pcmk__str_none)) {
1283 free(*str);
1284 *str = pcmk__str_copy(value);
1285 }
1286}
1287
1298void
1299pcmk__g_strcat(GString *buffer, ...)
1300{
1301 va_list ap;
1302
1303 pcmk__assert(buffer != NULL);
1304 va_start(ap, buffer);
1305
1306 while (true) {
1307 const char *ele = va_arg(ap, const char *);
1308
1309 if (ele == NULL) {
1310 break;
1311 }
1312 g_string_append(buffer, ele);
1313 }
1314 va_end(ap);
1315}
const char * name
Definition cib.c:26
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:246
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:80
char data[0]
Definition cpg.c:10
crm_time_t * crm_time_parse_duration(const char *duration_str)
Parse a time duration from an ISO 8601 duration specification.
Definition iso8601.c:1121
void crm_time_free(crm_time_t *dt)
Definition iso8601.c:150
long long crm_time_get_seconds(const crm_time_t *dt)
Definition iso8601.c:331
struct crm_time_s crm_time_t
Definition iso8601.h:32
#define crm_warn(fmt, args...)
Definition logging.h:360
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_err(fmt, args...)
Definition logging.h:357
#define crm_trace(fmt, args...)
Definition logging.h:370
xmlNode * input
#define PCMK_VALUE_OFF
Definition options.h:185
#define PCMK_VALUE_TRUE
Definition options.h:219
#define PCMK_VALUE_FALSE
Definition options.h:154
pcmk__action_result_t result
Definition pcmk_fence.c:37
#define ENODATA
Definition portability.h:61
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:617
@ CRM_EX_OSERR
External (OS/environmental) problem.
Definition results.h:254
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition results.c:1058
@ pcmk_rc_before_range
Definition results.h:131
@ pcmk_rc_ok
Definition results.h:159
@ pcmk_rc_after_range
Definition results.h:129
@ pcmk_rc_underflow
Definition results.h:126
@ pcmk_rc_bad_input
Definition results.h:119
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition results.c:215
#define pcmk__assert(expr)
int pcmk__bzlib2rc(int bz2)
Map a bz2 return code to the most similar Pacemaker return code.
Definition results.c:1028
bool pcmk__strcase_any_of(const char *s,...)
Definition strings.c:1029
GHashTable * pcmk__str_table_dup(GHashTable *old_table)
Definition strings.c:768
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition strings.c:703
int pcmk__numeric_strcasecmp(const char *s1, const char *s2)
Definition strings.c:1081
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:116
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition strings.c:984
int pcmk__scan_port(const char *text, int *port)
Definition strings.c:150
char * crm_strdup_printf(char const *format,...)
Definition strings.c:891
int pcmk_parse_interval_spec(const char *input, guint *result_ms)
Parse milliseconds from a Pacemaker interval specification.
Definition strings.c:452
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:685
char * pcmk__str_copy_as(const char *file, const char *function, uint32_t line, const char *str)
Definition strings.c:1251
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition strings.c:351
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition strings.c:637
char * pcmk__trim(char *str)
Definition strings.c:530
int pcmk__scan_double(const char *text, double *result, const char *default_text, char **end_text)
Definition strings.c:191
bool pcmk__starts_with(const char *str, const char *prefix)
Check whether a string starts with a certain sequence.
Definition strings.c:558
int pcmk__compress(const char *data, unsigned int length, unsigned int max, char **result, unsigned int *result_len)
Definition strings.c:839
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition strings.c:92
void pcmk__str_update(char **str, const char *value)
Definition strings.c:1280
bool pcmk__ends_with(const char *s, const char *match)
Definition strings.c:610
int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, guint *result)
Definition strings.c:303
int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
Definition strings.c:1166
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition strings.c:905
gboolean crm_is_true(const char *s)
Definition strings.c:490
bool pcmk__str_any_of(const char *s,...)
Definition strings.c:1053
int crm_str_to_boolean(const char *s, int *ret)
Definition strings.c:498
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition strings.c:796
void pcmk__g_strcat(GString *buffer,...)
Definition strings.c:1299
GHashTable * pcmk__strikey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:741
@ pcmk__str_regex
@ pcmk__str_none
@ pcmk__str_null_matches
@ pcmk__str_star_matches
@ pcmk__str_casei
#define pcmk__str_copy(str)
#define PCMK__PARSE_DBL_DEFAULT
#define PCMK__PARSE_INT_DEFAULT
#define CRM_BZ2_WORK
Definition xml_io.h:34
#define CRM_BZ2_BLOCKS
Definition xml_io.h:33