pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
strings.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2023 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include "crm/common/results.h"
11 #include <crm_internal.h>
12 
13 #ifndef _GNU_SOURCE
14 # define _GNU_SOURCE
15 #endif
16 
17 #include <regex.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include <float.h> // DBL_MIN
23 #include <limits.h>
24 #include <bzlib.h>
25 #include <sys/types.h>
26 
42 static int
43 scan_ll(const char *text, long long *result, long long default_value,
44  char **end_text)
45 {
46  long long local_result = default_value;
47  char *local_end_text = NULL;
48  int rc = pcmk_rc_ok;
49 
50  errno = 0;
51  if (text != NULL) {
52  local_result = strtoll(text, &local_end_text, 10);
53  if (errno == ERANGE) {
54  rc = EOVERFLOW;
55  crm_warn("Integer parsed from '%s' was clipped to %lld",
56  text, local_result);
57 
58  } else if (errno != 0) {
59  rc = errno;
60  local_result = default_value;
61  crm_warn("Could not parse integer from '%s' (using %lld instead): "
62  "%s", text, default_value, pcmk_rc_str(rc));
63 
64  } else if (local_end_text == text) {
65  rc = EINVAL;
66  local_result = default_value;
67  crm_warn("Could not parse integer from '%s' (using %lld instead): "
68  "No digits found", text, default_value);
69  }
70 
71  if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) {
72  crm_warn("Characters left over after parsing '%s': '%s'",
73  text, local_end_text);
74  }
75  errno = rc;
76  }
77  if (end_text != NULL) {
78  *end_text = local_end_text;
79  }
80  if (result != NULL) {
81  *result = local_result;
82  }
83  return rc;
84 }
85 
96 int
97 pcmk__scan_ll(const char *text, long long *result, long long default_value)
98 {
99  long long local_result = default_value;
100  int rc = pcmk_rc_ok;
101 
102  if (text != NULL) {
103  rc = scan_ll(text, &local_result, default_value, NULL);
104  if (rc != pcmk_rc_ok) {
105  local_result = default_value;
106  }
107  }
108  if (result != NULL) {
109  *result = local_result;
110  }
111  return rc;
112 }
113 
126 int
127 pcmk__scan_min_int(const char *text, int *result, int minimum)
128 {
129  int rc;
130  long long result_ll;
131 
132  rc = pcmk__scan_ll(text, &result_ll, (long long) minimum);
133 
134  if (result_ll < (long long) minimum) {
135  crm_warn("Clipped '%s' to minimum acceptable value %d", text, minimum);
136  result_ll = (long long) minimum;
137 
138  } else if (result_ll > INT_MAX) {
139  crm_warn("Clipped '%s' to maximum integer %d", text, INT_MAX);
140  result_ll = (long long) INT_MAX;
141  rc = EOVERFLOW;
142  }
143 
144  if (result != NULL) {
145  *result = (int) result_ll;
146  }
147  return rc;
148 }
149 
160 int
161 pcmk__scan_port(const char *text, int *port)
162 {
163  long long port_ll;
164  int rc = pcmk__scan_ll(text, &port_ll, -1LL);
165 
166  if ((text != NULL) && (rc == pcmk_rc_ok) // wasn't default or invalid
167  && ((port_ll < 0LL) || (port_ll > 65535LL))) {
168  crm_warn("Ignoring port specification '%s' "
169  "not in valid range (0-65535)", text);
170  rc = (port_ll < 0LL)? pcmk_rc_before_range : pcmk_rc_after_range;
171  port_ll = -1LL;
172  }
173  if (port != NULL) {
174  *port = (int) port_ll;
175  }
176  return rc;
177 }
178 
198 int
199 pcmk__scan_double(const char *text, double *result, const char *default_text,
200  char **end_text)
201 {
202  int rc = pcmk_rc_ok;
203  char *local_end_text = NULL;
204 
205  CRM_ASSERT(result != NULL);
207 
208  text = (text != NULL) ? text : default_text;
209 
210  if (text == NULL) {
211  rc = EINVAL;
212  crm_debug("No text and no default conversion value supplied");
213 
214  } else {
215  errno = 0;
216  *result = strtod(text, &local_end_text);
217 
218  if (errno == ERANGE) {
219  /*
220  * Overflow: strtod() returns +/- HUGE_VAL and sets errno to
221  * ERANGE
222  *
223  * Underflow: strtod() returns "a value whose magnitude is
224  * no greater than the smallest normalized
225  * positive" double. Whether ERANGE is set is
226  * implementation-defined.
227  */
228  const char *over_under;
229 
230  if (QB_ABS(*result) > DBL_MIN) {
231  rc = EOVERFLOW;
232  over_under = "over";
233  } else {
234  rc = pcmk_rc_underflow;
235  over_under = "under";
236  }
237 
238  crm_debug("Floating-point value parsed from '%s' would %sflow "
239  "(using %g instead)", text, over_under, *result);
240 
241  } else if (errno != 0) {
242  rc = errno;
243  // strtod() set *result = 0 on parse failure
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) {
274  rc = pcmk_rc_underflow;
275  crm_debug("Floating-point value parsed from '%s' would "
276  "underflow (using %g instead)", text, *result);
277  break;
278  }
279  }
280 
281  } else {
282  crm_trace("Floating-point value parsed successfully from "
283  "'%s': %g", text, *result);
284  }
285 
286  if ((end_text == NULL) && !pcmk__str_empty(local_end_text)) {
287  crm_debug("Characters left over after parsing '%s': '%s'",
288  text, local_end_text);
289  }
290  }
291 
292  if (end_text != NULL) {
293  *end_text = local_end_text;
294  }
295 
296  return rc;
297 }
298 
310 int
311 pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val,
312  guint *result)
313 {
314  const char *value;
315  long long value_ll;
316  int rc = pcmk_rc_ok;
317 
318  CRM_CHECK((table != NULL) && (key != NULL), return EINVAL);
319 
320  if (result != NULL) {
321  *result = default_val;
322  }
323 
324  value = g_hash_table_lookup(table, key);
325  if (value == NULL) {
326  return pcmk_rc_ok;
327  }
328 
329  rc = pcmk__scan_ll(value, &value_ll, 0LL);
330  if (rc != pcmk_rc_ok) {
331  return rc;
332  }
333 
334  if ((value_ll < 0) || (value_ll > G_MAXUINT)) {
335  crm_warn("Could not parse non-negative integer from %s", value);
336  return ERANGE;
337  }
338 
339  if (result != NULL) {
340  *result = (guint) value_ll;
341  }
342  return pcmk_rc_ok;
343 }
344 
345 #ifndef NUMCHARS
346 # define NUMCHARS "0123456789."
347 #endif
348 
349 #ifndef WHITESPACE
350 # define WHITESPACE " \t\n\r\f"
351 #endif
352 
363 long long
364 crm_get_msec(const char *input)
365 {
366  const char *num_start = NULL;
367  const char *units;
368  long long multiplier = 1000;
369  long long divisor = 1;
370  long long msec = PCMK__PARSE_INT_DEFAULT;
371  size_t num_len = 0;
372  char *end_text = NULL;
373 
374  if (input == NULL) {
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 
415 gboolean
416 crm_is_true(const char *s)
417 {
418  gboolean ret = FALSE;
419 
420  return (crm_str_to_boolean(s, &ret) < 0)? FALSE : ret;
421 }
422 
423 int
424 crm_str_to_boolean(const char *s, int *ret)
425 {
426  if (s == NULL) {
427  return -1;
428 
429  } else if (strcasecmp(s, "true") == 0
430  || strcasecmp(s, "on") == 0
431  || strcasecmp(s, "yes") == 0 || strcasecmp(s, "y") == 0 || strcasecmp(s, "1") == 0) {
432  *ret = TRUE;
433  return 1;
434 
435  } else if (strcasecmp(s, "false") == 0
436  || strcasecmp(s, "off") == 0
437  || strcasecmp(s, "no") == 0 || strcasecmp(s, "n") == 0 || strcasecmp(s, "0") == 0) {
438  *ret = FALSE;
439  return 1;
440  }
441  return -1;
442 }
443 
452 char *
453 pcmk__trim(char *str)
454 {
455  int len;
456 
457  if (str == NULL) {
458  return str;
459  }
460 
461  for (len = strlen(str) - 1; len >= 0 && str[len] == '\n'; len--) {
462  str[len] = '\0';
463  }
464 
465  return str;
466 }
467 
480 bool
481 pcmk__starts_with(const char *str, const char *prefix)
482 {
483  const char *s = str;
484  const char *p = prefix;
485 
486  if (!s || !p) {
487  return false;
488  }
489  while (*s && *p) {
490  if (*s++ != *p++) {
491  return false;
492  }
493  }
494  return (*p == 0);
495 }
496 
497 static inline bool
498 ends_with(const char *s, const char *match, bool as_extension)
499 {
500  if (pcmk__str_empty(match)) {
501  return true;
502  } else if (s == NULL) {
503  return false;
504  } else {
505  size_t slen, mlen;
506 
507  /* Besides as_extension, we could also check
508  !strchr(&match[1], match[0]) but that would be inefficient.
509  */
510  if (as_extension) {
511  s = strrchr(s, match[0]);
512  return (s == NULL)? false : !strcmp(s, match);
513  }
514 
515  mlen = strlen(match);
516  slen = strlen(s);
517  return ((slen >= mlen) && !strcmp(s + slen - mlen, match));
518  }
519 }
520 
532 bool
533 pcmk__ends_with(const char *s, const char *match)
534 {
535  return ends_with(s, match, false);
536 }
537 
559 bool
560 pcmk__ends_with_ext(const char *s, const char *match)
561 {
562  return ends_with(s, match, true);
563 }
564 
584 static guint
585 pcmk__str_hash(gconstpointer v)
586 {
587  const signed char *p;
588  guint32 h = 0;
589 
590  for (p = v; *p != '\0'; p++)
591  h = (h << 5) - h + *p;
592 
593  return h;
594 }
595 
607 GHashTable *
608 pcmk__strkey_table(GDestroyNotify key_destroy_func,
609  GDestroyNotify value_destroy_func)
610 {
611  return g_hash_table_new_full(pcmk__str_hash, g_str_equal,
612  key_destroy_func, value_destroy_func);
613 }
614 
615 /* used with hash tables where case does not matter */
616 static gboolean
617 pcmk__strcase_equal(gconstpointer a, gconstpointer b)
618 {
619  return pcmk__str_eq((const char *)a, (const char *)b, pcmk__str_casei);
620 }
621 
622 static guint
623 pcmk__strcase_hash(gconstpointer v)
624 {
625  const signed char *p;
626  guint32 h = 0;
627 
628  for (p = v; *p != '\0'; p++)
629  h = (h << 5) - h + g_ascii_tolower(*p);
630 
631  return h;
632 }
633 
645 GHashTable *
646 pcmk__strikey_table(GDestroyNotify key_destroy_func,
647  GDestroyNotify value_destroy_func)
648 {
649  return g_hash_table_new_full(pcmk__strcase_hash, pcmk__strcase_equal,
650  key_destroy_func, value_destroy_func);
651 }
652 
653 static void
654 copy_str_table_entry(gpointer key, gpointer value, gpointer user_data)
655 {
656  if (key && value && user_data) {
657  g_hash_table_insert((GHashTable*)user_data, strdup(key), strdup(value));
658  }
659 }
660 
671 GHashTable *
672 pcmk__str_table_dup(GHashTable *old_table)
673 {
674  GHashTable *new_table = NULL;
675 
676  if (old_table) {
677  new_table = pcmk__strkey_table(free, free);
678  g_hash_table_foreach(old_table, copy_str_table_entry, new_table);
679  }
680  return new_table;
681 }
682 
699 void
700 pcmk__add_separated_word(GString **list, size_t init_size, const char *word,
701  const char *separator)
702 {
703  CRM_ASSERT(list != NULL);
704 
705  if (pcmk__str_empty(word)) {
706  return;
707  }
708 
709  if (*list == NULL) {
710  if (init_size > 0) {
711  *list = g_string_sized_new(init_size);
712  } else {
713  *list = g_string_new(NULL);
714  }
715  }
716 
717  if ((*list)->len == 0) {
718  // Don't add a separator before the first word in the list
719  separator = "";
720 
721  } else if (separator == NULL) {
722  // Default to space-separated
723  separator = " ";
724  }
725 
726  g_string_append(*list, separator);
727  g_string_append(*list, word);
728 }
729 
742 int
743 pcmk__compress(const char *data, unsigned int length, unsigned int max,
744  char **result, unsigned int *result_len)
745 {
746  int rc;
747  char *compressed = NULL;
748  char *uncompressed = strdup(data);
749 #ifdef CLOCK_MONOTONIC
750  struct timespec after_t;
751  struct timespec before_t;
752 #endif
753 
754  if (max == 0) {
755  max = (length * 1.01) + 601; // Size guaranteed to hold result
756  }
757 
758 #ifdef CLOCK_MONOTONIC
759  clock_gettime(CLOCK_MONOTONIC, &before_t);
760 #endif
761 
762  compressed = calloc((size_t) max, sizeof(char));
763  CRM_ASSERT(compressed);
764 
765  *result_len = max;
766  rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length,
768  rc = pcmk__bzlib2rc(rc);
769 
770  free(uncompressed);
771 
772  if (rc != pcmk_rc_ok) {
773  crm_err("Compression of %d bytes failed: %s " CRM_XS " rc=%d",
774  length, pcmk_rc_str(rc), rc);
775  free(compressed);
776  return rc;
777  }
778 
779 #ifdef CLOCK_MONOTONIC
780  clock_gettime(CLOCK_MONOTONIC, &after_t);
781 
782  crm_trace("Compressed %d bytes into %d (ratio %d:1) in %.0fms",
783  length, *result_len, length / (*result_len),
784  (after_t.tv_sec - before_t.tv_sec) * 1000 +
785  (after_t.tv_nsec - before_t.tv_nsec) / 1e6);
786 #else
787  crm_trace("Compressed %d bytes into %d (ratio %d:1)",
788  length, *result_len, length / (*result_len));
789 #endif
790 
791  *result = compressed;
792  return pcmk_rc_ok;
793 }
794 
795 char *
796 crm_strdup_printf(char const *format, ...)
797 {
798  va_list ap;
799  int len = 0;
800  char *string = NULL;
801 
802  va_start(ap, format);
803  len = vasprintf (&string, format, ap);
804  CRM_ASSERT(len > 0);
805  va_end(ap);
806  return string;
807 }
808 
809 int
810 pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
811 {
812  char *remainder = NULL;
813  int rc = pcmk_rc_ok;
814 
815  CRM_ASSERT(start != NULL && end != NULL);
816 
817  *start = PCMK__PARSE_INT_DEFAULT;
819 
820  crm_trace("Attempting to decode: [%s]", srcstring);
821  if (pcmk__str_eq(srcstring, "", pcmk__str_null_matches)) {
822  return ENODATA;
823  } else if (pcmk__str_eq(srcstring, "-", pcmk__str_none)) {
824  return pcmk_rc_bad_input;
825  }
826 
827  /* String starts with a dash, so this is either a range with
828  * no beginning or garbage.
829  * */
830  if (*srcstring == '-') {
831  int rc = scan_ll(srcstring+1, end, PCMK__PARSE_INT_DEFAULT, &remainder);
832 
833  if (rc != pcmk_rc_ok || *remainder != '\0') {
834  return pcmk_rc_bad_input;
835  } else {
836  return pcmk_rc_ok;
837  }
838  }
839 
840  rc = scan_ll(srcstring, start, PCMK__PARSE_INT_DEFAULT, &remainder);
841  if (rc != pcmk_rc_ok) {
842  return rc;
843  }
844 
845  if (*remainder && *remainder == '-') {
846  if (*(remainder+1)) {
847  char *more_remainder = NULL;
848  int rc = scan_ll(remainder+1, end, PCMK__PARSE_INT_DEFAULT,
849  &more_remainder);
850 
851  if (rc != pcmk_rc_ok) {
852  return rc;
853  } else if (*more_remainder != '\0') {
854  return pcmk_rc_bad_input;
855  }
856  }
857  } else if (*remainder && *remainder != '-') {
858  *start = PCMK__PARSE_INT_DEFAULT;
859  return pcmk_rc_bad_input;
860  } else {
861  /* The input string contained only one number. Set start and end
862  * to the same value and return pcmk_rc_ok. This gives the caller
863  * a way to tell this condition apart from a range with no end.
864  */
865  *end = *start;
866  }
867 
868  return pcmk_rc_ok;
869 }
870 
887 gboolean
888 pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
889 {
890  for (const GList *ele = lst; ele != NULL; ele = ele->next) {
891  if (pcmk__str_eq(s, ele->data, flags)) {
892  return TRUE;
893  }
894  }
895 
896  return FALSE;
897 }
898 
899 static bool
900 str_any_of(const char *s, va_list args, uint32_t flags)
901 {
902  if (s == NULL) {
904  }
905 
906  while (1) {
907  const char *ele = va_arg(args, const char *);
908 
909  if (ele == NULL) {
910  break;
911  } else if (pcmk__str_eq(s, ele, flags)) {
912  return true;
913  }
914  }
915 
916  return false;
917 }
918 
932 bool
933 pcmk__strcase_any_of(const char *s, ...)
934 {
935  va_list ap;
936  bool rc;
937 
938  va_start(ap, s);
939  rc = str_any_of(s, ap, pcmk__str_casei);
940  va_end(ap);
941  return rc;
942 }
943 
956 bool
957 pcmk__str_any_of(const char *s, ...)
958 {
959  va_list ap;
960  bool rc;
961 
962  va_start(ap, s);
963  rc = str_any_of(s, ap, pcmk__str_none);
964  va_end(ap);
965  return rc;
966 }
967 
979 bool
981 {
982  bool rc = false;
983  va_list ap;
984 
985  /*
986  * Passing a char to va_start() can generate compiler warnings,
987  * so ch is declared as an int.
988  */
989  va_start(ap, ch);
990 
991  while (1) {
992  const char *ele = va_arg(ap, const char *);
993 
994  if (ele == NULL) {
995  break;
996  } else if (strchr(ele, ch) != NULL) {
997  rc = true;
998  break;
999  }
1000  }
1001 
1002  va_end(ap);
1003  return rc;
1004 }
1005 
1022 int
1023 pcmk__numeric_strcasecmp(const char *s1, const char *s2)
1024 {
1025  CRM_ASSERT((s1 != NULL) && (s2 != NULL));
1026 
1027  while (*s1 && *s2) {
1028  if (isdigit(*s1) && isdigit(*s2)) {
1029  // If node names contain a number, sort numerically
1030 
1031  char *end1 = NULL;
1032  char *end2 = NULL;
1033  long num1 = strtol(s1, &end1, 10);
1034  long num2 = strtol(s2, &end2, 10);
1035 
1036  // allow ordering e.g. 007 > 7
1037  size_t len1 = end1 - s1;
1038  size_t len2 = end2 - s2;
1039 
1040  if (num1 < num2) {
1041  return -1;
1042  } else if (num1 > num2) {
1043  return 1;
1044  } else if (len1 < len2) {
1045  return -1;
1046  } else if (len1 > len2) {
1047  return 1;
1048  }
1049  s1 = end1;
1050  s2 = end2;
1051  } else {
1052  // Compare non-digits case-insensitively
1053  int lower1 = tolower(*s1);
1054  int lower2 = tolower(*s2);
1055 
1056  if (lower1 < lower2) {
1057  return -1;
1058  } else if (lower1 > lower2) {
1059  return 1;
1060  }
1061  ++s1;
1062  ++s2;
1063  }
1064  }
1065  if (!*s1 && *s2) {
1066  return -1;
1067  } else if (*s1 && !*s2) {
1068  return 1;
1069  }
1070  return 0;
1071 }
1072 
1107 int
1108 pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
1109 {
1110  /* If this flag is set, the second string is a regex. */
1112  regex_t r_patt;
1113  int reg_flags = REG_EXTENDED | REG_NOSUB;
1114  int regcomp_rc = 0;
1115  int rc = 0;
1116 
1117  if (s1 == NULL || s2 == NULL) {
1118  return 1;
1119  }
1120 
1122  reg_flags |= REG_ICASE;
1123  }
1124  regcomp_rc = regcomp(&r_patt, s2, reg_flags);
1125  if (regcomp_rc != 0) {
1126  rc = 1;
1127  crm_err("Bad regex '%s' for update: %s", s2, strerror(regcomp_rc));
1128  } else {
1129  rc = regexec(&r_patt, s1, 0, NULL, 0);
1130  regfree(&r_patt);
1131  if (rc != 0) {
1132  rc = 1;
1133  }
1134  }
1135  return rc;
1136  }
1137 
1138  /* If the strings are the same pointer, return 0 immediately. */
1139  if (s1 == s2) {
1140  return 0;
1141  }
1142 
1143  /* If this flag is set, return 0 if either (or both) of the input strings
1144  * are NULL. If neither one is NULL, we need to continue and compare
1145  * them normally.
1146  */
1148  if (s1 == NULL || s2 == NULL) {
1149  return 0;
1150  }
1151  }
1152 
1153  /* Handle the cases where one is NULL and the str_null_matches flag is not set.
1154  * A NULL string always sorts to the beginning.
1155  */
1156  if (s1 == NULL) {
1157  return -1;
1158  } else if (s2 == NULL) {
1159  return 1;
1160  }
1161 
1162  /* If this flag is set, return 0 if either (or both) of the input strings
1163  * are "*". If neither one is, we need to continue and compare them
1164  * normally.
1165  */
1167  if (strcmp(s1, "*") == 0 || strcmp(s2, "*") == 0) {
1168  return 0;
1169  }
1170  }
1171 
1173  return strcasecmp(s1, s2);
1174  } else {
1175  return strcmp(s1, s2);
1176  }
1177 }
1178 
1192 void
1193 pcmk__str_update(char **str, const char *value)
1194 {
1195  if ((str != NULL) && !pcmk__str_eq(*str, value, pcmk__str_none)) {
1196  free(*str);
1197  if (value == NULL) {
1198  *str = NULL;
1199  } else {
1200  *str = strdup(value);
1201  CRM_ASSERT(*str != NULL);
1202  }
1203  }
1204 }
1205 
1216 void
1217 pcmk__g_strcat(GString *buffer, ...)
1218 {
1219  va_list ap;
1220 
1221  CRM_ASSERT(buffer != NULL);
1222  va_start(ap, buffer);
1223 
1224  while (true) {
1225  const char *ele = va_arg(ap, const char *);
1226 
1227  if (ele == NULL) {
1228  break;
1229  }
1230  g_string_append(buffer, ele);
1231  }
1232  va_end(ap);
1233 }
1234 
1235 // Deprecated functions kept only for backward API compatibility
1236 // LCOV_EXCL_START
1237 
1238 #include <crm/common/util_compat.h>
1239 
1240 gboolean
1241 safe_str_neq(const char *a, const char *b)
1242 {
1243  if (a == b) {
1244  return FALSE;
1245 
1246  } else if (a == NULL || b == NULL) {
1247  return TRUE;
1248 
1249  } else if (strcasecmp(a, b) == 0) {
1250  return FALSE;
1251  }
1252  return TRUE;
1253 }
1254 
1255 gboolean
1256 crm_str_eq(const char *a, const char *b, gboolean use_case)
1257 {
1258  if (use_case) {
1259  return g_strcmp0(a, b) == 0;
1260 
1261  /* TODO - Figure out which calls, if any, really need to be case independent */
1262  } else if (a == b) {
1263  return TRUE;
1264 
1265  } else if (a == NULL || b == NULL) {
1266  /* shouldn't be comparing NULLs */
1267  return FALSE;
1268 
1269  } else if (strcasecmp(a, b) == 0) {
1270  return TRUE;
1271  }
1272  return FALSE;
1273 }
1274 
1275 char *
1276 crm_itoa_stack(int an_int, char *buffer, size_t len)
1277 {
1278  if (buffer != NULL) {
1279  snprintf(buffer, len, "%d", an_int);
1280  }
1281  return buffer;
1282 }
1283 
1284 guint
1285 g_str_hash_traditional(gconstpointer v)
1286 {
1287  return pcmk__str_hash(v);
1288 }
1289 
1290 gboolean
1291 crm_strcase_equal(gconstpointer a, gconstpointer b)
1292 {
1293  return pcmk__strcase_equal(a, b);
1294 }
1295 
1296 guint
1297 crm_strcase_hash(gconstpointer v)
1298 {
1299  return pcmk__strcase_hash(v);
1300 }
1301 
1302 GHashTable *
1303 crm_str_table_dup(GHashTable *old_table)
1304 {
1305  return pcmk__str_table_dup(old_table);
1306 }
1307 
1308 long long
1309 crm_parse_ll(const char *text, const char *default_text)
1310 {
1311  long long result;
1312 
1313  if (text == NULL) {
1314  text = default_text;
1315  if (text == NULL) {
1316  crm_err("No default conversion value supplied");
1317  errno = EINVAL;
1318  return PCMK__PARSE_INT_DEFAULT;
1319  }
1320  }
1321  scan_ll(text, &result, PCMK__PARSE_INT_DEFAULT, NULL);
1322  return result;
1323 }
1324 
1325 int
1326 crm_parse_int(const char *text, const char *default_text)
1327 {
1328  long long result = crm_parse_ll(text, default_text);
1329 
1330  if (result < INT_MIN) {
1331  // If errno is ERANGE, crm_parse_ll() has already logged a message
1332  if (errno != ERANGE) {
1333  crm_err("Conversion of %s was clipped: %lld", text, result);
1334  errno = ERANGE;
1335  }
1336  return INT_MIN;
1337 
1338  } else if (result > INT_MAX) {
1339  // If errno is ERANGE, crm_parse_ll() has already logged a message
1340  if (errno != ERANGE) {
1341  crm_err("Conversion of %s was clipped: %lld", text, result);
1342  errno = ERANGE;
1343  }
1344  return INT_MAX;
1345  }
1346 
1347  return (int) result;
1348 }
1349 
1350 char *
1352 {
1353  return pcmk__trim(str);
1354 }
1355 
1356 int
1357 pcmk_numeric_strcasecmp(const char *s1, const char *s2)
1358 {
1359  return pcmk__numeric_strcasecmp(s1, s2);
1360 }
1361 
1362 // LCOV_EXCL_STOP
1363 // End deprecated API
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
bool pcmk__starts_with(const char *str, const char *prefix)
Check whether a string starts with a certain sequence.
Definition: strings.c:481
char data[0]
Definition: cpg.c:55
char * crm_strdup_printf(char const *format,...)
Definition: strings.c:796
int pcmk_numeric_strcasecmp(const char *s1, const char *s2)
Definition: strings.c:1357
guint g_str_hash_traditional(gconstpointer v)
Definition: strings.c:1285
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:608
#define PCMK__PARSE_DBL_DEFAULT
int pcmk__scan_port(const char *text, int *port)
Definition: strings.c:161
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:1023
bool pcmk__str_any_of(const char *s,...)
Definition: strings.c:957
int pcmk__scan_double(const char *text, double *result, const char *default_text, char **end_text)
Definition: strings.c:199
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:501
#define CRM_BZ2_WORK
Definition: xml.h:47
void pcmk__str_update(char **str, const char *value)
Definition: strings.c:1193
#define PCMK__PARSE_INT_DEFAULT
#define crm_warn(fmt, args...)
Definition: logging.h:382
int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, guint *result)
Definition: strings.c:311
#define crm_debug(fmt, args...)
Definition: logging.h:386
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition: strings.c:700
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition: strings.c:810
int pcmk__compress(const char *data, unsigned int length, unsigned int max, char **result, unsigned int *result_len)
Definition: strings.c:743
#define crm_trace(fmt, args...)
Definition: logging.h:387
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:99
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition: strings.c:364
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:127
GHashTable * pcmk__str_table_dup(GHashTable *old_table)
Definition: strings.c:672
char * crm_itoa_stack(int an_int, char *buffer, size_t len)
Definition: strings.c:1276
Function and executable result codes.
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:424
#define CRM_XS
Definition: logging.h:56
char * crm_strip_trailing_newline(char *str)
Definition: strings.c:1351
int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags)
Definition: strings.c:1108
GHashTable * pcmk__strikey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:646
gboolean crm_is_true(const char *s)
Definition: strings.c:416
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:560
int pcmk__bzlib2rc(int bz2)
Map a bz2 return code to the most similar Pacemaker return code.
Definition: results.c:906
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:1256
int crm_parse_int(const char *text, const char *default_text)
Definition: strings.c:1326
#define ENODATA
Definition: portability.h:106
long long crm_parse_ll(const char *text, const char *default_text)
Definition: strings.c:1309
pcmk__action_result_t result
Definition: pcmk_fence.c:35
#define crm_err(fmt, args...)
Definition: logging.h:381
#define CRM_ASSERT(expr)
Definition: results.h:42
Deprecated Pacemaker utilities.
xmlNode * input
gboolean crm_strcase_equal(gconstpointer a, gconstpointer b)
Definition: strings.c:1291
void pcmk__g_strcat(GString *buffer,...)
Definition: strings.c:1217
gboolean pcmk__str_in_list(const gchar *s, const GList *lst, uint32_t flags)
Definition: strings.c:888
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:1241
guint crm_strcase_hash(gconstpointer v)
Definition: strings.c:1297
#define CRM_BZ2_BLOCKS
Definition: xml.h:46
GHashTable * crm_str_table_dup(GHashTable *old_table)
Definition: strings.c:1303
char * pcmk__trim(char *str)
Definition: strings.c:453
#define WHITESPACE
Definition: strings.c:350
bool pcmk__char_in_any_str(int ch,...)
Definition: strings.c:980
bool pcmk__ends_with(const char *s, const char *match)
Definition: strings.c:533
#define NUMCHARS
Definition: strings.c:346
uint64_t flags
Definition: remote.c:215
bool pcmk__strcase_any_of(const char *s,...)
Definition: strings.c:933