pacemaker 2.1.8-2.1.8
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/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
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 (QB_ABS(*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 (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) {
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
355long long
356crm_get_msec(const char *input)
357{
358 char *units = NULL; // Do not free; will point to part of input
359 long long multiplier = 1000;
360 long long divisor = 1;
361 long long msec = PCMK__PARSE_INT_DEFAULT;
362
363 if (input == NULL) {
365 }
366
367 // Skip initial whitespace
368 while (isspace(*input)) {
369 input++;
370 }
371
372 // Reject negative and unparsable inputs
373 scan_ll(input, &msec, -1, &units);
374 if (msec < 0) {
376 }
377
378 /* If the number is a decimal, scan_ll() reads only the integer part. Skip
379 * any remaining digits or decimal characters.
380 *
381 * @COMPAT Well-formed and malformed decimals are both accepted inputs. For
382 * example, "3.14 ms" and "3.1.4 ms" are treated the same as "3ms" and
383 * parsed successfully. At a compatibility break, decide if this is still
384 * desired.
385 */
386 while (isdigit(*units) || (*units == '.')) {
387 units++;
388 }
389
390 // Skip any additional whitespace after the number
391 while (isspace(*units)) {
392 units++;
393 }
394
395 /* @COMPAT Use exact comparisons. Currently, we match too liberally, and the
396 * second strncasecmp() in each case is redundant.
397 */
398 if ((*units == '\0')
399 || (strncasecmp(units, "s", 1) == 0)
400 || (strncasecmp(units, "sec", 3) == 0)) {
401 multiplier = 1000;
402 divisor = 1;
403
404 } else if ((strncasecmp(units, "ms", 2) == 0)
405 || (strncasecmp(units, "msec", 4) == 0)) {
406 multiplier = 1;
407 divisor = 1;
408
409 } else if ((strncasecmp(units, "us", 2) == 0)
410 || (strncasecmp(units, "usec", 4) == 0)) {
411 multiplier = 1;
412 divisor = 1000;
413
414 } else if ((strncasecmp(units, "m", 1) == 0)
415 || (strncasecmp(units, "min", 3) == 0)) {
416 multiplier = 60 * 1000;
417 divisor = 1;
418
419 } else if ((strncasecmp(units, "h", 1) == 0)
420 || (strncasecmp(units, "hr", 2) == 0)) {
421 multiplier = 60 * 60 * 1000;
422 divisor = 1;
423
424 } else {
425 // Invalid units
427 }
428
429 // Apply units, capping at LLONG_MAX
430 if (msec > (LLONG_MAX / multiplier)) {
431 return LLONG_MAX;
432 }
433 return (msec * multiplier) / divisor;
434}
435
450int
451pcmk_parse_interval_spec(const char *input, guint *result_ms)
452{
453 long long msec = PCMK__PARSE_INT_DEFAULT;
454 int rc = pcmk_rc_ok;
455
456 if (input == NULL) {
457 msec = 0;
458 goto done;
459 }
460
461 if (input[0] == 'P') {
463
464 if (period_s != NULL) {
465 msec = 1000 * crm_time_get_seconds(period_s);
466 crm_time_free(period_s);
467 }
468
469 } else {
470 msec = crm_get_msec(input);
471 }
472
473 if (msec == PCMK__PARSE_INT_DEFAULT) {
474 crm_warn("Using 0 instead of invalid interval specification '%s'",
475 input);
476 msec = 0;
477 rc = EINVAL;
478 }
479
480done:
481 if (result_ms != NULL) {
482 *result_ms = (msec >= G_MAXUINT)? G_MAXUINT : (guint) msec;
483 }
484 return rc;
485}
486
487gboolean
488crm_is_true(const char *s)
489{
490 gboolean ret = FALSE;
491
492 return (crm_str_to_boolean(s, &ret) < 0)? FALSE : ret;
493}
494
495int
496crm_str_to_boolean(const char *s, int *ret)
497{
498 if (s == NULL) {
499 return -1;
500 }
501
502 if (pcmk__strcase_any_of(s, PCMK_VALUE_TRUE, "on", "yes", "y", "1", NULL)) {
503 if (ret != NULL) {
504 *ret = TRUE;
505 }
506 return 1;
507 }
508
509 if (pcmk__strcase_any_of(s, PCMK_VALUE_FALSE, "off", "no", "n", "0",
510 NULL)) {
511 if (ret != NULL) {
512 *ret = FALSE;
513 }
514 return 1;
515 }
516 return -1;
517}
518
527char *
528pcmk__trim(char *str)
529{
530 int len;
531
532 if (str == NULL) {
533 return str;
534 }
535
536 for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
537 str[len] = '\0';
538 }
539
540 return str;
541}
542
555bool
556pcmk__starts_with(const char *str, const char *prefix)
557{
558 const char *s = str;
559 const char *p = prefix;
560
561 if (!s || !p) {
562 return false;
563 }
564 while (*s && *p) {
565 if (*s++ != *p++) {
566 return false;
567 }
568 }
569 return (*p == 0);
570}
571
572static inline bool
573ends_with(const char *s, const char *match, bool as_extension)
574{
575 if (pcmk__str_empty(match)) {
576 return true;
577 } else if (s == NULL) {
578 return false;
579 } else {
580 size_t slen, mlen;
581
582 /* Besides as_extension, we could also check
583 !strchr(&match[1], match[0]) but that would be inefficient.
584 */
585 if (as_extension) {
586 s = strrchr(s, match[0]);
587 return (s == NULL)? false : !strcmp(s, match);
588 }
589
590 mlen = strlen(match);
591 slen = strlen(s);
592 return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
593 }
594}
595
607bool
608pcmk__ends_with(const char *s, const char *match)
609{
610 return ends_with(s, match, false);
611}
612
634bool
635pcmk__ends_with_ext(const char *s, const char *match)
636{
637 return ends_with(s, match, true);
638}
639
659static guint
660pcmk__str_hash(gconstpointer v)
661{
662 const signed char *p;
663 guint32 h = 0;
664
665 for (p = v; *p != '\0'; p++)
666 h = (h << 5) - h + *p;
667
668 return h;
669}
670
682GHashTable *
683pcmk__strkey_table(GDestroyNotify key_destroy_func,
684 GDestroyNotify value_destroy_func)
685{
686 return g_hash_table_new_full(pcmk__str_hash, g_str_equal,
687 key_destroy_func, value_destroy_func);
688}
689
700void
701pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
702{
703 CRM_ASSERT((table != NULL) && (name != NULL));
704
705 g_hash_table_insert(table, pcmk__str_copy(name), pcmk__str_copy(value));
706}
707
708/* used with hash tables where case does not matter */
709static gboolean
710pcmk__strcase_equal(gconstpointer a, gconstpointer b)
711{
712 return pcmk__str_eq((const char *)a, (const char *)b, pcmk__str_casei);
713}
714
715static guint
716pcmk__strcase_hash(gconstpointer v)
717{
718 const signed char *p;
719 guint32 h = 0;
720
721 for (p = v; *p != '\0'; p++)
722 h = (h << 5) - h + g_ascii_tolower(*p);
723
724 return h;
725}
726
738GHashTable *
739pcmk__strikey_table(GDestroyNotify key_destroy_func,
740 GDestroyNotify value_destroy_func)
741{
742 return g_hash_table_new_full(pcmk__strcase_hash, pcmk__strcase_equal,
743 key_destroy_func, value_destroy_func);
744}
745
746static void
747copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
748{
749 if (key && value && user_data) {
750 pcmk__insert_dup((GHashTable *) user_data,
751 (const char *) key, (const char *) value);
752 }
753}
754
765GHashTable *
766pcmk__str_table_dup(GHashTable *old_table)
767{
768 GHashTable *new_table = NULL;
769
770 if (old_table) {
771 new_table = pcmk__strkey_table(free, free);
772 g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
773 }
774 return new_table;
775}
776
793void
794pcmk__add_separated_word(GString **list, size_t init_size, const char *word,
795 const char *separator)
796{
797 CRM_ASSERT(list != NULL);
798
799 if (pcmk__str_empty(word)) {
800 return;
801 }
802
803 if (*list == NULL) {
804 if (init_size > 0) {
805 *list = g_string_sized_new(init_size);
806 } else {
807 *list = g_string_new(NULL);
808 }
809 }
810
811 if ((*list)->len == 0) {
812 // Don't add a separator before the first word in the list
813 separator = "";
814
815 } else if (separator == NULL) {
816 // Default to space-separated
817 separator = " ";
818 }
819
820 g_string_append(*list, separator);
821 g_string_append(*list, word);
822}
823
836int
837pcmk__compress(const char *data, unsigned int length, unsigned int max,
838 char **result, unsigned int *result_len)
839{
840 int rc;
841 char *compressed = NULL;
842 char *uncompressed = strdup(data);
843#ifdef CLOCK_MONOTONIC
844 struct timespec after_t;
845 struct timespec before_t;
846#endif
847
848 if (max == 0) {
849 max = (length * 1.01) + 601; // Size guaranteed to hold result
850 }
851
852#ifdef CLOCK_MONOTONIC
853 clock_gettime(CLOCK_MONOTONIC, &before_t);
854#endif
855
856 compressed = pcmk__assert_alloc((size_t) max, sizeof(char));
857
858 *result_len = max;
859 rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length,
861 rc = pcmk__bzlib2rc(rc);
862
863 free(uncompressed);
864
865 if (rc != pcmk_rc_ok) {
866 crm_err("Compression of %d bytes failed: %s " CRM_XS " rc=%d",
867 length, pcmk_rc_str(rc), rc);
868 free(compressed);
869 return rc;
870 }
871
872#ifdef CLOCK_MONOTONIC
873 clock_gettime(CLOCK_MONOTONIC, &after_t);
874
875 crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
876 length, *result_len, length / (*result_len),
877 (after_t.tv_sec - before_t.tv_sec) * 1000 +
878 (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
879#else
880 crm_trace("Compressed %d bytes into %d (ratio %d:1)",
881 length, *result_len, length / (*result_len));
882#endif
883
884 *result = compressed;
885 return pcmk_rc_ok;
886}
887
888char *
889crm_strdup_printf(char const *format, ...)
890{
891 va_list ap;
892 int len = 0;
893 char *string = NULL;
894
895 va_start(ap, format);
896 len = vasprintf (&string, format, ap);
897 CRM_ASSERT(len > 0);
898 va_end(ap);
899 return string;
900}
901
902int
903pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
904{
905 char *remainder = NULL;
906 int rc = pcmk_rc_ok;
907
908 CRM_ASSERT(start != NULL && end != NULL);
909
912
913 crm_trace("Attempting to decode: [%s]", srcstring);
914 if (pcmk__str_eq(srcstring, "", pcmk__str_null_matches)) {
915 return ENODATA;
916 } else if (pcmk__str_eq(srcstring, "-", pcmk__str_none)) {
917 return pcmk_rc_bad_input;
918 }
919
920 /* String starts with a dash, so this is either a range with
921 * no beginning or garbage.
922 * */
923 if (*srcstring == '-') {
924 int rc = scan_ll(srcstring+1, end, PCMK__PARSE_INT_DEFAULT, &remainder);
925
926 if (rc != pcmk_rc_ok || *remainder != '\0') {
927 return pcmk_rc_bad_input;
928 } else {
929 return pcmk_rc_ok;
930 }
931 }
932
933 rc = scan_ll(srcstring, start, PCMK__PARSE_INT_DEFAULT, &remainder);
934 if (rc != pcmk_rc_ok) {
935 return rc;
936 }
937
938 if (*remainder && *remainder == '-') {
939 if (*(remainder+1)) {
940 char *more_remainder = NULL;
941 int rc = scan_ll(remainder+1, end, PCMK__PARSE_INT_DEFAULT,
942 &more_remainder);
943
944 if (rc != pcmk_rc_ok) {
945 return rc;
946 } else if (*more_remainder != '\0') {
947 return pcmk_rc_bad_input;
948 }
949 }
950 } else if (*remainder && *remainder != '-') {
952 return pcmk_rc_bad_input;
953 } else {
954 /* The input string contained only one number. Set start and end
955 * to the same value and return pcmk_rc_ok. This gives the caller
956 * a way to tell this condition apart from a range with no end.
957 */
958 *end = *start;
959 }
960
961 return pcmk_rc_ok;
962}
963
980gboolean
981pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
982{
983 for (const GList *ele = lst; ele != NULL; ele = ele->next) {
984 if (pcmk__str_eq(s, ele->data, flags)) {
985 return TRUE;
986 }
987 }
988
989 return FALSE;
990}
991
992static bool
993str_any_of(const char *s, va_list args, uint32_t flags)
994{
995 if (s == NULL) {
997 }
998
999 while (1) {
1000 const char *ele = va_arg(args, const char *);
1001
1002 if (ele == NULL) {
1003 break;
1004 } else if (pcmk__str_eq(s, ele, flags)) {
1005 return true;
1006 }
1007 }
1008
1009 return false;
1010}
1011
1025bool
1026pcmk__strcase_any_of(const char *s, ...)
1027{
1028 va_list ap;
1029 bool rc;
1030
1031 va_start(ap, s);
1032 rc = str_any_of(s, ap, pcmk__str_casei);
1033 va_end(ap);
1034 return rc;
1035}
1036
1049bool
1050pcmk__str_any_of(const char *s, ...)
1051{
1052 va_list ap;
1053 bool rc;
1054
1055 va_start(ap, s);
1056 rc = str_any_of(s, ap, pcmk__str_none);
1057 va_end(ap);
1058 return rc;
1059}
1060
1077int
1078pcmk__numeric_strcasecmp(const char *s1, const char *s2)
1079{
1080 CRM_ASSERT((s1 != NULL) && (s2 != NULL));
1081
1082 while (*s1 && *s2) {
1083 if (isdigit(*s1) && isdigit(*s2)) {
1084 // If node names contain a number, sort numerically
1085
1086 char *end1 = NULL;
1087 char *end2 = NULL;
1088 long num1 = strtol(s1, &end1, 10);
1089 long num2 = strtol(s2, &end2, 10);
1090
1091 // allow ordering e.g. 007 > 7
1092 size_t len1 = end1 - s1;
1093 size_t len2 = end2 - s2;
1094
1095 if (num1 < num2) {
1096 return -1;
1097 } else if (num1 > num2) {
1098 return 1;
1099 } else if (len1 < len2) {
1100 return -1;
1101 } else if (len1 > len2) {
1102 return 1;
1103 }
1104 s1 = end1;
1105 s2 = end2;
1106 } else {
1107 // Compare non-digits case-insensitively
1108 int lower1 = tolower(*s1);
1109 int lower2 = tolower(*s2);
1110
1111 if (lower1 < lower2) {
1112 return -1;
1113 } else if (lower1 > lower2) {
1114 return 1;
1115 }
1116 ++s1;
1117 ++s2;
1118 }
1119 }
1120 if (!*s1 && *s2) {
1121 return -1;
1122 } else if (*s1 && !*s2) {
1123 return 1;
1124 }
1125 return 0;
1126}
1127
1162int
1163pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
1164{
1165 /* If this flag is set, the second string is a regex. */
1167 regex_t r_patt;
1168 int reg_flags = REG_EXTENDED | REG_NOSUB;
1169 int regcomp_rc = 0;
1170 int rc = 0;
1171
1172 if (s1 == NULL || s2 == NULL) {
1173 return 1;
1174 }
1175
1177 reg_flags |= REG_ICASE;
1178 }
1179 regcomp_rc = regcomp(&r_patt, s2, reg_flags);
1180 if (regcomp_rc != 0) {
1181 rc = 1;
1182 crm_err("Bad regex '%s' for update: %s", s2, strerror(regcomp_rc));
1183 } else {
1184 rc = regexec(&r_patt, s1, 0, NULL, 0);
1185 regfree(&r_patt);
1186 if (rc != 0) {
1187 rc = 1;
1188 }
1189 }
1190 return rc;
1191 }
1192
1193 /* If the strings are the same pointer, return 0 immediately. */
1194 if (s1 == s2) {
1195 return 0;
1196 }
1197
1198 /* If this flag is set, return 0 if either (or both) of the input strings
1199 * are NULL. If neither one is NULL, we need to continue and compare
1200 * them normally.
1201 */
1203 if (s1 == NULL || s2 == NULL) {
1204 return 0;
1205 }
1206 }
1207
1208 /* Handle the cases where one is NULL and the str_null_matches flag is not set.
1209 * A NULL string always sorts to the beginning.
1210 */
1211 if (s1 == NULL) {
1212 return -1;
1213 } else if (s2 == NULL) {
1214 return 1;
1215 }
1216
1217 /* If this flag is set, return 0 if either (or both) of the input strings
1218 * are "*". If neither one is, we need to continue and compare them
1219 * normally.
1220 */
1222 if (strcmp(s1, "*") == 0 || strcmp(s2, "*") == 0) {
1223 return 0;
1224 }
1225 }
1226
1228 return strcasecmp(s1, s2);
1229 } else {
1230 return strcmp(s1, s2);
1231 }
1232}
1233
1247char *
1248pcmk__str_copy_as(const char *file, const char *function, uint32_t line,
1249 const char *str)
1250{
1251 if (str != NULL) {
1252 char *result = strdup(str);
1253
1254 if (result == NULL) {
1255 crm_abort(file, function, line, "Out of memory", FALSE, TRUE);
1257 }
1258 return result;
1259 }
1260 return NULL;
1261}
1262
1276void
1277pcmk__str_update(char **str, const char *value)
1278{
1279 if ((str != NULL) && !pcmk__str_eq(*str, value, pcmk__str_none)) {
1280 free(*str);
1281 *str = pcmk__str_copy(value);
1282 }
1283}
1284
1295void
1296pcmk__g_strcat(GString *buffer, ...)
1297{
1298 va_list ap;
1299
1300 CRM_ASSERT(buffer != NULL);
1301 va_start(ap, buffer);
1302
1303 while (true) {
1304 const char *ele = va_arg(ap, const char *);
1305
1306 if (ele == NULL) {
1307 break;
1308 }
1309 g_string_append(buffer, ele);
1310 }
1311 va_end(ap);
1312}
1313
1314// Deprecated functions kept only for backward API compatibility
1315// LCOV_EXCL_START
1316
1317#include <crm/common/util_compat.h>
1318
1319gboolean
1320safe_str_neq(const char *a, const char *b)
1321{
1322 if (a == b) {
1323 return FALSE;
1324
1325 } else if (a == NULL || b == NULL) {
1326 return TRUE;
1327
1328 } else if (strcasecmp(a, b) == 0) {
1329 return FALSE;
1330 }
1331 return TRUE;
1332}
1333
1334gboolean
1335crm_str_eq(const char *a, const char *b, gboolean use_case)
1336{
1337 if (use_case) {
1338 return g_strcmp0(a, b) == 0;
1339
1340 /* TODO - Figure out which calls, if any, really need to be case independent */
1341 } else if (a == b) {
1342 return TRUE;
1343
1344 } else if (a == NULL || b == NULL) {
1345 /* shouldn't be comparing NULLs */
1346 return FALSE;
1347
1348 } else if (strcasecmp(a, b) == 0) {
1349 return TRUE;
1350 }
1351 return FALSE;
1352}
1353
1354char *
1355crm_itoa_stack(int an_int, char *buffer, size_t len)
1356{
1357 if (buffer != NULL) {
1358 snprintf(buffer, len, "%d", an_int);
1359 }
1360 return buffer;
1361}
1362
1363guint
1365{
1366 return pcmk__str_hash(v);
1367}
1368
1369gboolean
1370crm_strcase_equal(gconstpointer a, gconstpointer b)
1371{
1372 return pcmk__strcase_equal(a, b);
1373}
1374
1375guint
1376crm_strcase_hash(gconstpointer v)
1377{
1378 return pcmk__strcase_hash(v);
1379}
1380
1381GHashTable *
1382crm_str_table_dup(GHashTable *old_table)
1383{
1384 return pcmk__str_table_dup(old_table);
1385}
1386
1387long long
1388crm_parse_ll(const char *text, const char *default_text)
1389{
1390 long long result;
1391
1392 if (text == NULL) {
1393 text = default_text;
1394 if (text == NULL) {
1395 crm_err("No default conversion value supplied");
1396 errno = EINVAL;
1398 }
1399 }
1400 scan_ll(text, &result, PCMK__PARSE_INT_DEFAULT, NULL);
1401 return result;
1402}
1403
1404int
1405crm_parse_int(const char *text, const char *default_text)
1406{
1407 long long result = crm_parse_ll(text, default_text);
1408
1409 if (result < INT_MIN) {
1410 // If errno is ERANGE, crm_parse_ll() has already logged a message
1411 if (errno != ERANGE) {
1412 crm_err("Conversion of %s was clipped: %lld", text, result);
1413 errno = ERANGE;
1414 }
1415 return INT_MIN;
1416
1417 } else if (result > INT_MAX) {
1418 // If errno is ERANGE, crm_parse_ll() has already logged a message
1419 if (errno != ERANGE) {
1420 crm_err("Conversion of %s was clipped: %lld", text, result);
1421 errno = ERANGE;
1422 }
1423 return INT_MAX;
1424 }
1425
1426 return (int) result;
1427}
1428
1429char *
1431{
1432 return pcmk__trim(str);
1433}
1434
1435int
1436pcmk_numeric_strcasecmp(const char *s1, const char *s2)
1437{
1438 return pcmk__numeric_strcasecmp(s1, s2);
1439}
1440
1441// LCOV_EXCL_STOP
1442// End deprecated API
const char * name
Definition cib.c:26
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:297
uint64_t flags
Definition remote.c:3
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition utils.c:356
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:98
Deprecated Pacemaker utilities.
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:1086
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:316
struct crm_time_s crm_time_t
Definition iso8601.h:32
#define crm_warn(fmt, args...)
Definition logging.h:392
#define CRM_XS
Definition logging.h:56
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_debug(fmt, args...)
Definition logging.h:400
#define crm_err(fmt, args...)
Definition logging.h:389
#define crm_trace(fmt, args...)
Definition logging.h:402
xmlNode * input
#define PCMK_VALUE_TRUE
Definition options.h:215
#define PCMK_VALUE_FALSE
Definition options.h:152
pcmk__action_result_t result
Definition pcmk_fence.c:35
#define ENODATA
Function and executable result codes.
#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:501
@ CRM_EX_OSERR
External (OS/environmental) problem.
Definition results.h:276
_Noreturn crm_exit_t crm_exit(crm_exit_t rc)
Definition results.c:936
@ pcmk_rc_before_range
Definition results.h:134
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_rc_after_range
Definition results.h:132
@ pcmk_rc_underflow
Definition results.h:129
@ pcmk_rc_bad_input
Definition results.h:122
int pcmk__bzlib2rc(int bz2)
Map a bz2 return code to the most similar Pacemaker return code.
Definition results.c:906
guint g_str_hash_traditional(gconstpointer v)
Definition strings.c:1364
gboolean crm_strcase_equal(gconstpointer a, gconstpointer b)
Definition strings.c:1370
bool pcmk__strcase_any_of(const char *s,...)
Definition strings.c:1026
GHashTable * pcmk__str_table_dup(GHashTable *old_table)
Definition strings.c:766
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition strings.c:701
int pcmk__numeric_strcasecmp(const char *s1, const char *s2)
Definition strings.c:1078
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition strings.c:1335
char * crm_itoa_stack(int an_int, char *buffer, size_t len)
Definition strings.c:1355
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:127
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition strings.c:981
GHashTable * crm_str_table_dup(GHashTable *old_table)
Definition strings.c:1382
int pcmk__scan_port(const char *text, int *port)
Definition strings.c:161
char * crm_strdup_printf(char const *format,...)
Definition strings.c:889
int pcmk_parse_interval_spec(const char *input, guint *result_ms)
Parse milliseconds from a Pacemaker interval specification.
Definition strings.c:451
gboolean safe_str_neq(const char *a, const char *b)
Definition strings.c:1320
guint crm_strcase_hash(gconstpointer v)
Definition strings.c:1376
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:683
char * pcmk__str_copy_as(const char *file, const char *function, uint32_t line, const char *str)
Definition strings.c:1248
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition strings.c:356
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition strings.c:635
char * pcmk__trim(char *str)
Definition strings.c:528
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:556
int pcmk__compress(const char *data, unsigned int length, unsigned int max, char **result, unsigned int *result_len)
Definition strings.c:837
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:1277
int pcmk_numeric_strcasecmp(const char *s1, const char *s2)
Definition strings.c:1436
char * crm_strip_trailing_newline(char *str)
Definition strings.c:1430
bool pcmk__ends_with(const char *s, const char *match)
Definition strings.c:608
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:1163
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition strings.c:903
gboolean crm_is_true(const char *s)
Definition strings.c:488
long long crm_parse_ll(const char *text, const char *default_text)
Definition strings.c:1388
bool pcmk__str_any_of(const char *s,...)
Definition strings.c:1050
int crm_str_to_boolean(const char *s, int *ret)
Definition strings.c:496
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition strings.c:794
void pcmk__g_strcat(GString *buffer,...)
Definition strings.c:1296
GHashTable * pcmk__strikey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:739
int crm_parse_int(const char *text, const char *default_text)
Definition strings.c:1405
@ 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